Skip to content

Commit

Permalink
feat: 썸원 이벤트 대응
Browse files Browse the repository at this point in the history
  • Loading branch information
CChuYong committed Nov 29, 2024
1 parent 22610dd commit 285ccdf
Show file tree
Hide file tree
Showing 12 changed files with 149 additions and 14 deletions.
6 changes: 6 additions & 0 deletions api-gateway/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ spring:
allowedHeaders: '*'
allowedMethods: '*'
routes:
- id: sumone-event
uri: http://photo-service
predicates:
- Path=/sumone/**
filters:
- RewritePath=/sumone/(?<segment>/?.*), /$\{segment}
- id: admin-service
uri: http://admin-service
predicates:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
public class PhotoServiceApplication {

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package kr.mafoo.photo.config;

import java.util.List;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.config.EnableWebFlux;
Expand All @@ -10,6 +14,7 @@

@EnableWebFlux
@Configuration
@EnableCaching
public class WebFluxConfig implements WebFluxConfigurer {
@Override
public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
Expand All @@ -29,4 +34,12 @@ public WebClient externalServiceWebClient() {
})
.build();
}

@Bean
public CacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
cacheManager.setAllowNullValues(false);
cacheManager.setCacheNames(List.of("albumCount"));
return cacheManager;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public Mono<AlbumResponse> createAlbum(
String memberId,
AlbumCreateRequest request
){
if (request.name().equals("SUMONE")) {
throw new RuntimeException(); // should not be happened
}
return albumService
.addAlbum(request.name(), request.type(), memberId)
.map(AlbumResponse::fromEntity);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import kr.mafoo.photo.annotation.RequestMemberId;
import java.util.concurrent.atomic.AtomicInteger;
import kr.mafoo.photo.controller.dto.request.ObjectStoragePreSignedUrlRequest;
import kr.mafoo.photo.controller.dto.response.AlbumResponse;
import kr.mafoo.photo.controller.dto.response.PhotoResponse;
import kr.mafoo.photo.controller.dto.response.PreSignedUrlResponse;
import kr.mafoo.photo.controller.dto.response.SumoneAlbumResponse;
import kr.mafoo.photo.controller.dto.response.SumoneBulkUrlRequest;
import kr.mafoo.photo.controller.dto.response.SumonePhotoResponse;
import kr.mafoo.photo.controller.dto.response.SumoneSummaryResponse;
import kr.mafoo.photo.domain.enums.AlbumType;
import kr.mafoo.photo.domain.enums.BrandType;
import kr.mafoo.photo.exception.AlbumNotFoundException;
import kr.mafoo.photo.exception.PhotoNotFoundException;
import kr.mafoo.photo.service.AlbumCommand;
import kr.mafoo.photo.service.AlbumQuery;
import kr.mafoo.photo.service.ObjectStorageService;
import kr.mafoo.photo.service.PhotoCommand;
import kr.mafoo.photo.service.PhotoQuery;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
Expand All @@ -27,33 +35,69 @@
@RestController
@RequestMapping("/v1/sumone")
public class SumoneController {
private final static String sumoneAlbumCommonName = "2024 연말 결산 이벤트";
private final static String sumoneAlbumCommonMemberId = "01JDVYCPARAM63X560YSNDXQ4S";

private final PhotoCommand photoCommand;
private final AlbumQuery albumQuery;
private final PhotoQuery photoQuery;
private final AlbumCommand albumCommand;
private final ObjectStorageService objectStorageService;

@Operation(summary = "통계 api")
@GetMapping("/summary")
public SumoneSummaryResponse getSummary() {
return new SumoneSummaryResponse(1024);
public Mono<SumoneSummaryResponse> getSummary() {
return albumQuery
.countAlbumByAlbumType(AlbumType.SUMONE)
.map(SumoneSummaryResponse::new);
}

@Operation(summary = "앨범 생성 api")
@PostMapping("/albums")
public Mono<AlbumResponse> createAlbum() {
return Mono.just(new AlbumResponse("test_album_id", "야뿌들", AlbumType.SUMONE, "6"));
public Mono<SumoneAlbumResponse> createAlbum() {
return albumCommand
.addAlbum(sumoneAlbumCommonName, "SUMONE", sumoneAlbumCommonMemberId)
.map(SumoneAlbumResponse::fromEntity);
}

@Operation(summary = "앨범에 이미지 url 추가")
@PostMapping("/albums/{albumId}/photos")
public Flux<PhotoResponse> addPhotos(
public Flux<SumonePhotoResponse> addPhotos(
@PathVariable String albumId,
@RequestBody SumoneBulkUrlRequest request
) {
return Flux.empty();
return albumQuery.findById(albumId).flatMapMany(album -> {
if(album.getType() != AlbumType.SUMONE) {
return Mono.error(new AlbumNotFoundException());
}
AtomicInteger displayIndex = new AtomicInteger(album.getPhotoCount());
return Flux
.fromArray(request.fileUrls())
.concatMap(fileUrl -> objectStorageService.setObjectPublicRead(fileUrl)
.flatMap(fileLink -> photoCommand.addPhoto(fileLink, BrandType.EXTERNAL, albumId, displayIndex.getAndIncrement(), album.getOwnerMemberId()))
)
.collectList()
.flatMapMany(addedPhotos ->
albumCommand.increaseAlbumPhotoCount(album, addedPhotos.size())
.thenMany(Flux.fromIterable(addedPhotos))
);
}).map(SumonePhotoResponse::fromEntity);
}

@Operation(summary = "앨범에 이미지 조회")
@GetMapping("/albums/{albumId}/photos")
public Flux<PhotoResponse> getPhotos(
public Flux<SumonePhotoResponse> getPhotos(
@PathVariable String albumId
) {
return Flux.empty();
return albumQuery.findById(albumId).flatMapMany(album -> {
if(album.getType() != AlbumType.SUMONE) {
return Mono.error(new AlbumNotFoundException());
}
//TODO: add timeout
return photoQuery
.findAllByAlbumIdOrderByCreatedAtDesc(albumId)
.onErrorResume(PhotoNotFoundException.class, ex -> Flux.empty());
}).map(SumonePhotoResponse::fromEntity);
}

@Operation(summary = "Pre-signed Url 요청", description = "Pre-signed Url 목록을 발급합니다.")
Expand All @@ -63,6 +107,13 @@ Mono<PreSignedUrlResponse> createPreSignedUrls(
@RequestBody
ObjectStoragePreSignedUrlRequest request
) {
return Mono.empty();
return albumQuery.findById(albumId).flatMap(album -> {
if (album.getType() != AlbumType.SUMONE) {
return Mono.error(new AlbumNotFoundException());
}
return objectStorageService
.createPreSignedUrls(request.fileNames(), album.getOwnerMemberId())
.map(PreSignedUrlResponse::fromStringArray);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package kr.mafoo.photo.controller.dto.response;


import io.swagger.v3.oas.annotations.media.Schema;
import kr.mafoo.photo.domain.AlbumEntity;

@Schema(description = "앨범 응답")
public record SumoneAlbumResponse(
@Schema(description = "앨범 ID", example = "test_album_id")
String albumId
) {
public static SumoneAlbumResponse fromEntity(AlbumEntity entity) {
return new SumoneAlbumResponse(
entity.getAlbumId()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package kr.mafoo.photo.controller.dto.response;


import io.swagger.v3.oas.annotations.media.Schema;
import kr.mafoo.photo.domain.PhotoEntity;

@Schema(description = "앨범 응답")
public record SumonePhotoResponse(
@Schema(description = "사진 URL", example = "photo_url")
String photoUrl
) {
public static SumonePhotoResponse fromEntity(PhotoEntity entity) {
return new SumonePhotoResponse(
entity.getPhotoUrl()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
@Schema(description = "리캡 응답")
public record SumoneSummaryResponse(
@Schema(description = "사용자 수", example = "1565")
int userCount
Long userCount
){

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ public interface AlbumRepository extends R2dbcRepository<AlbumEntity, String> {
Flux<AlbumEntity> findAllByTypeOrderByAlbumIdDesc(AlbumType type, Pageable pageable);

Flux<AlbumEntity> findAllByOwnerMemberIdOrderByAlbumIdDesc(String ownerMemberId, Pageable pageable);

Mono<Long> countAlbumEntityByType(AlbumType albumType);
}
19 changes: 19 additions & 0 deletions photo-service/src/main/java/kr/mafoo/photo/service/AlbumQuery.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package kr.mafoo.photo.service;

import java.time.Duration;
import kr.mafoo.photo.domain.AlbumEntity;
import kr.mafoo.photo.domain.enums.AlbumType;
import kr.mafoo.photo.exception.AlbumNotFoundException;
import kr.mafoo.photo.repository.AlbumRepository;
import lombok.RequiredArgsConstructor;
import org.slf4j.LoggerFactory;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand All @@ -23,4 +29,17 @@ public Flux<AlbumEntity> findByMemberId(String memberId) {
return albumRepository.findAllByOwnerMemberIdOrderByDisplayIndex(memberId)
.switchIfEmpty(Mono.error(new AlbumNotFoundException()));
}

@Cacheable(value = "albumCount", key = "#albumType")
public Mono<Long> countAlbumByAlbumType(AlbumType albumType) {
return albumRepository
.countAlbumEntityByType(albumType)
.cache();
}

@CacheEvict(value = "albumCount", allEntries = true)
@Scheduled(fixedDelay = 1000 * 10)
public void clearAlbumCountCache() {
LoggerFactory.getLogger(AlbumQuery.class).debug("clear album count cache");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.util.Comparator;
import java.util.Optional;
import kr.mafoo.photo.domain.AlbumEntity;
import kr.mafoo.photo.domain.enums.AlbumType;
import kr.mafoo.photo.exception.AlbumNotFoundException;
import kr.mafoo.photo.exception.AlbumOwnerChangeDeniedException;
import kr.mafoo.photo.exception.SharedMemberNotFoundException;
Expand Down Expand Up @@ -105,4 +106,7 @@ public Mono<Void> removeAlbum(String albumId, String requestMemberId) {
.flatMap(albumCommand::removeAlbum);
}

}
public Mono<Long> countAlbumByAlbumType(AlbumType albumType) {
return albumQuery.countAlbumByAlbumType(albumType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ALTER TABLE album ADD INDEX idx_album_type (type);

0 comments on commit 285ccdf

Please sign in to comment.