Skip to content

Commit c429a3c

Browse files
committed
Merge branch 'develop' of https://github.com/prgrms-web-devcourse-final-project/WEB5_7_3star_BE into feat/#144/lesson-apply
2 parents c3f078c + da722b1 commit c429a3c

38 files changed

+1037
-405
lines changed

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ dependencies {
3333
implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
3434
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.8.9'
3535
implementation 'io.awspring.cloud:spring-cloud-starter-aws:2.4.4'
36+
implementation 'io.awspring.cloud:spring-cloud-starter-aws-secrets-manager-config:2.4.4'
3637
implementation 'com.opencsv:opencsv:5.11.2'
3738
compileOnly 'org.projectlombok:lombok'
3839
runtimeOnly 'com.mysql:mysql-connector-j'

docker-compose.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,33 @@ services:
6161
networks:
6262
- app-tier
6363

64+
prometheus:
65+
user: "root"
66+
image: prom/prometheus
67+
container_name: prometheus_container
68+
volumes:
69+
- /home/ubuntu/WEB5_7_3star_BE/prometheus-grafana/prometheus/config:/etc/prometheus
70+
- /home/ubuntu/WEB5_7_3star_BE/prometheus-grafana/prometheus/volume:/prometheus/data
71+
ports:
72+
- 9090:9090
73+
command:
74+
- '--config.file=/etc/prometheus/prometheus.yml'
75+
restart: always
76+
networks:
77+
- app-tier
78+
79+
grafana:
80+
user: "root"
81+
image: grafana/grafana
82+
container_name: grafana_container
83+
ports:
84+
- 3000:3000
85+
volumes:
86+
- ./grafana/volume:/var/lib/grafana
87+
restart: always
88+
networks:
89+
- app-tier
90+
6491
volumes:
6592
mysqldb-data:
6693
driver: local
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
global:
2+
scrape_interval: 15s
3+
scrape_timeout: 15s
4+
evaluation_interval: 2m
5+
external_labels:
6+
monitor: 'monitor'
7+
query_log_file: query_log_file.log
8+
rule_files:
9+
- "rule.yml"
10+
scrape_configs:
11+
- job_name: 'prometheus'
12+
scrape_interval: 10s
13+
scrape_timeout: 10s
14+
metrics_path: '/metrics'
15+
honor_labels: false
16+
honor_timestamps: false
17+
scheme: 'http'
18+
static_configs:
19+
- targets: [ '43.202.206.47:9090' ]
20+
# - targets: [ 'localhost:9090' ]
21+
labels:
22+
service: 'monitor-1'
23+
- job_name: 'node'
24+
static_configs:
25+
# - targets: [ 'localhost:9100' ]
26+
- targets: [ '43.202.206.47:9100' ]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
groups:
2+
- name: rule
3+
rules:
4+
- alert: InstanceDown
5+
expr: up == 0
6+
for: 5m
7+
labels:
8+
severity: page
9+
annotations:
10+
summary: "Instance {{ $labels.instance }} down"
11+
description: "{{ $labels.instance }} of job {{ $labels.job }} has been down for more than 5 minutes."
12+
- alert: APIHighRequestLatency
13+
expr: api_http_request_latencies_second{quantile="0.5"} > 1
14+
for: 10m
15+
annotations:
16+
summary: "High request latency on {{ $labels.instance }}"
17+
description: "{{ $labels.instance }} has a median request latency above 1s (current value: {{ $value }}s)"

src/main/java/com/threestar/trainus/domain/comment/controller/CommentController.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,23 @@
22

33
import java.util.List;
44

5-
import org.springframework.beans.factory.annotation.Value;
65
import org.springframework.http.HttpStatus;
76
import org.springframework.http.ResponseEntity;
87
import org.springframework.web.bind.annotation.DeleteMapping;
98
import org.springframework.web.bind.annotation.GetMapping;
9+
import org.springframework.web.bind.annotation.ModelAttribute;
1010
import org.springframework.web.bind.annotation.PathVariable;
1111
import org.springframework.web.bind.annotation.PostMapping;
1212
import org.springframework.web.bind.annotation.RequestBody;
1313
import org.springframework.web.bind.annotation.RequestMapping;
14-
import org.springframework.web.bind.annotation.RequestParam;
1514
import org.springframework.web.bind.annotation.RestController;
1615

1716
import com.threestar.trainus.domain.comment.dto.CommentCreateRequestDto;
1817
import com.threestar.trainus.domain.comment.dto.CommentPageResponseDto;
1918
import com.threestar.trainus.domain.comment.dto.CommentResponseDto;
2019
import com.threestar.trainus.domain.comment.service.CommentService;
2120
import com.threestar.trainus.global.annotation.LoginUser;
21+
import com.threestar.trainus.global.dto.PageRequestDto;
2222
import com.threestar.trainus.global.unit.BaseResponse;
2323
import com.threestar.trainus.global.unit.PagedResponse;
2424

@@ -34,8 +34,6 @@
3434
public class CommentController {
3535

3636
private final CommentService commentService;
37-
@Value("${spring.page.size.limit}")
38-
private int pageSizeLimit;
3937

4038
@PostMapping("/{lessonId}")
4139
@Operation(summary = "댓글 작성", description = "레슨 ID에 해당되는 댓글을 작성합니다.")
@@ -48,11 +46,9 @@ public ResponseEntity<BaseResponse<CommentResponseDto>> createComment(@PathVaria
4846
@GetMapping("/{lessonId}")
4947
@Operation(summary = "댓글 조회", description = "레슨 ID에 해당되는 댓글들을 조회합니다.")
5048
public ResponseEntity<PagedResponse<List<CommentResponseDto>>> readAll(@PathVariable Long lessonId,
51-
@RequestParam("page") int page,
52-
@RequestParam("pageSize") int pageSize) {
53-
int correctPage = Math.max(page, 1);
54-
int correctPageSize = Math.max(1, Math.min(pageSize, pageSizeLimit));
55-
CommentPageResponseDto commentsInfo = commentService.readAll(lessonId, correctPage, correctPageSize);
49+
@Valid @ModelAttribute PageRequestDto pageRequestDto) {
50+
CommentPageResponseDto commentsInfo = commentService.readAll(lessonId, pageRequestDto.getPage(),
51+
pageRequestDto.getLimit());
5652
return PagedResponse.ok("댓글 조회 성공", commentsInfo.comments(), commentsInfo.count(), HttpStatus.OK);
5753
}
5854

src/main/java/com/threestar/trainus/domain/comment/repository/CommentRepository.java

Lines changed: 29 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,46 +14,46 @@ public interface CommentRepository extends JpaRepository<Comment, Long> {
1414

1515
//create index idx_lesson_id_parent_comment_id_comment_id on comments(lesson_id, parent_comment_id asc, comment_id asc); 인덱스 통해 조회 성능 최적화
1616
@Query(value = """
17-
select
18-
c.comment_id as commentId,
19-
c.content as content,
20-
c.parent_comment_id as parentCommentId,
21-
c.deleted as deleted,
22-
c.created_at as createdAt,
23-
u.id as userId,
24-
u.nickname as nickname
25-
from (
26-
select comment_id
27-
from comments c
28-
join user u on c.user_id = u.id and u.deleted_at IS NULL
29-
where lesson_id = :lessonId
30-
order by parent_comment_id asc, comment_id asc
31-
limit :limit offset :offset
17+
SELECT
18+
c.comment_id AS commentId,
19+
c.content AS content,
20+
c.parent_comment_id AS parentCommentId,
21+
c.deleted AS deleted,
22+
c.created_at AS createdAt,
23+
u.id AS userId,
24+
u.nickname AS nickname
25+
FROM (
26+
SELECT comment_id
27+
FROM comments c
28+
JOIN user u ON c.user_id = u.id AND u.deleted_at IS NULL
29+
WHERE lesson_id = :lessonId
30+
ORDER BY parent_comment_id ASC, comment_id ASC
31+
LIMIT :limit OFFSET :offset
3232
) t
33-
join comments c on t.comment_id = c.comment_id
34-
join user u on c.user_id = u.id
33+
JOIN comments c ON t.comment_id = c.comment_id
34+
JOIN user u ON c.user_id = u.id
3535
""", nativeQuery = true)
3636
List<CommentWithUserProjection> findAll(@Param("lessonId") Long lessonId, @Param("offset") int offset,
3737
@Param("limit") int limit);
3838

3939
@Query(value = """
40-
select count(*) from (
41-
select comment_id
42-
from comments c
43-
join user u on c.user_id = u.id and u.deleted_at IS NULL
44-
where lesson_id = :lessonId
45-
limit :limit
40+
SELECT count(*) FROM (
41+
SELECT comment_id
42+
FROM comments c
43+
JOIN user u ON c.user_id = u.id AND u.deleted_at IS NULL
44+
WHERE lesson_id = :lessonId
45+
LIMIT :limit
4646
) t
4747
""", nativeQuery = true)
4848
Integer count(@Param("lessonId") Long lessonId, @Param("limit") int limit);
4949

5050
@Query(value = """
51-
select count(*) from (
52-
select comment_id
53-
from comments c
54-
join user u on c.user_id = u.id and u.deleted_at IS NULL
55-
where lesson_id = :lessonId and parent_comment_id = :parentCommentId
56-
limit :limit
51+
SELECT count(*) FROM (
52+
SELECT comment_id
53+
FROM comments c
54+
JOIN user u ON c.user_id = u.id AND u.deleted_at IS NULL
55+
WHERE lesson_id = :lessonId AND parent_comment_id = :parentCommentId
56+
LIMIT :limit
5757
) t
5858
""", nativeQuery = true)
5959
Long countBy(@Param("lessonId") Long lessonId, @Param("parentCommentId") Long parentCommentId,

src/main/java/com/threestar/trainus/domain/comment/service/CommentService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ private boolean hasChildren(Comment comment) {
9292
private void delete(Comment comment) {
9393
commentRepository.delete(comment);
9494
if (!comment.isRoot()) {
95-
commentRepository.findById(comment.getCommentId())
95+
commentRepository.findById(comment.getParentCommentId())
9696
.filter(Comment::getDeleted)
9797
.filter(not(this::hasChildren))
9898
.ifPresent(this::delete);

src/main/java/com/threestar/trainus/domain/coupon/admin/mapper/AdminCouponMapper.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.threestar.trainus.domain.coupon.admin.mapper;
22

3-
import org.springframework.data.domain.Page;
3+
import java.util.List;
44

55
import com.threestar.trainus.domain.coupon.admin.dto.CouponCreateRequestDto;
66
import com.threestar.trainus.domain.coupon.admin.dto.CouponCreateResponseDto;
@@ -57,10 +57,12 @@ public static CouponListItemDto toCouponListItemDto(Coupon coupon) {
5757
);
5858
}
5959

60-
public static CouponListResponseDto toCouponListResponseDto(Page<Coupon> couponPage) {
60+
public static CouponListResponseDto toCouponListResponseDto(
61+
List<Coupon> coupons, int totalCount
62+
) {
6163
return new CouponListResponseDto(
62-
(int)couponPage.getTotalElements(),
63-
couponPage.getContent().stream()
64+
totalCount,
65+
coupons.stream()
6466
.map(AdminCouponMapper::toCouponListItemDto)
6567
.toList()
6668
);

src/main/java/com/threestar/trainus/domain/coupon/admin/service/AdminCouponService.java

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
package com.threestar.trainus.domain.coupon.admin.service;
22

33
import java.time.LocalDateTime;
4+
import java.util.List;
45

5-
import org.springframework.data.domain.Page;
6-
import org.springframework.data.domain.PageRequest;
7-
import org.springframework.data.domain.Pageable;
8-
import org.springframework.data.domain.Sort;
96
import org.springframework.stereotype.Service;
107
import org.springframework.transaction.annotation.Transactional;
118

@@ -25,6 +22,7 @@
2522
import com.threestar.trainus.domain.user.service.UserService;
2623
import com.threestar.trainus.global.exception.domain.ErrorCode;
2724
import com.threestar.trainus.global.exception.handler.BusinessException;
25+
import com.threestar.trainus.global.utils.PageLimitCalculator;
2826

2927
import lombok.RequiredArgsConstructor;
3028

@@ -54,21 +52,32 @@ public CouponCreateResponseDto createCoupon(CouponCreateRequestDto request, Long
5452

5553
//쿠폰 조회
5654
@Transactional(readOnly = true)
57-
public CouponListResponseDto getCoupons(int page, int limit, CouponStatus status, CouponCategory category,
58-
Long userId) {
55+
public CouponListResponseDto getCoupons(
56+
int page, int limit,
57+
CouponStatus status, CouponCategory category,
58+
Long userId
59+
) {
5960
// 관리자 권한 검증
6061
userService.validateAdminRole(userId);
6162

62-
Pageable pageable = PageRequest.of(page - 1, limit, Sort.by("createdAt").descending());
63+
// offset / countLimit 계산
64+
int offset = (page - 1) * limit;
65+
int countLimit = PageLimitCalculator.calculatePageLimit(page, limit, 5);
6366

64-
// 조건에 따른 쿠폰 조회
65-
Page<Coupon> couponPage = couponRepository.findCouponsWithFilters(status, category, pageable);
67+
// 쿠폰 조회 및 count
68+
List<Coupon> coupons = couponRepository.findCouponsWithFilters(
69+
status, category, offset, limit
70+
);
6671

67-
//응답 DTO 변환
68-
return AdminCouponMapper.toCouponListResponseDto(couponPage);
72+
int total = couponRepository.countCouponsWithFilters(
73+
status, category, countLimit
74+
);
75+
76+
// 응답 DTO 변환
77+
return AdminCouponMapper.toCouponListResponseDto(coupons, total);
6978
}
7079

71-
//쿠폰 상세 조횧
80+
//쿠폰 상세 조회
7281
@Transactional(readOnly = true)
7382
public CouponDetailResponseDto getCouponDetail(Long couponId, Long userId) {
7483
// 관리자 권한 검증

src/main/java/com/threestar/trainus/domain/coupon/user/repository/CouponRepository.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,36 @@ Page<Coupon> findCouponsWithFilters(
7474
AND c.deletedAt IS NULL
7575
""")
7676
List<Coupon> findActiveCouponsToDeactivate(@Param("now") LocalDateTime now);
77+
78+
@Query(value = """
79+
SELECT *
80+
FROM coupons c
81+
WHERE (:status IS NULL OR c.status = :status)
82+
AND (:category IS NULL OR c.category = :category)
83+
AND c.deleted_at IS NULL
84+
ORDER BY c.created_at DESC
85+
LIMIT :limit OFFSET :offset
86+
""", nativeQuery = true)
87+
List<Coupon> findCouponsWithFilters(
88+
@Param("status") CouponStatus status,
89+
@Param("category") CouponCategory category,
90+
@Param("offset") int offset,
91+
@Param("limit") int limit
92+
);
93+
94+
@Query(value = """
95+
SELECT COUNT(*) FROM (
96+
SELECT c.id
97+
FROM coupons c
98+
WHERE (:status IS NULL OR c.status = :status)
99+
AND (:category IS NULL OR c.category = :category)
100+
AND c.deleted_at IS NULL
101+
LIMIT :limit
102+
) t
103+
""", nativeQuery = true)
104+
int countCouponsWithFilters(
105+
@Param("status") CouponStatus status,
106+
@Param("category") CouponCategory category,
107+
@Param("limit") int limit
108+
);
77109
}

0 commit comments

Comments
 (0)