Skip to content

Commit

Permalink
Merge pull request #161 from IT-Cotato/release
Browse files Browse the repository at this point in the history
[Main] V2.2024.09.20.01
  • Loading branch information
Youthhing authored Sep 20, 2024
2 parents 9f14c48 + 3eff067 commit 8dd2c94
Show file tree
Hide file tree
Showing 45 changed files with 403 additions and 284 deletions.
1 change: 1 addition & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ jobs:
aes.secret.salt: ${{ secrets.AES_SECRET_SALT }}
spring.mail.username: ${{ secrets.SENDER_EMAIL }}
spring.mail.password: ${{ secrets.SENDER_PASSWORD }}
location.distance: ${{ secrets.STANDARD_DISTANCE }}


# [2] 실행 권한 부여
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ jobs:
aes.secret.salt: ${{ secrets.AES_SECRET_SALT }}
spring.mail.username: ${{ secrets.SENDER_EMAIL }}
spring.mail.password: ${{ secrets.SENDER_PASSWORD }}
location.distance: ${{ secrets.STANDARD_DISTANCE }}


# [2] 실행 권한 부여
Expand Down
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ dependencies {
//S3 관련 의존성 부여
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

//webp 변환 라이브러리
implementation "com.sksamuel.scrimage:scrimage-core:4.0.32"
implementation "com.sksamuel.scrimage:scrimage-webp:4.0.32"

implementation group: 'org.redisson', name: 'redisson-spring-boot-starter', version: '3.32.0'

implementation 'io.jsonwebtoken:jjwt:0.9.1'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package org.cotato.csquiz.api.attendance.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import java.time.LocalDate;
import java.time.LocalDateTime;
import org.cotato.csquiz.domain.attendance.entity.Attendance;
import org.cotato.csquiz.domain.attendance.entity.AttendanceRecord;
Expand All @@ -21,7 +20,7 @@ public record MemberAttendResponse(
@Schema(description = "세션 타이틀", example = "3주차 세션")
String sessionTitle,
@Schema(description = "세션 날짜")
LocalDate sessionDate,
LocalDateTime sessionDateTime,
@Schema(description = "출결 진행 여부", examples = {
"CLOSED", "OPEN"
})
Expand All @@ -31,29 +30,30 @@ public record MemberAttendResponse(
@Schema(description = "마감된 출석에 대한 출결 결과", nullable = true)
AttendanceResult attendanceResult
) {
public static MemberAttendResponse closedAttendanceResponse(Session session, AttendanceRecord attendanceRecord) {
public static MemberAttendResponse unrecordedAttendance(Session session, Attendance attendance, Long memberId) {
return new MemberAttendResponse(
session.getId(),
attendanceRecord.getAttendance().getId(),
attendanceRecord.getMemberId(),
attendance.getId(),
memberId,
session.getTitle(),
session.getSessionDate(),
AttendanceOpenStatus.CLOSED,
attendanceRecord.getAttendanceType(),
attendanceRecord.getAttendanceResult()
session.getSessionDateTime(),
AttendanceUtil.getAttendanceOpenStatus(session.getSessionDateTime(), attendance, LocalDateTime.now()),
null,
null
);
}

public static MemberAttendResponse openedAttendanceResponse(Attendance attendance, Session session, Long memberId) {
public static MemberAttendResponse recordedAttendance(Session session, Attendance attendance,
AttendanceRecord attendanceRecord) {
return new MemberAttendResponse(
session.getId(),
attendance.getId(),
memberId,
attendanceRecord.getAttendanceId(),
attendanceRecord.getMemberId(),
session.getTitle(),
session.getSessionDate(),
AttendanceUtil.getAttendanceOpenStatus(attendance, LocalDateTime.now()),
null,
null
session.getSessionDateTime(),
AttendanceUtil.getAttendanceOpenStatus(session.getSessionDateTime(), attendance, LocalDateTime.now()),
attendanceRecord.getAttendanceType(),
attendanceRecord.getAttendanceResult()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public ResponseEntity<CreateProjectResponse> createProject(@RequestBody @Valid C
@PostMapping(value = "/images", consumes = "multipart/form-data")
public ResponseEntity<Void> createProjectImage(@ModelAttribute CreateProjectImageRequest request)
throws ImageException {
projectImageService.createProjectImage(request.projectId(), request.logoImage(), request.thumbNailImage());
projectImageService.createProjectImage(request.projectId(), request.logoImage(), request.thumbNailImage(), request.detailImages());
return ResponseEntity.status(HttpStatus.CREATED).build();
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package org.cotato.csquiz.api.project.dto;

import java.util.List;
import org.springframework.web.multipart.MultipartFile;

public record CreateProjectImageRequest(
Long projectId,
MultipartFile logoImage,
MultipartFile thumbNailImage
MultipartFile thumbNailImage,
List<MultipartFile> detailImages
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@
public record ProjectImageInfoResponse(
Long imageId,
String imageUrl,
ProjectImageType projectImageType
ProjectImageType projectImageType,
int imageOrder
) {
public static ProjectImageInfoResponse from(ProjectImage projectImage) {
return new ProjectImageInfoResponse(
projectImage.getId(),
projectImage.getS3Info().getUrl(),
projectImage.getProjectImageType()
projectImage.getProjectImageType(),
projectImage.getImageOrder()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import org.cotato.csquiz.api.session.dto.CsEducationOnSessionNumberResponse;
import org.cotato.csquiz.api.session.dto.DeleteSessionImageRequest;
import org.cotato.csquiz.api.session.dto.SessionListResponse;
import org.cotato.csquiz.api.session.dto.UpdateSessionNumberRequest;
import org.cotato.csquiz.api.session.dto.UpdateSessionImageOrderRequest;
import org.cotato.csquiz.api.session.dto.UpdateSessionRequest;
import org.cotato.csquiz.domain.generation.service.SessionImageService;
Expand Down Expand Up @@ -42,7 +41,7 @@ public class SessionController {
private final SessionImageService sessionImageService;

@Operation(summary = "세션 목록 반환 API")
@GetMapping("")
@GetMapping
public ResponseEntity<List<SessionListResponse>> findSessionsByGenerationId(@RequestParam Long generationId) {
return ResponseEntity.status(HttpStatus.OK).body(sessionService.findSessionsByGenerationId(generationId));
}
Expand All @@ -69,13 +68,6 @@ public ResponseEntity<Void> updateSession(@RequestBody @Valid UpdateSessionReque
return ResponseEntity.noContent().build();
}

@Operation(summary = "세션 숫자 변경 API")
@PatchMapping("/number")
public ResponseEntity<Void> updateSessionNumber(@RequestBody @Valid UpdateSessionNumberRequest request) {
sessionService.updateSessionNumber(request);
return ResponseEntity.noContent().build();
}

@Operation(summary = "세션 사진 순서 변경 API")
@PatchMapping("/image/order")
public ResponseEntity<Void> updateSessionImageOrder(@RequestBody UpdateSessionImageOrderRequest request) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public record AddSessionImageRequest(
@NotNull
Long sessionId,
@NotNull
MultipartFile image
MultipartFile image,
@NotNull
Integer order
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.List;
import org.cotato.csquiz.domain.generation.enums.CSEducation;
Expand All @@ -23,14 +23,15 @@ public record AddSessionRequest(
Double latitude,
Double longitude,
String placeName,
@Schema(description = "세션 날짜 및 시작 시간")
@NotNull
LocalDate sessionDate,
LocalDateTime sessionDateTime,

@Schema(example = "19:05:00")
@Schema(example = "19:10:00", description = "출석 마감 시간, 해당 시간 이후 지각 처리")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss")
LocalTime attendanceDeadLine,

@Schema(example = "19:20:00")
@Schema(example = "19:20:00", description = "지각 마감 시간, 해당 시간 이후 결석 처리")
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "HH:mm:ss")
LocalTime lateDeadLine,

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

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import org.cotato.csquiz.domain.generation.embedded.SessionContents;
import org.cotato.csquiz.domain.generation.entity.Session;
Expand All @@ -14,7 +14,7 @@ public record SessionListResponse(
String description,
Long generationId,
String placeName,
LocalDate sessionDate,
LocalDateTime sessionDateTime,
SessionContents sessionContents
) {
public static SessionListResponse of(Session session, List<SessionImage> sessionImages) {
Expand All @@ -28,7 +28,7 @@ public static SessionListResponse of(Session session, List<SessionImage> session
session.getDescription(),
session.getGeneration().getId(),
session.getPlaceName(),
session.getSessionDate(),
session.getSessionDateTime(),
session.getSessionContents()
);
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import static org.cotato.csquiz.domain.attendance.enums.DeadLine.DEFAULT_ATTENDANCE_DEADLINE;
import static org.cotato.csquiz.domain.attendance.enums.DeadLine.DEFAULT_LATE_DEADLINE;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Objects;
import org.cotato.csquiz.api.attendance.dto.AttendanceDeadLineDto;
import org.cotato.csquiz.domain.attendance.embedded.Location;
Expand All @@ -19,7 +19,7 @@ public record UpdateSessionRequest(
String title,
String description,
@NotNull
LocalDate sessionDate,
LocalDateTime sessionDateTime,
String placeName,
Location location,
AttendanceDeadLineDto attendTime,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers(WHITE_LIST).permitAll()
.requestMatchers("/v1/api/education/result/**").hasAnyRole("MEMBER", "EDUCATION", "ADMIN")
.requestMatchers("/v1/api/education/from").hasAnyRole("MEMBER", "EDUCATION", "ADMIN")
.requestMatchers(new AntPathRequestMatcher("/v1/api/education/winner", "GET")).hasAnyRole("MEMBER", "EDUCATION", "ADMIN")
.requestMatchers(new AntPathRequestMatcher("/v1/api/education/kings", "GET")).hasAnyRole("MEMBER", "EDUCATION", "ADMIN")
.requestMatchers(new AntPathRequestMatcher("/v1/api/education/status", "GET"))
.hasAnyRole("MEMBER", "EDUCATION", "ADMIN")
.requestMatchers(new AntPathRequestMatcher("/v1/api/education", "GET")).authenticated()
Expand Down
15 changes: 10 additions & 5 deletions src/main/java/org/cotato/csquiz/common/error/ErrorCode.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ public enum ErrorCode {
EDUCATION_STATUS_NOT_BEFORE(HttpStatus.BAD_REQUEST, "E-402", "이미 시작한 적이 있는 교육입니다."),
MEMBER_CANT_ACCESS(HttpStatus.BAD_REQUEST, "E-403", "해당 멤버의 ROLE로 접근할 수 없습니다"),

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

FILE_EXTENSION_FAULT(HttpStatus.BAD_REQUEST, "F-001", "해당 파일 확장자 명이 존재하지 않습니다."),
FILE_IS_EMPTY(HttpStatus.BAD_REQUEST, "F-002", "파일이 비어있습니다"),

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

INVALID_ANSWER(HttpStatus.BAD_REQUEST, "Q-101", "객관식 문제는 숫자 형식의 값만 정답으로 추가할 수 있습니다."),
CONTENT_IS_NOT_ANSWER(HttpStatus.BAD_REQUEST, "Q-201", "추가되지 않은 정답을 추가할 수 없습니다."),
QUIZ_NUMBER_DUPLICATED(HttpStatus.CONFLICT, "Q-301", "퀴즈 번호는 중복될 수 없습니다."),
Expand All @@ -82,7 +82,11 @@ public enum ErrorCode {
OFFLINE_ATTEND_FAIL(HttpStatus.BAD_REQUEST, "AT-101", "거리 부적합으로 인한 대면 출석 실패"),
INVALID_ATTEND_TIME(HttpStatus.BAD_REQUEST, "AT-102", "시간 입력 범위가 잘못되었습니다."),
ALREADY_ATTEND(HttpStatus.CONFLICT, "AT-301", "이미 해당 타입으로 출석한 기록이 있습니다."),
ATTENDANCE_CLOSED(HttpStatus.BAD_REQUEST, "AT-401", "아직 출석 시간이 아닙니다."),
ATTENDANCE_NOT_OPEN(HttpStatus.BAD_REQUEST, "AT-401", "출석 시간이 아닙니다."),

//프로젝트 관련
LOGO_IMAGE_EXIST(HttpStatus.CONFLICT, "PJ-301", "이미 로고 이미지가 존재합니다."),
THUMBNAIL_IMAGE_EXIST(HttpStatus.CONFLICT, "PJ-302", "이미 썸네일 이미지가 존재합니다."),

// 500 오류 -> 서버측에서 처리가 실패한 부분들
WEBSOCKET_SEND_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR, "S-001", "소캣 메세지 전송 실패"),
Expand All @@ -93,6 +97,7 @@ public enum ErrorCode {
SCORER_LOCK_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "S-006", "득점자 락 획득 과정에서 에러 발생"),
IMAGE_CONVERT_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "S-007", "로컬 이미지 변환에 실패했습니다"),
SSE_SEND_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "S-008", "서버 이벤트 전송간 오류 발생"),
WEBP_CONVERT_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "S-009", "webp 변환에 실패했습니다")
;

private final HttpStatus httpStatus;
Expand Down
33 changes: 9 additions & 24 deletions src/main/java/org/cotato/csquiz/common/s3/S3Uploader.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,21 @@
package org.cotato.csquiz.common.s3;

import static org.cotato.csquiz.common.util.FileUtil.extractFileExtension;
import static org.cotato.csquiz.common.util.FileUtil.convert;
import static org.cotato.csquiz.common.util.FileUtil.isImageFileExtension;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.CannedAccessControlList;
import com.amazonaws.services.s3.model.ObjectMetadata;
import com.amazonaws.services.s3.model.PutObjectRequest;
import org.cotato.csquiz.common.entity.S3Info;
import org.cotato.csquiz.common.error.ErrorCode;
import org.cotato.csquiz.common.error.exception.ImageException;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

@Slf4j
@RequiredArgsConstructor
Expand All @@ -36,13 +32,17 @@ public S3Info uploadFiles(MultipartFile multipartFile, String folderName) throws
log.info("{} 사진 업로드", multipartFile.getOriginalFilename());
File localUploadFile = convert(multipartFile);

String fileName = folderName + "/" + localUploadFile.getName();
String uploadUrl = putS3(localUploadFile, fileName);
localUploadFile.delete();
return uploadFiles(localUploadFile, folderName);
}

public S3Info uploadFiles(File file, String folderName) {
String fileName = folderName + "/" + file.getName();
String uploadUrl = putS3(file, fileName);
file.delete();

return S3Info.builder()
.folderName(folderName)
.fileName(localUploadFile.getName())
.fileName(file.getName())
.url(uploadUrl)
.build();
}
Expand Down Expand Up @@ -74,19 +74,4 @@ private boolean isImageFile(File file) {

return isImageFileExtension(extension);
}

private File convert(MultipartFile file) throws ImageException {
String fileExtension = extractFileExtension(file);
File convertFile = new File(System.getProperty("user.dir") + "/" + UUID.randomUUID() + "." + fileExtension);

try {
FileOutputStream fos = new FileOutputStream(convertFile);
fos.write(file.getBytes());
fos.close();

return convertFile;
} catch (IOException e) {
throw new ImageException(ErrorCode.IMAGE_CONVERT_FAIL);
}
}
}
Loading

0 comments on commit 8dd2c94

Please sign in to comment.