Skip to content

Commit

Permalink
Merge branch 'develop' into refactor/image-upload-notify-seperation
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/main/java/in/koreatech/koin/domain/coop/service/CoopService.java
  • Loading branch information
dradnats1012 committed Dec 15, 2024
2 parents ca4c6de + 885875d commit ef80465
Show file tree
Hide file tree
Showing 47 changed files with 1,417 additions and 179 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import in.koreatech.koin.admin.benefit.dto.AdminDeleteShopsRequest;
import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryRequest;
import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryResponse;
import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitShopsRequest;
import in.koreatech.koin.admin.benefit.dto.AdminSearchBenefitShopsResponse;
import in.koreatech.koin.global.auth.Auth;
import io.swagger.v3.oas.annotations.Operation;
Expand Down Expand Up @@ -127,6 +128,22 @@ ResponseEntity<AdminCreateBenefitShopsResponse> createBenefitShops(
@Auth(permit = {ADMIN}) Integer adminId
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "201"),
@ApiResponse(responseCode = "400", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
}
)
@Operation(summary = "특정 혜택을 제공하는 상점을 수정한다.")
@PutMapping
ResponseEntity<Void> modifyBenefitShops(
@RequestBody AdminModifyBenefitShopsRequest request,
@Auth(permit = {ADMIN}) Integer adminId
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "204"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import in.koreatech.koin.admin.benefit.dto.AdminDeleteShopsRequest;
import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryRequest;
import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryResponse;
import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitShopsRequest;
import in.koreatech.koin.admin.benefit.dto.AdminSearchBenefitShopsResponse;
import in.koreatech.koin.admin.benefit.service.AdminBenefitService;
import in.koreatech.koin.global.auth.Auth;
Expand Down Expand Up @@ -90,6 +91,15 @@ public ResponseEntity<AdminCreateBenefitShopsResponse> createBenefitShops(
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}

@PutMapping
public ResponseEntity<Void> modifyBenefitShops(
@RequestBody AdminModifyBenefitShopsRequest request,
@Auth(permit = {ADMIN}) Integer adminId
) {
adminBenefitService.modifyBenefitShops(request);
return ResponseEntity.ok().build();
}

@DeleteMapping("/{id}/shops")
public ResponseEntity<Void> deleteBenefitShops(
@PathVariable("id") Integer benefitId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

import in.koreatech.koin.domain.benefit.model.BenefitCategoryMap;
import in.koreatech.koin.domain.shop.model.shop.Shop;
import io.swagger.v3.oas.annotations.media.Schema;

@JsonNaming(SnakeCaseStrategy.class)
public record AdminBenefitShopsResponse(
@Schema(example = "3", description = "상점 개수")
Integer count,
Expand All @@ -16,27 +18,36 @@ public record AdminBenefitShopsResponse(
List<InnerShopResponse> shops
) {

public static AdminBenefitShopsResponse from(List<Shop> shops) {
public static AdminBenefitShopsResponse from(List<BenefitCategoryMap> benefitCategoryMaps) {
return new AdminBenefitShopsResponse(
shops.size(),
shops.stream()
benefitCategoryMaps.size(),
benefitCategoryMaps.stream()
.map(InnerShopResponse::from)
.toList()
);
}

@JsonNaming(SnakeCaseStrategy.class)
private record InnerShopResponse(
@Schema(example = "1", description = "상점혜택 매핑id")
Integer shopBenefitMapId,

@Schema(example = "1", description = "고유 id")
Integer id,

@Schema(example = "수신반점", description = "이름")
String name
String name,

@Schema(example = "4인 이상 픽업서비스", description = "혜택 미리보기 문구")
String detail
) {

public static InnerShopResponse from(Shop shop) {
public static InnerShopResponse from(BenefitCategoryMap benefitCategoryMap) {
return new InnerShopResponse(
shop.getId(),
shop.getName()
benefitCategoryMap.getId(),
benefitCategoryMap.getShop().getId(),
benefitCategoryMap.getShop().getName(),
benefitCategoryMap.getDetail()
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,27 @@

import in.koreatech.koin.global.validation.NotBlankElement;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

@JsonNaming(SnakeCaseStrategy.class)
public record AdminCreateBenefitShopsRequest(
@Schema(description = "상점 ID 리스트", example = "[1, 2, 5]", requiredMode = REQUIRED)
@NotNull(message = "상점 ID 리스트는 필수입니다.")
@NotBlankElement(message = "상점 ID 리스트는 빈 요소가 존재할 수 없습니다.")
List<Integer> shopIds
@NotNull(message = "상점정보 리스트는 필수입니다.")
@NotBlankElement(message = "상점정보 리스트는 빈 요소가 존재할 수 없습니다.")
List<InnerBenefitShopsRequest> shopDetails
) {

@JsonNaming(SnakeCaseStrategy.class)
public record InnerBenefitShopsRequest(
@Schema(description = "상점 고유 id", example = "2", requiredMode = REQUIRED)
@NotNull(message = "상점은 필수입니다.")
Integer shopId,

@Schema(description = "혜택 미리보기 문구", example = "4인 이상 픽업서비스", requiredMode = REQUIRED)
@NotBlank(message = "혜택 미리보기 문구는 필수입니다.")
@Size(min = 2, max = 15, message = "혜택 미리보기 문구는 최소 2자 최대 15자입니다.")
String detail
) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,19 @@
import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

import in.koreatech.koin.domain.benefit.model.BenefitCategoryMap;
import in.koreatech.koin.domain.shop.model.shop.Shop;
import io.swagger.v3.oas.annotations.media.Schema;

public record AdminCreateBenefitShopsResponse(
@Schema(description = "상점 리스트")
@Schema(description = "상점 정보")
List<InnerShopResponse> shops
) {
public static AdminCreateBenefitShopsResponse from(List<Shop> shops) {
public static AdminCreateBenefitShopsResponse from(List<BenefitCategoryMap> benefitCategoryMaps) {
return new AdminCreateBenefitShopsResponse(
shops.stream().map(InnerShopResponse::from).toList()
benefitCategoryMaps.stream()
.map(InnerShopResponse::from)
.toList()
);
}

Expand All @@ -23,13 +26,17 @@ private record InnerShopResponse(
Integer id,

@Schema(description = "상점 이름", example = "수신반점")
String name
String name,

@Schema(example = "4인 이상 픽업서비스", description = "혜택 미리보기 문구")
String detail
) {

public static InnerShopResponse from(Shop shop) {
public static InnerShopResponse from(BenefitCategoryMap benefitCategoryMap) {
return new InnerShopResponse(
shop.getId(),
shop.getName()
benefitCategoryMap.getShop().getId(),
benefitCategoryMap.getShop().getName(),
benefitCategoryMap.getDetail()
);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package in.koreatech.koin.admin.benefit.dto;

import static com.fasterxml.jackson.databind.PropertyNamingStrategies.*;
import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

import java.util.List;

import com.fasterxml.jackson.databind.annotation.JsonNaming;

import in.koreatech.koin.global.validation.NotBlankElement;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

@JsonNaming(SnakeCaseStrategy.class)
public record AdminModifyBenefitShopsRequest(
@NotNull(message = "혜택문구 변경정보 리스트는 필수입니다.")
@NotBlankElement(message = "혜택문구 변경정보 리스트는 빈 요소가 존재할 수 없습니다.")
List<InnerBenefitShopsRequest> modifyDetails
) {

@JsonNaming(SnakeCaseStrategy.class)
public record InnerBenefitShopsRequest(
@Schema(description = "상점혜택 매핑id", example = "2", requiredMode = REQUIRED)
@NotNull(message = "상점혜택 매핑id는 필수입니다.")
Integer shopBenefitMapId,

@Schema(description = "혜택 미리보기 문구", example = "4인 이상 픽업서비스", requiredMode = REQUIRED)
@NotBlank(message = "혜택 미리보기 문구는 필수입니다.")
@Size(min = 2, max = 15, message = "혜택 미리보기 문구는 최소 2자 최대 15자입니다.")
String detail
) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package in.koreatech.koin.admin.benefit.exception;

import in.koreatech.koin.global.exception.DataNotFoundException;

public class BenefitMapNotFoundException extends DataNotFoundException {

private static final String DEFAULT_MESSAGE = "해당 혜택 카테고리에 존재하지 않는 입니다.";

public BenefitMapNotFoundException(String message) {
super(message);
}

public BenefitMapNotFoundException(String message, String detail) {
super(message, detail);
}

public static BenefitMapNotFoundException withDetail(String detail) {
return new BenefitMapNotFoundException(DEFAULT_MESSAGE, detail);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@

import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.CrudRepository;

import in.koreatech.koin.domain.benefit.model.BenefitCategoryMap;
import org.springframework.data.repository.query.Param;

public interface AdminBenefitCategoryMapRepository extends Repository<BenefitCategoryMap, Integer> {
public interface AdminBenefitCategoryMapRepository extends CrudRepository<BenefitCategoryMap, Integer> {

void save(BenefitCategoryMap benefitCategoryMap);
List<BenefitCategoryMap> findAllByIdIn(List<Integer> ids);

@Query("""
SELECT bcm
Expand All @@ -22,6 +22,16 @@ public interface AdminBenefitCategoryMapRepository extends Repository<BenefitCat
""")
List<BenefitCategoryMap> findAllByBenefitCategoryIdOrderByShopName(@Param("benefitId") Integer benefitId);

@Query("""
SELECT bcm
FROM BenefitCategoryMap bcm
WHERE bcm.benefitCategory.id = :benefitId AND bcm.shop.id IN :shopIds
""")
List<BenefitCategoryMap> findAllByBenefitCategoryIdAndShopIds(
@Param("benefitId") Integer benefitId,
@Param("shopIds") List<Integer> shopIds
);

@Modifying
@Query("""
DELETE FROM BenefitCategoryMap bcm
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package in.koreatech.koin.admin.benefit.service;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

Expand All @@ -16,9 +17,11 @@
import in.koreatech.koin.admin.benefit.dto.AdminDeleteShopsRequest;
import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryRequest;
import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitCategoryResponse;
import in.koreatech.koin.admin.benefit.dto.AdminModifyBenefitShopsRequest;
import in.koreatech.koin.admin.benefit.dto.AdminSearchBenefitShopsResponse;
import in.koreatech.koin.admin.benefit.exception.BenefitDuplicationException;
import in.koreatech.koin.admin.benefit.exception.BenefitLimitException;
import in.koreatech.koin.admin.benefit.exception.BenefitMapNotFoundException;
import in.koreatech.koin.admin.benefit.repository.AdminBenefitCategoryMapRepository;
import in.koreatech.koin.admin.benefit.repository.AdminBenefitCategoryRepository;
import in.koreatech.koin.admin.shop.repository.shop.AdminShopRepository;
Expand Down Expand Up @@ -90,29 +93,61 @@ public void deleteBenefitCategory(Integer categoryId) {
public AdminBenefitShopsResponse getBenefitShops(Integer benefitId) {
List<BenefitCategoryMap> benefitCategoryMaps =
adminBenefitCategoryMapRepository.findAllByBenefitCategoryIdOrderByShopName(benefitId);
List<Shop> shops = benefitCategoryMaps.stream()
.map(BenefitCategoryMap::getShop)
.toList();
return AdminBenefitShopsResponse.from(shops);
return AdminBenefitShopsResponse.from(benefitCategoryMaps);
}

@Transactional
public AdminCreateBenefitShopsResponse createBenefitShops(
Integer benefitId,
AdminCreateBenefitShopsRequest request
) {
List<Shop> shops = adminShopRepository.findAllByIdIn(request.shopIds());
BenefitCategory benefitCategory = adminBenefitCategoryRepository.getById(benefitId);
for (Shop shop : shops) {
BenefitCategoryMap benefitCategoryMap = BenefitCategoryMap.builder()
Map<Integer, String> shopIdToDetail = request.shopDetails().stream()
.collect(Collectors.toMap(
AdminCreateBenefitShopsRequest.InnerBenefitShopsRequest::shopId,
AdminCreateBenefitShopsRequest.InnerBenefitShopsRequest::detail
));
List<Shop> shops = adminShopRepository.findAllByIdIn(shopIdToDetail.keySet().stream().toList());

List<BenefitCategoryMap> benefitCategoryMaps = shops.stream()
.map(shop -> BenefitCategoryMap.builder()
.shop(shop)
.benefitCategory(benefitCategory)
.build();
adminBenefitCategoryMapRepository.save(benefitCategoryMap);
}
return AdminCreateBenefitShopsResponse.from(shops);
.detail(shopIdToDetail.get(shop.getId()))
.build()
)
.toList();
adminBenefitCategoryMapRepository.saveAll(benefitCategoryMaps);
return AdminCreateBenefitShopsResponse.from(benefitCategoryMaps);
}

@Transactional
public void modifyBenefitShops(AdminModifyBenefitShopsRequest request) {
Map<Integer, String> shopBenefitIdToDetail = request.modifyDetails().stream()
.collect(Collectors.toMap(
AdminModifyBenefitShopsRequest.InnerBenefitShopsRequest::shopBenefitMapId,
AdminModifyBenefitShopsRequest.InnerBenefitShopsRequest::detail
));

List<BenefitCategoryMap> benefitCategoryMaps =
adminBenefitCategoryMapRepository.findAllByIdIn(shopBenefitIdToDetail.keySet().stream().toList());

validateBenefitMapIds(shopBenefitIdToDetail, benefitCategoryMaps);
benefitCategoryMaps.forEach(map -> map.modifyDetail(shopBenefitIdToDetail.get(map.getId())));
}

private static void validateBenefitMapIds(
Map<Integer, String> shopBenefitIdToDetail,
List<BenefitCategoryMap> benefitCategoryMaps
) {
List<Integer> notFoundMapIds = shopBenefitIdToDetail.keySet().stream()
.filter(mapId -> benefitCategoryMaps.stream().noneMatch(map -> map.getId().equals(mapId)))
.toList();

if (!notFoundMapIds.isEmpty()) {
throw new BenefitMapNotFoundException("해당 혜택 카테고리에 존재하지 않는 상점이 포함되어 있습니다. shopBenefitMapId: " + notFoundMapIds);
}
}

@Transactional
public void deleteBenefitShops(Integer benefitId, AdminDeleteShopsRequest request) {
Expand Down
Loading

0 comments on commit ef80465

Please sign in to comment.