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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ docker-compose -f ./docker/monitoring-compose.yml up
Root
├── apps ( spring-applications )
│ ├── 📦 commerce-api
│ ├── 📦 commerce-batch
│ ├── 📦 commerce-streamer
│ └── 📦 pg-simulator
├── modules ( reusable-configurations )
Expand Down
2 changes: 2 additions & 0 deletions apps/commerce-api/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ dependencies {
// UUIDv7
implementation("com.fasterxml.uuid:java-uuid-generator")

implementation("org.threeten:threeten-extra")

// querydsl
annotationProcessor("com.querydsl:querydsl-apt::jakarta")
annotationProcessor("jakarta.persistence:jakarta.persistence-api")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ public class RankingFacade {
private final RankingService rankingService;
private final ProductService productService;

public RankingOutput.SearchRankings searchRankings(RankingInput.SearchRankings input) {
RankingCommand.SearchRanks rankCommand = new RankingCommand.SearchRanks(input.date(), input.page(), input.size());
RankingResult.SearchRanks ranks = rankingService.searchRanks(rankCommand);
public RankingOutput.SearchDaily searchDaily(RankingInput.SearchDaily input) {
RankingResult.SearchDaily ranks = rankingService.searchDaily(new RankingCommand.SearchDaily(
input.date(),
input.page(),
input.size()
));

if (CollectionUtils.isEmpty(ranks.items())) {
return RankingOutput.SearchRankings.empty(ranks);
return RankingOutput.SearchDaily.empty(ranks);
}

List<ProductResult.GetProductDetail> details = ranks.items()
Expand All @@ -35,7 +38,49 @@ public RankingOutput.SearchRankings searchRankings(RankingInput.SearchRankings i
)
.toList();

return RankingOutput.SearchRankings.from(ranks, details);
return RankingOutput.SearchDaily.from(ranks, details);
}

public RankingOutput.SearchWeekly searchWeekly(RankingInput.SearchWeekly input) {
RankingResult.SearchWeekly ranks = rankingService.searchWeekly(new RankingCommand.SearchWeekly(
input.yearWeek(),
input.page(),
input.size()
));

if (CollectionUtils.isEmpty(ranks.items())) {
return RankingOutput.SearchWeekly.empty(ranks);
}

List<ProductResult.GetProductDetail> details = ranks.items()
.stream()
.map(rank -> productService.getProductDetail(rank.productId())
.orElseThrow(() -> new BusinessException(CommonErrorType.NOT_FOUND))
)
.toList();

return RankingOutput.SearchWeekly.from(ranks, details);
}

public RankingOutput.SearchMonthly searchMonthly(RankingInput.SearchMonthly input) {
RankingResult.SearchMonthly ranks = rankingService.searchMonthly(new RankingCommand.SearchMonthly(
input.yearMonth(),
input.page(),
input.size()
));

if (CollectionUtils.isEmpty(ranks.items())) {
return RankingOutput.SearchMonthly.empty(ranks);
}

List<ProductResult.GetProductDetail> details = ranks.items()
.stream()
.map(rank -> productService.getProductDetail(rank.productId())
.orElseThrow(() -> new BusinessException(CommonErrorType.NOT_FOUND))
)
.toList();

return RankingOutput.SearchMonthly.from(ranks, details);
}

}
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
package com.loopers.application.ranking;

import org.threeten.extra.YearWeek;

import java.time.LocalDate;
import java.time.YearMonth;

public record RankingInput() {

public record SearchRankings(
public record SearchDaily(
LocalDate date,
Integer page,
Integer size
) {
}

// -------------------------------------------------------------------------------------------------

public record SearchWeekly(
YearWeek yearWeek,
Integer page,
Integer size
) {
}

// -------------------------------------------------------------------------------------------------

public record SearchMonthly(
YearMonth yearMonth,
Integer page,
Integer size
) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,95 @@

public record RankingOutput() {

public record SearchRankings(
public record SearchDaily(
Integer totalPages,
Long totalItems,
Integer page,
Integer size,
List<Item> items
) {
public static SearchRankings empty(RankingResult.SearchRanks result) {
return new SearchRankings(result.totalPages(), result.totalItems(), result.page(), result.size(), List.of());
public static SearchDaily empty(RankingResult.SearchDaily result) {
return new SearchDaily(result.totalPages(), result.totalItems(), result.page(), result.size(), List.of());
}

public static SearchRankings from(RankingResult.SearchRanks ranks, List<ProductResult.GetProductDetail> details) {
return new SearchRankings(
public static SearchDaily from(RankingResult.SearchDaily ranks, List<ProductResult.GetProductDetail> details) {
return new SearchDaily(
ranks.totalPages(),
ranks.totalItems(),
ranks.page(),
ranks.size(),
details.stream()
.map(detail -> new Item(
detail.productId(),
detail.productName(),
detail.basePrice(),
detail.likeCount(),
detail.brandId(),
detail.brandName()
))
.toList()
details.stream().map(Item::from).toList()
);
}
}

// -------------------------------------------------------------------------------------------------

public record SearchWeekly(
Integer totalPages,
Long totalItems,
Integer page,
Integer size,
List<Item> items
) {
public static SearchWeekly empty(RankingResult.SearchWeekly result) {
return new SearchWeekly(result.totalPages(), result.totalItems(), result.page(), result.size(), List.of());
}

public static SearchWeekly from(RankingResult.SearchWeekly ranks, List<ProductResult.GetProductDetail> details) {
return new SearchWeekly(
ranks.totalPages(),
ranks.totalItems(),
ranks.page(),
ranks.size(),
details.stream().map(Item::from).toList()
);
}
}

// -------------------------------------------------------------------------------------------------

public record Item(
Long productId,
String productName,
Integer basePrice,
Long likeCount,
Long brandId,
String brandName
) {
public record SearchMonthly(
Integer totalPages,
Long totalItems,
Integer page,
Integer size,
List<Item> items
) {
public static SearchMonthly empty(RankingResult.SearchMonthly result) {
return new SearchMonthly(result.totalPages(), result.totalItems(), result.page(), result.size(), List.of());
}

public static SearchMonthly from(RankingResult.SearchMonthly ranks, List<ProductResult.GetProductDetail> details) {
return new SearchMonthly(
ranks.totalPages(),
ranks.totalItems(),
ranks.page(),
ranks.size(),
details.stream().map(Item::from).toList()
);
}
}

// -------------------------------------------------------------------------------------------------

public record Item(
Long productId,
String productName,
Integer basePrice,
Long likeCount,
Long brandId,
String brandName
) {
public static Item from(ProductResult.GetProductDetail detail) {
return new Item(
detail.productId(),
detail.productName(),
detail.basePrice(),
detail.likeCount(),
detail.brandId(),
detail.brandName()
);
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.loopers.config.web;

import com.loopers.config.web.converter.YearWeekFormatter;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addFormatters(FormatterRegistry registry) {
registry.addFormatter(new YearWeekFormatter());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.loopers.config.web.converter;

import org.springframework.format.Formatter;
import org.threeten.extra.YearWeek;

import java.util.Locale;

public class YearWeekFormatter implements Formatter<YearWeek> {

@Override
public YearWeek parse(String text, Locale locale) {
return YearWeek.parse(text);
}

@Override
public String print(YearWeek object, Locale locale) {
return object.toString();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.loopers.domain.ranking;

import com.loopers.domain.BaseEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.YearMonth;

@Getter
@Entity
@Table(
name = "product_rankings_monthly",
uniqueConstraints = @UniqueConstraint(columnNames = {"year_month", "ref_product_id"}),
indexes = @Index(columnList = "year_month, ref_product_id, rank")
)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ProductRankingMonthly extends BaseEntity {

/**
* 아이디
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "product_ranking_weekly_id", nullable = false, updatable = false)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix incorrect column name for the monthly ranking entity's primary key.

The column name product_ranking_weekly_id appears to be a copy-paste error from the weekly entity. It should be product_ranking_monthly_id for consistency.

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
-   @Column(name = "product_ranking_weekly_id", nullable = false, updatable = false)
+   @Column(name = "product_ranking_monthly_id", nullable = false, updatable = false)
    private Long id;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Column(name = "product_ranking_weekly_id", nullable = false, updatable = false)
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "product_ranking_monthly_id", nullable = false, updatable = false)
private Long id;
🤖 Prompt for AI Agents
In
apps/commerce-api/src/main/java/com/loopers/domain/ranking/ProductRankingMonthly.java
around line 26, the JPA @Column annotation uses the wrong column name
"product_ranking_weekly_id" (copy-paste error); change it to
"product_ranking_monthly_id" and keep nullable=false, updatable=false as-is so
the primary key column name matches the monthly entity.

private Long id;

/**
* 한 해의 주차
*/
@Column(name = "year_month", nullable = false, updatable = false)
private YearMonth yearMonth;

/**
* 순위
*/
@Column(name = "rank", nullable = false, updatable = false)
private Integer rank;

// -------------------------------------------------------------------------------------------------

/**
* 상품 아이디
*/
@Column(name = "ref_product_id", nullable = false, updatable = false)
private Long productId;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.loopers.domain.ranking;

import com.loopers.domain.BaseEntity;
import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.threeten.extra.YearWeek;

@Getter
@Entity
@Table(
name = "product_rankings_weekly",
uniqueConstraints = @UniqueConstraint(columnNames = {"year_week", "ref_product_id"}),
indexes = @Index(columnList = "year_week, ref_product_id, rank")
)
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class ProductRankingWeekly extends BaseEntity {

/**
* 아이디
*/
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "product_ranking_weekly_id", nullable = false, updatable = false)
private Long id;

/**
* 한 해의 주차
*/
@Column(name = "year_week", nullable = false, updatable = false)
private YearWeek yearWeek;

/**
* 순위
*/
@Column(name = "rank", nullable = false, updatable = false)
private Integer rank;

// -------------------------------------------------------------------------------------------------

/**
* 상품 아이디
*/
@Column(name = "ref_product_id", nullable = false, updatable = false)
private Long productId;

}
Loading