Skip to content

Commit

Permalink
fix: API URL 경로 수정 및 인가 처리 (#242)
Browse files Browse the repository at this point in the history
  • Loading branch information
OziinG committed Dec 4, 2024
2 parents 56f1289 + d44ebc8 commit 38af456
Show file tree
Hide file tree
Showing 13 changed files with 224 additions and 5 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.ioteatime.meonghanyangserver.common.type;

public enum ImageSuccessType implements SuccessTypeCode {
GET_LIST_OF_DATE(200, "OK", "날짜에 해당하는 이미지 목록 조회에 성공하였습니다."),
CREATE_PRESIGNED_URL(200, "OK", "Presigned Url 생성에 성공하였습니다.");

private final Integer code;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package org.ioteatime.meonghanyangserver.image.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.ioteatime.meonghanyangserver.common.api.Api;
import org.ioteatime.meonghanyangserver.common.utils.LoginMember;
import org.ioteatime.meonghanyangserver.image.dto.response.GroupDateImageResponse;
import org.springframework.web.bind.annotation.RequestParam;

@Tag(name = "Image Api", description = "Image 관련 API 목록입니다.")
public interface ImageApi {
@Operation(
summary = "회원이 속한 그룹의 날짜별 객체 탐지 이미지 목록을 최근 시간 순서대로 조회합니다.",
description =
"담당자: 양원채\n\n달력의 날짜를 클릭했을 때 해당 날짜의 이미지 목록을 조회할 떄 사용합니다.\n\nURL QueryParameter로 날짜를 담아 요청 주시면 됩니다.")
Api<GroupDateImageResponse> groupDateImageList(
@LoginMember Long memberId,
@RequestParam("year") Short year,
@RequestParam("month") Byte month,
@RequestParam("day") Byte day);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.ioteatime.meonghanyangserver.image.controller;

import lombok.RequiredArgsConstructor;
import org.ioteatime.meonghanyangserver.common.api.Api;
import org.ioteatime.meonghanyangserver.common.type.ImageSuccessType;
import org.ioteatime.meonghanyangserver.common.utils.LoginMember;
import org.ioteatime.meonghanyangserver.image.dto.response.GroupDateImageResponse;
import org.ioteatime.meonghanyangserver.image.dto.response.ImageSaveUrlResponse;
import org.ioteatime.meonghanyangserver.image.service.ImageService;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/image")
public class ImageController implements ImageApi {
private final ImageService imageService;

@GetMapping
public Api<GroupDateImageResponse> groupDateImageList(
@LoginMember Long memberId,
@RequestParam("year") Short year,
@RequestParam("month") Byte month,
@RequestParam("day") Byte day) {
GroupDateImageResponse response =
imageService.findAllByMemberIdAndDate(memberId, year, month, day);
return Api.success(ImageSuccessType.GET_LIST_OF_DATE, response);
}

@GetMapping("/{fileName}")
public Api<ImageSaveUrlResponse> getImageSaveUrl(
@LoginMember Long cctvId, @PathVariable String fileName) {
ImageSaveUrlResponse imageSaveUrlResponse = imageService.getImageSaveUrl(cctvId, fileName);
return Api.success(ImageSuccessType.CREATE_PRESIGNED_URL, imageSaveUrlResponse);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.ioteatime.meonghanyangserver.common.api.Api;
import org.ioteatime.meonghanyangserver.common.utils.LoginMember;
import org.ioteatime.meonghanyangserver.image.dto.response.ImageSaveUrlResponse;
import org.springframework.web.bind.annotation.PathVariable;

@Tag(name = "Image Api", description = "Image 관련 API 목록입니다.")
public interface ImageDeviceApi {
@Operation(summary = "이미지 저장을 위한 presigned url을 발급 받습니다.", description = "담당자: 임지인")
@Operation(
summary = "이미지 저장을 위한 presigned url을 발급 받습니다.",
description =
"담당자: 임지인\n\n캡쳐 이미지 저장을 위한 URL 발급 요청입니다.\n\nCCTV의 Access Token과 함께 요청 하시면 됩니다.")
public Api<ImageSaveUrlResponse> getImageSaveUrl(
@PathVariable Long cctvId, @PathVariable String fileName);
@LoginMember Long cctvId, @PathVariable String fileName);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@

@RestController
@RequiredArgsConstructor
@RequestMapping("/open-api/image")
@RequestMapping("/api/image-device")
public class ImageDeviceController implements ImageDeviceApi {
private final ImageService imageService;

@GetMapping("/{cctvId}/{fileName}")
@GetMapping("/{fileName}")
public Api<ImageSaveUrlResponse> getImageSaveUrl(
@PathVariable Long cctvId, @PathVariable String fileName) {
ImageSaveUrlResponse imageSaveUrlResponse = imageService.getImageSaveUrl(cctvId, fileName);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.ioteatime.meonghanyangserver.image.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import java.util.List;

@Schema(description = "날짜에 해당하는 그룹의 이미지 목록 응답")
public record GroupDateImageResponse(
@Schema(description = "날짜에 해당하는 그룹의 이미지 목록") List<ImageResponse> images) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package org.ioteatime.meonghanyangserver.image.dto.response;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Getter;

@Getter
@Schema(description = "이미지 단일 응답")
public class ImageResponse {
@NotNull
@Schema(description = "이미지 ID", example = "1")
private final Long imageId;

@NotNull
@Schema(description = "이미지 원본 이름", example = "test-image.jpg")
private final String imageName;

@NotNull
@Schema(
description = "이미지 URL",
example = "https://bucket.s3.ap-northeast-2.amazonaws.com/path/to/test-image.jpg")
private String imagePath;

@NotNull
@Schema(description = "형식이 있는 이미지 생성 시각", example = "2024.10.22.13:00")
private final String formattedCreatedAt;

@Builder
public ImageResponse(
Long imageId, String imageName, String imagePath, String formattedCreatedAt) {
this.imageId = imageId;
this.imageName = imageName;
this.imagePath = imagePath;
this.formattedCreatedAt = formattedCreatedAt;
}

public ImageResponse updateToPresignedUrl(String imagePresignedPath) {
this.imagePath = imagePresignedPath;
return this;
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
package org.ioteatime.meonghanyangserver.image.mapper;

import java.util.List;
import org.ioteatime.meonghanyangserver.image.dto.response.GroupDateImageResponse;
import org.ioteatime.meonghanyangserver.image.dto.response.ImageResponse;
import org.ioteatime.meonghanyangserver.image.dto.response.ImageSaveUrlResponse;

public class ImageResponseMapper {
public static GroupDateImageResponse from(List<ImageResponse> images) {
return new GroupDateImageResponse(images);
}

public static ImageSaveUrlResponse form(String presignedUrl) {
return new ImageSaveUrlResponse(presignedUrl);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.ioteatime.meonghanyangserver.image.repository;

import org.ioteatime.meonghanyangserver.image.domain.ImageEntity;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ImageJpaRepository extends JpaRepository<ImageEntity, Long> {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.ioteatime.meonghanyangserver.image.repository;

import java.time.LocalDateTime;
import java.util.List;
import org.ioteatime.meonghanyangserver.image.dto.response.ImageResponse;

public interface ImageRepository {
List<ImageResponse> findAllByGroupIdAndDate(Long groupId, LocalDateTime searchDate);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package org.ioteatime.meonghanyangserver.image.repository;

import static org.ioteatime.meonghanyangserver.group.domain.QGroupEntity.groupEntity;
import static org.ioteatime.meonghanyangserver.image.domain.QImageEntity.imageEntity;

import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.Expressions;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.ioteatime.meonghanyangserver.image.dto.response.ImageResponse;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class ImageRepositoryImpl implements ImageRepository {
private final JPAQueryFactory queryFactory;
private final ImageJpaRepository imageJpaRepository;

@Override
public List<ImageResponse> findAllByGroupIdAndDate(Long groupId, LocalDateTime searchDate) {
String dateFormat = "DATE_FORMAT({0}, '%Y.%m.%d.%H:%i')";
return queryFactory
.select(
Projections.constructor(
ImageResponse.class,
imageEntity.id.as("imageId"),
imageEntity.imageName.as("imageName"),
imageEntity.imagePath.as("imagePath"),
Expressions.stringTemplate(dateFormat, imageEntity.createdAt)
.as("formattedCreatedAt")))
.from(imageEntity)
.join(imageEntity.group, groupEntity)
.where(
imageEntity
.group
.id
.eq(groupId)
.and(imageEntity.createdAt.year().eq(searchDate.getYear()))
.and(imageEntity.createdAt.month().eq(searchDate.getMonthValue()))
.and(
imageEntity
.createdAt
.dayOfMonth()
.eq(searchDate.getDayOfMonth())))
.orderBy(imageEntity.createdAt.desc())
.fetch();
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
package org.ioteatime.meonghanyangserver.image.service;

import com.amazonaws.HttpMethod;
import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.ioteatime.meonghanyangserver.cctv.repository.CctvRepository;
import org.ioteatime.meonghanyangserver.clients.s3.S3Client;
import org.ioteatime.meonghanyangserver.common.exception.NotFoundException;
import org.ioteatime.meonghanyangserver.common.type.CctvErrorType;
import org.ioteatime.meonghanyangserver.common.type.GroupErrorType;
import org.ioteatime.meonghanyangserver.group.domain.GroupEntity;
import org.ioteatime.meonghanyangserver.groupmember.repository.GroupMemberRepository;
import org.ioteatime.meonghanyangserver.image.dto.response.GroupDateImageResponse;
import org.ioteatime.meonghanyangserver.image.dto.response.ImageResponse;
import org.ioteatime.meonghanyangserver.image.dto.response.ImageSaveUrlResponse;
import org.ioteatime.meonghanyangserver.image.mapper.ImageResponseMapper;
import org.ioteatime.meonghanyangserver.image.repository.ImageRepository;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class ImageService {
private final S3Client s3Client;
private final CctvRepository cctvRepository;
private final ImageRepository imageRepository;
private final GroupMemberRepository groupMemberRepository;

public ImageSaveUrlResponse getImageSaveUrl(Long cctvId, String fileName) {
if (!cctvRepository.existsById(cctvId)) {
Expand All @@ -24,4 +34,30 @@ public ImageSaveUrlResponse getImageSaveUrl(Long cctvId, String fileName) {
String presignedUrl = s3Client.generatePreSignUrl(fileName, HttpMethod.PUT);
return ImageResponseMapper.form(presignedUrl);
}

public GroupDateImageResponse findAllByMemberIdAndDate(
Long memberId, Short year, Byte month, Byte day) {
// 그룹멤버를 찾아 그룹 id 확인
// 없으면 에러, 이미지를 조회할 수 없음
// 그룹 id와 날짜를 기준으로 이미지 리스트 조회
GroupEntity groupEntity =
groupMemberRepository
.findGroupFromGroupMember(memberId)
.orElseThrow(
() -> new NotFoundException(GroupErrorType.GROUP_MEMBER_NOT_FOUND));
LocalDateTime searchDate =
LocalDateTime.of(year.intValue(), month.intValue(), day.intValue(), 0, 0);
List<ImageResponse> imageResponses =
imageRepository.findAllByGroupIdAndDate(groupEntity.getId(), searchDate).stream()
.map(
image -> {
// 경로를 PresignedUrl로 변경
String preSignedUrl =
s3Client.generatePreSignUrl(
image.getImagePath(), HttpMethod.GET);
return image.updateToPresignedUrl(preSignedUrl);
})
.toList();
return ImageResponseMapper.from(imageResponses);
}
}
2 changes: 1 addition & 1 deletion src/main/resources/application-prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ spring:
format_sql: true
default_batch_fetch_size: 100
hibernate:
ddl-auto: update
ddl-auto: none

0 comments on commit 38af456

Please sign in to comment.