diff --git a/.gitignore b/.gitignore index c2065bc..75c4010 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ build/ !gradle/wrapper/gradle-wrapper.jar !**/src/main/**/build/ !**/src/test/**/build/ +.DS_Store ### STS ### .apt_generated diff --git a/build.gradle b/build.gradle index 7025e84..3e20c13 100644 --- a/build.gradle +++ b/build.gradle @@ -20,10 +20,9 @@ repositories { dependencies { implementation 'org.springframework.boot:spring-boot-starter:3.4.4' implementation 'org.springframework.boot:spring-boot-starter-web:3.4.4' - implementation 'org.springframework.boot:spring-boot-starter-jdbc:3.4.4' + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:3.4.4' - - implementation 'dev.akkinoc.spring.boot:logback-access-spring-boot-starter:4.5.1' + implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'io.jsonwebtoken:jjwt-api:0.12.6' implementation 'io.jsonwebtoken:jjwt-impl:0.12.6' diff --git a/src/main/java/com/yourssu/roomescape/auth/AdminAuthorizationInterceptor.java b/src/main/java/com/yourssu/roomescape/auth/AdminAuthorizationInterceptor.java index 93ef923..87c409f 100644 --- a/src/main/java/com/yourssu/roomescape/auth/AdminAuthorizationInterceptor.java +++ b/src/main/java/com/yourssu/roomescape/auth/AdminAuthorizationInterceptor.java @@ -1,7 +1,5 @@ package com.yourssu.roomescape.auth; -import com.yourssu.roomescape.exception.CustomException; -import com.yourssu.roomescape.exception.ErrorCode; import com.yourssu.roomescape.jwt.TokenExtractor; import com.yourssu.roomescape.member.Member; import jakarta.servlet.http.Cookie; @@ -25,9 +23,10 @@ public boolean preHandle(HttpServletRequest request, HttpServletResponse respons String token = TokenExtractor.extractTokenFromCookies(cookies); Member member = authService.checkLogin(token); - if(!"ADMIN".equals(member.getRole())) { + if(!member.isAdmin()) { response.setStatus(401); - response.getWriter().write("해당 접근에 대한 권한이 없습니다."); + response.setContentType("application/json; charset=UTF-8"); + response.getWriter().write("{\"code\":\"NOT_ADMIN\",\"message\":\"관리자 권한이 필요합니다.\"}"); return false; } diff --git a/src/main/java/com/yourssu/roomescape/auth/AuthController.java b/src/main/java/com/yourssu/roomescape/auth/AuthController.java index 21b8dbd..01e140d 100644 --- a/src/main/java/com/yourssu/roomescape/auth/AuthController.java +++ b/src/main/java/com/yourssu/roomescape/auth/AuthController.java @@ -1,5 +1,7 @@ package com.yourssu.roomescape.auth; +import com.yourssu.roomescape.auth.dto.CheckLoginResponse; +import com.yourssu.roomescape.auth.dto.LoginRequest; import com.yourssu.roomescape.jwt.TokenExtractor; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletRequest; @@ -10,8 +12,6 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; -import java.util.Arrays; - @RestController public class AuthController { @@ -36,7 +36,6 @@ public ResponseEntity login(@RequestBody LoginRequest loginRequest, HttpSe public ResponseEntity loginCheck(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); - System.out.println(Arrays.toString(cookies)); String token = TokenExtractor.extractTokenFromCookies(cookies); String name = authService.checkLogin(token).getName(); diff --git a/src/main/java/com/yourssu/roomescape/auth/AuthService.java b/src/main/java/com/yourssu/roomescape/auth/AuthService.java index d1a962a..ab4ca34 100644 --- a/src/main/java/com/yourssu/roomescape/auth/AuthService.java +++ b/src/main/java/com/yourssu/roomescape/auth/AuthService.java @@ -1,28 +1,33 @@ package com.yourssu.roomescape.auth; +import com.yourssu.roomescape.auth.dto.LoginRequest; +import com.yourssu.roomescape.exception.CustomException; +import com.yourssu.roomescape.exception.ErrorCode; import com.yourssu.roomescape.jwt.TokenProvider; import com.yourssu.roomescape.member.Member; -import com.yourssu.roomescape.member.MemberDao; +import com.yourssu.roomescape.member.MemberRepository; import org.springframework.stereotype.Service; @Service public class AuthService { - private final MemberDao memberDao; + private final MemberRepository memberRepository; private final TokenProvider tokenProvider; - public AuthService(TokenProvider tokenProvider, MemberDao memberDao) { + public AuthService(TokenProvider tokenProvider, MemberRepository memberRepository) { this.tokenProvider = tokenProvider; - this.memberDao = memberDao; + this.memberRepository = memberRepository; } public String login(LoginRequest loginRequest) { - Member member = memberDao.findByEmailAndPassword(loginRequest.email(), loginRequest.password()); + Member member = memberRepository.findByEmailAndPassword(loginRequest.email(), loginRequest.password()) + .orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND)); return tokenProvider.createToken(member.getEmail()); } public Member checkLogin(String token) { String payload = tokenProvider.getPayload(token); - return memberDao.findByEmail(payload); + return memberRepository.findByEmail(payload) + .orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND)); } } diff --git a/src/main/java/com/yourssu/roomescape/auth/CheckLoginResponse.java b/src/main/java/com/yourssu/roomescape/auth/dto/CheckLoginResponse.java similarity index 55% rename from src/main/java/com/yourssu/roomescape/auth/CheckLoginResponse.java rename to src/main/java/com/yourssu/roomescape/auth/dto/CheckLoginResponse.java index da6cb25..42eb311 100644 --- a/src/main/java/com/yourssu/roomescape/auth/CheckLoginResponse.java +++ b/src/main/java/com/yourssu/roomescape/auth/dto/CheckLoginResponse.java @@ -1,4 +1,4 @@ -package com.yourssu.roomescape.auth; +package com.yourssu.roomescape.auth.dto; public record CheckLoginResponse(String name) { } diff --git a/src/main/java/com/yourssu/roomescape/auth/LoginRequest.java b/src/main/java/com/yourssu/roomescape/auth/dto/LoginRequest.java similarity index 60% rename from src/main/java/com/yourssu/roomescape/auth/LoginRequest.java rename to src/main/java/com/yourssu/roomescape/auth/dto/LoginRequest.java index 93c1c9a..a35a80f 100644 --- a/src/main/java/com/yourssu/roomescape/auth/LoginRequest.java +++ b/src/main/java/com/yourssu/roomescape/auth/dto/LoginRequest.java @@ -1,4 +1,4 @@ -package com.yourssu.roomescape.auth; +package com.yourssu.roomescape.auth.dto; public record LoginRequest(String email, String password) { } diff --git a/src/main/java/com/yourssu/roomescape/exception/ErrorCode.java b/src/main/java/com/yourssu/roomescape/exception/ErrorCode.java index 090d449..8f4f125 100644 --- a/src/main/java/com/yourssu/roomescape/exception/ErrorCode.java +++ b/src/main/java/com/yourssu/roomescape/exception/ErrorCode.java @@ -5,12 +5,31 @@ public enum ErrorCode { INTERNAL_SERVER_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "서버에 문제가 생겼습니다."), + + // Auth COOKIE_NOT_FOUND(HttpStatus.UNAUTHORIZED, "쿠키가 존재하지 않습니다."), TOKEN_NOT_FOUND(HttpStatus.UNAUTHORIZED, "쿠키에 토큰이 존재하지 않습니다."), + + // Member MEMBER_NOT_FOUND(HttpStatus.NOT_FOUND, "사용자의 정보를 찾을 수 없습니다."), - INVALID_RESERVATION_REQUEST(HttpStatus.BAD_REQUEST, "예약 요청 값이 누락되었습니다."), - UNAUTHORIZED_ACCESS(HttpStatus.FORBIDDEN, "해당 접근에 대한 권한이 없습니다."), - DUPLICATE_MEMBER_NAME_EXISTS(HttpStatus.INTERNAL_SERVER_ERROR, "중복된 이름이 존재합니다. 관리자에게 문의하세요."); + NOT_ADMIN(HttpStatus.FORBIDDEN, "관리자 권한이 필요합니다."), + EMAIL_ALREADY_EXISTS(HttpStatus.CONFLICT, "이메일 정보가 이미 존재합니다."), + + // Reservation + RESERVATION_NOT_FOUND(HttpStatus.NOT_FOUND, "예약 정보를 찾을 수 없습니다."), + NO_PERMISSION_FOR_RESERVATION(HttpStatus.FORBIDDEN, "해당 예약에 대한 권한이 없습니다."), + RESERVATION_ALREADY_EXISTS(HttpStatus.CONFLICT, "예약 정보가 이미 존재합니다."), + CANNOT_WAIT_WITHOUT_RESERVED(HttpStatus.BAD_REQUEST, "예약이 없는 상태에서는 대기를 신청할 수 없습니다."), + + // Time + TIME_NOT_FOUND(HttpStatus.NOT_FOUND, "시간 정보를 찾을 수 없습니다."), + TIME_IN_USE(HttpStatus.BAD_REQUEST, "해당 시간은 예약이 존재하여 삭제할 수 없습니다."), + TIME_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 존재하는 시간입니다."), + + // Theme + THEME_NOT_FOUND(HttpStatus.NOT_FOUND, "테마 정보를 찾을 수 없습니다."), + THEME_IN_USE(HttpStatus.BAD_REQUEST, "해당 테마는 예약이 존재하여 삭제할 수 없습니다."), + THEME_ALREADY_EXISTS(HttpStatus.CONFLICT, "이미 존재하는 테마입니다."); private final HttpStatus status; private final String message; diff --git a/src/main/java/com/yourssu/roomescape/member/Member.java b/src/main/java/com/yourssu/roomescape/member/Member.java index 6d655ef..f348fcc 100644 --- a/src/main/java/com/yourssu/roomescape/member/Member.java +++ b/src/main/java/com/yourssu/roomescape/member/Member.java @@ -1,26 +1,40 @@ package com.yourssu.roomescape.member; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity public class Member { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; + + @Column(unique = true) private String email; + private String password; - private String role; - public Member(Long id, String name, String email, String role) { - this.id = id; - this.name = name; - this.email = email; - this.role = role; - } + @Enumerated(EnumType.STRING) + private MemberRole role; - public Member(String name, String email, String password, String role) { + public Member(String name, String email, String password, MemberRole role) { this.name = name; this.email = email; this.password = password; this.role = role; } + protected Member() { + + } + public Long getId() { return id; } @@ -37,7 +51,11 @@ public String getPassword() { return password; } - public String getRole() { + public MemberRole getRole() { return role; } + + public boolean isAdmin() { + return this.role == MemberRole.ADMIN; + } } diff --git a/src/main/java/com/yourssu/roomescape/member/MemberController.java b/src/main/java/com/yourssu/roomescape/member/MemberController.java index f90da55..10615d3 100644 --- a/src/main/java/com/yourssu/roomescape/member/MemberController.java +++ b/src/main/java/com/yourssu/roomescape/member/MemberController.java @@ -1,7 +1,10 @@ package com.yourssu.roomescape.member; +import com.yourssu.roomescape.member.dto.MemberRequest; +import com.yourssu.roomescape.member.dto.MemberResponse; import jakarta.servlet.http.Cookie; import jakarta.servlet.http.HttpServletResponse; +import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -18,9 +21,9 @@ public MemberController(MemberService memberService) { } @PostMapping("/members") - public ResponseEntity createMember(@RequestBody MemberRequest memberRequest) { + public ResponseEntity createMember(@RequestBody @Valid MemberRequest memberRequest) { MemberResponse member = memberService.createMember(memberRequest); - return ResponseEntity.created(URI.create("/members/" + member.getId())).body(member); + return ResponseEntity.created(URI.create("/members/" + member.id())).body(member); } @PostMapping("/logout") diff --git a/src/main/java/com/yourssu/roomescape/member/MemberDao.java b/src/main/java/com/yourssu/roomescape/member/MemberDao.java deleted file mode 100644 index 8950465..0000000 --- a/src/main/java/com/yourssu/roomescape/member/MemberDao.java +++ /dev/null @@ -1,80 +0,0 @@ -package com.yourssu.roomescape.member; - -import com.yourssu.roomescape.exception.CustomException; -import com.yourssu.roomescape.exception.ErrorCode; -import org.springframework.dao.DataAccessException; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Optional; - -@Repository -public class MemberDao { - private JdbcTemplate jdbcTemplate; - - public MemberDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - private final RowMapper memberRowMapper = (rs, rowNum) -> new Member( - rs.getLong("id"), - rs.getString("name"), - rs.getString("email"), - rs.getString("role") - ); - - public Member save(Member member) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - var ps = connection.prepareStatement("INSERT INTO member(name, email, password, role) VALUES (?, ?, ?, ?)", new String[]{"id"}); - ps.setString(1, member.getName()); - ps.setString(2, member.getEmail()); - ps.setString(3, member.getPassword()); - ps.setString(4, member.getRole()); - return ps; - }, keyHolder); - - return new Member(keyHolder.getKey().longValue(), member.getName(), member.getEmail(), "USER"); - } - - public Member findByEmailAndPassword(String email, String password) { - try { - return jdbcTemplate.queryForObject( - "SELECT id, name, email, role FROM member WHERE email = ? AND password = ?", - memberRowMapper, - email, password - ); - } catch (DataAccessException e) { - throw new CustomException(ErrorCode.MEMBER_NOT_FOUND); - } - } - - public Member findByEmail(String email) { - try { - return jdbcTemplate.queryForObject( - "SELECT id, name, email, role FROM member WHERE email = ?", - memberRowMapper, - email - ); - } catch (DataAccessException e) { - throw new CustomException(ErrorCode.MEMBER_NOT_FOUND); - } - } - - public Optional findByName(String name) { - List members = jdbcTemplate.query( - "SELECT id, name, email, role FROM member WHERE name = ?", - memberRowMapper, - name - ); - if (members.size() > 1) { - throw new CustomException(ErrorCode.DUPLICATE_MEMBER_NAME_EXISTS); - } - - return members.isEmpty() ? Optional.empty() : Optional.of(members.get(0)); - } -} diff --git a/src/main/java/com/yourssu/roomescape/member/MemberRepository.java b/src/main/java/com/yourssu/roomescape/member/MemberRepository.java new file mode 100644 index 0000000..023b51f --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/member/MemberRepository.java @@ -0,0 +1,13 @@ +package com.yourssu.roomescape.member; + +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface MemberRepository extends JpaRepository { + + Optional findByEmailAndPassword(String email, String password); + Optional findByEmail(String email); + Optional findByName(String name); + boolean existsByEmail(String email); +} diff --git a/src/main/java/com/yourssu/roomescape/member/MemberRequest.java b/src/main/java/com/yourssu/roomescape/member/MemberRequest.java deleted file mode 100644 index 7ee60b6..0000000 --- a/src/main/java/com/yourssu/roomescape/member/MemberRequest.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.yourssu.roomescape.member; - -public class MemberRequest { - private String name; - private String email; - private String password; - - public String getName() { - return name; - } - - public String getEmail() { - return email; - } - - public String getPassword() { - return password; - } -} diff --git a/src/main/java/com/yourssu/roomescape/member/MemberResponse.java b/src/main/java/com/yourssu/roomescape/member/MemberResponse.java deleted file mode 100644 index fe37b56..0000000 --- a/src/main/java/com/yourssu/roomescape/member/MemberResponse.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.yourssu.roomescape.member; - -public class MemberResponse { - private Long id; - private String name; - private String email; - - public MemberResponse(Long id, String name, String email) { - this.id = id; - this.name = name; - this.email = email; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getEmail() { - return email; - } -} diff --git a/src/main/java/com/yourssu/roomescape/member/MemberRole.java b/src/main/java/com/yourssu/roomescape/member/MemberRole.java new file mode 100644 index 0000000..601aae6 --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/member/MemberRole.java @@ -0,0 +1,5 @@ +package com.yourssu.roomescape.member; + +public enum MemberRole { + ADMIN, USER +} diff --git a/src/main/java/com/yourssu/roomescape/member/MemberService.java b/src/main/java/com/yourssu/roomescape/member/MemberService.java index ff138c4..9ea6a9f 100644 --- a/src/main/java/com/yourssu/roomescape/member/MemberService.java +++ b/src/main/java/com/yourssu/roomescape/member/MemberService.java @@ -1,17 +1,27 @@ package com.yourssu.roomescape.member; +import com.yourssu.roomescape.exception.CustomException; +import com.yourssu.roomescape.exception.ErrorCode; +import com.yourssu.roomescape.member.dto.MemberRequest; +import com.yourssu.roomescape.member.dto.MemberResponse; import org.springframework.stereotype.Service; @Service public class MemberService { - private MemberDao memberDao; + private final MemberRepository memberRepository; - public MemberService(MemberDao memberDao) { - this.memberDao = memberDao; + public MemberService(MemberRepository memberRepository) { + this.memberRepository = memberRepository; } public MemberResponse createMember(MemberRequest memberRequest) { - Member member = memberDao.save(new Member(memberRequest.getName(), memberRequest.getEmail(), memberRequest.getPassword(), "USER")); + + if(memberRepository.existsByEmail(memberRequest.email())) { + throw new CustomException(ErrorCode.EMAIL_ALREADY_EXISTS); + } + + Member member = memberRepository.save(new Member(memberRequest.name(), memberRequest.email(), memberRequest.password(), MemberRole.USER)); + return new MemberResponse(member.getId(), member.getName(), member.getEmail()); } } diff --git a/src/main/java/com/yourssu/roomescape/member/dto/MemberRequest.java b/src/main/java/com/yourssu/roomescape/member/dto/MemberRequest.java new file mode 100644 index 0000000..9511272 --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/member/dto/MemberRequest.java @@ -0,0 +1,17 @@ +package com.yourssu.roomescape.member.dto; + + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; + +public record MemberRequest( + @NotBlank + String name, + + @NotBlank + @Email(message = "이메일 형식이 올바르지 않습니다.") + String email, + + @NotBlank + String password +) {} \ No newline at end of file diff --git a/src/main/java/com/yourssu/roomescape/member/dto/MemberResponse.java b/src/main/java/com/yourssu/roomescape/member/dto/MemberResponse.java new file mode 100644 index 0000000..4f9afad --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/member/dto/MemberResponse.java @@ -0,0 +1,3 @@ +package com.yourssu.roomescape.member.dto; + +public record MemberResponse(Long id, String name, String email) {} diff --git a/src/main/java/com/yourssu/roomescape/reservation/Reservation.java b/src/main/java/com/yourssu/roomescape/reservation/Reservation.java index 66a0376..f437eca 100644 --- a/src/main/java/com/yourssu/roomescape/reservation/Reservation.java +++ b/src/main/java/com/yourssu/roomescape/reservation/Reservation.java @@ -1,31 +1,51 @@ package com.yourssu.roomescape.reservation; +import com.yourssu.roomescape.member.Member; import com.yourssu.roomescape.theme.Theme; import com.yourssu.roomescape.time.Time; +import jakarta.persistence.*; +@Entity public class Reservation { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String name; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id") + private Member member; + private String date; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "time_id") private Time time; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "theme_id") private Theme theme; - public Reservation(Long id, String name, String date, Time time, Theme theme) { + @Enumerated(EnumType.STRING) + private ReservationStatus status; + + public Reservation(Long id, Member member, String date, Time time, Theme theme) { this.id = id; - this.name = name; + this.member = member; this.date = date; this.time = time; this.theme = theme; } - public Reservation(String name, String date, Time time, Theme theme) { - this.name = name; + public Reservation(Member member, String date, Time time, Theme theme, ReservationStatus status) { + this.member = member; this.date = date; this.time = time; this.theme = theme; + this.status = status; } - public Reservation() { + protected Reservation() { } @@ -33,8 +53,8 @@ public Long getId() { return id; } - public String getName() { - return name; + public Member getMember() { + return member; } public String getDate() { @@ -48,4 +68,8 @@ public Time getTime() { public Theme getTheme() { return theme; } + + public ReservationStatus getStatus() { + return status; + } } diff --git a/src/main/java/com/yourssu/roomescape/reservation/ReservationController.java b/src/main/java/com/yourssu/roomescape/reservation/ReservationController.java index 32fc9bf..5148f77 100644 --- a/src/main/java/com/yourssu/roomescape/reservation/ReservationController.java +++ b/src/main/java/com/yourssu/roomescape/reservation/ReservationController.java @@ -1,10 +1,12 @@ package com.yourssu.roomescape.reservation; import com.yourssu.roomescape.auth.LoginMember; -import com.yourssu.roomescape.exception.CustomException; -import com.yourssu.roomescape.exception.ErrorCode; import com.yourssu.roomescape.member.Member; -import com.yourssu.roomescape.theme.ThemeDao; +import com.yourssu.roomescape.reservation.dto.ReservationFindAllForAdminResponse; +import com.yourssu.roomescape.reservation.dto.ReservationFindAllResponse; +import com.yourssu.roomescape.reservation.dto.ReservationSaveRequest; +import com.yourssu.roomescape.reservation.dto.ReservationSaveResponse; +import jakarta.validation.Valid; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -12,6 +14,7 @@ import java.util.List; @RestController +@RequestMapping("/reservations") public class ReservationController { private final ReservationService reservationService; @@ -20,25 +23,27 @@ public ReservationController(ReservationService reservationService) { this.reservationService = reservationService; } - @GetMapping("/reservations") - public List list() { - return reservationService.findAll(); + @GetMapping("/mine") + public List getMyReservations(@LoginMember Member member) { + return reservationService.getMyReservations(member); } - @PostMapping("/reservations") - public ResponseEntity create(@RequestBody ReservationRequest reservationRequest, @LoginMember Member member) { - if (reservationRequest.getDate() == null || reservationRequest.getTheme() == null || reservationRequest.getTime() == null) { - throw new CustomException(ErrorCode.INVALID_RESERVATION_REQUEST); - } + @GetMapping("/admin") + public List getAllReservations(@LoginMember Member member) { + return reservationService.getAllReservations(member); + } + + @PostMapping + public ResponseEntity create(@RequestBody @Valid ReservationSaveRequest reservationSaveRequest, @LoginMember Member member) { - ReservationResponse reservation = reservationService.save(reservationRequest, member); + ReservationSaveResponse reservation = reservationService.save(reservationSaveRequest, member); - return ResponseEntity.created(URI.create("/reservations/" + reservation.getId())).body(reservation); + return ResponseEntity.created(URI.create("/reservations/" + reservation.id())).body(reservation); } - @DeleteMapping("/reservations/{id}") - public ResponseEntity delete(@PathVariable Long id) { - reservationService.deleteById(id); + @DeleteMapping("/{id}") + public ResponseEntity delete(@PathVariable Long id, @LoginMember Member member) { + reservationService.deleteById(id, member); return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/com/yourssu/roomescape/reservation/ReservationDao.java b/src/main/java/com/yourssu/roomescape/reservation/ReservationDao.java deleted file mode 100644 index 6dec33f..0000000 --- a/src/main/java/com/yourssu/roomescape/reservation/ReservationDao.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.yourssu.roomescape.reservation; - -import com.yourssu.roomescape.theme.Theme; -import com.yourssu.roomescape.time.Time; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; - -import java.sql.PreparedStatement; -import java.util.List; - -@Repository -public class ReservationDao { - - private final JdbcTemplate jdbcTemplate; - - public ReservationDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List findAll() { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id", - - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } - - public Reservation save(Reservation reservation) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - PreparedStatement ps = connection.prepareStatement("INSERT INTO reservation(date, name, theme_id, time_id) VALUES (?, ?, ?, ?)", new String[]{"id"}); - ps.setString(1, reservation.getDate()); - ps.setString(2, reservation.getName()); - ps.setLong(3, reservation.getTheme().getId()); - ps.setLong(4, reservation.getTime().getId()); - return ps; - }, keyHolder); - - return new Reservation( - keyHolder.getKey().longValue(), - reservation.getName(), - reservation.getDate(), - reservation.getTime(), - reservation.getTheme() - ); - } - - public void deleteById(Long id) { - jdbcTemplate.update("DELETE FROM reservation WHERE id = ?", id); - } - - public List findReservationsByDateAndTheme(String date, Long themeId) { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id" + - "WHERE r.date = ? AND r.theme_id = ?", - new Object[]{date, themeId}, - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } - - public List findByDateAndThemeId(String date, Long themeId) { - return jdbcTemplate.query( - "SELECT r.id AS reservation_id, r.name as reservation_name, r.date as reservation_date, " + - "t.id AS theme_id, t.name AS theme_name, t.description AS theme_description, " + - "ti.id AS time_id, ti.time_value AS time_value " + - "FROM reservation r " + - "JOIN theme t ON r.theme_id = t.id " + - "JOIN time ti ON r.time_id = ti.id " + - "WHERE r.date = ? AND r.theme_id = ?", - new Object[]{date, themeId}, - (rs, rowNum) -> new Reservation( - rs.getLong("reservation_id"), - rs.getString("reservation_name"), - rs.getString("reservation_date"), - new Time( - rs.getLong("time_id"), - rs.getString("time_value") - ), - new Theme( - rs.getLong("theme_id"), - rs.getString("theme_name"), - rs.getString("theme_description") - ))); - } -} diff --git a/src/main/java/com/yourssu/roomescape/reservation/ReservationRepository.java b/src/main/java/com/yourssu/roomescape/reservation/ReservationRepository.java new file mode 100644 index 0000000..490e507 --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/reservation/ReservationRepository.java @@ -0,0 +1,62 @@ +package com.yourssu.roomescape.reservation; + +import com.yourssu.roomescape.member.Member; +import com.yourssu.roomescape.reservation.dto.ReservationWaitingWithRank; +import com.yourssu.roomescape.theme.Theme; +import com.yourssu.roomescape.time.Time; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface ReservationRepository extends JpaRepository { + + @Query("SELECT r FROM Reservation r " + + "JOIN FETCH r.member " + + "JOIN FETCH r.time " + + "JOIN FETCH r.theme " + + "WHERE r.status = :status") + List findByStatus(@Param("status") ReservationStatus status); + + @EntityGraph(attributePaths = {"member", "time", "theme"}) + List findByMemberAndStatus(Member member, ReservationStatus status); + + @EntityGraph(attributePaths = {"time"}) + List findByDateAndThemeId(String date, Long themeId); + + boolean existsByMemberAndThemeAndDateAndTime(Member member, Theme theme, String date, Time time); + + @Query("SELECT new com.yourssu.roomescape.reservation.dto.ReservationWaitingWithRank(" + + " r, " + + " (SELECT COUNT(r2) " + + " FROM Reservation r2 " + + " WHERE r2.theme = r.theme " + + " AND r2.date = r.date " + + " AND r2.time = r.time " + + " AND r2.status = 'WAITING' " + + " AND r2.id < r.id)) " + + "FROM Reservation r " + + "WHERE r.member = :member " + + "AND r.status = 'WAITING'" + ) + List findWaitingsWithRankByMember(Member member); + + @Query("SELECT COUNT(r2) " + + "FROM Reservation r2 " + + "WHERE r2.theme = :theme " + + " AND r2.date = :date " + + " AND r2.time = :time " + + " AND r2.status = 'WAITING' " + + " AND r2.id < :reservationId") + Long findWaitingRank(@Param("theme") Theme theme, + @Param("date") String date, + @Param("time") Time time, + @Param("reservationId") Long reservationId); + + boolean existsByTimeId(Long timeId); + boolean existsByThemeId(Long themeId); + + boolean existsByThemeAndDateAndTimeAndStatus(Theme theme, String date, Time time, ReservationStatus status); +} diff --git a/src/main/java/com/yourssu/roomescape/reservation/ReservationRequest.java b/src/main/java/com/yourssu/roomescape/reservation/ReservationRequest.java deleted file mode 100644 index 00f716a..0000000 --- a/src/main/java/com/yourssu/roomescape/reservation/ReservationRequest.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.yourssu.roomescape.reservation; - -public class ReservationRequest { - private String name; - - private String date; - private Long theme; - private Long time; - - public ReservationRequest(String name, String date, Long theme, Long time) { - this.name = name; - this.date = date; - this.theme = theme; - this.time = time; - } - - public String getName() { - return name; - } - - public String getDate() { - return date; - } - - public Long getTheme() { - return theme; - } - - public Long getTime() { - return time; - } -} diff --git a/src/main/java/com/yourssu/roomescape/reservation/ReservationResponse.java b/src/main/java/com/yourssu/roomescape/reservation/ReservationResponse.java deleted file mode 100644 index c8b83aa..0000000 --- a/src/main/java/com/yourssu/roomescape/reservation/ReservationResponse.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.yourssu.roomescape.reservation; - -public class ReservationResponse { - private Long id; - private String name; - private String theme; - private String date; - private String time; - - public ReservationResponse(Long id, String name, String theme, String date, String time) { - this.id = id; - this.name = name; - this.theme = theme; - this.date = date; - this.time = time; - } - - public Long getId() { - return id; - } - - public String getName() { - return name; - } - - public String getTheme() { - return theme; - } - - public String getDate() { - return date; - } - - public String getTime() { - return time; - } -} diff --git a/src/main/java/com/yourssu/roomescape/reservation/ReservationService.java b/src/main/java/com/yourssu/roomescape/reservation/ReservationService.java index b2a00c0..cd42355 100644 --- a/src/main/java/com/yourssu/roomescape/reservation/ReservationService.java +++ b/src/main/java/com/yourssu/roomescape/reservation/ReservationService.java @@ -1,70 +1,131 @@ -package com.yourssu.roomescape.reservation; - -import com.yourssu.roomescape.exception.CustomException; -import com.yourssu.roomescape.exception.ErrorCode; -import com.yourssu.roomescape.member.Member; -import com.yourssu.roomescape.member.MemberDao; -import com.yourssu.roomescape.theme.Theme; -import com.yourssu.roomescape.theme.ThemeDao; -import com.yourssu.roomescape.time.Time; -import com.yourssu.roomescape.time.TimeDao; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Optional; - -@Service -public class ReservationService { - private final ReservationDao reservationDao; - private final MemberDao memberDao; - private final ThemeDao themeDao; - private final TimeDao timeDao; - - public ReservationService(ReservationDao reservationDao, MemberDao memberDao, ThemeDao themeDao, TimeDao timeDao) { - this.reservationDao = reservationDao; - this.memberDao = memberDao; - this.themeDao = themeDao; - this.timeDao = timeDao; - } + package com.yourssu.roomescape.reservation; + + import com.yourssu.roomescape.exception.CustomException; + import com.yourssu.roomescape.exception.ErrorCode; + import com.yourssu.roomescape.member.Member; + import com.yourssu.roomescape.member.MemberRepository; + import com.yourssu.roomescape.reservation.dto.ReservationFindAllForAdminResponse; + import com.yourssu.roomescape.reservation.dto.ReservationFindAllResponse; + import com.yourssu.roomescape.reservation.dto.ReservationSaveRequest; + import com.yourssu.roomescape.reservation.dto.ReservationSaveResponse; + import com.yourssu.roomescape.reservation.dto.ReservationWaitingWithRank; + import com.yourssu.roomescape.theme.Theme; + import com.yourssu.roomescape.theme.ThemeRepository; + import com.yourssu.roomescape.time.Time; + import com.yourssu.roomescape.time.TimeRepository; + import org.springframework.stereotype.Service; + import org.springframework.transaction.annotation.Transactional; - public ReservationResponse save(ReservationRequest reservationRequest, Member member) { - String memberName = reservationRequest.getName(); + import java.util.List; + import java.util.stream.Collectors; + import java.util.stream.Stream; - if (memberName != null) { - Member existingMember = memberDao.findByName(memberName) - .orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND)); - memberName = existingMember.getName(); - } else { - memberName = member.getName(); + @Service + @Transactional(readOnly = true) + public class ReservationService { + private final ReservationRepository reservationRepository; + private final MemberRepository memberRepository; + private final TimeRepository timeRepository; + private final ThemeRepository themeRepository; + + public ReservationService(ReservationRepository reservationRepository, MemberRepository memberRepository, TimeRepository timeRepository, ThemeRepository themeRepository) { + this.reservationRepository = reservationRepository; + this.memberRepository = memberRepository; + this.timeRepository = timeRepository; + this.themeRepository = themeRepository; } - Time time = timeDao.findById(reservationRequest.getTime()); - Theme theme = themeDao.findById(reservationRequest.getTheme()); + @Transactional + public ReservationSaveResponse save(ReservationSaveRequest reservationSaveRequest, Member member) { - Reservation reservation = new Reservation( - memberName, - reservationRequest.getDate(), - time, - theme - ); + Member existingMember; - Reservation newReservation = reservationDao.save(reservation); + if (reservationSaveRequest.name() != null) { + existingMember = memberRepository.findByName(reservationSaveRequest.name()) + .orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND)); + } else { + existingMember = member; + } - return new ReservationResponse( - newReservation.getId(), - newReservation.getName(), - newReservation.getTheme().getName(), - newReservation.getDate(), - newReservation.getTime().getValue()); - } + Time time = timeRepository.findById(reservationSaveRequest.time()) + .orElseThrow(() -> new CustomException(ErrorCode.TIME_NOT_FOUND)); + Theme theme = themeRepository.findById(reservationSaveRequest.theme()) + .orElseThrow(() -> new CustomException(ErrorCode.THEME_NOT_FOUND)); - public void deleteById(Long id) { - reservationDao.deleteById(id); - } + checkDuplicateReservation(existingMember, theme, reservationSaveRequest.date(), time); + validateWaitingCondition(theme, reservationSaveRequest.date(), time, reservationSaveRequest.status()); + + Reservation reservation = new Reservation( + existingMember, + reservationSaveRequest.date(), + time, + theme, + reservationSaveRequest.status() + ); + + Reservation newReservation = reservationRepository.save(reservation); + + Long waitingRank = reservationRepository.findWaitingRank( + newReservation.getTheme(), + newReservation.getDate(), + newReservation.getTime(), + newReservation.getId() + ); + + return ReservationSaveResponse.of(newReservation, waitingRank); + } + + @Transactional + public void deleteById(Long id, Member member) { + + Reservation reservation = reservationRepository.findById(id) + .orElseThrow(() -> new CustomException(ErrorCode.RESERVATION_NOT_FOUND)); + + if(reservation.getMember() != member && !member.isAdmin()) { + throw new CustomException(ErrorCode.NO_PERMISSION_FOR_RESERVATION); + } - public List findAll() { - return reservationDao.findAll().stream() - .map(it -> new ReservationResponse(it.getId(), it.getName(), it.getTheme().getName(), it.getDate(), it.getTime().getValue())) - .toList(); + reservationRepository.deleteById(id); + } + + public List getMyReservations(Member member) { + List reservedReservations = reservationRepository.findByMemberAndStatus(member, ReservationStatus.RESERVED); + List waitingsWithRank = reservationRepository.findWaitingsWithRankByMember(member); + + return Stream.concat( + reservedReservations.stream().map(ReservationFindAllResponse::fromReservation), + waitingsWithRank.stream().map(ReservationFindAllResponse::fromWaitingWithRank) + ) + .toList(); + } + + public List getAllReservations(Member member) { + if(!member.isAdmin()) { + throw new CustomException(ErrorCode.NOT_ADMIN); + } + + List reservedReservations = reservationRepository.findByStatus(ReservationStatus.RESERVED); + + return reservedReservations.stream() + .map(ReservationFindAllForAdminResponse::from) + .collect(Collectors.toList()); + } + + private void checkDuplicateReservation(Member member, Theme theme, String date, Time time) { + boolean reservationExists = reservationRepository.existsByMemberAndThemeAndDateAndTime(member, theme, date, time); + if (reservationExists) { + throw new CustomException(ErrorCode.RESERVATION_ALREADY_EXISTS); + } + } + + private void validateWaitingCondition(Theme theme, String date, Time time, ReservationStatus status) { + if (status == ReservationStatus.WAITING) { + boolean reservedExists = reservationRepository.existsByThemeAndDateAndTimeAndStatus( + theme, date, time, ReservationStatus.RESERVED + ); + if (!reservedExists) { + throw new CustomException(ErrorCode.CANNOT_WAIT_WITHOUT_RESERVED); + } + } + } } -} diff --git a/src/main/java/com/yourssu/roomescape/reservation/ReservationStatus.java b/src/main/java/com/yourssu/roomescape/reservation/ReservationStatus.java new file mode 100644 index 0000000..f323df5 --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/reservation/ReservationStatus.java @@ -0,0 +1,17 @@ +package com.yourssu.roomescape.reservation; + +public enum ReservationStatus { + RESERVED("예약"), + WAITING("예약대기"), + ; + + private final String description; + + ReservationStatus(String description) { + this.description = description; + } + + public String getDescription() { + return description; + } +} diff --git a/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationFindAllForAdminResponse.java b/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationFindAllForAdminResponse.java new file mode 100644 index 0000000..22cd635 --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationFindAllForAdminResponse.java @@ -0,0 +1,16 @@ +package com.yourssu.roomescape.reservation.dto; + +import com.yourssu.roomescape.reservation.Reservation; + +public record ReservationFindAllForAdminResponse(Long reservationId, String name, String theme, String date, String time) { + + public static ReservationFindAllForAdminResponse from(Reservation newReservation) { + return new ReservationFindAllForAdminResponse( + newReservation.getId(), + newReservation.getMember().getName(), + newReservation.getTheme().getName(), + newReservation.getDate(), + newReservation.getTime().getValue() + ); + } +} diff --git a/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationFindAllResponse.java b/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationFindAllResponse.java new file mode 100644 index 0000000..00f650f --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationFindAllResponse.java @@ -0,0 +1,26 @@ +package com.yourssu.roomescape.reservation.dto; + +import com.yourssu.roomescape.reservation.Reservation; + +public record ReservationFindAllResponse(Long reservationId, String theme, String date, String time, String status) { + + public static ReservationFindAllResponse fromReservation(Reservation reservation) { + return new ReservationFindAllResponse( + reservation.getId(), + reservation.getTheme().getName(), + reservation.getDate(), + reservation.getTime().getValue(), + reservation.getStatus().getDescription() + ); + } + + public static ReservationFindAllResponse fromWaitingWithRank(ReservationWaitingWithRank waitingWithRank) { + return new ReservationFindAllResponse( + waitingWithRank.reservation().getId(), + waitingWithRank.reservation().getTheme().getName(), + waitingWithRank.reservation().getDate(), + waitingWithRank.reservation().getTime().getValue(), + (waitingWithRank.rank() + 1) + "번째 예약대기" + ); + } +} diff --git a/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationSaveRequest.java b/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationSaveRequest.java new file mode 100644 index 0000000..e2131d7 --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationSaveRequest.java @@ -0,0 +1,12 @@ +package com.yourssu.roomescape.reservation.dto; + +import com.yourssu.roomescape.reservation.ReservationStatus; +import jakarta.validation.constraints.NotNull; + +public record ReservationSaveRequest( + String name, + @NotNull String date, + @NotNull Long theme, + @NotNull Long time, + @NotNull ReservationStatus status +) { } diff --git a/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationSaveResponse.java b/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationSaveResponse.java new file mode 100644 index 0000000..bb6531b --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationSaveResponse.java @@ -0,0 +1,19 @@ +package com.yourssu.roomescape.reservation.dto; + +import com.yourssu.roomescape.reservation.Reservation; +import com.yourssu.roomescape.reservation.ReservationStatus; + +public record ReservationSaveResponse(Long id, String name, String theme, String date, String time, ReservationStatus status, Long waitingRank) { + + public static ReservationSaveResponse of(Reservation newReservation, Long waitingRank) { + return new ReservationSaveResponse( + newReservation.getId(), + newReservation.getMember().getName(), + newReservation.getTheme().getName(), + newReservation.getDate(), + newReservation.getTime().getValue(), + newReservation.getStatus(), + waitingRank + ); + } +} diff --git a/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationWaitingWithRank.java b/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationWaitingWithRank.java new file mode 100644 index 0000000..3e65b2a --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/reservation/dto/ReservationWaitingWithRank.java @@ -0,0 +1,5 @@ +package com.yourssu.roomescape.reservation.dto; + +import com.yourssu.roomescape.reservation.Reservation; + +public record ReservationWaitingWithRank(Reservation reservation, Long rank) {} diff --git a/src/main/java/com/yourssu/roomescape/theme/Theme.java b/src/main/java/com/yourssu/roomescape/theme/Theme.java index 0fb5d06..4303516 100644 --- a/src/main/java/com/yourssu/roomescape/theme/Theme.java +++ b/src/main/java/com/yourssu/roomescape/theme/Theme.java @@ -1,11 +1,20 @@ package com.yourssu.roomescape.theme; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity public class Theme { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String name; private String description; - public Theme() { + protected Theme() { } public Theme(Long id, String name, String description) { diff --git a/src/main/java/com/yourssu/roomescape/theme/ThemeController.java b/src/main/java/com/yourssu/roomescape/theme/ThemeController.java index 485d949..093b2cf 100644 --- a/src/main/java/com/yourssu/roomescape/theme/ThemeController.java +++ b/src/main/java/com/yourssu/roomescape/theme/ThemeController.java @@ -7,27 +7,30 @@ import java.util.List; @RestController + public class ThemeController { - private ThemeDao themeDao; - public ThemeController(ThemeDao themeDao) { - this.themeDao = themeDao; + private final ThemeService themeService; + + public ThemeController(ThemeService themeService) { + this.themeService = themeService; } @PostMapping("/themes") public ResponseEntity createTheme(@RequestBody Theme theme) { - Theme newTheme = themeDao.save(theme); + Theme newTheme = themeService.save(theme); return ResponseEntity.created(URI.create("/themes/" + newTheme.getId())).body(newTheme); } @GetMapping("/themes") public ResponseEntity> list() { - return ResponseEntity.ok(themeDao.findAll()); + return ResponseEntity.ok(themeService.findAll()); } @DeleteMapping("/themes/{id}") public ResponseEntity deleteTheme(@PathVariable Long id) { - themeDao.deleteById(id); + + themeService.deleteById(id); return ResponseEntity.noContent().build(); } } diff --git a/src/main/java/com/yourssu/roomescape/theme/ThemeDao.java b/src/main/java/com/yourssu/roomescape/theme/ThemeDao.java deleted file mode 100644 index c519923..0000000 --- a/src/main/java/com/yourssu/roomescape/theme/ThemeDao.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.yourssu.roomescape.theme; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; - -import java.util.List; - -@Repository -public class ThemeDao { - private JdbcTemplate jdbcTemplate; - - public ThemeDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List findAll() { - return jdbcTemplate.query("SELECT * FROM theme where deleted = false", (rs, rowNum) -> new Theme( - rs.getLong("id"), - rs.getString("name"), - rs.getString("description") - )); - } - - public Theme findById(Long id) { - return jdbcTemplate.queryForObject("SELECT * FROM theme WHERE id = ?", - (rs, rowNum) -> new Theme(rs.getLong("id"), rs.getString("name"), rs.getString("description")), - id); - } - - public Theme save(Theme theme) { - KeyHolder keyHolder = new GeneratedKeyHolder(); - jdbcTemplate.update(connection -> { - var ps = connection.prepareStatement("INSERT INTO theme(name, description) VALUES (?, ?)", new String[]{"id"}); - ps.setString(1, theme.getName()); - ps.setString(2, theme.getDescription()); - return ps; - }, keyHolder); - - return new Theme(keyHolder.getKey().longValue(), theme.getName(), theme.getDescription()); - } - - public void deleteById(Long id) { - jdbcTemplate.update("UPDATE theme SET deleted = true WHERE id = ?", id); - } -} diff --git a/src/main/java/com/yourssu/roomescape/theme/ThemeRepository.java b/src/main/java/com/yourssu/roomescape/theme/ThemeRepository.java new file mode 100644 index 0000000..a1f18e6 --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/theme/ThemeRepository.java @@ -0,0 +1,8 @@ +package com.yourssu.roomescape.theme; + +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ThemeRepository extends JpaRepository { + + boolean existsByName(String name); +} diff --git a/src/main/java/com/yourssu/roomescape/theme/ThemeService.java b/src/main/java/com/yourssu/roomescape/theme/ThemeService.java new file mode 100644 index 0000000..d2a3624 --- /dev/null +++ b/src/main/java/com/yourssu/roomescape/theme/ThemeService.java @@ -0,0 +1,40 @@ +package com.yourssu.roomescape.theme; + +import com.yourssu.roomescape.exception.CustomException; +import com.yourssu.roomescape.exception.ErrorCode; +import com.yourssu.roomescape.reservation.ReservationRepository; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +public class ThemeService { + + private final ThemeRepository themeRepository; + private final ReservationRepository reservationRepository; + + public ThemeService(ThemeRepository themeRepository, ReservationRepository reservationRepository) { + this.themeRepository = themeRepository; + this.reservationRepository = reservationRepository; + } + + public List findAll() { + return themeRepository.findAll(); + } + + public Theme save(Theme theme) { + if(themeRepository.existsByName(theme.getName())) { + throw new CustomException(ErrorCode.THEME_ALREADY_EXISTS); + } + + return themeRepository.save(theme); + } + + public void deleteById(Long id) { + if (reservationRepository.existsByThemeId(id)) { + throw new CustomException(ErrorCode.THEME_IN_USE); + } + + themeRepository.deleteById(id); + } +} diff --git a/src/main/java/com/yourssu/roomescape/time/Time.java b/src/main/java/com/yourssu/roomescape/time/Time.java index 4b298e2..e34af20 100644 --- a/src/main/java/com/yourssu/roomescape/time/Time.java +++ b/src/main/java/com/yourssu/roomescape/time/Time.java @@ -1,7 +1,19 @@ package com.yourssu.roomescape.time; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; + +@Entity public class Time { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; + + @Column(name = "time_value", unique = true) private String value; public Time(Long id, String value) { @@ -13,7 +25,7 @@ public Time(String value) { this.value = value; } - public Time() { + protected Time() { } diff --git a/src/main/java/com/yourssu/roomescape/time/TimeDao.java b/src/main/java/com/yourssu/roomescape/time/TimeDao.java deleted file mode 100644 index 56edc58..0000000 --- a/src/main/java/com/yourssu/roomescape/time/TimeDao.java +++ /dev/null @@ -1,47 +0,0 @@ -package com.yourssu.roomescape.time; - -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.support.GeneratedKeyHolder; -import org.springframework.jdbc.support.KeyHolder; -import org.springframework.stereotype.Repository; - -import java.sql.PreparedStatement; -import java.util.List; - -@Repository -public class TimeDao { - private final JdbcTemplate jdbcTemplate; - - public TimeDao(JdbcTemplate jdbcTemplate) { - this.jdbcTemplate = jdbcTemplate; - } - - public List