Skip to content

Commit 06a8967

Browse files
Merge pull request #25 from GDGoC-Gachon/hotfix-cors
🔥 404에러 해결
2 parents d2446dd + 08ba7be commit 06a8967

File tree

6 files changed

+107
-79
lines changed

6 files changed

+107
-79
lines changed

src/main/java/com/gdg/homepage/common/domain/StatisticsProjection.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
public interface StatisticsProjection {
44
int getCurrent(); // 기준일 이후 수치
5-
int getPrevious(); // 기준일 이전 수치
6-
int getTotal();
5+
Integer getPrevious(); // 기준일 이전 수치
6+
Integer getTotal();
77

88
// "증가량": 현재 기간 동안 새로 늘어난 값
9-
default int increase() {
9+
default Integer increase() {
1010
return getCurrent();
1111
}
1212

@@ -16,7 +16,7 @@ default int total() {
1616
}
1717

1818
// "증감": 전체 수의 증감 여부 판단 시 사용
19-
default int change() {
19+
default Integer change() {
2020
return getCurrent() - getPrevious();
2121
}
2222
}

src/main/java/com/gdg/homepage/landing/admin/controller/AdminApi.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
import com.gdg.homepage.landing.admin.dto.JoinPeriodRequest;
88
import com.gdg.homepage.landing.admin.dto.JoinPeriodResponse;
99
import com.gdg.homepage.landing.admin.service.AdminServiceImpl;
10-
import com.gdg.homepage.landing.admin.service.MemberAdminServiceImpl;
11-
import com.gdg.homepage.landing.register.service.RegisterService;
1210
import io.swagger.v3.oas.annotations.Operation;
1311
import io.swagger.v3.oas.annotations.tags.Tag;
12+
import jakarta.validation.Valid;
1413
import lombok.RequiredArgsConstructor;
1514
import org.springframework.web.bind.annotation.*;
1615

@@ -29,7 +28,7 @@ public class AdminApi {
2928
description = "리크루팅에 대한 새로운 가입 일정을 생성합니다."
3029
)
3130
@PostMapping("/joinPeriod/create")
32-
public ApiResponse<String> createJoinPeriod(@RequestBody JoinPeriodRequest joinPeriodRequest) {
31+
public ApiResponse<String> createJoinPeriod(@RequestBody @Valid JoinPeriodRequest joinPeriodRequest) {
3332
try {
3433
adminService.createJoinPeriod(joinPeriodRequest);
3534
return ApiResponse.created("JoinPeriod is created.");
@@ -43,7 +42,7 @@ public ApiResponse<String> createJoinPeriod(@RequestBody JoinPeriodRequest joinP
4342
description = "특정 ID의 가입 일정을 수정합니다."
4443
)
4544
@PutMapping("/joinPeriod/update/{id}")
46-
public ApiResponse<JoinPeriodResponse> updateJoinPeriod(@PathVariable("id") Long id, @RequestBody JoinPeriodRequest joinPeriodRequest) {
45+
public ApiResponse<JoinPeriodResponse> updateJoinPeriod(@PathVariable("id") Long id,@Valid @RequestBody JoinPeriodRequest joinPeriodRequest) {
4746
try {
4847
JoinPeriodResponse responseDto = adminService.updateJoinPeriod(id, joinPeriodRequest);
4948
return ApiResponse.ok(responseDto);

src/main/java/com/gdg/homepage/landing/admin/dto/AnalyticsResponse.java

Lines changed: 25 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,45 @@
33
import lombok.Builder;
44
import lombok.Getter;
55

6-
import java.util.Optional;
7-
86
@Getter
97
@Builder
108
public class AnalyticsResponse {
119

12-
private int totalMember; // 총 멤버 수
13-
private int memberIncrease; // 신규 가입자 수
10+
private int totalMember; // 총 멤버 수
11+
private Integer memberIncrease; // 신규 가입자 수 (null 허용)
1412

15-
private int registerCount; // 가입 신청 수
16-
private int registerIncrease; // 가입 신청 증가 수
13+
private int registerCount; // 가입 신청 수
14+
private Integer registerIncrease; // 가입 신청 증가 수 (null 허용)
1715

18-
private int deactivateMemberCount; // 탈퇴 회원 수
19-
private int deactivationsIncrease;// 탈퇴 회원 증가 수
16+
private int deactivateMemberCount; // 탈퇴 회원 수
17+
private Integer deactivationsIncrease; // 탈퇴 회원 증가 수 (null 허용)
2018

21-
private int pageView; // 페이지 뷰 수
22-
private int pageViewIncrease; // 페이지 뷰 증가 수
19+
private int pageView; // 페이지 뷰 수
20+
private Integer pageViewIncrease; // 페이지 뷰 증가 수 (null 허용)
2321

24-
private String popularStack; // 인기 역할
22+
private String popularStack; // 인기 역할
2523

2624
public static AnalyticsResponse from(
2725
Integer totalMember,
28-
int memberIncrease,
29-
int registerCount,
30-
int registerIncrease,
31-
int pageView,
32-
int pageViewIncrease,
33-
int deactivateMemberCount,
34-
int deactivationIncrease,
26+
Integer memberIncrease,
27+
Integer registerCount,
28+
Integer registerIncrease,
29+
Integer pageView,
30+
Integer pageViewIncrease,
31+
Integer deactivateMemberCount,
32+
Integer deactivationIncrease,
3533
String popularStack) {
34+
3635
return AnalyticsResponse.builder()
37-
.totalMember(Optional.ofNullable(totalMember).orElse(0))
38-
.memberIncrease(Optional.ofNullable(memberIncrease).orElse(0))
39-
.registerCount(Optional.ofNullable(registerCount).orElse(0))
40-
.registerIncrease(Optional.ofNullable(registerIncrease).orElse(0))
41-
.pageView(Optional.ofNullable(pageView).orElse(0))
42-
.pageViewIncrease(Optional.ofNullable(pageViewIncrease).orElse((int) 0))
43-
.deactivateMemberCount(Optional.ofNullable(deactivateMemberCount).orElse(0))
44-
.deactivationsIncrease(Optional.ofNullable(deactivationIncrease).orElse(0))
36+
.totalMember(totalMember != null ? totalMember : 0)
37+
.memberIncrease(memberIncrease) // null 그대로 전달
38+
.registerCount(registerCount != null ? registerCount : 0)
39+
.registerIncrease(registerIncrease)
40+
.pageView(pageView != null ? pageView : 0)
41+
.pageViewIncrease(pageViewIncrease)
42+
.deactivateMemberCount(deactivateMemberCount != null ? deactivateMemberCount : 0)
43+
.deactivationsIncrease(deactivationIncrease)
4544
.popularStack(popularStack)
4645
.build();
4746
}
4847
}
49-

src/main/java/com/gdg/homepage/landing/admin/dto/JoinPeriodRequest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.gdg.homepage.landing.admin.dto;
22

33
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.AssertTrue;
5+
import jakarta.validation.constraints.NotNull;
46
import lombok.Getter;
57

68
import java.time.LocalDateTime;
@@ -12,13 +14,19 @@ public class JoinPeriodRequest {
1214
private String title;
1315

1416
@Schema(description = "시작 날짜", example = "2025-03-30T12:30:00")
17+
@NotNull(message = "시작 날짜는 필수입니다.")
1518
private LocalDateTime startDate;
1619

1720
@Schema(description = "끝나는 날짜", example = "2025-05-30T12:30:00")
21+
@NotNull(message = "끝나는 날짜는 필수입니다.")
1822
private LocalDateTime endDate;
1923

2024
@Schema(description = "모집 최대 인원", example = "3")
2125
private int maxMember;
2226

23-
27+
@AssertTrue(message = "끝나는 날짜는 시작 날짜와 같거나 이후여야 합니다.")
28+
public boolean isEndDateAfterStartDate() {
29+
if (startDate == null || endDate == null) return true; // 다른 @NotNull로 별도 체크 권장
30+
return !endDate.isBefore(startDate);
31+
}
2432
}

src/main/java/com/gdg/homepage/landing/admin/service/AdminServiceImpl.java

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.time.LocalDateTime;
2121
import java.util.List;
22+
import java.util.Optional;
2223
import java.util.stream.Collectors;
2324

2425
@Slf4j
@@ -57,12 +58,17 @@ public void createJoinPeriod(JoinPeriodRequest joinPeriodRequest) {
5758

5859
@Override
5960
public JoinPeriodResponse updateJoinPeriod(Long id, JoinPeriodRequest joinPeriodRequest) {
60-
//
61-
JoinPeriod joinPeriod = joinPeriodRepository.findById(id).orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_END_POINT));
62-
// 요청 값이 존재하면 수정
63-
if (joinPeriodRequest != null) {
64-
joinPeriod.updateJoinPeriod(joinPeriodRequest);
61+
LocalDateTime startDate = joinPeriodRequest.getStartDate();
62+
LocalDateTime endDate = joinPeriodRequest.getEndDate();
63+
boolean isOverlapping = joinPeriodRepository.periodExist(endDate, startDate);
64+
65+
if (isOverlapping) {
66+
throw new IllegalArgumentException("해당 기간은 이미 존재하는 가입 기간과 겹칩니다.");
6567
}
68+
69+
JoinPeriod joinPeriod = joinPeriodRepository.findById(id).orElseThrow(() -> new CustomException(ErrorCode.NOT_FOUND_END_POINT));
70+
joinPeriod.updateJoinPeriod(joinPeriodRequest);
71+
6672
JoinPeriod updatedJoinPeriod = joinPeriodRepository.save(joinPeriod);
6773
return JoinPeriodResponse.from(updatedJoinPeriod);
6874
}
@@ -106,32 +112,40 @@ public void incrementPageView() {
106112
@Override
107113
public AnalyticsResponse collectStatistics() {
108114

109-
JoinPeriod joinPeriod = getCurrentJoinPeriod();
110-
LocalDateTime startDate = joinPeriod.getStartDate();
111-
LocalDateTime endDate = joinPeriod.getEndDate(); // 필요 시 사용
115+
Optional<JoinPeriod> joinPeriodOpt =getCurrentJoinPeriod();
116+
boolean hasJoinPeriod = joinPeriodOpt.isPresent();
117+
LocalDateTime startDate = hasJoinPeriod ? joinPeriodOpt.get().getStartDate() : null;
118+
LocalDateTime endDate = hasJoinPeriod ? joinPeriodOpt.get().getEndDate() : null;
112119

120+
// 총계는 항상 가져옴
113121
var memberStats = memberRepository.getMemberStatistics(startDate);
114-
var appStats = registerRepository.getApplicationStatistics(startDate);
115-
var viewStats = pageViewRepository.getPageViewStatistics(startDate);
122+
var appStats = hasJoinPeriod ? registerRepository.getApplicationStatistics(startDate) : null;
123+
var viewStats = hasJoinPeriod ? pageViewRepository.getPageViewStatistics(startDate) : null;
116124
var deactivationStats = memberRepository.getDeactivationStatistics(startDate);
125+
126+
// 인기 스택은 항상 조회
117127
var popularStack = memberRepository.findPopularStack(startDate, endDate);
118128

119129
return AnalyticsResponse.from(
120-
memberStats.total(),
121-
memberStats.change(),
122-
appStats.total(),
123-
appStats.change(),
124-
viewStats.total(),
125-
viewStats.change(),
126-
deactivationStats.total(),
127-
deactivationStats.change(),
130+
memberStats != null ? memberStats.total() : 0,
131+
hasJoinPeriod ? memberStats.change() : null,
132+
133+
appStats != null ? appStats.total() : 0,
134+
hasJoinPeriod ? appStats.change() : null,
135+
136+
viewStats != null ? viewStats.total() : 0,
137+
hasJoinPeriod ? viewStats.change() : null,
138+
139+
deactivationStats != null ? deactivationStats.total() : 0,
140+
hasJoinPeriod ? deactivationStats.change() : null,
141+
128142
popularStack.toString()
129143
);
130144
}
131145

132-
private JoinPeriod getCurrentJoinPeriod() {
133-
return joinPeriodRepository.findCurrentJoinPeriod(LocalDateTime.now())
134-
.orElseThrow(() -> new EntityNotFoundException("가입 기간을 찾을 수 없습니다."));
146+
147+
private Optional<JoinPeriod> getCurrentJoinPeriod() {
148+
return joinPeriodRepository.findCurrentJoinPeriod(LocalDateTime.now());
135149
}
136150

137151

src/main/java/com/gdg/homepage/landing/member/repository/MemberRepository.java

Lines changed: 31 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,39 +38,48 @@ public interface MemberRepository extends JpaRepository<Member, Long> {
3838
@Query("""
3939
SELECT
4040
COUNT(m) as total,
41-
COUNT(CASE WHEN m.createdAt >= :startOfPeriod THEN 1 END) as current,
42-
COUNT(CASE WHEN m.createdAt < :startOfPeriod THEN 1 END) as previous
41+
COUNT(CASE WHEN (:startDate IS NOT NULL AND m.createdAt >= :startDate) THEN 1 ELSE NULL END) as current,
42+
COUNT(CASE WHEN (:startDate IS NOT NULL AND m.createdAt < :startDate) THEN 1 ELSE NULL END) as previous
43+
4344
FROM Member m
4445
""")
45-
StatisticsProjection getMemberStatistics(@Param("startOfPeriod") LocalDateTime startOfPeriod);
46+
StatisticsProjection getMemberStatistics(@Param("startDate") LocalDateTime startDate);
4647

47-
// 탈퇴 총계 가져오기
48+
// 탈퇴
4849
@Query("""
4950
SELECT
5051
COUNT(m) as total,
51-
COUNT(CASE WHEN m.createdAt >= :startDate THEN 1 ELSE NULL END) as current,
52-
COUNT(CASE WHEN m.createdAt < :startDate THEN 1 ELSE NULL END) as previous
52+
COUNT(CASE WHEN (:startDate IS NOT NULL AND m.createdAt >= :startDate) THEN 1 ELSE NULL END) as current,
53+
COUNT(CASE WHEN (:startDate IS NOT NULL AND m.createdAt < :startDate) THEN 1 ELSE NULL END) as previous
54+
5355
FROM Member m
5456
WHERE m.withDraw = true
5557
""")
56-
StatisticsProjection getDeactivationStatistics(@Param("startDate") LocalDateTime startOfPeriod);
58+
StatisticsProjection getDeactivationStatistics(@Param("startDate") LocalDateTime startDate);
5759

58-
// 인기 스택 가져오기 (중복 제거)
60+
// 인기 스택 조회
5961
@Query(value = """
60-
SELECT r.tech_field
61-
FROM register r
62-
WHERE r.created_at BETWEEN :start AND :end
63-
AND r.tech_field IS NOT NULL
64-
GROUP BY r.tech_field
65-
HAVING COUNT(*) = (
66-
SELECT MAX(cnt) FROM (
67-
SELECT COUNT(*) AS cnt
68-
FROM register
69-
WHERE created_at BETWEEN :start AND :end
70-
AND tech_field IS NOT NULL
71-
GROUP BY tech_field
72-
) AS sub
73-
)
62+
SELECT rst.tech_stack
63+
FROM register r
64+
JOIN register_snippet_tech_stack rst ON r.id = rst.register_id
65+
WHERE (
66+
(:start IS NULL AND :end IS NULL) OR
67+
(r.created_at BETWEEN :start AND :end)
68+
)
69+
GROUP BY rst.tech_stack
70+
HAVING COUNT(*) = (
71+
SELECT MAX(cnt) FROM (
72+
SELECT COUNT(*) AS cnt
73+
FROM register r2
74+
JOIN register_snippet_tech_stack rst2 ON r2.id = rst2.register_id
75+
WHERE (
76+
(:start IS NULL AND :end IS NULL) OR
77+
(r2.created_at BETWEEN :start AND :end)
78+
)
79+
GROUP BY rst2.tech_stack
80+
) AS sub
81+
)
7482
""", nativeQuery = true)
7583
List<String> findPopularStack(@Param("start") LocalDateTime start, @Param("end") LocalDateTime end);
84+
7685
}

0 commit comments

Comments
 (0)