Skip to content

Commit 6d8abe3

Browse files
authored
feat : 홈화면 추천 스터디 (#403)
1 parent 111e844 commit 6d8abe3

28 files changed

+1589
-0
lines changed

src/main/java/com/gamzabat/algohub/feature/group/studygroup/repository/GroupMemberRepository.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
package com.gamzabat.algohub.feature.group.studygroup.repository;
22

3+
import java.time.LocalDate;
34
import java.util.List;
45
import java.util.Optional;
56

67
import org.springframework.data.jpa.repository.JpaRepository;
78
import org.springframework.data.jpa.repository.Modifying;
89
import org.springframework.data.jpa.repository.Query;
10+
import org.springframework.data.repository.query.Param;
911

1012
import com.gamzabat.algohub.feature.group.studygroup.domain.GroupMember;
1113
import com.gamzabat.algohub.feature.group.studygroup.domain.StudyGroup;
@@ -31,4 +33,13 @@ public interface GroupMemberRepository extends JpaRepository<GroupMember, Long>
3133
@Modifying
3234
@Query("delete from GroupMember gm where gm.studyGroup = :studyGroup")
3335
void deleteAllByStudyGroup(StudyGroup studyGroup);
36+
37+
@Query("SELECT COUNT(gm) FROM GroupMember gm WHERE gm.studyGroup = :studyGroup AND gm.joinDate BETWEEN :start AND :end")
38+
Integer countByStudyGroupAndJoinDateBetween(@Param("studyGroup") StudyGroup studyGroup,
39+
@Param("start") LocalDate start, @Param("end") LocalDate end);
40+
41+
@Query("SELECT COUNT(gm) FROM GroupMember gm WHERE gm.studyGroup = :studyGroup AND gm.joinDate < :date")
42+
Integer countByStudyGroupAndJoinDateBefore(@Param("studyGroup") StudyGroup studyGroup,
43+
@Param("date") LocalDate date);
44+
3445
}

src/main/java/com/gamzabat/algohub/feature/problem/repository/ProblemRepository.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import org.springframework.data.jpa.repository.JpaRepository;
1010
import org.springframework.data.jpa.repository.Modifying;
1111
import org.springframework.data.jpa.repository.Query;
12+
import org.springframework.data.repository.query.Param;
1213

1314
import com.gamzabat.algohub.feature.group.studygroup.domain.StudyGroup;
1415
import com.gamzabat.algohub.feature.problem.domain.Problem;
@@ -62,4 +63,8 @@ AND EXISTS (
6263
@Modifying
6364
@Query("update Problem p set p.deletedAt = CURRENT_TIMESTAMP where p.studyGroup = :studyGroup")
6465
void deleteAllByStudyGroup(StudyGroup studyGroup);
66+
67+
@Query("SELECT p FROM Problem p WHERE p.studyGroup = :studyGroup AND p.startDate BETWEEN :start AND :end AND p.deletedAt IS NULL")
68+
List<Problem> findAllByStudyGroupAndStartDateBetween(@Param("studyGroup") StudyGroup studyGroup,
69+
@Param("start") LocalDate start, @Param("end") LocalDate end);
6570
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.gamzabat.algohub.feature.recommendation.controller;
2+
3+
import org.springframework.http.ResponseEntity;
4+
import org.springframework.web.bind.annotation.GetMapping;
5+
import org.springframework.web.bind.annotation.RequestMapping;
6+
import org.springframework.web.bind.annotation.RestController;
7+
8+
import com.gamzabat.algohub.common.annotation.AuthedUser;
9+
import com.gamzabat.algohub.feature.recommendation.dto.HomeRecommendationsResponse;
10+
import com.gamzabat.algohub.feature.recommendation.service.RecommendationService;
11+
import com.gamzabat.algohub.feature.user.domain.User;
12+
13+
import io.swagger.v3.oas.annotations.Operation;
14+
import io.swagger.v3.oas.annotations.tags.Tag;
15+
import lombok.RequiredArgsConstructor;
16+
17+
@RestController
18+
@RequiredArgsConstructor
19+
@RequestMapping("/api")
20+
@Tag(name = "추천 API", description = "홈 화면 추천 스터디 API")
21+
public class RecommendationController {
22+
23+
private final RecommendationService recommendationService;
24+
25+
@GetMapping("/home/recommendations")
26+
@Operation(summary = "홈 추천 스터디 조회 API")
27+
public ResponseEntity<HomeRecommendationsResponse> getHomeRecommendations(
28+
@AuthedUser User user
29+
) {
30+
HomeRecommendationsResponse response = recommendationService.getHomeRecommendations(user.getId());
31+
return ResponseEntity.ok().body(response);
32+
}
33+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package com.gamzabat.algohub.feature.recommendation.domain;
2+
3+
import java.time.LocalDateTime;
4+
5+
import com.gamzabat.algohub.feature.group.studygroup.domain.StudyGroup;
6+
7+
import jakarta.persistence.Column;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.FetchType;
10+
import jakarta.persistence.GeneratedValue;
11+
import jakarta.persistence.GenerationType;
12+
import jakarta.persistence.Id;
13+
import jakarta.persistence.JoinColumn;
14+
import jakarta.persistence.ManyToOne;
15+
import jakarta.persistence.Table;
16+
import jakarta.validation.constraints.NotNull;
17+
import lombok.AccessLevel;
18+
import lombok.AllArgsConstructor;
19+
import lombok.Builder;
20+
import lombok.Getter;
21+
import lombok.NoArgsConstructor;
22+
23+
@Entity
24+
@Table(name = "group_activity_weekly")
25+
@Getter
26+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
27+
@AllArgsConstructor
28+
@Builder
29+
public class GroupActivityWeekly {
30+
31+
@Id
32+
@GeneratedValue(strategy = GenerationType.IDENTITY)
33+
private Long id;
34+
35+
@ManyToOne(fetch = FetchType.LAZY)
36+
@JoinColumn(name = "study_group_id", nullable = false)
37+
private StudyGroup studyGroup;
38+
39+
@Column(name = "week_start", nullable = false)
40+
private LocalDateTime weekStart;
41+
42+
@Column(name = "week_end", nullable = false)
43+
private LocalDateTime weekEnd;
44+
45+
@Column(name = "submissions", nullable = false)
46+
private Integer submissions;
47+
48+
@Column(name = "comments", nullable = false)
49+
private Integer comments;
50+
51+
@Column(name = "active_score", nullable = false)
52+
private Double activeScore;
53+
54+
@Builder
55+
public GroupActivityWeekly(
56+
@NotNull StudyGroup studyGroup,
57+
@NotNull LocalDateTime weekStart,
58+
@NotNull LocalDateTime weekEnd,
59+
@NotNull Integer submissions,
60+
@NotNull Integer comments,
61+
@NotNull Double activeScore) {
62+
this.studyGroup = studyGroup;
63+
this.weekStart = weekStart;
64+
this.weekEnd = weekEnd;
65+
this.submissions = submissions;
66+
this.comments = comments;
67+
this.activeScore = activeScore;
68+
}
69+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package com.gamzabat.algohub.feature.recommendation.domain;
2+
3+
import java.time.LocalDateTime;
4+
5+
import com.gamzabat.algohub.feature.group.studygroup.domain.StudyGroup;
6+
7+
import jakarta.persistence.Column;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.FetchType;
10+
import jakarta.persistence.GeneratedValue;
11+
import jakarta.persistence.GenerationType;
12+
import jakarta.persistence.Id;
13+
import jakarta.persistence.JoinColumn;
14+
import jakarta.persistence.ManyToOne;
15+
import jakarta.persistence.Table;
16+
import jakarta.validation.constraints.NotNull;
17+
import lombok.AllArgsConstructor;
18+
import lombok.Builder;
19+
import lombok.Getter;
20+
import lombok.NoArgsConstructor;
21+
22+
@Entity
23+
@Table(name = "group_difficulty_monthly_rolling")
24+
@Getter
25+
@NoArgsConstructor
26+
@AllArgsConstructor
27+
public class GroupDifficultyMonthlyRolling {
28+
29+
@Id
30+
@GeneratedValue(strategy = GenerationType.IDENTITY)
31+
private Long id;
32+
33+
@ManyToOne(fetch = FetchType.LAZY)
34+
@JoinColumn(name = "study_group_id", nullable = false)
35+
private StudyGroup studyGroup;
36+
37+
@Column(name = "window_start", nullable = false)
38+
private LocalDateTime windowStart;
39+
40+
@Column(name = "window_end", nullable = false)
41+
private LocalDateTime windowEnd;
42+
43+
@Column(name = "avg_difficulty", nullable = false)
44+
private Double avgDifficulty;
45+
46+
@Builder
47+
public GroupDifficultyMonthlyRolling(
48+
@NotNull StudyGroup studyGroup,
49+
@NotNull LocalDateTime windowStart,
50+
@NotNull LocalDateTime windowEnd,
51+
@NotNull Double avgDifficulty) {
52+
this.studyGroup = studyGroup;
53+
this.windowStart = windowStart;
54+
this.windowEnd = windowEnd;
55+
this.avgDifficulty = avgDifficulty;
56+
}
57+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package com.gamzabat.algohub.feature.recommendation.domain;
2+
3+
import java.time.LocalDateTime;
4+
5+
import com.gamzabat.algohub.feature.group.studygroup.domain.StudyGroup;
6+
7+
import jakarta.persistence.Column;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.FetchType;
10+
import jakarta.persistence.GeneratedValue;
11+
import jakarta.persistence.GenerationType;
12+
import jakarta.persistence.Id;
13+
import jakarta.persistence.JoinColumn;
14+
import jakarta.persistence.ManyToOne;
15+
import jakarta.persistence.Table;
16+
import jakarta.validation.constraints.NotNull;
17+
import lombok.AllArgsConstructor;
18+
import lombok.Builder;
19+
import lombok.Getter;
20+
import lombok.NoArgsConstructor;
21+
22+
@Entity
23+
@Table(name = "group_join_monthly_rolling")
24+
@Getter
25+
@NoArgsConstructor
26+
@AllArgsConstructor
27+
public class GroupJoinMonthlyRolling {
28+
29+
@Id
30+
@GeneratedValue(strategy = GenerationType.IDENTITY)
31+
private Long id;
32+
33+
@ManyToOne(fetch = FetchType.LAZY)
34+
@JoinColumn(name = "study_group_id", nullable = false)
35+
private StudyGroup studyGroup;
36+
37+
@Column(name = "window_start", nullable = false)
38+
private LocalDateTime windowStart;
39+
40+
@Column(name = "window_end", nullable = false)
41+
private LocalDateTime windowEnd;
42+
43+
@Column(name = "new_members", nullable = false)
44+
private Integer newMembers;
45+
46+
@Column(name = "members_before_window", nullable = false)
47+
private Integer membersBeforeWindow;
48+
49+
@Column(name = "join_rate", nullable = false)
50+
private Double joinRate;
51+
52+
@Builder
53+
public GroupJoinMonthlyRolling(
54+
@NotNull StudyGroup studyGroup,
55+
@NotNull LocalDateTime windowStart,
56+
@NotNull LocalDateTime windowEnd,
57+
@NotNull Integer newMembers,
58+
@NotNull Integer membersBeforeWindow,
59+
@NotNull Double joinRate) {
60+
this.studyGroup = studyGroup;
61+
this.windowStart = windowStart;
62+
this.windowEnd = windowEnd;
63+
this.newMembers = newMembers;
64+
this.membersBeforeWindow = membersBeforeWindow;
65+
this.joinRate = joinRate;
66+
}
67+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
package com.gamzabat.algohub.feature.recommendation.domain;
2+
3+
import java.time.LocalDateTime;
4+
5+
import com.gamzabat.algohub.feature.group.studygroup.domain.StudyGroup;
6+
7+
import jakarta.persistence.Column;
8+
import jakarta.persistence.Entity;
9+
import jakarta.persistence.EnumType;
10+
import jakarta.persistence.Enumerated;
11+
import jakarta.persistence.FetchType;
12+
import jakarta.persistence.GeneratedValue;
13+
import jakarta.persistence.GenerationType;
14+
import jakarta.persistence.Id;
15+
import jakarta.persistence.JoinColumn;
16+
import jakarta.persistence.ManyToOne;
17+
import jakarta.persistence.Table;
18+
import jakarta.validation.constraints.NotNull;
19+
import lombok.AllArgsConstructor;
20+
import lombok.Builder;
21+
import lombok.Getter;
22+
import lombok.NoArgsConstructor;
23+
24+
@Entity
25+
@Table(name = "study_group_tag")
26+
@Getter
27+
@NoArgsConstructor
28+
@AllArgsConstructor
29+
public class StudyGroupTag {
30+
31+
@Id
32+
@GeneratedValue(strategy = GenerationType.IDENTITY)
33+
private Long id;
34+
35+
@ManyToOne(fetch = FetchType.LAZY)
36+
@JoinColumn(name = "study_group_id", nullable = false)
37+
private StudyGroup studyGroup;
38+
39+
@Enumerated(EnumType.STRING)
40+
@Column(name = "tag_type", nullable = false, length = 64)
41+
private TagType tagType;
42+
43+
@Column(name = "score", nullable = false)
44+
private Double score;
45+
46+
@Column(name = "first_achieved_at", nullable = false)
47+
private LocalDateTime firstAchievedAt;
48+
49+
@Column(name = "window_start", nullable = false)
50+
private LocalDateTime windowStart;
51+
52+
@Column(name = "window_end", nullable = false)
53+
private LocalDateTime windowEnd;
54+
55+
@Column(name = "computed_at", nullable = false)
56+
private LocalDateTime computedAt;
57+
58+
@Builder
59+
public StudyGroupTag(
60+
@NotNull StudyGroup studyGroup,
61+
@NotNull TagType tagType,
62+
@NotNull Double score,
63+
@NotNull LocalDateTime firstAchievedAt,
64+
@NotNull LocalDateTime windowStart,
65+
@NotNull LocalDateTime windowEnd,
66+
@NotNull LocalDateTime computedAt) {
67+
this.studyGroup = studyGroup;
68+
this.tagType = tagType;
69+
this.score = score;
70+
this.firstAchievedAt = firstAchievedAt;
71+
this.windowStart = windowStart;
72+
this.windowEnd = windowEnd;
73+
this.computedAt = computedAt;
74+
}
75+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.gamzabat.algohub.feature.recommendation.domain;
2+
3+
public enum TagType {
4+
MOST_ACTIVE_THIS_WEEK,
5+
HIGH_JOIN_RATE_RECENT,
6+
SIMILAR_DIFFICULTY
7+
}

0 commit comments

Comments
 (0)