From 1fd3cbf9c66ec3ae6e13c44c38369c79bf6aeee6 Mon Sep 17 00:00:00 2001 From: yskim6772 Date: Wed, 1 Oct 2025 13:54:56 +0900 Subject: [PATCH 1/6] =?UTF-8?q?[Feat]=20#11=20=EC=B6=9C=EA=B7=BC=ED=95=98?= =?UTF-8?q?=EA=B8=B0,=20=ED=87=B4=EA=B7=BC=ED=95=98=EA=B8=B0=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/attendance/model/Attendance.java | 49 +++++++++++++++ .../repository/AttendanceRepository.java | 12 ++++ .../club/controller/ClubController.java | 28 +++++++++ .../club/repository/ClubRepository.java | 7 +++ .../domain/club/service/ClubService.java | 63 +++++++++++++++++++ .../user/repository/UserRepository.java | 2 + .../global/common/response/ErrorCode.java | 4 ++ src/main/resources/application.yml | 2 +- 8 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/model/Attendance.java create mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/repository/AttendanceRepository.java create mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java create mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/repository/ClubRepository.java create mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/model/Attendance.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/model/Attendance.java new file mode 100644 index 0000000..a978d9a --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/model/Attendance.java @@ -0,0 +1,49 @@ +package com.WhoIsRoom.WhoIs_Server.domain.attendance.model; + +import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; +import com.WhoIsRoom.WhoIs_Server.domain.user.model.User; +import com.WhoIsRoom.WhoIs_Server.global.common.model.BaseEntity; +import jakarta.persistence.*; +import lombok.*; + +import java.time.LocalDateTime; + +@Entity +@Table(name = "attendances") +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@Getter @Setter +public class Attendance extends BaseEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "user_id", nullable = false) + private User user; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "club_id", nullable = false) + private Club club; + + @Column(name = "check_in_at", nullable = false) + private LocalDateTime checkInAt; + + @Column(name = "check_out_at") + private LocalDateTime checkOutAt; + + @Builder + public Attendance(User user, Club club, LocalDateTime checkInAt) { + this.user = user; + this.club = club; + this.checkInAt = checkInAt; + } + + public void checkOut() { + this.checkOutAt = LocalDateTime.now(); + } + + public boolean isActive() { + return this.checkOutAt == null; + } +} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/repository/AttendanceRepository.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/repository/AttendanceRepository.java new file mode 100644 index 0000000..1b86fe4 --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/repository/AttendanceRepository.java @@ -0,0 +1,12 @@ +package com.WhoIsRoom.WhoIs_Server.domain.attendance.repository; + +import com.WhoIsRoom.WhoIs_Server.domain.attendance.model.Attendance; +import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; +import com.WhoIsRoom.WhoIs_Server.domain.user.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface AttendanceRepository extends JpaRepository { + Optional findByUserAndClubAndCheckOutAtIsNull(User user, Club club); +} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java new file mode 100644 index 0000000..7e5c647 --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java @@ -0,0 +1,28 @@ +package com.WhoIsRoom.WhoIs_Server.domain.club.controller; + +import com.WhoIsRoom.WhoIs_Server.domain.club.service.ClubService; +import com.WhoIsRoom.WhoIs_Server.global.common.response.BaseResponse; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/clubs") +public class ClubController { + + private final ClubService clubService; + + @PostMapping("/{clubId}/check-in") + public BaseResponse checkIn(@PathVariable final Long clubId) { + clubService.checkIn(clubId); + return BaseResponse.ok(null); + } + + @DeleteMapping("/{clubId}/check-out") + public BaseResponse checkOut(@PathVariable final Long clubId) { + clubService.checkOut(clubId); + return BaseResponse.ok(null); + } +} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/repository/ClubRepository.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/repository/ClubRepository.java new file mode 100644 index 0000000..02fb5d2 --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/repository/ClubRepository.java @@ -0,0 +1,7 @@ +package com.WhoIsRoom.WhoIs_Server.domain.club.repository; + +import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ClubRepository extends JpaRepository { +} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java new file mode 100644 index 0000000..211eae3 --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java @@ -0,0 +1,63 @@ +package com.WhoIsRoom.WhoIs_Server.domain.club.service; + +import com.WhoIsRoom.WhoIs_Server.domain.attendance.model.Attendance; +import com.WhoIsRoom.WhoIs_Server.domain.attendance.repository.AttendanceRepository; +import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; +import com.WhoIsRoom.WhoIs_Server.domain.club.repository.ClubRepository; +import com.WhoIsRoom.WhoIs_Server.domain.user.model.User; +import com.WhoIsRoom.WhoIs_Server.domain.user.repository.UserRepository; +import com.WhoIsRoom.WhoIs_Server.global.common.exception.BusinessException; +import com.WhoIsRoom.WhoIs_Server.global.common.response.ErrorCode; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ClubService { + private final ClubRepository clubRepository; + private final UserRepository userRepository; + private final AttendanceRepository attendanceRepository; + + @Transactional + public void checkIn(Long clubId) { + Club club = clubRepository.findById(clubId) + .orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND)); + + User user = getCurrentUser(); + + attendanceRepository.findByUserAndClubAndCheckOutAtIsNull(user, club) + .ifPresent(a -> { throw new BusinessException(ErrorCode.ALREADY_CHECKED_IN); }); + + Attendance attendance = Attendance.builder() + .user(user) + .club(club) + .checkInAt(LocalDateTime.now()) + .build(); + attendanceRepository.save(attendance); + } + + @Transactional + public void checkOut(Long clubId) { + Club club = clubRepository.findById(clubId) + .orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND)); + + User user = getCurrentUser(); + + Attendance attendance = attendanceRepository.findByUserAndClubAndCheckOutAtIsNull(user, club) + .orElseThrow(() -> new BusinessException(ErrorCode.ATTENDANCE_NOT_FOUND)); + + attendance.checkOut(); + } + + private User getCurrentUser() { + String nickname = SecurityContextHolder.getContext().getAuthentication().getName(); + return userRepository.findByNickName(nickname) + .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); + } +} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/repository/UserRepository.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/repository/UserRepository.java index 6457836..e1cefba 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/repository/UserRepository.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/user/repository/UserRepository.java @@ -8,5 +8,7 @@ public interface UserRepository extends JpaRepository { Optional findByEmail(String email); boolean existsByEmail(String email); + + Optional findByNickName(String nickname); boolean existsByNickName(String nickName); } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java index 828eb4c..44abcd6 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java @@ -23,6 +23,10 @@ public enum ErrorCode{ USER_DUPLICATE_EMAIL(201, HttpStatus.BAD_REQUEST.value(), "중복된 이메일의 시용자가 있습니다."), USER_DUPLICATE_NICKNAME(202, HttpStatus.BAD_REQUEST.value(), "중복된 닉네임의 사용자가 있습니다."), + // Attendance + ATTENDANCE_NOT_FOUND(300, HttpStatus.BAD_REQUEST.value(), "출근 기록이 없습니다."), + ALREADY_CHECKED_IN(301, HttpStatus.BAD_REQUEST.value(), "이미 출근 중입니다."), + // Auth SECURITY_UNAUTHORIZED(600,HttpStatus.UNAUTHORIZED.value(), "인증 정보가 유효하지 않습니다"), INVALID_TOKEN_TYPE(601, HttpStatus.UNAUTHORIZED.value(), "토큰 타입이 유효하지 않습니다."), diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index b24d5f6..20fd30f 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -19,7 +19,7 @@ spring: driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: - ddl-auto: create + ddl-auto: update properties: hibernate: format_sql: true From 666ed125ddfcedd7a93ca7ee9a6594a6e0ff5f8f Mon Sep 17 00:00:00 2001 From: yskim6772 Date: Wed, 1 Oct 2025 14:24:31 +0900 Subject: [PATCH 2/6] =?UTF-8?q?[Feat]=20#11=20=EC=B6=9C=ED=87=B4=EA=B7=BC?= =?UTF-8?q?=20=EC=97=90=EB=9F=AC=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/attendance/model/Attendance.java | 49 ------------------- .../repository/AttendanceRepository.java | 12 ----- .../domain/club/service/ClubService.java | 41 ++++++++-------- .../domain/member/model/Member.java | 4 +- .../member/repository/MemberRepository.java | 12 +++++ .../global/common/response/ErrorCode.java | 10 +++- 6 files changed, 43 insertions(+), 85 deletions(-) delete mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/model/Attendance.java delete mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/repository/AttendanceRepository.java create mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/model/Attendance.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/model/Attendance.java deleted file mode 100644 index a978d9a..0000000 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/model/Attendance.java +++ /dev/null @@ -1,49 +0,0 @@ -package com.WhoIsRoom.WhoIs_Server.domain.attendance.model; - -import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; -import com.WhoIsRoom.WhoIs_Server.domain.user.model.User; -import com.WhoIsRoom.WhoIs_Server.global.common.model.BaseEntity; -import jakarta.persistence.*; -import lombok.*; - -import java.time.LocalDateTime; - -@Entity -@Table(name = "attendances") -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@Getter @Setter -public class Attendance extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "user_id", nullable = false) - private User user; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "club_id", nullable = false) - private Club club; - - @Column(name = "check_in_at", nullable = false) - private LocalDateTime checkInAt; - - @Column(name = "check_out_at") - private LocalDateTime checkOutAt; - - @Builder - public Attendance(User user, Club club, LocalDateTime checkInAt) { - this.user = user; - this.club = club; - this.checkInAt = checkInAt; - } - - public void checkOut() { - this.checkOutAt = LocalDateTime.now(); - } - - public boolean isActive() { - return this.checkOutAt == null; - } -} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/repository/AttendanceRepository.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/repository/AttendanceRepository.java deleted file mode 100644 index 1b86fe4..0000000 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/attendance/repository/AttendanceRepository.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.WhoIsRoom.WhoIs_Server.domain.attendance.repository; - -import com.WhoIsRoom.WhoIs_Server.domain.attendance.model.Attendance; -import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; -import com.WhoIsRoom.WhoIs_Server.domain.user.model.User; -import org.springframework.data.jpa.repository.JpaRepository; - -import java.util.Optional; - -public interface AttendanceRepository extends JpaRepository { - Optional findByUserAndClubAndCheckOutAtIsNull(User user, Club club); -} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java index 211eae3..e637f0c 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java @@ -1,9 +1,9 @@ package com.WhoIsRoom.WhoIs_Server.domain.club.service; -import com.WhoIsRoom.WhoIs_Server.domain.attendance.model.Attendance; -import com.WhoIsRoom.WhoIs_Server.domain.attendance.repository.AttendanceRepository; import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; import com.WhoIsRoom.WhoIs_Server.domain.club.repository.ClubRepository; +import com.WhoIsRoom.WhoIs_Server.domain.member.model.Member; +import com.WhoIsRoom.WhoIs_Server.domain.member.repository.MemberRepository; import com.WhoIsRoom.WhoIs_Server.domain.user.model.User; import com.WhoIsRoom.WhoIs_Server.domain.user.repository.UserRepository; import com.WhoIsRoom.WhoIs_Server.global.common.exception.BusinessException; @@ -14,45 +14,46 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.time.LocalDateTime; - @Slf4j @Service @RequiredArgsConstructor public class ClubService { private final ClubRepository clubRepository; private final UserRepository userRepository; - private final AttendanceRepository attendanceRepository; + private final MemberRepository memberRepository; @Transactional public void checkIn(Long clubId) { + User user = getCurrentUser(); + Club club = clubRepository.findById(clubId) - .orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND)); + .orElseThrow(() -> new BusinessException(ErrorCode.CLUB_NOT_FOUND)); - User user = getCurrentUser(); + Member member = memberRepository.findByUserAndClub(user, club) + .orElseThrow(() -> new BusinessException(ErrorCode.MEMBER_NOT_FOUND)); - attendanceRepository.findByUserAndClubAndCheckOutAtIsNull(user, club) - .ifPresent(a -> { throw new BusinessException(ErrorCode.ALREADY_CHECKED_IN); }); + if (Boolean.TRUE.equals(member.getIsExist())) { + throw new BusinessException(ErrorCode.ALREADY_CHECKED_IN); + } - Attendance attendance = Attendance.builder() - .user(user) - .club(club) - .checkInAt(LocalDateTime.now()) - .build(); - attendanceRepository.save(attendance); + member.setExist(true); } @Transactional public void checkOut(Long clubId) { + User user = getCurrentUser(); + Club club = clubRepository.findById(clubId) - .orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND)); + .orElseThrow(() -> new BusinessException(ErrorCode.CLUB_NOT_FOUND)); - User user = getCurrentUser(); + Member member = memberRepository.findByUserAndClub(user, club) + .orElseThrow(() -> new BusinessException(ErrorCode.MEMBER_NOT_FOUND)); - Attendance attendance = attendanceRepository.findByUserAndClubAndCheckOutAtIsNull(user, club) - .orElseThrow(() -> new BusinessException(ErrorCode.ATTENDANCE_NOT_FOUND)); + if (Boolean.FALSE.equals(member.getIsExist())) { + throw new BusinessException(ErrorCode.ATTENDANCE_NOT_FOUND); + } - attendance.checkOut(); + member.setExist(false); } private User getCurrentUser() { diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/model/Member.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/model/Member.java index bbc14d1..ed2d22a 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/model/Member.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/model/Member.java @@ -35,7 +35,7 @@ public Member(User user, Club club, Boolean isExist) { this.isExist = isExist; } - public void setExist(){ - this.isExist = true; + public void setExist(boolean isExist){ + this.isExist = isExist; } } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java new file mode 100644 index 0000000..8a47e1f --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java @@ -0,0 +1,12 @@ +package com.WhoIsRoom.WhoIs_Server.domain.member.repository; + +import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; +import com.WhoIsRoom.WhoIs_Server.domain.member.model.Member; +import com.WhoIsRoom.WhoIs_Server.domain.user.model.User; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface MemberRepository extends JpaRepository { + Optional findByUserAndClub(User user, Club club); +} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java index 44abcd6..6389427 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java @@ -23,9 +23,15 @@ public enum ErrorCode{ USER_DUPLICATE_EMAIL(201, HttpStatus.BAD_REQUEST.value(), "중복된 이메일의 시용자가 있습니다."), USER_DUPLICATE_NICKNAME(202, HttpStatus.BAD_REQUEST.value(), "중복된 닉네임의 사용자가 있습니다."), + // Club + CLUB_NOT_FOUND(300, HttpStatus.NOT_FOUND.value(), "해당 동아리를 찾을 수 없습니다."), + + // Member + MEMBER_NOT_FOUND(400, HttpStatus.NOT_FOUND.value(), "해당 동아리의 회원이 아닙니다."), + // Attendance - ATTENDANCE_NOT_FOUND(300, HttpStatus.BAD_REQUEST.value(), "출근 기록이 없습니다."), - ALREADY_CHECKED_IN(301, HttpStatus.BAD_REQUEST.value(), "이미 출근 중입니다."), + ATTENDANCE_NOT_FOUND(500, HttpStatus.BAD_REQUEST.value(), "출근 기록이 없습니다."), + ALREADY_CHECKED_IN(501, HttpStatus.BAD_REQUEST.value(), "이미 출근 중입니다."), // Auth SECURITY_UNAUTHORIZED(600,HttpStatus.UNAUTHORIZED.value(), "인증 정보가 유효하지 않습니다"), From 05321f02b1228c94730e62a5a84791a4a8092886 Mon Sep 17 00:00:00 2001 From: yskim6772 Date: Wed, 1 Oct 2025 14:50:17 +0900 Subject: [PATCH 3/6] =?UTF-8?q?[Feat]=20#11=20=EB=82=B4=20=EB=8F=99?= =?UTF-8?q?=EC=95=84=EB=A6=AC=20=EC=B6=94=EA=B0=80=ED=95=98=EA=B8=B0=20API?= =?UTF-8?q?=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../club/controller/ClubController.java | 6 ++++++ .../domain/club/service/ClubService.java | 20 +++++++++++++++++++ .../global/common/response/ErrorCode.java | 5 +++-- 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java index 7e5c647..8610943 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java @@ -25,4 +25,10 @@ public BaseResponse checkOut(@PathVariable final Long clubId) { clubService.checkOut(clubId); return BaseResponse.ok(null); } + + @PostMapping("/{clubId}") + public BaseResponse joinClub(@PathVariable final Long clubId) { + clubService.joinClub(clubId); + return BaseResponse.ok(null); + } } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java index e637f0c..0b4ca3a 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java @@ -61,4 +61,24 @@ private User getCurrentUser() { return userRepository.findByNickName(nickname) .orElseThrow(() -> new BusinessException(ErrorCode.USER_NOT_FOUND)); } + + @Transactional + public void joinClub(Long clubId) { + User user = getCurrentUser(); + + Club club = clubRepository.findById(clubId) + .orElseThrow(() -> new BusinessException(ErrorCode.CLUB_NOT_FOUND)); + + memberRepository.findByUserAndClub(user, club).ifPresent(member -> { + throw new BusinessException(ErrorCode.ALREADY_MEMBER); + }); + + Member member = Member.builder() + .user(user) + .club(club) + .isExist(false) + .build(); + + memberRepository.save(member); + } } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java index 6389427..c47fb4d 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java @@ -20,14 +20,15 @@ public enum ErrorCode{ // User USER_NOT_FOUND(200, HttpStatus.NOT_FOUND.value(), "사용자를 찾을 수 없습니다."), - USER_DUPLICATE_EMAIL(201, HttpStatus.BAD_REQUEST.value(), "중복된 이메일의 시용자가 있습니다."), + USER_DUPLICATE_EMAIL(201, HttpStatus.BAD_REQUEST.value(), "중복된 이메일의 사용자가 있습니다."), USER_DUPLICATE_NICKNAME(202, HttpStatus.BAD_REQUEST.value(), "중복된 닉네임의 사용자가 있습니다."), // Club - CLUB_NOT_FOUND(300, HttpStatus.NOT_FOUND.value(), "해당 동아리를 찾을 수 없습니다."), + CLUB_NOT_FOUND(300, HttpStatus.NOT_FOUND.value(), "해당 동아리가 존재하지 않습니다."), // Member MEMBER_NOT_FOUND(400, HttpStatus.NOT_FOUND.value(), "해당 동아리의 회원이 아닙니다."), + ALREADY_MEMBER(401, HttpStatus.BAD_REQUEST.value(), "이미 동아리에 가입된 사용자입니다."), // Attendance ATTENDANCE_NOT_FOUND(500, HttpStatus.BAD_REQUEST.value(), "출근 기록이 없습니다."), From 69fcbea811cae7ca8a2122b12017ead8ca236dd5 Mon Sep 17 00:00:00 2001 From: yskim6772 Date: Wed, 1 Oct 2025 15:02:59 +0900 Subject: [PATCH 4/6] =?UTF-8?q?[Feat]=20#11=20=EB=8F=99=EC=95=84=EB=A6=AC?= =?UTF-8?q?=20=ED=99=95=EC=9D=B8=ED=95=98=EA=B8=B0=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/club/controller/ClubController.java | 7 +++++++ .../domain/club/dto/response/ClubResponse.java | 11 +++++++++++ .../WhoIs_Server/domain/club/model/Club.java | 2 +- .../domain/club/repository/ClubRepository.java | 3 +++ .../WhoIs_Server/domain/club/service/ClubService.java | 9 +++++++++ 5 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubResponse.java diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java index 8610943..f863a19 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java @@ -1,5 +1,6 @@ package com.WhoIsRoom.WhoIs_Server.domain.club.controller; +import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.ClubResponse; import com.WhoIsRoom.WhoIs_Server.domain.club.service.ClubService; import com.WhoIsRoom.WhoIs_Server.global.common.response.BaseResponse; import lombok.RequiredArgsConstructor; @@ -31,4 +32,10 @@ public BaseResponse joinClub(@PathVariable final Long clubId) { clubService.joinClub(clubId); return BaseResponse.ok(null); } + + @GetMapping + public BaseResponse getClubByClubNumber(@RequestParam String clubNumber) { + ClubResponse response = clubService.getClubByClubNumber(clubNumber); + return BaseResponse.ok(response); + } } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubResponse.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubResponse.java new file mode 100644 index 0000000..d28ef13 --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubResponse.java @@ -0,0 +1,11 @@ +package com.WhoIsRoom.WhoIs_Server.domain.club.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class ClubResponse { + private Long clubId; + private String clubName; +} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/model/Club.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/model/Club.java index 811a8aa..3466150 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/model/Club.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/model/Club.java @@ -18,7 +18,7 @@ public class Club extends BaseEntity { @Column(name = "name", length = 200, nullable = false, unique = true) private String name; - @Column(name = "club_number", length = 100, nullable = false) + @Column(name = "club_number", length = 100, nullable = false, unique = true) private String clubNumber; @Builder diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/repository/ClubRepository.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/repository/ClubRepository.java index 02fb5d2..daa0b7a 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/repository/ClubRepository.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/repository/ClubRepository.java @@ -3,5 +3,8 @@ import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface ClubRepository extends JpaRepository { + Optional findByClubNumber(String clubNumber); } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java index 0b4ca3a..1736d2c 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java @@ -1,5 +1,6 @@ package com.WhoIsRoom.WhoIs_Server.domain.club.service; +import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.ClubResponse; import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; import com.WhoIsRoom.WhoIs_Server.domain.club.repository.ClubRepository; import com.WhoIsRoom.WhoIs_Server.domain.member.model.Member; @@ -81,4 +82,12 @@ public void joinClub(Long clubId) { memberRepository.save(member); } + + @Transactional(readOnly = true) + public ClubResponse getClubByClubNumber(String clubNumber) { + Club club = clubRepository.findByClubNumber(clubNumber) + .orElseThrow(() -> new BusinessException(ErrorCode.CLUB_NOT_FOUND)); + + return new ClubResponse(club.getId(), club.getName()); + } } From 828d14885557f65074969fddf551306ce7b0a96a Mon Sep 17 00:00:00 2001 From: yskim6772 Date: Wed, 1 Oct 2025 15:31:55 +0900 Subject: [PATCH 5/6] =?UTF-8?q?[Feat]=20#11=20=EB=82=B4=20=EB=8F=99?= =?UTF-8?q?=EC=95=84=EB=A6=AC=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../club/controller/ClubController.java | 7 +++++++ .../club/dto/response/ClubResponse.java | 2 ++ .../club/dto/response/MyClubsResponse.java | 14 +++++++++++++ .../domain/club/service/ClubService.java | 21 +++++++++++++++++++ .../member/repository/MemberRepository.java | 2 ++ 5 files changed, 46 insertions(+) create mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/MyClubsResponse.java diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java index f863a19..94386d5 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java @@ -1,6 +1,7 @@ package com.WhoIsRoom.WhoIs_Server.domain.club.controller; import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.ClubResponse; +import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.MyClubsResponse; import com.WhoIsRoom.WhoIs_Server.domain.club.service.ClubService; import com.WhoIsRoom.WhoIs_Server.global.common.response.BaseResponse; import lombok.RequiredArgsConstructor; @@ -38,4 +39,10 @@ public BaseResponse getClubByClubNumber(@RequestParam String clubN ClubResponse response = clubService.getClubByClubNumber(clubNumber); return BaseResponse.ok(response); } + + @GetMapping("/my") + public BaseResponse getMyClub() { + MyClubsResponse response = clubService.getMyClubs(); + return BaseResponse.ok(response); + } } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubResponse.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubResponse.java index d28ef13..749ce8b 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubResponse.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubResponse.java @@ -1,9 +1,11 @@ package com.WhoIsRoom.WhoIs_Server.domain.club.dto.response; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; @Getter +@Builder @AllArgsConstructor public class ClubResponse { private Long clubId; diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/MyClubsResponse.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/MyClubsResponse.java new file mode 100644 index 0000000..012d4eb --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/MyClubsResponse.java @@ -0,0 +1,14 @@ +package com.WhoIsRoom.WhoIs_Server.domain.club.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +import java.util.List; + +@Getter +@Builder +@AllArgsConstructor +public class MyClubsResponse { + private List userClubs; +} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java index 1736d2c..57dc435 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java @@ -1,6 +1,7 @@ package com.WhoIsRoom.WhoIs_Server.domain.club.service; import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.ClubResponse; +import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.MyClubsResponse; import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; import com.WhoIsRoom.WhoIs_Server.domain.club.repository.ClubRepository; import com.WhoIsRoom.WhoIs_Server.domain.member.model.Member; @@ -15,6 +16,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Slf4j @Service @RequiredArgsConstructor @@ -90,4 +93,22 @@ public ClubResponse getClubByClubNumber(String clubNumber) { return new ClubResponse(club.getId(), club.getName()); } + + @Transactional(readOnly = true) + public MyClubsResponse getMyClubs() { + User user = getCurrentUser(); + + List members = memberRepository.findByUser(user); + + List userClubs = members.stream() + .map(member -> ClubResponse.builder() + .clubId(member.getClub().getId()) + .clubName(member.getClub().getName()) + .build()) + .toList(); + + return MyClubsResponse.builder() + .userClubs(userClubs) + .build(); + } } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java index 8a47e1f..60ad597 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java @@ -5,8 +5,10 @@ import com.WhoIsRoom.WhoIs_Server.domain.user.model.User; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; import java.util.Optional; public interface MemberRepository extends JpaRepository { Optional findByUserAndClub(User user, Club club); + List findByUser(User user); } From 29d1fb6a5c23f1103d9bd8ef16ca049ace27106a Mon Sep 17 00:00:00 2001 From: yskim6772 Date: Wed, 1 Oct 2025 16:23:18 +0900 Subject: [PATCH 6/6] =?UTF-8?q?[Feat]=20#11=20=EC=9E=AC=EC=8B=A4=EC=9E=90?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84,=20status=20co?= =?UTF-8?q?de=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../club/controller/ClubController.java | 9 ++++++- .../dto/response/ClubPresenceResponse.java | 13 ++++++++++ .../club/dto/response/PresenceResponse.java | 14 +++++++++++ .../domain/club/service/ClubService.java | 24 +++++++++++++++++++ .../member/repository/MemberRepository.java | 1 + .../global/common/response/ErrorCode.java | 2 +- 6 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubPresenceResponse.java create mode 100644 src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/PresenceResponse.java diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java index 94386d5..26adb47 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java @@ -1,5 +1,6 @@ package com.WhoIsRoom.WhoIs_Server.domain.club.controller; +import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.ClubPresenceResponse; import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.ClubResponse; import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.MyClubsResponse; import com.WhoIsRoom.WhoIs_Server.domain.club.service.ClubService; @@ -41,8 +42,14 @@ public BaseResponse getClubByClubNumber(@RequestParam String clubN } @GetMapping("/my") - public BaseResponse getMyClub() { + public BaseResponse getMyClubs() { MyClubsResponse response = clubService.getMyClubs(); return BaseResponse.ok(response); } + + @GetMapping("/{clubId}/presences") + public BaseResponse getClubPresence(@PathVariable final Long clubId) { + ClubPresenceResponse response = clubService.getClubPresence(clubId); + return BaseResponse.ok(response); + } } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubPresenceResponse.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubPresenceResponse.java new file mode 100644 index 0000000..1d715c4 --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubPresenceResponse.java @@ -0,0 +1,13 @@ +package com.WhoIsRoom.WhoIs_Server.domain.club.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class ClubPresenceResponse { + private String clubName; + private List presentMembers; +} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/PresenceResponse.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/PresenceResponse.java new file mode 100644 index 0000000..5bf696c --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/PresenceResponse.java @@ -0,0 +1,14 @@ +package com.WhoIsRoom.WhoIs_Server.domain.club.dto.response; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class PresenceResponse { + private String userName; + + @JsonProperty("isMe") + private boolean me; +} diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java index 57dc435..f4efbac 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java @@ -1,7 +1,9 @@ package com.WhoIsRoom.WhoIs_Server.domain.club.service; +import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.ClubPresenceResponse; import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.ClubResponse; import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.MyClubsResponse; +import com.WhoIsRoom.WhoIs_Server.domain.club.dto.response.PresenceResponse; import com.WhoIsRoom.WhoIs_Server.domain.club.model.Club; import com.WhoIsRoom.WhoIs_Server.domain.club.repository.ClubRepository; import com.WhoIsRoom.WhoIs_Server.domain.member.model.Member; @@ -111,4 +113,26 @@ public MyClubsResponse getMyClubs() { .userClubs(userClubs) .build(); } + + @Transactional(readOnly = true) + public ClubPresenceResponse getClubPresence(Long clubId) { + User user = getCurrentUser(); + + Club club = clubRepository.findById(clubId) + .orElseThrow(() -> new BusinessException(ErrorCode.CLUB_NOT_FOUND)); + + memberRepository.findByUserAndClub(user, club) + .orElseThrow(() -> new BusinessException(ErrorCode.MEMBER_NOT_FOUND)); + + List presentMembers = memberRepository.findAllByClubAndIsExistTrue(club); + + List response = presentMembers.stream() + .map(member -> new PresenceResponse( + member.getUser().getNickName(), + member.getUser().getId().equals(user.getId()) + )) + .toList(); + + return new ClubPresenceResponse(club.getName(), response); + } } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java index 60ad597..cda8677 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/member/repository/MemberRepository.java @@ -11,4 +11,5 @@ public interface MemberRepository extends JpaRepository { Optional findByUserAndClub(User user, Club club); List findByUser(User user); + List findAllByClubAndIsExistTrue(Club club); } diff --git a/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java index c47fb4d..63eac3e 100644 --- a/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/global/common/response/ErrorCode.java @@ -27,7 +27,7 @@ public enum ErrorCode{ CLUB_NOT_FOUND(300, HttpStatus.NOT_FOUND.value(), "해당 동아리가 존재하지 않습니다."), // Member - MEMBER_NOT_FOUND(400, HttpStatus.NOT_FOUND.value(), "해당 동아리의 회원이 아닙니다."), + MEMBER_NOT_FOUND(400, HttpStatus.FORBIDDEN.value(), "해당 동아리의 회원이 아닙니다."), ALREADY_MEMBER(401, HttpStatus.BAD_REQUEST.value(), "이미 동아리에 가입된 사용자입니다."), // Attendance