Skip to content

Commit

Permalink
공유페이지 멀티파트 파일 처리를 위한 저장 리팩토링 (Fastcampus-Final-Team3#203)
Browse files Browse the repository at this point in the history
* feat : ApiStatus enum에 FILE_PROCESSING_NOT_SUPPORTED 필드 추가 (Fastcampus-Final-Team3#201)

* feat : MoveBlockStrategy 인터페이스에 uploadFile 메서드 추가 (Fastcampus-Final-Team3#201)

* refactor : FileBlockStrategy에 파일 업로드 기능 추가 및 파일 처리 로직 변경 (Fastcampus-Final-Team3#201)

* refactor : FileBlockStrategy에서 currentFileName 필드를 AtomicReference로 변경 (Fastcampus-Final-Team3#201)

- FileBlockStrategy 클래스에서 currentFileName 필드의 타입을 String에서 AtomicReference로 변경하였습니다.

- 이는 동시성 문제를 해결하고자 함입니다. AtomicReference는 원자적 연산을 지원하여 여러 스레드에서 동시에 접근해도 안전하게 값을 변경하거나 가져올 수 있습니다.

* feat : FixBlockStrategy 인터페이스에 uploadSingleFile, uploadTwoFiles 메서드 추가 (Fastcampus-Final-Team3#201)

* feat : FixBlockStrategy 인터페이스에 saveStringBlocks 메서드 추가 (Fastcampus-Final-Team3#201)

* refactor : StyleSettingStrategy 클래스에 단일 파일 업로드 기능 추가 및 base64 스타일 설정 저장 메서드 분리 (Fastcampus-Final-Team3#201)

* refactor : WallInfoBlockStrategy 클래스에 여러 파일 업로드 기능 추가 및 base64 스타일 설정 저장 메서드 분리 (Fastcampus-Final-Team3#201)

* refactor : MoveBlockStrategy 인터페이스에 saveStringBlocks 메서드 추가 (Fastcampus-Final-Team3#201)

* refactor : base64 저장 메서드 분리 (Fastcampus-Final-Team3#201)

* refactor : saveStringBlocks 메서드로 변경 (Fastcampus-Final-Team3#201)

* refactor : FileUploadService save 메서드 로직 수정(Fastcampus-Final-Team3#201)

* refactor : string에서 enum으로 변경 (Fastcampus-Final-Team3#201)
  • Loading branch information
dpdmstjs authored Oct 25, 2023
1 parent fd7b788 commit 17459ed
Show file tree
Hide file tree
Showing 12 changed files with 307 additions and 83 deletions.
1 change: 1 addition & 0 deletions src/main/java/com/javajober/core/exception/ApiStatus.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public enum ApiStatus {
FILE_UPLOAD_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "file upload fail"),
FILE_DELETE_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "file delete fail"),
FILE_DOWNLOAD_FAIL(HttpStatus.INTERNAL_SERVER_ERROR, "file download fail"),
FILE_PROCESSING_NOT_SUPPORTED(HttpStatus.UNSUPPORTED_MEDIA_TYPE, "file processing not supported"),
INVALID_DATA(HttpStatus.BAD_REQUEST, "invalid data"),
EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR, "exception"),
IO_EXCEPTION(HttpStatus.INTERNAL_SERVER_ERROR, "io exception"),
Expand Down
167 changes: 104 additions & 63 deletions src/main/java/com/javajober/spaceWall/service/FileUploadService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.javajober.blocks.freeBlock.dto.request.FreeBlockSaveRequest;
import com.javajober.blocks.styleSetting.backgroundSetting.domain.BackgroundSetting;
import com.javajober.blocks.styleSetting.backgroundSetting.filedto.BackgroundSettingSaveRequest;
import com.javajober.blocks.styleSetting.backgroundSetting.filedto.BackgroundSettingUpdateRequest;
Expand All @@ -14,6 +13,8 @@
import com.javajober.blocks.styleSetting.blockSetting.dto.request.BlockSettingUpdateRequest;
import com.javajober.blocks.styleSetting.blockSetting.repository.BlockSettingRepository;
import com.javajober.blocks.styleSetting.themeSetting.dto.request.ThemeSettingSaveRequest;
import com.javajober.core.exception.ApiStatus;
import com.javajober.core.exception.ApplicationException;
import com.javajober.core.util.file.FileImageService;
import com.javajober.blocks.fileBlock.domain.FileBlock;
import com.javajober.blocks.fileBlock.filedto.FileBlockSaveRequest;
Expand All @@ -38,6 +39,7 @@
import com.javajober.spaceWall.domain.FlagType;
import com.javajober.spaceWall.domain.SpaceWall;
import com.javajober.spaceWall.dto.request.BlockSaveRequest;
import com.javajober.spaceWall.filedto.DataSaveRequest;
import com.javajober.spaceWall.filedto.DataUpdateRequest;
import com.javajober.spaceWall.filedto.SpaceWallSaveRequest;
import com.javajober.spaceWall.dto.response.SpaceWallSaveResponse;
Expand All @@ -59,6 +61,12 @@
import com.javajober.blocks.wallInfoBlock.filedto.WallInfoBlockSaveRequest;
import com.javajober.blocks.wallInfoBlock.filedto.WallInfoBlockUpdateRequest;
import com.javajober.blocks.wallInfoBlock.repository.WallInfoBlockRepository;
import com.javajober.spaceWall.strategy.BlockJsonProcessor;
import com.javajober.spaceWall.strategy.BlockStrategyFactory;
import com.javajober.spaceWall.strategy.BlockStrategyName;
import com.javajober.spaceWall.strategy.FixBlockStrategy;
import com.javajober.spaceWall.strategy.MoveBlockStrategy;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
Expand All @@ -70,7 +78,7 @@

@Service
public class FileUploadService {

private static final Long INITIAL_POSITION = 1L;
private final SpaceWallRepository spaceWallRepository;
private final SNSBlockRepository snsBlockRepository;
private final FreeBlockRepository freeBlockRepository;
Expand All @@ -85,14 +93,17 @@ public class FileUploadService {
private final MemberRepository memberRepository;
private final AddSpaceRepository addSpaceRepository;
private final FileImageService fileImageService;
private final BlockStrategyFactory blockStrategyFactory;
private final BlockJsonProcessor blockJsonProcessor;

public FileUploadService(final SpaceWallRepository spaceWallRepository, final SNSBlockRepository snsBlockRepository,
final FreeBlockRepository freeBlockRepository, final TemplateBlockRepository templateBlockRepository,
final WallInfoBlockRepository wallInfoBlockRepository, final FileBlockRepository fileBlockRepository,
final ListBlockRepository listBlockRepository, final FileImageService fileImageService,
final StyleSettingRepository styleSettingRepository, final BackgroundSettingRepository backgroundSettingRepository,
final BlockSettingRepository blockSettingRepository, final ThemeSettingRepository themeSettingRepository,
final MemberRepository memberRepository, final AddSpaceRepository addSpaceRepository) {
final MemberRepository memberRepository, final AddSpaceRepository addSpaceRepository,
BlockStrategyFactory blockStrategyFactory, BlockJsonProcessor blockJsonProcessor) {

this.spaceWallRepository = spaceWallRepository;
this.snsBlockRepository = snsBlockRepository;
Expand All @@ -108,89 +119,119 @@ public FileUploadService(final SpaceWallRepository spaceWallRepository, final SN
this.memberRepository = memberRepository;
this.addSpaceRepository = addSpaceRepository;
this.fileImageService = fileImageService;
this.blockStrategyFactory = blockStrategyFactory;
this.blockJsonProcessor = blockJsonProcessor;
}

@Transactional
public SpaceWallSaveResponse save(final Long memberId, final SpaceWallSaveRequest spaceWallSaveRequest, FlagType flagType,
public SpaceWallSaveResponse save(final Long memberId, final SpaceWallSaveRequest spaceWallRequest, FlagType flagType,
final List<MultipartFile> files, final MultipartFile backgroundImgURL,
final MultipartFile wallInfoImgURL, final MultipartFile styleImgURL) {

SpaceWallCategoryType spaceWallCategoryType = SpaceWallCategoryType.findSpaceWallCategoryTypeByString(spaceWallSaveRequest.getData().getCategory());
AddSpace addSpace = addSpaceRepository.findAddSpace(spaceWallSaveRequest.getData().getSpaceId());
Member member = memberRepository.findMember(memberId);

Long blocksPosition = 2L;
AtomicLong blocksPositionCounter = new AtomicLong(blocksPosition);
ObjectMapper jsonMapper = new ObjectMapper();
ArrayNode blockInfoArray = jsonMapper.createArrayNode();
AtomicInteger i = new AtomicInteger();
DataSaveRequest data = spaceWallRequest.getData();

WallInfoBlockSaveRequest wallInfoBlockSaveRequest = spaceWallSaveRequest.getData().getWallInfoBlock();
Long wallInfoBlock = saveWallInfoBlock(wallInfoBlockSaveRequest, backgroundImgURL, wallInfoImgURL);
String wallInfoBlockType = BlockType.WALL_INFO_BLOCK.getEngTitle();
Long blockStartPosition = 1L;
addBlockToJsonArray(blockInfoArray, jsonMapper, blockStartPosition, wallInfoBlockType, wallInfoBlock);
AddSpace addSpace = addSpaceRepository.findAddSpace(data.getSpaceId());

validateSpaceOwnership(member, addSpace);

validateAddSpaceId(addSpace.getId());

SpaceWallCategoryType spaceWallCategoryType = SpaceWallCategoryType.findSpaceWallCategoryTypeByString(data.getCategory());

ArrayNode blockInfoArray = blockJsonProcessor.createArrayNode();

AtomicLong blocksPositionCounter = new AtomicLong(INITIAL_POSITION);

processWallInfoBlock(backgroundImgURL, wallInfoImgURL, data, blockInfoArray, blocksPositionCounter);

List<BlockSaveRequest<?>> blocks = data.getBlocks();

processBlocks(blocks, blockInfoArray, blocksPositionCounter, files);

processStyleSettingBlock(styleImgURL, data, blockInfoArray, blocksPositionCounter);

String shareURL = spaceWallRequest.getData().getShareURL();

Long spaceWallId = saveSpaceWall(spaceWallCategoryType, member, addSpace, shareURL, flagType, blockInfoArray);

return new SpaceWallSaveResponse(spaceWallId);
}

private void validateSpaceOwnership(final Member member, final AddSpace addSpace) {
Long memberId = member.getId();
Long spaceId = addSpace.getMember().getId();

if (!memberId.equals(spaceId)) {
throw new ApplicationException(ApiStatus.INVALID_DATA, "사용자의 스페이스를 찾을 수가 없습니다.");
}
}

private void validateAddSpaceId (final Long spaceId) {
boolean existsSpaceId = spaceWallRepository.existsByAddSpaceId(spaceId);
if (existsSpaceId) {
throw new ApplicationException(ApiStatus.INVALID_DATA, "스페이스 하나당 공유페이지 하나만 생성 가능합니다.");
}
}

private void processWallInfoBlock(final MultipartFile backgroundImgURL, final MultipartFile wallInfoImgURL,
final DataSaveRequest data, final ArrayNode blockInfoArray,
final AtomicLong blocksPositionCounter) {

String wallInfoBlockStrategyName = BlockType.WALL_INFO_BLOCK.getStrategyName();
FixBlockStrategy wallInfoBlockStrategy = blockStrategyFactory.findFixBlockStrategy(wallInfoBlockStrategyName);

wallInfoBlockStrategy.uploadTwoFiles(backgroundImgURL, wallInfoImgURL);

Long wallInfoBlockPosition = blocksPositionCounter.getAndIncrement();
wallInfoBlockStrategy.saveBlocks(data, blockInfoArray, wallInfoBlockPosition);
}

private void processBlocks(final List<BlockSaveRequest<?>> blocks, final ArrayNode blockInfoArray,
final AtomicLong blocksPositionCounter, final List<MultipartFile> files) {

AtomicInteger fileIndexCounter = new AtomicInteger();

blocks.forEach(block -> {

spaceWallSaveRequest.getData().getBlocks().forEach(block -> {
BlockType blockType = BlockType.findBlockTypeByString(block.getBlockType());
Long position = blocksPositionCounter.getAndIncrement();
switch (blockType) {
case FREE_BLOCK:
List<com.javajober.blocks.freeBlock.dto.request.FreeBlockSaveRequest> freeBlockRequests = jsonMapper.convertValue(block.getSubData(),
new TypeReference<List<FreeBlockSaveRequest>>() {
});
List<Long> freeBlockIds = saveFreeBlocks(freeBlockRequests);
freeBlockIds.forEach(freeBlockId -> addBlockInfoToArray(blockInfoArray, jsonMapper, blockType, position, freeBlockId, block));
break;
case SNS_BLOCK:
List<SNSBlockSaveRequest> snsBlockSaveRequests = jsonMapper.convertValue(block.getSubData(),
new TypeReference<List<SNSBlockSaveRequest>>() {
});
List<Long> snsBlockIds = saveSnsBlocks(snsBlockSaveRequests);
snsBlockIds.forEach(snsBlockId -> addBlockInfoToArray(blockInfoArray, jsonMapper, blockType, position, snsBlockId, block));
break;
case TEMPLATE_BLOCK:
List<TemplateBlockSaveRequest> templateBlockSaveRequests = jsonMapper.convertValue(block.getSubData(),
new TypeReference<List<TemplateBlockSaveRequest>>() {
});
List<Long> templateBlockIds = saveTemplateBlocks(templateBlockSaveRequests);
templateBlockIds.forEach(templateBlockId -> addBlockInfoToArray(blockInfoArray, jsonMapper, blockType, position, templateBlockId, block));
break;
case FILE_BLOCK:
List<FileBlockSaveRequest> fileBlockSaveRequests = jsonMapper.convertValue(block.getSubData(),
new TypeReference<List<FileBlockSaveRequest>>() {
});
List<Long> fileBlockIds = saveFileBlocks(fileBlockSaveRequests, files.get(i.getAndIncrement()));
fileBlockIds.forEach(templateBlockId -> addBlockInfoToArray(blockInfoArray, jsonMapper, blockType, position, templateBlockId, block));
break;
case LIST_BLOCK:
List<ListBlockSaveRequest> listBlockRequests = jsonMapper.convertValue(block.getSubData(),
new TypeReference<List<ListBlockSaveRequest>>() {
});
List<Long> listBlockIds = saveListBlocks(listBlockRequests);
listBlockIds.forEach(listBlockId -> addBlockInfoToArray(blockInfoArray, jsonMapper, blockType, position, listBlockId, block));

String strategyName = blockType.getStrategyName();
MoveBlockStrategy blockProcessingStrategy = blockStrategyFactory.findMoveBlockStrategy(strategyName);

if (BlockStrategyName.FileBlockStrategy.name().equals(blockProcessingStrategy.getStrategyName())) {
blockProcessingStrategy.uploadFile(files.get(fileIndexCounter.getAndIncrement()));
}

blockProcessingStrategy.saveBlocks(block, blockInfoArray, position);
});
}

StyleSettingSaveRequest styleSettingSaveRequest = spaceWallSaveRequest.getData().getStyleSetting();
Long styleSetting = saveStyleSetting(styleSettingSaveRequest, styleImgURL);
String styleSettingString = "styleSetting";
Long stylePosition = blocksPositionCounter.getAndIncrement();
addBlockToJsonArray(blockInfoArray, jsonMapper, stylePosition, styleSettingString, styleSetting);
private void processStyleSettingBlock(final MultipartFile styleImgURL, final DataSaveRequest data, final ArrayNode blockInfoArray,
final AtomicLong blocksPositionCounter) {

String styleSettingBlockStrategyName = BlockType.STYLE_SETTING.getStrategyName();
FixBlockStrategy styleSettingBlockStrategy = blockStrategyFactory.findFixBlockStrategy(styleSettingBlockStrategyName);

styleSettingBlockStrategy.uploadSingleFile(styleImgURL);

Long styleSettingPosition = blocksPositionCounter.getAndIncrement();
styleSettingBlockStrategy.saveBlocks(data, blockInfoArray, styleSettingPosition);
}
private Long saveSpaceWall(final SpaceWallCategoryType spaceWallCategoryType, final Member member, final AddSpace addSpace, final String shareURL, final FlagType flagType, final ArrayNode blockInfoArray) {

String blockInfoArrayAsString = blockInfoArray.toString();
String shareURL = spaceWallSaveRequest.getData().getShareURL();
SpaceWall spaceWall = SpaceWallSaveRequest.toEntity(spaceWallCategoryType, member, addSpace, shareURL, flagType, blockInfoArrayAsString);

Long spaceWallId = spaceWallRepository.save(spaceWall).getId();

return new SpaceWallSaveResponse(spaceWallId);
return spaceWallRepository.save(spaceWall).getId();
}

@Transactional
public SpaceWallSaveResponse update(final Long memberId, final SpaceWallUpdateRequest spaceWallRequest, FlagType flagType,
final List<MultipartFile> files, final MultipartFile backgroundImgURL,
final MultipartFile wallInfoImgURL, final MultipartFile styleImgURL){
final List<MultipartFile> files, final MultipartFile backgroundImgURL,
final MultipartFile wallInfoImgURL, final MultipartFile styleImgURL){

DataUpdateRequest dataUpdateRequest = spaceWallRequest.getData();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ private void processWallInfoBlock(final DataStringSaveRequest data, final ArrayN
FixBlockStrategy wallInfoBlockStrategy = blockStrategyFactory.findFixBlockStrategy(wallInfoBlockStrategyName);

Long wallInfoBlockPosition = blocksPositionCounter.getAndIncrement();
wallInfoBlockStrategy.saveBlocks(data, blockInfoArray, wallInfoBlockPosition);
wallInfoBlockStrategy.saveStringBlocks(data, blockInfoArray, wallInfoBlockPosition);
}

private void processBlocks(final List<BlockSaveRequest<?>> blocks, final ArrayNode blockInfoArray, final AtomicLong blocksPositionCounter) {
Expand All @@ -175,7 +175,7 @@ private void processBlocks(final List<BlockSaveRequest<?>> blocks, final ArrayNo
String strategyName = blockType.getStrategyName();
MoveBlockStrategy blockProcessingStrategy = blockStrategyFactory.findMoveBlockStrategy(strategyName);

blockProcessingStrategy.saveBlocks(block, blockInfoArray, position);
blockProcessingStrategy.saveStringBlocks(block, blockInfoArray, position);
});
}

Expand All @@ -185,7 +185,7 @@ private void processStyleSettingBlock(final DataStringSaveRequest data, final Ar
FixBlockStrategy styleSettingBlockStrategy = blockStrategyFactory.findFixBlockStrategy(styleSettingBlockStrategyName);

Long styleSettingPosition = blocksPositionCounter.getAndIncrement();
styleSettingBlockStrategy.saveBlocks(data, blockInfoArray, styleSettingPosition);
styleSettingBlockStrategy.saveStringBlocks(data, blockInfoArray, styleSettingPosition);
}

private Long saveSpaceWall(final SpaceWallCategoryType spaceWallCategoryType, final Member member, final AddSpace addSpace, final String shareURL, final FlagType flagType, final ArrayNode blockInfoArray) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,32 @@
package com.javajober.spaceWall.strategy;

import static com.javajober.core.exception.ApiStatus.*;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.javajober.core.exception.ApplicationException;
import com.javajober.core.util.response.CommonResponse;
import com.javajober.spaceWall.dto.request.DataStringSaveRequest;
import com.javajober.spaceWall.filedto.DataSaveRequest;

import java.util.List;

import org.springframework.web.multipart.MultipartFile;

public interface FixBlockStrategy {
void saveBlocks(final DataStringSaveRequest data, final ArrayNode blockInfoArray, final Long position);
void saveStringBlocks(final DataStringSaveRequest data, final ArrayNode blockInfoArray, final Long position);

void saveBlocks(final DataSaveRequest data, ArrayNode blockInfoArray, Long position);

CommonResponse createFixBlockDTO(final List<JsonNode> fixBlocks);

String getStrategyName();

default void uploadTwoFiles(final MultipartFile firstFile, final MultipartFile secondFile) {
throw new ApplicationException(FILE_PROCESSING_NOT_SUPPORTED, "파일 처리를 지원하지 않습니다.");
}

default void uploadSingleFile(final MultipartFile singleFile) {
throw new ApplicationException(FILE_PROCESSING_NOT_SUPPORTED, "파일 처리를 지원하지 않습니다.");
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,29 @@
package com.javajober.spaceWall.strategy;

import static com.javajober.core.exception.ApiStatus.*;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.javajober.core.exception.ApplicationException;
import com.javajober.core.util.response.CommonResponse;
import com.javajober.spaceWall.dto.request.BlockSaveRequest;

import java.util.List;

import org.springframework.web.multipart.MultipartFile;

public interface MoveBlockStrategy {
void saveStringBlocks(final BlockSaveRequest<?> block, final ArrayNode blockInfoArray, final Long position);

void saveBlocks(final BlockSaveRequest<?> block, final ArrayNode blockInfoArray, final Long position);


List<CommonResponse> createMoveBlockDTO(List<JsonNode> blocksWithSamePosition);

String getStrategyName();

default void uploadFile(final MultipartFile file) {
throw new ApplicationException(FILE_PROCESSING_NOT_SUPPORTED, "파일 처리를 지원하지 않습니다.");
}
}

Loading

0 comments on commit 17459ed

Please sign in to comment.