Skip to content

Commit

Permalink
Merge pull request #196 from Team-Shaka/feature/194
Browse files Browse the repository at this point in the history
Feature/194 : 브리핑 조회수 증가
  • Loading branch information
swa07016 authored Mar 9, 2024
2 parents 0cfd1ba + 46a54f8 commit e16dbd6
Show file tree
Hide file tree
Showing 9 changed files with 182 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ public BriefingResponseDTO.BriefingPreviewListDTO findBriefings(
return BriefingConverter.toBriefingPreviewListDTO(params.getDate(), briefingList);
}

@Transactional(readOnly = true)
@Transactional
public BriefingResponseDTO.BriefingDetailDTO findBriefing(final Long id, Member member) {
briefingCommandService.increaseViewCountById(id);
Boolean isScrap =
Optional.ofNullable(member)
.map(m -> scrapQueryService.existsByMemberIdAndBriefingId(m.getId(), id))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.List;
import java.util.Optional;

import com.example.briefingapi.briefing.implement.service.BriefingCommandService;
import com.example.briefingapi.briefing.implement.service.BriefingQueryService;
import com.example.briefingapi.briefing.presentation.dto.BriefingRequestParam;
import com.example.briefingapi.briefing.presentation.dto.BriefingResponseDTO;
Expand All @@ -20,6 +21,7 @@
public class BriefingV2Facade {

private final BriefingQueryService briefingQueryService;
private final BriefingCommandService briefingCommandService;
private final ScrapQueryService scrapQueryService;
private static final APIVersion version = APIVersion.V2;

Expand All @@ -30,8 +32,9 @@ public BriefingResponseDTO.BriefingPreviewListDTOV2 findBriefings(
return BriefingConverter.toBriefingPreviewListDTOV2(params.getDate(), briefingList);
}

@Transactional(readOnly = true)
@Transactional
public BriefingResponseDTO.BriefingDetailDTOV2 findBriefing(final Long id, Member member) {
briefingCommandService.increaseViewCountById(id);
Boolean isScrap =
Optional.ofNullable(member)
.map(m -> scrapQueryService.existsByMemberIdAndBriefingId(m.getId(), id))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,8 @@ public Briefing update(
briefing.updateBriefing(title, subTitle, content);
return briefing;
}

public void increaseViewCountById(final Long id) {
briefingRepository.updateViewCountById(id);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public WebSecurityCustomizer webSecurityCustomizer() {
return (web) ->
web.ignoring()
.requestMatchers(
"","/",
"/",
"/schedule",
"/v3/api-docs",
"/v3/api-docs/**",
Expand Down
37 changes: 37 additions & 0 deletions Briefing-Api/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,40 @@ openai:
token: ${OPEN_API_TOKEN}
url:
chat: https://api.openai.com/v1/chat/completions
---
spring:
config:
activate:
on-profile: test
datasource:
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
driver-class-name: org.h2.Driver
username: sa
password:
jpa:
database-platform: org.hibernate.dialect.H2Dialect
properties:
hibernate:
ddl-hbm2ddl:
auto: create
# show_sql: true
# format_sql: true
# use_sql_comments: true
default_batch_fetch_size: 1000
data:
redis:
host: ${REDIS_URL}
port: 6379
jwt:
header: Authorization
# dev server
secret: ${JWT_SECRET}
# secret : ${JWT_SECRET}
authorities-key: authoritiesKey
access-token-validity-in-seconds: 30000 # 30 m
refresh-token-validity-in-seconds: 1210000000 # 14 d

openai:
token: ${OPEN_API_TOKEN}
url:
chat: https://api.openai.com/v1/chat/completions
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
package com.example.briefingapi.briefing.presentation;

import com.example.briefingcommon.domain.repository.article.BriefingRepository;
import com.example.briefingcommon.entity.Briefing;
import com.example.briefingcommon.entity.enums.BriefingType;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@SpringBootTest
@AutoConfigureMockMvc
@ActiveProfiles("test")
class BriefingV2ApiTest {
@Autowired
private WebApplicationContext webApplicationContext;
@Autowired
private BriefingRepository briefingRepository;

private MockMvc mockMvc;

@BeforeEach
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}

@Test
@DisplayName("[BriefingV2Api] 상세 조회 - OK")
void 브리핑_상세_조회_OK() throws Exception {
// given
Briefing briefing =
Briefing.builder()
.title("제목")
.subtitle("부제목")
.content("내용")
.ranks(1)
.type(BriefingType.SOCIAL)
.build();
Briefing savedBriefing = briefingRepository.save(briefing);
Long briefingId = savedBriefing.getId();

// when & then
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/v2/briefings/{id}", briefingId);
mockMvc.perform(requestBuilder)
.andExpect(status().isOk())
.andExpect(jsonPath("$.isSuccess").value(true))
.andExpect(jsonPath("$.result.title").value("제목"))
.andExpect(jsonPath("$.result.id").value(briefingId));
}

@Test
@DisplayName("[BriefingV2Api] 상세 조회 - NOT FOUND")
void 브리핑_상세_조회_NOT_FOUND() throws Exception {
// given
Long briefingId = 0L;

// when & then
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/v2/briefings/{id}", briefingId);
mockMvc.perform(requestBuilder)
.andExpect(status().isNotFound())
.andExpect(jsonPath("$.isSuccess").value(false));
}

@Test
@DisplayName("[BriefingV2Api] 상세 조회 - 100명이 동시에 조회하면 100의 조회수가 늘어나야 합니다.")
void 브리핑_상세_조회_동시_요청() throws Exception {
// given
final int numberOfThreads = 100;
ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);
CountDownLatch latch = new CountDownLatch(numberOfThreads);
Briefing briefing =
Briefing.builder()
.title("제목")
.subtitle("부제목")
.content("내용")
.ranks(1)
.type(BriefingType.SOCIAL)
.build();
Briefing savedBriefing = briefingRepository.save(briefing);
Long briefingId = savedBriefing.getId();

// when
for (int i = 0; i < numberOfThreads; i++) {
executorService.execute(
() -> {
try {
MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders.get("/v2/briefings/{id}", briefingId);
mockMvc.perform(requestBuilder);
} catch (Exception e) {
e.printStackTrace();
}
finally {
latch.countDown();
}
});
}

latch.await();
executorService.shutdown();

// then
Briefing updatedBriefing = briefingRepository.findById(briefingId).orElseThrow();
assertEquals(numberOfThreads, updatedBriefing.getViewCount());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ public class QBriefing extends EntityPathBase<Briefing> {
//inherited
public final DateTimePath<java.time.LocalDateTime> updatedAt = _super.updatedAt;

public final NumberPath<Integer> viewCount = createNumber("viewCount", Integer.class);

public QBriefing(String variable) {
super(Briefing.class, forVariable(variable));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
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;

Expand All @@ -21,4 +22,8 @@ List<Briefing> findAllByTypeAndCreatedAtBetweenOrderByRanks(

@Query("SELECT b from Briefing b where b.ranks = 1 and b.type = :type order by b.createdAt desc")
Page<Briefing> getBestTodayBriefing(@Param("type") BriefingType type, Pageable pageable);

@Modifying
@Query("update Briefing b set b.viewCount = b.viewCount + 1 where b.id = :id")
void updateViewCountById(Long id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@


import lombok.*;
import org.hibernate.annotations.ColumnDefault;

@Entity
@Getter
Expand Down Expand Up @@ -53,6 +54,10 @@ public class Briefing extends BaseDateTimeEntity {
@Enumerated(EnumType.STRING)
private GptModel gptModel = GptModel.GPT_3_5_TURBO;

@ColumnDefault("0")
@Builder.Default
private int viewCount = 0;

public void setScrapCount(Integer scrapCount) {
this.scrapCount = scrapCount;
}
Expand Down

0 comments on commit e16dbd6

Please sign in to comment.