Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
141 changes: 141 additions & 0 deletions src/main/java/com/mos/backend/banners/application/BannerService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package com.mos.backend.banners.application;

import com.mos.backend.banners.application.responsedto.BannerResponseDto;
import com.mos.backend.banners.entity.Banner;
import com.mos.backend.banners.entity.exception.BannerErrorCode;
import com.mos.backend.banners.infrastructure.BannerRepository;
import com.mos.backend.banners.presentation.requestdto.BannerCreateRequestDto;
import com.mos.backend.banners.presentation.requestdto.BannerUpdateRequestDto;
import com.mos.backend.common.exception.MosException;
import com.mos.backend.studymaterials.application.UploadType;
import com.mos.backend.studymaterials.application.fileuploader.Uploader;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class BannerService {
private static final Long FOLDER_NAME = 1L;
private final BannerRepository bannerRepository;
private final Uploader uploader;

/**
* 배너 생성
*/
@Transactional
public BannerResponseDto createBanner(BannerCreateRequestDto requestDto, MultipartFile imageFile) {
long totalBanners = bannerRepository.count();
int requestedOrder = requestDto.getSortOrder();

// 배너 순서 검증
if (requestedOrder < 1 || requestedOrder > totalBanners + 1) {
throw new MosException(BannerErrorCode.SORT_ORDER_NOT_PROPER);
}

// 새로운 배너의 순서 뒤쪽 배너는 모두 순서 + 1
bannerRepository.incrementSortOrderGreaterThanOrEqual(requestedOrder);

// 배너 이미지 업로드
String uuidFileName = uploader.generateUUIDFileName(imageFile);
String newFilePath = uploader.uploadFileSync(uuidFileName, FOLDER_NAME, UploadType.BANNER, imageFile);

Banner banner = Banner.builder()
.title(requestDto.getTitle())
.content(requestDto.getContent())
.imageUrl(newFilePath)
.linkUrl(requestDto.getLinkUrl())
.sortOrder(requestedOrder)
.build();

Banner savedBanner = bannerRepository.save(banner);
return new BannerResponseDto(savedBanner);
}

/**
* Banner 수정
*/
@Transactional
public BannerResponseDto updateBanner(Long bannerId, BannerUpdateRequestDto requestDto, MultipartFile imageFile) {
Banner banner = bannerRepository.findById(bannerId)
.orElseThrow(() -> new MosException(BannerErrorCode.NOT_FOUND));

int originalOrder = banner.getSortOrder();
int newOrder = requestDto.getSortOrder();

long totalBanners = bannerRepository.count();

// 배너 순서 검증
if (newOrder < 1 || newOrder > totalBanners) {
throw new MosException(BannerErrorCode.SORT_ORDER_NOT_PROPER);
}

String imageUrl = banner.getImageUrl();

// 배너의 수정할 이미지가 존재하면 기존 이미지 삭제하고 새로운 이미지 업로드
if (imageFile != null && !imageFile.isEmpty()) {
uploader.deleteFile(imageUrl);
String generateUUIDFileName = uploader.generateUUIDFileName(imageFile);
imageUrl = uploader.uploadFileSync(generateUUIDFileName, FOLDER_NAME, UploadType.BANNER, imageFile);
}

// 배너의 순서를 변경했다면
if (originalOrder != newOrder) {
// 기존 순서 뒤쪽은 순서를 -1
bannerRepository.decrementSortOrderGreaterThan(originalOrder);
// 새로운 순서부터 뒤 순서는 +1
bannerRepository.incrementSortOrderGreaterThanOrEqual(newOrder);
}

banner.update(
requestDto.getTitle(),
requestDto.getContent(),
imageUrl,
requestDto.getLinkUrl(),
newOrder
);

return new BannerResponseDto(banner);
}

/**
* Banner 다 건 조회
*/
public List<BannerResponseDto> findAllBanners() {
return bannerRepository.findAllByOrderBySortOrderAsc().stream()
.map(BannerResponseDto::new)
.toList();
}

/**
* 단 건 조회
*/
public BannerResponseDto findBanner(Long bannerId) {
Banner banner = bannerRepository.findById(bannerId)
.orElseThrow(() -> new MosException(BannerErrorCode.NOT_FOUND));
return new BannerResponseDto(banner);
}

/**
* 배너 삭제
*/
@Transactional
public void deleteBanner(Long bannerId) {
Banner bannerToDelete = bannerRepository.findById(bannerId)
.orElseThrow(() -> new MosException(BannerErrorCode.NOT_FOUND));

int deletedOrder = bannerToDelete.getSortOrder();

// S3에 업로드된 이미지 파일 삭제
uploader.deleteFile(bannerToDelete.getImageUrl());

bannerRepository.delete(bannerToDelete);

// 빈자리를 채우기 위해 뒷순서 배너들을 1씩 앞으로 당김
bannerRepository.decrementSortOrderGreaterThan(deletedOrder);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.mos.backend.banners.application.responsedto;

import com.mos.backend.banners.entity.Banner;
import lombok.Getter;

@Getter
public class BannerResponseDto {
private Long id;
private String title;
private String content;
private String imageUrl;
private String linkUrl;
private int sortOrder;

public BannerResponseDto(Banner banner) {
this.id = banner.getId();
this.title = banner.getTitle();
this.content = banner.getContent();
this.imageUrl = banner.getImageUrl();
this.linkUrl = banner.getLinkUrl();
this.sortOrder = banner.getSortOrder();
}
}
43 changes: 43 additions & 0 deletions src/main/java/com/mos/backend/banners/entity/Banner.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.mos.backend.banners.entity;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Table(name = "banners")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Banner {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String title;
private String content;
private String imageUrl;
private String linkUrl;

@Column(nullable = false)
private int sortOrder;

@Builder
public Banner(String title, String content, String imageUrl, String linkUrl, int sortOrder) {
this.title = title;
this.content = content;
this.imageUrl = imageUrl;
this.linkUrl = linkUrl;
this.sortOrder = sortOrder;
}

public void update(String title, String content, String imageUrl, String linkUrl, int sortOrder) {
this.title = title;
this.content = content;
this.imageUrl = imageUrl;
this.linkUrl = linkUrl;
this.sortOrder = sortOrder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.mos.backend.banners.entity.exception;

import com.mos.backend.common.exception.ErrorCode;
import lombok.RequiredArgsConstructor;
import org.springframework.context.MessageSource;
import org.springframework.http.HttpStatus;

import java.util.Locale;

@RequiredArgsConstructor
public enum BannerErrorCode implements ErrorCode {
NOT_FOUND(HttpStatus.NOT_FOUND, "banner.not-found"),
SORT_ORDER_NOT_PROPER(HttpStatus.BAD_REQUEST, "banner.sort-order.not-proper")
;


private final HttpStatus httpStatus;
private final String messageKey;

@Override
public HttpStatus getStatus() {
return httpStatus;
}

@Override
public String getErrorName() {
return this.name();
}

@Override
public String getMessage(MessageSource messageSource) {
return messageSource.getMessage(messageKey, null, Locale.getDefault());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.mos.backend.banners.infrastructure;

import com.mos.backend.banners.entity.Banner;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

public interface BannerJpaRepository extends JpaRepository<Banner, Long> {

List<Banner> findAllByOrderBySortOrderAsc();

// 생성 시 순서 조정을 위한 쿼리
@Modifying
@Query("UPDATE Banner b SET b.sortOrder = b.sortOrder + 1 WHERE b.sortOrder >= :sortOrder")
void incrementSortOrderGreaterThanOrEqual(@Param("sortOrder") int sortOrder);

@Modifying
@Query("UPDATE Banner b SET b.sortOrder = b.sortOrder - 1 WHERE b.sortOrder > :sortOrder")
void decrementSortOrderGreaterThan(@Param("sortOrder") int sortOrder);

long count();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.mos.backend.banners.infrastructure;

import com.mos.backend.banners.entity.Banner;

import java.util.List;
import java.util.Optional;

public interface BannerRepository {
long count();

void incrementSortOrderGreaterThanOrEqual(int sortOrder);

void decrementSortOrderGreaterThan(int sortOrder);

List<Banner> findAllByOrderBySortOrderAsc();

Banner save(Banner banner);

Optional<Banner> findById(Long bannerId);

void delete(Banner banner);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.mos.backend.banners.infrastructure;

import com.mos.backend.banners.entity.Banner;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

@Repository
@RequiredArgsConstructor
public class BannerRepositoryImpl implements BannerRepository{
private final BannerJpaRepository bannerJpaRepository;

@Override
public long count() {
return bannerJpaRepository.count();
}

@Override
public void incrementSortOrderGreaterThanOrEqual(int sortOrder) {
bannerJpaRepository.incrementSortOrderGreaterThanOrEqual(sortOrder);
}

@Override
public void decrementSortOrderGreaterThan(int sortOrder) {
bannerJpaRepository.decrementSortOrderGreaterThan(sortOrder);
}

@Override
public List<Banner> findAllByOrderBySortOrderAsc() {
return bannerJpaRepository.findAllByOrderBySortOrderAsc();
}

@Override
public Banner save(Banner banner) {
return bannerJpaRepository.save(banner);
}

@Override
public Optional<Banner> findById(Long bannerId) {
return bannerJpaRepository.findById(bannerId);
}

@Override
public void delete(Banner banner) {
bannerJpaRepository.delete(banner);
}
}
Loading