diff --git a/build.gradle b/build.gradle index b3fc14b5..fc987d12 100644 --- a/build.gradle +++ b/build.gradle @@ -8,11 +8,9 @@ ext { set('jwtVersion', '0.12.5') set('springdocOpenapiUIVersion', '2.3.0') set('mockitoVersion', '5.10.0') -} - -ext { set('jwtVersion', '0.12.5') set('springdocOpenapiUIVersion', '2.3.0') + set('springRetryVersion', '2.0.7') } group = 'com.dnd' @@ -33,6 +31,10 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' + /* spring retry */ + implementation("org.springframework.retry:spring-retry:${springRetryVersion}") + implementation("org.springframework:spring-aspects") + /* micrometer */ implementation 'io.micrometer:micrometer-registry-prometheus' diff --git a/src/main/java/com/dnd/namuiwiki/common/util/ListUtils.java b/src/main/java/com/dnd/namuiwiki/common/util/ListUtils.java new file mode 100644 index 00000000..f39ff173 --- /dev/null +++ b/src/main/java/com/dnd/namuiwiki/common/util/ListUtils.java @@ -0,0 +1,19 @@ +package com.dnd.namuiwiki.common.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +public class ListUtils { + public static List convertList(Object obj) { + if (obj.getClass().isArray()) { + return Arrays.asList((T[]) obj); + } else if (obj instanceof Collection) { + return new ArrayList<>((Collection) obj); + } else { + throw new ClassCastException("Object is not a List."); + } + + } +} diff --git a/src/main/java/com/dnd/namuiwiki/config/EventConfiguration.java b/src/main/java/com/dnd/namuiwiki/config/EventConfiguration.java index ffe40db8..9bfa4f60 100644 --- a/src/main/java/com/dnd/namuiwiki/config/EventConfiguration.java +++ b/src/main/java/com/dnd/namuiwiki/config/EventConfiguration.java @@ -1,5 +1,6 @@ package com.dnd.namuiwiki.config; +import com.dnd.namuiwiki.domain.dashboard.DashboardService; import com.dnd.namuiwiki.domain.statistic.StatisticsService; import com.dnd.namuiwiki.domain.survey.SurveyEventHandler; import lombok.RequiredArgsConstructor; @@ -10,10 +11,11 @@ @RequiredArgsConstructor public class EventConfiguration { private final StatisticsService statisticsService; + private final DashboardService dashboardService; @Bean public SurveyEventHandler surveyEventHandler() { - return new SurveyEventHandler(statisticsService); + return new SurveyEventHandler(statisticsService, dashboardService); } } diff --git a/src/main/java/com/dnd/namuiwiki/config/RetryConfiguration.java b/src/main/java/com/dnd/namuiwiki/config/RetryConfiguration.java new file mode 100644 index 00000000..72fd5194 --- /dev/null +++ b/src/main/java/com/dnd/namuiwiki/config/RetryConfiguration.java @@ -0,0 +1,9 @@ +package com.dnd.namuiwiki.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.retry.annotation.EnableRetry; + +@Configuration +@EnableRetry(proxyTargetClass=true) +public class RetryConfiguration { +} diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardCustomRepository.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardCustomRepository.java deleted file mode 100644 index 7e08bfd9..00000000 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardCustomRepository.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.dnd.namuiwiki.domain.dashboard; - -import com.dnd.namuiwiki.domain.survey.model.entity.Answer; -import com.dnd.namuiwiki.domain.survey.type.Period; -import com.dnd.namuiwiki.domain.survey.type.Relation; -import com.dnd.namuiwiki.domain.user.entity.User; -import com.dnd.namuiwiki.domain.wiki.WikiType; - -import java.util.List; - -public interface DashboardCustomRepository { - void updateDashboard(User owner, WikiType wikiType, Period period, Relation relation, List answers); -} diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardCustomRepositoryImpl.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardCustomRepositoryImpl.java deleted file mode 100644 index 3eaa1b06..00000000 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardCustomRepositoryImpl.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.dnd.namuiwiki.domain.dashboard; - -import com.dnd.namuiwiki.domain.dashboard.model.entity.Dashboard; -import com.dnd.namuiwiki.domain.survey.model.entity.Answer; -import com.dnd.namuiwiki.domain.survey.type.Period; -import com.dnd.namuiwiki.domain.survey.type.Relation; -import com.dnd.namuiwiki.domain.user.entity.User; -import com.dnd.namuiwiki.domain.wiki.WikiType; -import lombok.RequiredArgsConstructor; -import org.springframework.data.mongodb.core.FindAndModifyOptions; -import org.springframework.data.mongodb.core.MongoTemplate; -import org.springframework.data.mongodb.core.query.Criteria; -import org.springframework.data.mongodb.core.query.Query; -import org.springframework.data.mongodb.core.query.Update; - -import java.util.List; - -@RequiredArgsConstructor -public class DashboardCustomRepositoryImpl implements DashboardCustomRepository { - private final MongoTemplate mongoTemplate; - - @Override - public void updateDashboard(User owner, WikiType wikiType, Period period, Relation relation, List answers) { - Query query = Query.query(Criteria.where("user").is(owner) - .and("wikiType").is(wikiType) - .and("period").is(period) - .and("relation").is(relation)); - - Update update = new Update(); - - FindAndModifyOptions options = new FindAndModifyOptions().returnNew(true); - - for (Answer answer : answers) { - update.inc(String.format("statistics.statistics.%s.totalCount", answer.getQuestion().getId())); - - if (answer.getType().isOption()) { - update.inc(String.format("statistics.statistics.%s.legends.%s.count", - answer.getQuestion().getId(), answer.getAnswer().toString())); - } - } - - mongoTemplate.findAndModify(query, update, options, Dashboard.class); - } - -} diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardInternalProxyService.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardInternalProxyService.java new file mode 100644 index 00000000..198fc6d4 --- /dev/null +++ b/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardInternalProxyService.java @@ -0,0 +1,70 @@ +package com.dnd.namuiwiki.domain.dashboard; + + +import com.dnd.namuiwiki.domain.dashboard.model.entity.Dashboard; +import com.dnd.namuiwiki.domain.statistic.model.Statistics; +import com.dnd.namuiwiki.domain.survey.model.entity.Answer; +import com.dnd.namuiwiki.domain.survey.type.Period; +import com.dnd.namuiwiki.domain.survey.type.Relation; +import com.dnd.namuiwiki.domain.user.entity.User; +import com.dnd.namuiwiki.domain.wiki.WikiType; +import com.dnd.namuiwiki.external.DiscordClient; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.dao.OptimisticLockingFailureException; +import org.springframework.retry.annotation.Backoff; +import org.springframework.retry.annotation.Recover; +import org.springframework.retry.annotation.Retryable; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Slf4j +@RequiredArgsConstructor +@Service +public class DashboardInternalProxyService { + private final DashboardRepository dashboardRepository; + private final DiscordClient discordClient; + + @Retryable( + retryFor = {OptimisticLockingFailureException.class}, + maxAttempts = 3, + backoff = @Backoff(delay = 30 * 1000L) + ) + public void updateDashboard(User owner, WikiType wikiType, Period period, Relation relation, List answers) { + insertDashboardIfNotExist(owner, wikiType, period, relation, answers); + Dashboard dashboard = dashboardRepository.findByUserAndWikiTypeAndPeriodAndRelation(owner, wikiType, period, relation) + .orElseGet(() -> { + Dashboard d = Dashboard.builder() + .user(owner) + .wikiType(wikiType) + .period(period) + .relation(relation) + .statistics(Statistics.from(answers.stream().map(Answer::getQuestion).toList())) + .build(); + return dashboardRepository.save(d); + }); + + dashboard.updateStatistics(answers); + dashboardRepository.save(dashboard); + } + + @Recover + private void recoverForUpdateDashboard(Exception e) { + log.error("Failed to update dashboard", e); + discordClient.sendMessage(e.toString()); + } + + private void insertDashboardIfNotExist(User owner, WikiType wikiType, Period period, Relation relation, List answers) { + if (!dashboardRepository.existsByUserAndWikiTypeAndPeriodAndRelation(owner, wikiType, period, relation)) { + dashboardRepository.save(Dashboard.builder() + .user(owner) + .wikiType(wikiType) + .period(period) + .relation(relation) + .statistics(Statistics.from(answers.stream().map(Answer::getQuestion).toList())) + .build()); + } + } + +} diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardRepository.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardRepository.java index 5043f4b8..3d71767c 100644 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardRepository.java +++ b/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardRepository.java @@ -9,7 +9,7 @@ import java.util.Optional; -public interface DashboardRepository extends MongoRepository, DashboardCustomRepository { +public interface DashboardRepository extends MongoRepository { Optional findByUserAndWikiTypeAndPeriodAndRelation(User user, WikiType wikiType, Period period, Relation relation); boolean existsByUserAndWikiTypeAndPeriodAndRelation(User user, WikiType wikiType, Period period, Relation relation); diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardService.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardService.java index 477960ed..b954cbc3 100644 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardService.java +++ b/src/main/java/com/dnd/namuiwiki/domain/dashboard/DashboardService.java @@ -18,6 +18,7 @@ import com.dnd.namuiwiki.domain.statistic.model.BorrowingLimitEntireStatistic; import com.dnd.namuiwiki.domain.statistic.model.Statistics; import com.dnd.namuiwiki.domain.statistic.model.entity.PopulationStatistic; +import com.dnd.namuiwiki.domain.survey.model.entity.Survey; import com.dnd.namuiwiki.domain.survey.type.Period; import com.dnd.namuiwiki.domain.survey.type.Relation; import com.dnd.namuiwiki.domain.user.UserRepository; @@ -26,14 +27,17 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.Comparator; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor public class DashboardService { private final UserRepository userRepository; private final DashboardRepository dashboardRepository; + private final DashboardInternalProxyService dashboardInternalProxyService; private final StatisticsService statisticsService; private final QuestionRepository questionRepository; @@ -45,6 +49,21 @@ public DashboardDto getDashboard(TokenUserInfoDto tokenUserInfoDto, WikiType wik return dashboard.map(value -> convertToDto(value.getStatistics(), period, relation)).orElse(null); } + public void updateDashboards(Survey survey) { + User owner = survey.getOwner(); + WikiType wikiType = survey.getWikiType(); + Period period = survey.getPeriod(); + Relation relation = survey.getRelation(); + + var answers = survey.getAnswers().stream() + .filter(answer -> answer.getQuestion().getDashboardType().getStatisticsType().isNotNone()) + .toList(); + + dashboardInternalProxyService.updateDashboard(owner, wikiType, null, null, answers); + dashboardInternalProxyService.updateDashboard(owner, wikiType, period, null, answers); + dashboardInternalProxyService.updateDashboard(owner, wikiType, null, relation, answers); + } + private DashboardDto convertToDto(Statistics statistics, Period period, Relation relation) { List questions = questionRepository.findAll(); @@ -67,7 +86,7 @@ private DashboardDto convertToDto(Statistics statistics, Period period, Relation } else { throw new ApplicationErrorException(ApplicationErrorType.INVALID_DASHBOARD_TYPE); } - }).toList(); + }).sorted(Comparator.comparingLong(DashboardComponentV2::getDashboardOrder)).collect(Collectors.toList()); return new DashboardDto(dashboardComponents); } diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/AverageDashboardComponent.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/AverageDashboardComponent.java index 92c8d3c0..052774b3 100644 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/AverageDashboardComponent.java +++ b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/AverageDashboardComponent.java @@ -13,7 +13,7 @@ public class AverageDashboardComponent extends DashboardComponentV2 { private final long entireAverage; public AverageDashboardComponent(DashboardType dashboardType, Statistic statistic, long entireAverage, Question question) { - super(dashboardType, question.getId(), question.getTitle(), question.getName()); + super(dashboardType, question.getId(), question.getTitle(), question.getName(), question.getDashboardOrder()); if (!dashboardType.isAverageType()) { throw new IllegalArgumentException("Required AverageDashboardType"); diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/BestWorthDashboardComponent.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/BestWorthDashboardComponent.java deleted file mode 100644 index e79f1674..00000000 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/BestWorthDashboardComponent.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.dnd.namuiwiki.domain.dashboard.model; - -import com.dnd.namuiwiki.domain.dashboard.model.dto.RatioDto; -import com.dnd.namuiwiki.domain.dashboard.type.DashboardType; -import com.dnd.namuiwiki.domain.statistic.model.Legend; -import com.dnd.namuiwiki.domain.statistic.model.RatioStatistic; -import com.dnd.namuiwiki.domain.statistic.model.Statistics; -import lombok.Getter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -@Getter -public class BestWorthDashboardComponent extends DashboardComponent { - private final String questionId; - private List rank; - - public BestWorthDashboardComponent(Statistics statistics, String questionId) { - super(DashboardType.BEST_WORTH); - this.questionId = questionId; - - calculate(statistics); - } - - @Override - public void calculate(Statistics statistics) { - RatioStatistic bestWorth = (RatioStatistic) statistics.getStatisticsByDashboardType(this.dashboardType).get(0); - Long totalCount = bestWorth.getTotalCount(); - - List legends = bestWorth.getLegends(); - int optionPercentage = 0; - - this.rank = new ArrayList<>(); - - for (Legend legend : legends) { - if (totalCount == 0) { - rank.add(new RatioDto(legend.getText(), 0)); - continue; - } - int percentage = (int) (legend.getCount() * 100 / totalCount); - optionPercentage += percentage; - rank.add(new RatioDto(legend.getText(), percentage)); - } - - // 직접입력인 legend 인거 찾아서 새로 업데이트 - updateManualLegendPercentage(optionPercentage); - } - - private void updateManualLegendPercentage(int optionPercentage) { - Optional manualLegend = this.rank.stream() - .filter(legend -> legend.getLegend().contains("직접 입력")) - .findFirst(); - manualLegend.ifPresent(ratioDto -> ratioDto.setPercentage(100 - optionPercentage)); - } - -} diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/BinaryDashboardComponent.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/BinaryDashboardComponent.java index 566e2e25..07d07571 100644 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/BinaryDashboardComponent.java +++ b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/BinaryDashboardComponent.java @@ -14,7 +14,7 @@ public class BinaryDashboardComponent extends DashboardComponentV2 { private final int percentage; public BinaryDashboardComponent(Statistic statistic, Question question) { - super(DashboardType.BINARY, question.getId(), question.getTitle(), question.getName()); + super(DashboardType.BINARY, question.getId(), question.getTitle(), question.getName(), question.getDashboardOrder()); if (!dashboardType.isBinaryType()) { throw new IllegalArgumentException("Required BinaryDashboardType"); diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/CharacterDashboardComponent.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/CharacterDashboardComponent.java deleted file mode 100644 index ecfc8a5d..00000000 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/CharacterDashboardComponent.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.dnd.namuiwiki.domain.dashboard.model; - -import com.dnd.namuiwiki.common.exception.ApplicationErrorException; -import com.dnd.namuiwiki.common.exception.ApplicationErrorType; -import com.dnd.namuiwiki.domain.dashboard.type.DashboardType; -import com.dnd.namuiwiki.domain.question.type.QuestionName; -import com.dnd.namuiwiki.domain.statistic.model.Legend; -import com.dnd.namuiwiki.domain.statistic.model.RatioStatistic; -import com.dnd.namuiwiki.domain.statistic.model.Statistic; -import com.dnd.namuiwiki.domain.statistic.model.Statistics; -import lombok.Getter; -import lombok.RequiredArgsConstructor; - -import java.util.ArrayList; -import java.util.List; - -@Getter -public class CharacterDashboardComponent extends DashboardComponent { - private List characters; - - public CharacterDashboardComponent(Statistics statistics) { - super(DashboardType.CHARACTER); - this.characters = new ArrayList<>(); - calculate(statistics); - } - - @Override - public void calculate(Statistics statistics) { - List character = statistics.getStatisticsByDashboardType(this.dashboardType); - this.characters = character.stream().map(Character::from).toList(); - } - - @RequiredArgsConstructor - @Getter - static class Character { - private final String name; - private final boolean value; - private final String questionId; - - private static Character from(Statistic statistic) { - RatioStatistic ratioStatistic = (RatioStatistic) statistic; - List legends = ratioStatistic.getLegends(); - QuestionName questionName = ratioStatistic.getQuestionName(); - - return new Character( - questionName.name(), - getCharacterRatioResult(legends), - statistic.getQuestionId() - ); - } - - private static Legend getLegendByValue(List legends, boolean value) { - return legends.stream() - .filter(legend -> value == (boolean) legend.getValue()) - .findFirst() - .orElseThrow(() -> new ApplicationErrorException(ApplicationErrorType.INTERNAL_ERROR)); - } - - private static boolean getCharacterRatioResult(List legends) { - return getLegendByValue(legends, true).getCount() >= - getLegendByValue(legends, false).getCount(); - } - } - -} diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/DashboardComponent.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/DashboardComponent.java deleted file mode 100644 index 5f018d02..00000000 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/DashboardComponent.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.dnd.namuiwiki.domain.dashboard.model; - -import com.dnd.namuiwiki.domain.dashboard.type.DashboardType; -import com.dnd.namuiwiki.domain.statistic.model.Statistics; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.AllArgsConstructor; -import lombok.Getter; - -@Schema(description = "대시보드 타입", oneOf = { - BestWorthDashboardComponent.class, - CharacterDashboardComponent.class, - HappyDashboardComponent.class, - MoneyDashboardComponent.class, - SadDashboardComponent.class}) -@AllArgsConstructor -@Getter -public abstract class DashboardComponent { - - protected final DashboardType dashboardType; - - public abstract void calculate(Statistics statistics); - -} diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/DashboardComponentV2.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/DashboardComponentV2.java index dc18d696..3b529f42 100644 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/DashboardComponentV2.java +++ b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/DashboardComponentV2.java @@ -17,4 +17,5 @@ public abstract class DashboardComponentV2 { private final String questionId; private final String questionTitle; private final QuestionName questionName; + private final Long dashboardOrder; } diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/HappyDashboardComponent.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/HappyDashboardComponent.java deleted file mode 100644 index bd025e08..00000000 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/HappyDashboardComponent.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.dnd.namuiwiki.domain.dashboard.model; - -import com.dnd.namuiwiki.domain.dashboard.model.dto.RatioDto; -import com.dnd.namuiwiki.domain.dashboard.type.DashboardType; -import com.dnd.namuiwiki.domain.statistic.model.Legend; -import com.dnd.namuiwiki.domain.statistic.model.RatioStatistic; -import com.dnd.namuiwiki.domain.statistic.model.Statistics; -import lombok.Getter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -@Getter -public class HappyDashboardComponent extends DashboardComponent { - private List rank; - private final String questionId; - - public HappyDashboardComponent(Statistics statistics, String questionId) { - super(DashboardType.HAPPY); - this.questionId = questionId; - - calculate(statistics); - } - - @Override - public void calculate(Statistics statistics) { - RatioStatistic happy = (RatioStatistic) statistics.getStatisticsByDashboardType(this.dashboardType).get(0); - Long totalCount = happy.getTotalCount(); - - List legends = happy.getLegends(); - int optionPercentage = 0; - - this.rank = new ArrayList<>(); - - for (Legend legend : legends) { - if (totalCount == 0) { - rank.add(new RatioDto(legend.getText(), 0)); - continue; - } - int percentage = (int) (legend.getCount() * 100 / totalCount); - optionPercentage += percentage; - rank.add(new RatioDto(legend.getText(), percentage)); - } - - // 직접입력인 legend 인거 찾아서 새로 업데이트 - updateManualLegendPercentage(optionPercentage); - } - - private void updateManualLegendPercentage(int optionPercentage) { - Optional manualLegend = this.rank.stream() - .filter(legend -> legend.getLegend().contains("직접 입력")) - .findFirst(); - manualLegend.ifPresent(ratioDto -> ratioDto.setPercentage(100 - optionPercentage)); - } - -} diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/MoneyDashboardComponent.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/MoneyDashboardComponent.java deleted file mode 100644 index 6da4ab6d..00000000 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/MoneyDashboardComponent.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.dnd.namuiwiki.domain.dashboard.model; - -import com.dnd.namuiwiki.domain.dashboard.type.DashboardType; -import com.dnd.namuiwiki.domain.statistic.model.AverageStatistic; -import com.dnd.namuiwiki.domain.statistic.model.Statistics; -import lombok.Getter; - -@Getter -public class MoneyDashboardComponent extends DashboardComponent { - private final String questionId; - private long peopleCount; - private long average; - private long entireAverage; - - public MoneyDashboardComponent(Statistics statistics, long entireAverage, String questionId) { - super(DashboardType.MONEY); - this.entireAverage = entireAverage; - this.questionId = questionId; - - calculate(statistics); - } - - @Override - public void calculate(Statistics statistics) { - AverageStatistic money = (AverageStatistic) statistics.getStatisticsByDashboardType(this.dashboardType).get(0); - this.peopleCount = money.getTotalCount(); - this.average = money.getAverage(); - } - -} diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/RankDashboardComponent.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/RankDashboardComponent.java index d8141295..10dee5c2 100644 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/RankDashboardComponent.java +++ b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/RankDashboardComponent.java @@ -9,13 +9,14 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; @Getter public class RankDashboardComponent extends DashboardComponentV2 { private final List rank; public RankDashboardComponent(DashboardType dashboardType, Statistic statistic, Question question) { - super(dashboardType, question.getId(), question.getTitle(), question.getName()); + super(dashboardType, question.getId(), question.getTitle(), question.getName(), question.getDashboardOrder()); if (!dashboardType.isRankType()) { throw new IllegalArgumentException("Required RankDashboardType"); @@ -25,8 +26,20 @@ public RankDashboardComponent(DashboardType dashboardType, Statistic statistic, } private List getRank(RankStatistic rankStatistic) { - List rankList = new ArrayList<>(rankStatistic.getRanks().values().stream().toList()); - rankList.sort((o1, o2) -> Double.compare(o2.getPercentage(), o1.getPercentage())); - return rankList; + AtomicInteger totalPoint = new AtomicInteger(); + rankStatistic.getRanks().forEach((optionId, rank) -> { + totalPoint.addAndGet(rank.getPoint()); + }); + + List rankDtoList = new ArrayList<>(); + rankStatistic.getRanks().forEach((optionId, rank) -> { + if (totalPoint.get() == 0) { + return; + } + rankDtoList.add(new RankDto(rank.getText(), rank.getPoint(), (rank.getPoint() * 100 / totalPoint.get()))); + }); + + rankDtoList.sort((o1, o2) -> Integer.compare(o2.getPercentage(), o1.getPercentage())); + return rankDtoList; } } diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/RatioDashboardComponent.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/RatioDashboardComponent.java index 8df20eb4..a42773f7 100644 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/RatioDashboardComponent.java +++ b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/RatioDashboardComponent.java @@ -17,7 +17,7 @@ public class RatioDashboardComponent extends DashboardComponentV2 { private List rank; public RatioDashboardComponent(DashboardType dashboardType, Statistic statistic, Question question) { - super(dashboardType, question.getId(), question.getTitle(), question.getName()); + super(dashboardType, question.getId(), question.getTitle(), question.getName(), question.getDashboardOrder()); if (!dashboardType.isRatioType()) { throw new IllegalArgumentException("Required RatioDashboardType"); diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/SadDashboardComponent.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/SadDashboardComponent.java deleted file mode 100644 index c296d80b..00000000 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/SadDashboardComponent.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.dnd.namuiwiki.domain.dashboard.model; - -import com.dnd.namuiwiki.domain.dashboard.model.dto.RatioDto; -import com.dnd.namuiwiki.domain.dashboard.type.DashboardType; -import com.dnd.namuiwiki.domain.statistic.model.Legend; -import com.dnd.namuiwiki.domain.statistic.model.RatioStatistic; -import com.dnd.namuiwiki.domain.statistic.model.Statistics; -import lombok.Getter; - -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -@Getter -public class SadDashboardComponent extends DashboardComponent { - private final String questionId; - private List rank; - - public SadDashboardComponent(Statistics statistics, String questionId) { - super(DashboardType.SAD); - this.questionId = questionId; - - calculate(statistics); - } - - @Override - public void calculate(Statistics statistics) { - RatioStatistic sad = (RatioStatistic) statistics.getStatisticsByDashboardType(this.dashboardType).get(0); - Long totalCount = sad.getTotalCount(); - - List legends = sad.getLegends(); - int optionPercentage = 0; - - this.rank = new ArrayList<>(); - - for (Legend legend : legends) { - if (totalCount == 0) { - rank.add(new RatioDto(legend.getText(), 0)); - continue; - } - int percentage = (int) (legend.getCount() * 100 / totalCount); - optionPercentage += percentage; - rank.add(new RatioDto(legend.getText(), percentage)); - } - - // 직접입력인 legend 인거 찾아서 새로 업데이트 - updateManualLegendPercentage(optionPercentage); - } - - private void updateManualLegendPercentage(int optionPercentage) { - Optional manualLegend = this.rank.stream() - .filter(legend -> legend.getLegend().contains("직접 입력")) - .findFirst(); - manualLegend.ifPresent(ratioDto -> ratioDto.setPercentage(100 - optionPercentage)); - } - -} diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/dto/RankDto.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/dto/RankDto.java index 2f0a6bef..8ee974b9 100644 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/dto/RankDto.java +++ b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/dto/RankDto.java @@ -1,25 +1,15 @@ package com.dnd.namuiwiki.domain.dashboard.model.dto; -import com.dnd.namuiwiki.domain.option.entity.Option; import lombok.AllArgsConstructor; import lombok.Getter; -@Getter @AllArgsConstructor +@Getter public class RankDto { private String text; private int point; private int percentage; - - public static RankDto optionToRankDto(Option option) { - return new RankDto(option.getText(), 0, 0); - } - - public void addPoint(int point) { - this.point += point; - } - - public void updatePercentage(int percentage) { + public void setPercentage(int percentage) { this.percentage = percentage; } } diff --git a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/entity/Dashboard.java b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/entity/Dashboard.java index 366abeb6..0b5b5afb 100644 --- a/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/entity/Dashboard.java +++ b/src/main/java/com/dnd/namuiwiki/domain/dashboard/model/entity/Dashboard.java @@ -10,6 +10,7 @@ import lombok.Builder; import lombok.Getter; import org.springframework.data.annotation.Id; +import org.springframework.data.annotation.Version; import org.springframework.data.mongodb.core.index.CompoundIndex; import org.springframework.data.mongodb.core.mapping.Document; import org.springframework.data.mongodb.core.mapping.DocumentReference; @@ -22,6 +23,9 @@ @CompoundIndex(def = "{ 'user': 1, 'period': 1, 'relation': 1, 'wikiType': 1 }", unique = true) public class Dashboard extends BaseTimeEntity { + @Version + private Long version; + @Id private String id; diff --git a/src/main/java/com/dnd/namuiwiki/domain/question/entity/Question.java b/src/main/java/com/dnd/namuiwiki/domain/question/entity/Question.java index f6aeb9d6..45d16fac 100644 --- a/src/main/java/com/dnd/namuiwiki/domain/question/entity/Question.java +++ b/src/main/java/com/dnd/namuiwiki/domain/question/entity/Question.java @@ -1,5 +1,7 @@ package com.dnd.namuiwiki.domain.question.entity; +import com.dnd.namuiwiki.common.exception.ApplicationErrorException; +import com.dnd.namuiwiki.common.exception.ApplicationErrorType; import com.dnd.namuiwiki.common.model.BaseTimeEntity; import com.dnd.namuiwiki.domain.dashboard.type.DashboardType; import com.dnd.namuiwiki.domain.option.entity.Option; @@ -13,7 +15,6 @@ import org.springframework.data.mongodb.core.mapping.DocumentReference; import java.util.Map; -import java.util.Optional; @Getter @Builder @@ -27,14 +28,18 @@ public class Question extends BaseTimeEntity { private QuestionType type; private DashboardType dashboardType; private Long surveyOrder; + private Long dashboardOrder; private WikiType wikiType; private boolean reasonRequired; @DocumentReference(collection = "options") private Map options; - public Optional