Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weโ€™ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev backend #145

Merged
merged 23 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
8906151
refactor: News Category directory ๋ณ€๊ฒฝ (service -> entity)
yeonjy Jun 4, 2024
3f879b6
Merge branch 'Dev-backend' into feat/#136-category-statistics
yeonjy Jun 17, 2024
8eba20b
feat: QueryDsl ๊ด€๋ จ ์„ค์ •
yeonjy Jun 17, 2024
34db6e9
feat: ReadNews ๋‚ ์งœ๋ณ„ ์ฝ์€ ๋‰ด์Šค ๊ฐœ์ˆ˜ ์กฐํšŒ ๊ตฌํ˜„
yeonjy Jun 17, 2024
e4635f5
Merge branch 'fix/#141-backend-kakao-login' into feat/#136-category-sโ€ฆ
yeonjy Jun 17, 2024
6dbdbf8
chore: Querydsl ๊ด€๋ จ ์„ค์ •
yeonjy Jun 17, 2024
671c70b
feat: ๋‚ ์งœ๋ณ„ ๋‰ด์Šค ์กฐํšŒ์ˆ˜ ์กฐํšŒ api ๊ตฌํ˜„
yeonjy Jun 17, 2024
eac9ae8
feat: QuerydslConfig
yeonjy Jun 17, 2024
15fdc6d
feat: getViewOfDates ๋กœ์ง ๊ตฌํ˜„
yeonjy Jun 17, 2024
104eb4e
feat: QuerydslConfig ์ปจํ…์ŠคํŠธ ๋“ฑ๋ก
yeonjy Jun 17, 2024
152ccd9
test: StatisticsController ์ถ”๊ฐ€
yeonjy Jun 17, 2024
4448411
rename: test package ๋ช… controller -> api
yeonjy Jun 17, 2024
921bfb0
style: ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” import ์ œ๊ฑฐ
yeonjy Jun 17, 2024
c3427ec
test: StatisticsServiceTest ๊ตฌํ˜„
yeonjy Jun 17, 2024
c32c4fe
test: ReadNewsRepositoryTest ๊ตฌํ˜„
yeonjy Jun 17, 2024
dbdc4d9
feat: statistics.dto์— response ํŒจํ‚ค์ง€ ์ถ”๊ฐ€
yeonjy Jun 17, 2024
b199b0f
feat: getCategoryStatistics API ๊ตฌํ˜„
yeonjy Jun 17, 2024
a53117c
feat: getCountOfReadNewsByCategory querydsl ๋กœ์ง ๊ตฌํ˜„
yeonjy Jun 17, 2024
ca64137
feat: getCategoryStatistics service ๋กœ์ง ๊ตฌํ˜„
yeonjy Jun 17, 2024
f8b8136
test: ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์กฐํšŒํ•œ ๋‰ด์Šค ๊ฐœ์ˆ˜ ๊ด€๋ จ ํ…Œ์ŠคํŠธ ๊ตฌํ˜„
yeonjy Jun 17, 2024
e3217d2
refactor: ๋‚ ์งœ๋ณ„ ์ฝ์€ ๋‰ด์Šค ์กฐํšŒ์ˆ˜ ๋ฐ˜ํ™˜ ๋กœ์ง์— ๋กœ๊ทธ์ธ ์œ ์ € ํŒŒ๋ผ๋ฏธํ„ฐ ์ถ”๊ฐ€
yeonjy Jun 17, 2024
cfd4c6b
fix: NewsCategory directory ๋ณ€๊ฒฝ ์ ์šฉ
yeonjy Jun 17, 2024
f29d013
Merge pull request #144 from tukcomCD2024/feat/#136-category-statistics
yeonjy Jun 17, 2024
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
10 changes: 10 additions & 0 deletions backend/core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ repositories {
mavenCentral()
}

ext {
queryDslVersion = "5.0.0"
}

dependencies {
if (isAppleSilicon()) {
runtimeOnly("io.netty:netty-resolver-dns-native-macos:4.1.94.Final:osx-aarch_64")
Expand All @@ -41,6 +45,12 @@ dependencies {
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0'
implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.3.1.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'com.fasterxml.jackson.core:jackson-core:2.17.0'

implementation "com.querydsl:querydsl-jpa:${queryDslVersion}:jakarta"
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

annotationProcessor 'org.projectlombok:lombok'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.5.5.Final'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.rollthedice.backend.domain.news.dto.NewsUrlDto;
import com.rollthedice.backend.domain.news.entity.News;
import com.rollthedice.backend.domain.news.service.NewsCategory;
import com.rollthedice.backend.domain.news.entity.NewsCategory;
import com.rollthedice.backend.domain.news.service.NewsService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -11,7 +11,6 @@
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -32,7 +31,7 @@ public class NewsCrawlingService {
private final NewsService newsService;

@Transactional
// @Scheduled(cron = CRON, zone = ZONE)
// @Scheduled(cron = CRON, zone = ZONE)
public void scrap() throws IOException {
for (NewsCategory category : NewsCategory.values()) {
String categoryUrl = MAIN_URL + category.getNum();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.rollthedice.backend.domain.news.service;
package com.rollthedice.backend.domain.news.entity;

import lombok.Getter;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

import com.rollthedice.backend.domain.member.entity.Member;
import com.rollthedice.backend.domain.news.entity.ReadNews;
import com.rollthedice.backend.domain.statistics.repository.ReadNewsCustomRepository;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface ReadNewsRepository extends JpaRepository<ReadNews, Long> {
public interface ReadNewsRepository extends JpaRepository<ReadNews, Long>, ReadNewsCustomRepository {
List<ReadNews> getTop3ByMemberOrderByCreatedAtDesc(Member member);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.rollthedice.backend.domain.statistics.api;

import com.rollthedice.backend.domain.statistics.dto.response.CategoryStatisticsResponse;
import com.rollthedice.backend.domain.statistics.dto.response.DateViewStatisticsResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;

import java.util.List;

public interface StatisticsApi {
@Operation(
summary = "์ตœ๊ทผ ์ผ์ฃผ์ผ ๋‚ ์งœ๋ณ„ ๋‰ด์Šค ์กฐํšŒ์ˆ˜ ์กฐํšŒ",
description = "์ตœ๊ทผ ์ผ์ฃผ์ผ๊ฐ„ ๋‚ ์งœ๋ณ„๋กœ ๋‰ด์Šค ์กฐํšŒ์ˆ˜๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.",
security = {@SecurityRequirement(name = "access_token")},
tags = {"Statistics"}
)
@ApiResponse(
responseCode = "200",
description = "์š”์ฒญ์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค."
)
List<DateViewStatisticsResponse> getViewsOfDates();

@Operation(
summary = "์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์กฐํšŒ์ˆ˜ ์กฐํšŒ",
description = "์นดํ…Œ๊ณ ๋ฆฌ๋ณ„ ์กฐํšŒ์ˆ˜๋ฅผ ์กฐํšŒํ•ฉ๋‹ˆ๋‹ค.",
security = {@SecurityRequirement(name = "access_token")},
tags = {"Statistics"}
)
@ApiResponse(
responseCode = "200",
description = "์š”์ฒญ์— ์„ฑ๊ณตํ•˜์˜€์Šต๋‹ˆ๋‹ค."
)
List<CategoryStatisticsResponse> getCategoryStatistics();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.rollthedice.backend.domain.statistics.api;

import com.rollthedice.backend.domain.statistics.dto.response.CategoryStatisticsResponse;
import com.rollthedice.backend.domain.statistics.dto.response.DateViewStatisticsResponse;
import com.rollthedice.backend.domain.statistics.service.StatisticsService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequiredArgsConstructor
@RequestMapping("statistics")
public class StatisticsController implements StatisticsApi {
private final StatisticsService statisticsService;

@ResponseStatus(HttpStatus.OK)
@GetMapping("/per-dates")
@Override
public List<DateViewStatisticsResponse> getViewsOfDates() {
return statisticsService.getViewsOfDates();
}

@ResponseStatus(HttpStatus.OK)
@GetMapping("/categories")
@Override
public List<CategoryStatisticsResponse> getCategoryStatistics() {
return statisticsService.getCategoryStatistics();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.rollthedice.backend.domain.statistics.dto.response;

import lombok.*;

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CategoryStatisticsResponse {
private Long views;
private String category;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.rollthedice.backend.domain.statistics.dto.response;

import lombok.*;

import java.time.LocalDate;

@Getter
@Setter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class DateViewStatisticsResponse {
private Long views;
private LocalDate dateTime;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.rollthedice.backend.domain.statistics.repository;

import com.rollthedice.backend.domain.member.entity.Member;

import java.time.LocalDate;

public interface ReadNewsCustomRepository {
Long getCountOfReadNewsByDate(Member member, LocalDate date);

Long getCountOfReadNewsByCategory(Member member, String category);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.rollthedice.backend.domain.statistics.repository;

import com.querydsl.jpa.impl.JPAQueryFactory;
import com.rollthedice.backend.domain.member.entity.Member;
import lombok.RequiredArgsConstructor;

import java.time.LocalDate;

import static com.rollthedice.backend.domain.news.entity.QNews.news;
import static com.rollthedice.backend.domain.news.entity.QReadNews.readNews;

@RequiredArgsConstructor
public class ReadNewsCustomRepositoryImpl implements ReadNewsCustomRepository {
private final JPAQueryFactory queryFactory;

@Override
public Long getCountOfReadNewsByDate(Member member, LocalDate date) {
return queryFactory
.select(readNews.count())
.from(readNews)
.where(readNews.member.eq(member)
.and(readNews.createdAt.between(
date.atStartOfDay(),
date.plusDays(1).atStartOfDay().minusNanos(1))))
.fetchOne();
}

@Override
public Long getCountOfReadNewsByCategory(Member member, String category) {
return queryFactory
.select(readNews.count())
.from(readNews)
.join(readNews.news, news)
.where(readNews.member.eq(member)
.and(news.category.eq(category))
).fetchOne();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.rollthedice.backend.domain.statistics.service;

import com.rollthedice.backend.domain.member.entity.Member;
import com.rollthedice.backend.domain.news.entity.NewsCategory;
import com.rollthedice.backend.domain.news.repository.ReadNewsRepository;
import com.rollthedice.backend.domain.statistics.dto.response.CategoryStatisticsResponse;
import com.rollthedice.backend.domain.statistics.dto.response.DateViewStatisticsResponse;
import com.rollthedice.backend.global.oauth2.service.AuthService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.IntStream;

@Service
@RequiredArgsConstructor
public class StatisticsService {
private final ReadNewsRepository readNewsRepository;
private final AuthService authService;

public List<DateViewStatisticsResponse> getViewsOfDates() {
Member member = authService.getMember();
List<DateViewStatisticsResponse> responses = new ArrayList<>();
IntStream.range(0, 7)
.forEach(day -> responses.add(DateViewStatisticsResponse.builder()
.views(readNewsRepository.getCountOfReadNewsByDate(member, LocalDate.now().minusDays(day)))
.dateTime(LocalDate.now().minusDays(day)).build()));
return responses;
}

public List<CategoryStatisticsResponse> getCategoryStatistics() {
Member member = authService.getMember();
List<CategoryStatisticsResponse> responses = new ArrayList<>();
for (NewsCategory category : NewsCategory.values()) {
long views = readNewsRepository.getCountOfReadNewsByCategory(member, category.getName());
CategoryStatisticsResponse response = CategoryStatisticsResponse.builder()
.category(category.getName())
.views(views).build();
responses.add(response);
}
return responses;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.rollthedice.backend.global.config;


import com.querydsl.jpa.impl.JPAQueryFactory;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class QuerydslConfig {

private final EntityManager em;

@Autowired
public QuerydslConfig(EntityManager em) {
this.em = em;
}

@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(em);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.rollthedice.backend.domain.news.controller;
package com.rollthedice.backend.domain.news.api;

import com.rollthedice.backend.domain.news.api.NewsController;
import com.rollthedice.backend.domain.news.exception.NewsNotFoundException;
import com.rollthedice.backend.domain.news.repository.NewsRepository;
import com.rollthedice.backend.domain.news.service.NewsService;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.rollthedice.backend.domain.news.repository;

import com.rollthedice.backend.domain.member.entity.Member;
import com.rollthedice.backend.domain.member.repository.MemberRepository;
import com.rollthedice.backend.domain.news.entity.News;
import com.rollthedice.backend.domain.news.entity.NewsCategory;
import com.rollthedice.backend.domain.news.entity.ReadNews;
import com.rollthedice.backend.support.RepositoryTest;
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 java.time.LocalDate;

import static com.rollthedice.backend.domain.member.MemberFixture.MEMBER;
import static org.assertj.core.api.Assertions.assertThat;

@DisplayName("ReadNewsRepository์˜ ")
@RepositoryTest
public class ReadNewsRepositoryTest {
@Autowired
private ReadNewsRepository readNewsRepository;
@Autowired
private NewsRepository newsRepository;
@Autowired
private MemberRepository memberRepository;

private Member member;

@BeforeEach
void setUp() {
member = memberRepository.save(MEMBER());
}

@Test
@DisplayName("ํ•˜๋ฃจ๋™์•ˆ ์กฐํšŒํ•œ ๋‰ด์Šค ๊ฐœ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€")
void getCountOfReadNewsByDate() {
//given
int expect = 5;
for (int i = 0; i < expect; i++) {
readNewsRepository.save(ReadNews.builder().member(member).news(newsRepository.save(News.builder().id((long) i).build())).build());
}

//when
Long result = readNewsRepository.getCountOfReadNewsByDate(member, LocalDate.now());

//then
assertThat(result).isEqualTo(expect);
}

@Test
@DisplayName("ํšŒ์›์ด ์นดํ…Œ๊ณ ๋ฆฌ๋ณ„๋กœ ์กฐํšŒํ•œ ๋‰ด์Šค ๊ฐœ์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๋Š”๊ฐ€")
void getCountOfReadNewsByCategory() {
//given
int expect = 5;
String category = NewsCategory.SCIENCE.getName();
for (int i = 0; i < expect; i++) {
readNewsRepository.save(ReadNews.builder().member(member)
.news(newsRepository.save(
News.builder().id((long) i).category(category).build())).build());
}

//when
Long result = readNewsRepository.getCountOfReadNewsByCategory(member, category);

//then
assertThat(result).isEqualTo(expect);
}
}
Loading
Loading