diff --git a/src/main/java/run/backend/BackendApplication.java b/src/main/java/run/backend/BackendApplication.java index 6979cc9..f3a7d9f 100644 --- a/src/main/java/run/backend/BackendApplication.java +++ b/src/main/java/run/backend/BackendApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +@EnableJpaAuditing @SpringBootApplication public class BackendApplication { diff --git a/src/main/java/run/backend/domain/auth/service/AuthService.java b/src/main/java/run/backend/domain/auth/service/AuthService.java index dd48c2c..dbbfb03 100644 --- a/src/main/java/run/backend/domain/auth/service/AuthService.java +++ b/src/main/java/run/backend/domain/auth/service/AuthService.java @@ -98,7 +98,6 @@ public TokenResponse completeSignup(SignupRequest signupRequest, MultipartFile p .age(signupRequest.age()) .oauthId(oauthId) .oauthType(OAuthType.valueOf(providerName.toUpperCase())) - .role(Role.USER) .profileImage(profileImageName) .build(); memberRepository.save(newMember); diff --git a/src/main/java/run/backend/domain/crew/entity/Crew.java b/src/main/java/run/backend/domain/crew/entity/Crew.java new file mode 100644 index 0000000..5dd1642 --- /dev/null +++ b/src/main/java/run/backend/domain/crew/entity/Crew.java @@ -0,0 +1,59 @@ +package run.backend.domain.crew.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import run.backend.global.common.BaseEntity; + +import java.math.BigDecimal; +import java.util.UUID; + +@Entity +@Getter +@Table(name = "crews") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Crew extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + private String description; + + private String image; + + @Column(name = "invite_code") + private String inviteCode; + + @Column(name = "member_count") + private Long memberCount; + + @Column(name = "monthly_distance_total") + private BigDecimal monthlyDistanceTotal; + + @Column(name = "monthly_time_total") + private Long monthlyTimeTotal; + + @Column(name = "monthly_score_total") + private BigDecimal monthlyScoreTotal; + + @Builder + public Crew ( + String name, + String description, + String image + ) { + this.name = name; + this.description = description; + this.image = image; + this.inviteCode = UUID.randomUUID().toString(); + this.memberCount = 1L; + this.monthlyDistanceTotal = BigDecimal.ZERO; + this.monthlyTimeTotal = 0L; + this.monthlyScoreTotal = BigDecimal.ZERO; + } +} diff --git a/src/main/java/run/backend/domain/crew/entity/JoinCrew.java b/src/main/java/run/backend/domain/crew/entity/JoinCrew.java new file mode 100644 index 0000000..2ef434a --- /dev/null +++ b/src/main/java/run/backend/domain/crew/entity/JoinCrew.java @@ -0,0 +1,60 @@ +package run.backend.domain.crew.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import run.backend.domain.crew.enums.JoinStatus; +import run.backend.domain.member.entity.Member; +import run.backend.domain.member.enums.Role; +import run.backend.global.common.BaseEntity; + +import java.time.LocalDate; + + +@Entity +@Getter +@Table(name = "join_crews") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class JoinCrew extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Enumerated(EnumType.STRING) + @Column(name = "join_status") + private JoinStatus joinStatus; + + @Enumerated(EnumType.STRING) + @Column(name = "crew_role") + private Role role; + + @Column(name = "joined_date") + private LocalDate joinedDate; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "crew_id") + private Crew crew; + + void approveJoin() { + this.role = Role.MEMBER; + this.joinedDate = LocalDate.now(); + this.joinStatus = JoinStatus.APPROVED; + } + + @Builder + public JoinCrew( + Member member, + Crew crew + ) { + this.crew = crew; + this.member = member; + this.joinStatus = JoinStatus.APPLIED; + } +} diff --git a/src/main/java/run/backend/domain/crew/enumerate/CrewMemberRole.java b/src/main/java/run/backend/domain/crew/enumerate/CrewMemberRole.java deleted file mode 100644 index 1053161..0000000 --- a/src/main/java/run/backend/domain/crew/enumerate/CrewMemberRole.java +++ /dev/null @@ -1,4 +0,0 @@ -package run.backend.domain.crew.enumerate; - -public enum CrewMemberRole { -} diff --git a/src/main/java/run/backend/domain/crew/enums/JoinStatus.java b/src/main/java/run/backend/domain/crew/enums/JoinStatus.java new file mode 100644 index 0000000..146be82 --- /dev/null +++ b/src/main/java/run/backend/domain/crew/enums/JoinStatus.java @@ -0,0 +1,16 @@ +package run.backend.domain.crew.enums; + +import lombok.Getter; + +@Getter +public enum JoinStatus { + + APPLIED("가입 요청"), + APPROVED("가입 승인"); + + private final String description; + + JoinStatus(String description) { + this.description = description; + } +} diff --git a/src/main/java/run/backend/domain/crew/service/CrewService.java b/src/main/java/run/backend/domain/crew/service/CrewService.java index 70ea34f..f28d31e 100644 --- a/src/main/java/run/backend/domain/crew/service/CrewService.java +++ b/src/main/java/run/backend/domain/crew/service/CrewService.java @@ -2,7 +2,7 @@ import run.backend.domain.crew.dto.request.CrewInfoRequest; import run.backend.domain.crew.dto.response.*; -import run.backend.domain.crew.enumerate.CrewMemberRole; +import run.backend.domain.member.enums.Role; import java.time.YearMonth; @@ -20,7 +20,7 @@ public interface CrewService { CrewMemberResponse getCrewMemberProfile(Long crewId); - void updateCrewMemberRole(Long memberId, CrewMemberRole crewMemberRole); + void updateCrewMemberRole(Long memberId, Role role); CrewSearchResponse getRankCrew(); } diff --git a/src/main/java/run/backend/domain/event/entity/Event.java b/src/main/java/run/backend/domain/event/entity/Event.java new file mode 100644 index 0000000..af17ea1 --- /dev/null +++ b/src/main/java/run/backend/domain/event/entity/Event.java @@ -0,0 +1,79 @@ +package run.backend.domain.event.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import run.backend.domain.crew.entity.Crew; +import run.backend.domain.member.entity.Member; +import run.backend.domain.record.entity.CrewRecord; +import run.backend.global.common.BaseEntity; + +import java.time.LocalDate; +import java.time.LocalTime; + + +@Entity +@Getter +@Table(name = "events") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Event extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + + private LocalDate date; + + @Column(name = "start_time") + private LocalTime startTime; + + @Column(name = "end_time") + private LocalTime endTime; + + private String place; + + @Column(name = "expected_participants") + private Long expectedParticipants; + + @Column(name = "actual_participants") + private Long actualParticipants; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "crew_id") + private Crew crew; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "record_id") + private CrewRecord record; + + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "running_captain") + private Member member; + + @Builder + public Event( + String title, + LocalDate date, + LocalTime startTime, + LocalTime endTime, + String place, + Crew crew, + CrewRecord record, + Member member + ) { + this.title = title; + this.date = date; + this.startTime = startTime; + this.endTime = endTime; + this.place = place; + this.expectedParticipants = 1L; + this.actualParticipants = 0L; + this.crew = crew; + this.record = record; + this.member = member; + } +} diff --git a/src/main/java/run/backend/domain/event/entity/JoinEvent.java b/src/main/java/run/backend/domain/event/entity/JoinEvent.java new file mode 100644 index 0000000..0633682 --- /dev/null +++ b/src/main/java/run/backend/domain/event/entity/JoinEvent.java @@ -0,0 +1,40 @@ +package run.backend.domain.event.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import run.backend.domain.member.entity.Member; + +@Entity +@Getter +@Table(name = "join_events") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class JoinEvent { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "is_running") + private boolean isRunning; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "event_id") + private Event event; + + @Builder + public JoinEvent( + Member member, + Event event + ) { + this.isRunning = false; + this.member = member; + this.event = event; + } +} diff --git a/src/main/java/run/backend/domain/event/entity/PeriodicEvent.java b/src/main/java/run/backend/domain/event/entity/PeriodicEvent.java new file mode 100644 index 0000000..5e6c839 --- /dev/null +++ b/src/main/java/run/backend/domain/event/entity/PeriodicEvent.java @@ -0,0 +1,71 @@ +package run.backend.domain.event.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import run.backend.domain.crew.entity.Crew; +import run.backend.domain.event.enums.RepeatCycle; +import run.backend.domain.event.enums.WeekDay; +import run.backend.global.common.BaseEntity; + +import java.time.LocalDate; +import java.time.LocalTime; + +@Entity +@Getter +@Table(name = "periodic_events") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class PeriodicEvent extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String title; + + @Column(name = "base_date") + private LocalDate baseDate; + + @Enumerated(EnumType.STRING) + @Column(name = "repeat_cycle") + private RepeatCycle repeatCycle; + + @Enumerated(EnumType.STRING) + @Column(name = "repeat_days") + private WeekDay repeatDays; + + @Column(name = "start_time") + private LocalTime startTime; + + @Column(name = "end_time") + private LocalTime endTime; + + private String place; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "crew_id") + private Crew crew; + + @Builder + public PeriodicEvent( + String title, + LocalDate baseDate, + RepeatCycle repeatCycle, + WeekDay repeatDays, + LocalTime startTime, + LocalTime endTime, + String place, + Crew crew + ) { + this.title = title; + this.baseDate = baseDate; + this.repeatCycle = repeatCycle; + this.repeatDays = repeatDays; + this.startTime = startTime; + this.endTime = endTime; + this.place = place; + this.crew = crew; + } +} diff --git a/src/main/java/run/backend/domain/event/enums/RepeatCycle.java b/src/main/java/run/backend/domain/event/enums/RepeatCycle.java new file mode 100644 index 0000000..907dd63 --- /dev/null +++ b/src/main/java/run/backend/domain/event/enums/RepeatCycle.java @@ -0,0 +1,16 @@ +package run.backend.domain.event.enums; + +import lombok.Getter; + +@Getter +public enum RepeatCycle { + + NONE("주기 없음"), + WEEKLY("1주 마다"); + + private final String description; + + RepeatCycle(String description) { + this.description = description; + } +} diff --git a/src/main/java/run/backend/domain/event/enums/WeekDay.java b/src/main/java/run/backend/domain/event/enums/WeekDay.java new file mode 100644 index 0000000..b0b16b2 --- /dev/null +++ b/src/main/java/run/backend/domain/event/enums/WeekDay.java @@ -0,0 +1,23 @@ +package run.backend.domain.event.enums; + +import lombok.Getter; + +@Getter +public enum WeekDay { + + MONDAY(1, "월요일"), + TUESDAY(2, "화요일"), + WEDNESDAY(3, "수요일"), + THURSDAY(4, "목요일"), + FRIDAY(5, "금요일"), + SATURDAY(6, "토요일"), + SUNDAY(7, "일요일"); + + private final int order; + private final String description; + + WeekDay(int order, String description) { + this.order = order; + this.description = description; + } +} diff --git a/src/main/java/run/backend/domain/member/entity/Member.java b/src/main/java/run/backend/domain/member/entity/Member.java index 073595f..962995b 100644 --- a/src/main/java/run/backend/domain/member/entity/Member.java +++ b/src/main/java/run/backend/domain/member/entity/Member.java @@ -7,7 +7,6 @@ import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; -import java.time.LocalDateTime; import lombok.AccessLevel; import lombok.Builder; import lombok.Getter; @@ -15,12 +14,13 @@ import run.backend.domain.member.enums.Gender; import run.backend.domain.member.enums.OAuthType; import run.backend.domain.member.enums.Role; +import run.backend.global.common.BaseEntity; @Entity @Getter @Table(name = "members") @NoArgsConstructor(access = AccessLevel.PROTECTED) -public class Member { +public class Member extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -47,14 +47,8 @@ public class Member { private boolean pushEnabled; - private LocalDateTime createdAt; - - private LocalDateTime updatedAt; - - private LocalDateTime deletedAt; - @Builder - public Member(String username, String nickname, Gender gender, int age, String oauthId, OAuthType oauthType, String profileImage, Role role) { + public Member(String username, String nickname, Gender gender, int age, String oauthId, OAuthType oauthType, String profileImage) { this.username = username; this.nickname = nickname; this.gender = gender; @@ -63,7 +57,6 @@ public Member(String username, String nickname, Gender gender, int age, String o this.oauthType = oauthType; this.profileImage = profileImage; this.pushEnabled = true; - this.role = Role.USER; - this.createdAt = LocalDateTime.now(); + this.role = Role.NONE; } } diff --git a/src/main/java/run/backend/domain/member/enums/Role.java b/src/main/java/run/backend/domain/member/enums/Role.java index 4f6c675..253c471 100644 --- a/src/main/java/run/backend/domain/member/enums/Role.java +++ b/src/main/java/run/backend/domain/member/enums/Role.java @@ -1,5 +1,17 @@ package run.backend.domain.member.enums; +import lombok.Getter; + +@Getter public enum Role { - USER, ADMIN + NONE("앱유저"), + MEMBER("크루원"), + LEADER("크루장"), + MANAGER("운영진"); + + private final String description; + + Role(String description) { + this.description = description; + } } diff --git a/src/main/java/run/backend/domain/notification/entity/Notification.java b/src/main/java/run/backend/domain/notification/entity/Notification.java new file mode 100644 index 0000000..7f1db66 --- /dev/null +++ b/src/main/java/run/backend/domain/notification/entity/Notification.java @@ -0,0 +1,57 @@ +package run.backend.domain.notification.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import run.backend.domain.member.entity.Member; +import run.backend.domain.notification.enums.MessageType; +import run.backend.global.common.BaseEntity; + +@Entity +@Getter +@Table(name = "notifications") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class Notification extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String message; + + @Enumerated(EnumType.STRING) + @Column(name = "message_type") + private MessageType messageType; + + @Column(name = "target_id") + private Long targetId; // crew 가입 요청이라면 targetId에 joinCrew id 저장 + + @Column(name = "is_read") + private boolean isRead; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "sender_id") + private Member sender; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "receiver_id") + private Member receiver; + + @Builder + public Notification( + String message, + MessageType messageType, + Long targetId, + Member sender, + Member receiver + ) { + this.message = message; + this.messageType = messageType; + this.targetId = targetId; + this.isRead = false; + this.sender = sender; + this.receiver = receiver; + } +} diff --git a/src/main/java/run/backend/domain/notification/enums/MessageType.java b/src/main/java/run/backend/domain/notification/enums/MessageType.java new file mode 100644 index 0000000..d93357a --- /dev/null +++ b/src/main/java/run/backend/domain/notification/enums/MessageType.java @@ -0,0 +1,16 @@ +package run.backend.domain.notification.enums; + +import lombok.Getter; + +@Getter +public enum MessageType { + + BATTLE("대결"), + CREW("크루"); + + private final String description; + + MessageType(String description) { + this.description = description; + } +} diff --git a/src/main/java/run/backend/domain/record/entity/CrewRecord.java b/src/main/java/run/backend/domain/record/entity/CrewRecord.java new file mode 100644 index 0000000..c122877 --- /dev/null +++ b/src/main/java/run/backend/domain/record/entity/CrewRecord.java @@ -0,0 +1,56 @@ +package run.backend.domain.record.entity; + +import jakarta.persistence.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import run.backend.global.common.BaseEntity; + +import java.math.BigDecimal; +import java.time.LocalTime; + +@Entity +@Getter +@Table(name = "crew_records") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class CrewRecord extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private BigDecimal distance; + + @Column(name = "step_count") + private Long stepCount; + + @Column(name = "duration_time") + private Long durationTime; + + @Column(name = "average_pace") + private Long averagePace; + + @Column(name = "start_time") + private LocalTime startTime; + + @Column(name = "end_time") + private LocalTime endTime; + + @Builder + public CrewRecord( + BigDecimal distance, + Long stepCount, + Long durationTime, + Long averagePace, + LocalTime startTime, + LocalTime endTime + ) { + this.distance = distance; + this.stepCount = stepCount; + this.durationTime = durationTime; + this.averagePace = averagePace; + this.startTime = startTime; + this.endTime = endTime; + } +} diff --git a/src/main/java/run/backend/global/common/BaseEntity.java b/src/main/java/run/backend/global/common/BaseEntity.java new file mode 100644 index 0000000..5ee78e1 --- /dev/null +++ b/src/main/java/run/backend/global/common/BaseEntity.java @@ -0,0 +1,30 @@ +package run.backend.global.common; + +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; +import lombok.Getter; +import lombok.Setter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import java.time.LocalDateTime; + +@Getter +@Setter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseEntity { + + @CreatedDate + @Column(name = "created_at") + private LocalDateTime createdAt; + + @LastModifiedDate + @Column(name = "updated_at") + private LocalDateTime updatedAt; + + @Column(name = "deleted_at") + private LocalDateTime deletedAt; +} diff --git a/src/test/java/run/backend/domain/auth/service/AuthServiceTest.java b/src/test/java/run/backend/domain/auth/service/AuthServiceTest.java index b419e0c..828c058 100644 --- a/src/test/java/run/backend/domain/auth/service/AuthServiceTest.java +++ b/src/test/java/run/backend/domain/auth/service/AuthServiceTest.java @@ -119,7 +119,6 @@ void setUp() throws Exception { .age(25) .oauthId("123456789") .oauthType(OAuthType.GOOGLE) - .role(Role.USER) .build(); tokenResponse = new TokenResponse("access-token", "refresh-token"); @@ -302,7 +301,7 @@ void completeSignup_Success() { member.getAge() == 25 && member.getOauthId().equals("123456789") && member.getOauthType().equals(OAuthType.GOOGLE) && - member.getRole().equals(Role.USER) && + member.getRole().equals(Role.NONE) && member.getUsername().equals("테스트 유저") )); verify(jwtTokenProvider).generateToken(any());