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..26adb47 --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/controller/ClubController.java @@ -0,0 +1,55 @@ +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; +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); + } + + @PostMapping("/{clubId}") + 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); + } + + @GetMapping("/my") + 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/ClubResponse.java b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubResponse.java new file mode 100644 index 0000000..749ce8b --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/dto/response/ClubResponse.java @@ -0,0 +1,13 @@ +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; + private String clubName; +} 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/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/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 new file mode 100644 index 0000000..f4efbac --- /dev/null +++ b/src/main/java/com/WhoIsRoom/WhoIs_Server/domain/club/service/ClubService.java @@ -0,0 +1,138 @@ +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; +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; +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.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ClubService { + private final ClubRepository clubRepository; + private final UserRepository userRepository; + private final MemberRepository memberRepository; + + @Transactional + public void checkIn(Long clubId) { + User user = getCurrentUser(); + + Club club = clubRepository.findById(clubId) + .orElseThrow(() -> new BusinessException(ErrorCode.CLUB_NOT_FOUND)); + + Member member = memberRepository.findByUserAndClub(user, club) + .orElseThrow(() -> new BusinessException(ErrorCode.MEMBER_NOT_FOUND)); + + if (Boolean.TRUE.equals(member.getIsExist())) { + throw new BusinessException(ErrorCode.ALREADY_CHECKED_IN); + } + + member.setExist(true); + } + + @Transactional + public void checkOut(Long clubId) { + User user = getCurrentUser(); + + Club club = clubRepository.findById(clubId) + .orElseThrow(() -> new BusinessException(ErrorCode.CLUB_NOT_FOUND)); + + Member member = memberRepository.findByUserAndClub(user, club) + .orElseThrow(() -> new BusinessException(ErrorCode.MEMBER_NOT_FOUND)); + + if (Boolean.FALSE.equals(member.getIsExist())) { + throw new BusinessException(ErrorCode.ATTENDANCE_NOT_FOUND); + } + + member.setExist(false); + } + + private User getCurrentUser() { + String nickname = SecurityContextHolder.getContext().getAuthentication().getName(); + 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); + } + + @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()); + } + + @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(); + } + + @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/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 index 62a8f25..468ec51 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 @@ -1,6 +1,8 @@ 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 org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -8,6 +10,7 @@ import java.util.Collection; import java.util.List; +import java.util.Optional; public interface MemberRepository extends JpaRepository { List findByUserId(Long userId); @@ -18,4 +21,8 @@ public interface MemberRepository extends JpaRepository { @Modifying(clearAutomatically = true, flushAutomatically = true) @Query("delete from Member m where m.user.id = :userId and m.club.id in :clubIds") void deleteByUserIdAndClubIdIn(@Param("userId") Long userId, @Param("clubIds") Collection clubIds); + + Optional findByUserAndClub(User user, Club club); + List findByUser(User user); + List findAllByClubAndIsExistTrue(Club club); } 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 8c543e0..628fc33 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,11 +20,19 @@ 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.FORBIDDEN.value(), "해당 동아리의 회원이 아닙니다."), + ALREADY_MEMBER(401, HttpStatus.BAD_REQUEST.value(), "이미 동아리에 가입된 사용자입니다."), + + // Attendance + ATTENDANCE_NOT_FOUND(500, HttpStatus.BAD_REQUEST.value(), "출근 기록이 없습니다."), + ALREADY_CHECKED_IN(501, HttpStatus.BAD_REQUEST.value(), "이미 출근 중입니다."), // Auth SECURITY_UNAUTHORIZED(600,HttpStatus.UNAUTHORIZED.value(), "인증 정보가 유효하지 않습니다"), diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index ce5040c..eb810ac 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