Skip to content

Commit

Permalink
Merge branch 'develop' into feature/1130-bus-notice-api
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/main/java/in/koreatech/koin/domain/bus/controller/BusApi.java
#	src/main/java/in/koreatech/koin/domain/bus/controller/BusController.java
#	src/main/java/in/koreatech/koin/domain/bus/service/BusService.java
  • Loading branch information
Choon0414 committed Dec 14, 2024
2 parents 7b0d1a4 + 885875d commit cc6f9bb
Showing 46 changed files with 1,367 additions and 112 deletions.
Original file line number Diff line number Diff line change
@@ -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;
@@ -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"),
Original file line number Diff line number Diff line change
@@ -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;
@@ -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,
Original file line number Diff line number Diff line change
@@ -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,
@@ -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()
);
}
}
Original file line number Diff line number Diff line change
@@ -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
@@ -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()
);
}

@@ -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()
);
}
}
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
@@ -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
@@ -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
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;

@@ -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;
@@ -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) {
Loading

0 comments on commit cc6f9bb

Please sign in to comment.