Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: 세션 사진 수 증가 #47

Merged
merged 22 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
0a63381
feat(session): 세션 사진 갯수 증가에 따른 테이블 생성
gikhoon Jun 28, 2024
3ac6c24
feat(session): 세션에 추가로 사진을 저장하는 기능 구현
gikhoon Jun 28, 2024
5c71ca3
feat(session): 세션 사진 순서을 수정하는 기능 개발
gikhoon Jun 28, 2024
9f43414
feat: 세션 내 사진의 순서를 바꾸는 기능 구현
gikhoon Jun 28, 2024
d724646
feat: 세션 사진 순서 수정 시 예외 처리 추가
gikhoon Jun 28, 2024
27ae182
feat: 사진 한장 삭제 기능 개발
gikhoon Jul 3, 2024
62e80b5
feat: 세션 사진 증가에 따른 세션추가 수정
gikhoon Jul 3, 2024
eb4a984
feat(Session): 세션 사진 삭제시 순서 재정렬
gikhoon Jul 3, 2024
a8b91b2
feat(Session): 세션 수정 - 사진 추가 시 응답 항목 추가
gikhoon Jul 3, 2024
e51336c
feat(Session): 세션 사진 갯수 증가에 따른 세션 리스트 정보 반환 항목 추가
gikhoon Jul 3, 2024
89f3fad
feat(Session): 세션 수정 기능 수정
gikhoon Jul 3, 2024
5bd360a
feat(Session): 응답의 제약조건 제거
gikhoon Jul 3, 2024
a7a1968
refactor: 사진 갯수 증가 관련 기존 API 삭제
gikhoon Jul 4, 2024
e1e91fd
feat: 코드 리뷰를 바탕으로 코드 수정
gikhoon Jul 4, 2024
9c61967
feat: 세션 추가시 사진 검증 로직 추가
gikhoon Jul 4, 2024
2419d03
refactor: 사용하지 않는 메소드 삭제
gikhoon Jul 4, 2024
433f077
feat: 사진 순서 변경 시 검증 로직 추가
gikhoon Jul 4, 2024
89d402b
feat: 코드 리뷰를 기반으로 코드 수정
gikhoon Jul 4, 2024
e36b434
feat: 코드 리뷰를 기반으로 코드 수정
gikhoon Jul 4, 2024
1c7525d
refactor: 에러코드명 변경
gikhoon Jul 8, 2024
25d6952
feat: 기존 세션 사진 항목 삭제
gikhoon Jul 8, 2024
4b8d3b4
Merge branch 'develop' into feature/#TCTS-5-session-multi-photo
gikhoon Jul 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,21 +1,28 @@
package org.cotato.csquiz.api.session.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.cotato.csquiz.api.session.dto.AddSessionPhotoRequest;
import org.cotato.csquiz.api.session.dto.AddSessionPhotoResponse;
import org.cotato.csquiz.api.session.dto.AddSessionRequest;
import org.cotato.csquiz.api.session.dto.AddSessionResponse;
import org.cotato.csquiz.api.session.dto.CsEducationOnSessionNumberResponse;
import org.cotato.csquiz.api.session.dto.DeleteSessionPhotoRequest;
import org.cotato.csquiz.api.session.dto.SessionListResponse;
import org.cotato.csquiz.api.session.dto.UpdateSessionDescriptionRequest;
import org.cotato.csquiz.api.session.dto.UpdateSessionNumberRequest;
import org.cotato.csquiz.api.session.dto.UpdateSessionPhotoOrderRequest;
import org.cotato.csquiz.api.session.dto.UpdateSessionPhotoRequest;
import org.cotato.csquiz.api.session.dto.UpdateSessionRequest;
import org.cotato.csquiz.domain.generation.service.SessionService;
import org.cotato.csquiz.common.error.exception.ImageException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PatchMapping;
Expand All @@ -33,20 +40,21 @@ public class SessionController {

private final SessionService sessionService;

@Operation(summary = "Session 리스트 정보 얻기", description = "Get Session Infos")
@GetMapping("")
public ResponseEntity<List<SessionListResponse>> findSessionsByGenerationId(@RequestParam Long generationId) {
return ResponseEntity.status(HttpStatus.OK).body(sessionService.findSessionsByGenerationId(generationId));
}

@Operation(summary = "Session 추가하기", description = "세션 추가하기")
@PostMapping(value = "/add", consumes = "multipart/form-data")
public ResponseEntity<AddSessionResponse> addSession(@ModelAttribute @Valid AddSessionRequest request)
throws ImageException {
return ResponseEntity.status(HttpStatus.CREATED).body(sessionService.addSession(request));
}

@PatchMapping(value = "/update", consumes = "multipart/form-data")
public ResponseEntity<Void> updateSession(@ModelAttribute @Valid UpdateSessionRequest request)
throws ImageException {
public ResponseEntity<Void> updateSession(@RequestBody @Valid UpdateSessionRequest request) {
sessionService.updateSession(request);
return ResponseEntity.noContent().build();
}
Expand All @@ -57,16 +65,24 @@ public ResponseEntity<Void> updateSessionNumber(@RequestBody @Valid UpdateSessio
return ResponseEntity.noContent().build();
}

@PatchMapping("/description")
public ResponseEntity<Void> updateSessionDescription(@RequestBody @Valid UpdateSessionDescriptionRequest request) {
sessionService.updateSessionDescription(request);
@Operation(summary = "Session 수정 - 사진 순서", description = "세션 사진 순서 바꾸기")
@PatchMapping("/photo/order")
public ResponseEntity<Void> updateSessionPhotoOrder(@RequestBody UpdateSessionPhotoOrderRequest request) {
sessionService.updateSessionPhotoOrder(request);
return ResponseEntity.noContent().build();
}

@PatchMapping(value = "/update/photo", consumes = "multipart/form-data")
public ResponseEntity<Void> updateSessionPhoto(@ModelAttribute @Valid UpdateSessionPhotoRequest request)
@Operation(summary = "Session 수정 - 사진 추가하기", description = "세션 수정 시 사진 추가하기, photoId 반환")
@PostMapping(value = "/photo", consumes = "multipart/form-data")
public ResponseEntity<AddSessionPhotoResponse> additionalSessionPhoto(@ModelAttribute @Valid AddSessionPhotoRequest request)
throws ImageException {
sessionService.updateSessionPhoto(request);
return ResponseEntity.status(HttpStatus.CREATED).body(sessionService.additionalSessionPhoto(request));
}

@Operation(summary = "Session 수정 - 사진 삭제하기", description = "사진 삭제하기")
@DeleteMapping(value = "/photo")
public ResponseEntity<Void> deleteSessionPhoto(@RequestBody DeleteSessionPhotoRequest request) {
sessionService.deleteSessionPhoto(request);
return ResponseEntity.noContent().build();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.cotato.csquiz.api.session.dto;

import jakarta.validation.constraints.NotNull;
import org.springframework.web.multipart.MultipartFile;

public record AddSessionPhotoRequest(

@NotNull
Long sessionId,
@NotNull
MultipartFile photo
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.cotato.csquiz.api.session.dto;

import jakarta.validation.constraints.NotNull;
import org.cotato.csquiz.domain.generation.entity.SessionPhoto;
import org.springframework.web.multipart.MultipartFile;

public record AddSessionPhotoResponse(
Long photoId,
String photoUrl,
Integer order
) {
public static AddSessionPhotoResponse from(SessionPhoto sessionPhoto) {
return new AddSessionPhotoResponse(sessionPhoto.getId(),
sessionPhoto.getS3Info().getUrl(),
sessionPhoto.getOrder());
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.cotato.csquiz.api.session.dto;

import java.util.List;
import org.cotato.csquiz.domain.generation.enums.CSEducation;
import org.cotato.csquiz.domain.generation.enums.DevTalk;
import org.cotato.csquiz.domain.generation.enums.ItIssue;
Expand All @@ -10,7 +11,7 @@
public record AddSessionRequest(
@NotNull
Long generationId,
MultipartFile sessionImage,
List<MultipartFile> photos,
@NotNull
String title,
@NotNull
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.cotato.csquiz.api.session.dto;

import jakarta.validation.constraints.NotNull;

public record DeleteSessionPhotoRequest(
@NotNull
Long photoId
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.cotato.csquiz.api.session.dto;

import jakarta.validation.constraints.NotNull;
import java.util.List;
import org.cotato.csquiz.domain.generation.entity.SessionPhoto;

public record SessionListPhotoInfoResponse(
Long photoId,
String photoUrl,
Integer order
) {
public static SessionListPhotoInfoResponse from(SessionPhoto sessionPhoto) {
return new SessionListPhotoInfoResponse(sessionPhoto.getId(),
sessionPhoto.getS3Info().getUrl(),
sessionPhoto.getOrder());
}

public static List<SessionListPhotoInfoResponse> from(List<SessionPhoto> sessionPhotos) {
return sessionPhotos.stream()
.map(SessionListPhotoInfoResponse::from)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package org.cotato.csquiz.api.session.dto;

import java.util.List;
import org.cotato.csquiz.domain.generation.embedded.SessionContents;
import org.cotato.csquiz.domain.generation.entity.Session;

public record SessionListResponse(
Long sessionId,
Integer sessionNumber,
String title,
String photoUrl,
List<SessionListPhotoInfoResponse> photoInfos,
String description,
Long generationId,
SessionContents sessionContents
Expand All @@ -17,7 +18,7 @@ public static SessionListResponse from(Session session) {
session.getId(),
session.getNumber(),
session.getTitle(),
(session.getPhotoS3Info() != null) ? session.getPhotoS3Info().getUrl() : null,
SessionListPhotoInfoResponse.from(session.getSessionPhotos()),
session.getDescription(),
session.getGeneration().getId(),
session.getSessionContents()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.cotato.csquiz.api.session.dto;

import jakarta.validation.constraints.NotNull;

public record UpdateSessionPhotoOrderInfoRequest(
@NotNull
Long photoId,
@NotNull
Integer order
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.cotato.csquiz.api.session.dto;

import java.util.List;

public record UpdateSessionPhotoOrderRequest(
Long sessionId,
List<UpdateSessionPhotoOrderInfoRequest> orderInfos
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
public record UpdateSessionPhotoRequest(
@NotNull
Long sessionId,
MultipartFile sessionImage
MultipartFile photo
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,10 @@
import org.cotato.csquiz.domain.generation.enums.ItIssue;
import org.cotato.csquiz.domain.generation.enums.Networking;
import jakarta.validation.constraints.NotNull;
import org.springframework.web.multipart.MultipartFile;

public record UpdateSessionRequest(
@NotNull
Long sessionId,
MultipartFile sessionImage,
@NotNull
Boolean isPhotoUpdated,
String title,
String description,
@NotNull
Expand All @@ -21,7 +17,6 @@ public record UpdateSessionRequest(
Networking networking,
@NotNull
CSEducation csEducation,

@NotNull
DevTalk devTalk
) {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/org/cotato/csquiz/common/error/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,13 @@ public enum ErrorCode {
EDUCATION_STATUS_NOT_BEFORE(HttpStatus.BAD_REQUEST, "E-402", "이미 시작한 적이 있는 교육입니다."),
MEMBER_CANT_ACCESS(HttpStatus.BAD_REQUEST, "E-403", "해당 멤버의 ROLE로 접근할 수 없습니다"),

//세션 사진
SESSION_PHOTO_COUNT_MISMATCH(HttpStatus.BAD_REQUEST, "P-101", "저장된 사진 수와 요청 사진 수가 다릅니다."),
SESSION_ORDER_INVALID(HttpStatus.BAD_REQUEST, "P-102", "입력한 순서는 유효하지 않습니다."),

FILE_EXTENSION_FAULT(HttpStatus.BAD_REQUEST, "F-001", "해당 파일은 등록 할 수 없는 확장자명입니다."),


INVALID_ANSWER(HttpStatus.BAD_REQUEST, "Q-101", "객관식 문제는 숫자 형식의 값만 정답으로 추가할 수 있습니다."),
CONTENT_IS_NOT_ANSWER(HttpStatus.BAD_REQUEST, "Q-201", "추가되지 않은 정답을 추가할 수 없습니다."),
QUIZ_NUMBER_DUPLICATED(HttpStatus.CONFLICT, "Q-301", "퀴즈 번호는 중복될 수 없습니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import java.util.ArrayList;
import java.util.List;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -38,8 +41,8 @@ public class Session extends BaseTimeEntity {
@Column(name = "session_title", length = 100)
private String title;

@Embedded
private S3Info photoS3Info;
@OneToMany(mappedBy = "session", orphanRemoval = true)
private List<SessionPhoto> sessionPhotos = new ArrayList<>();

@Column(name = "session_description")
private String description;
Expand All @@ -61,9 +64,8 @@ public class Session extends BaseTimeEntity {
private SessionContents sessionContents;

@Builder
public Session(Integer number, S3Info s3Info, String title, String description, Generation generation, SessionContents sessionContents) {
public Session(Integer number, String title, String description, Generation generation, SessionContents sessionContents) {
this.number = number;
this.photoS3Info = s3Info;
this.title = title;
this.description = description;
this.generation = generation;
Expand All @@ -78,10 +80,6 @@ public void updateDescription(String description) {
this.description = description;
}

public void changePhotoUrl(S3Info photoUrl) {
this.photoS3Info = photoUrl;
}

public void updateSessionContents(SessionContents sessionContents) {
this.sessionContents = sessionContents;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.cotato.csquiz.domain.generation.entity;

import static jakarta.persistence.FetchType.LAZY;

import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import java.util.List;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.cotato.csquiz.api.session.dto.SessionListPhotoInfoResponse;
import org.cotato.csquiz.common.entity.BaseTimeEntity;
import org.cotato.csquiz.common.entity.S3Info;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class SessionPhoto extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "session_photo_id")
private Long id;

@Embedded
private S3Info s3Info;

@Column(name = "session_photo_order")
private Integer order;

@ManyToOne(fetch = LAZY)
@JoinColumn(name = "session_id")
private Session session;
Youthhing marked this conversation as resolved.
Show resolved Hide resolved

@Builder
public SessionPhoto(Session session, Integer order, S3Info s3Info) {
this.session = session;
this.order = order;
this.s3Info = s3Info;
}

public void updateOrder(Integer order) {
this.order = order;
}

public void decreaseOrder() {
if (order > 0) {
order--;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.cotato.csquiz.domain.generation.repository;

import java.util.List;
import java.util.Optional;
import org.cotato.csquiz.domain.generation.entity.Session;
import org.cotato.csquiz.domain.generation.entity.SessionPhoto;
import org.springframework.data.jpa.repository.JpaRepository;

public interface SessionPhotoRepository extends JpaRepository<SessionPhoto, Long> {
List<SessionPhoto> findAllBySession(Session session);

Optional<SessionPhoto> findFirstBySessionOrderByOrderDesc(Session session);
gikhoon marked this conversation as resolved.
Show resolved Hide resolved
}
Loading
Loading