Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 어드민 상점 혜택 카테고리 수정 기능 추가 #942

Merged
merged 11 commits into from
Oct 4, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
Expand All @@ -18,6 +19,8 @@
import in.koreatech.koin.admin.benefit.dto.AdminCreateBenefitShopsRequest;
import in.koreatech.koin.admin.benefit.dto.AdminCreateBenefitShopsResponse;
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.AdminSearchBenefitShopsResponse;
import in.koreatech.koin.global.auth.Auth;
import io.swagger.v3.oas.annotations.Operation;
Expand Down Expand Up @@ -56,8 +59,24 @@ ResponseEntity<AdminBenefitCategoryResponse> getBenefitCategories(
@Operation(summary = "혜택 카테고리를 추가한다.")
@PostMapping("/categories")
ResponseEntity<AdminCreateBenefitCategoryResponse> createBenefitCategory(
@Auth(permit = {ADMIN}) Integer adminId,
@RequestBody AdminCreateBenefitCategoryRequest request
@RequestBody AdminCreateBenefitCategoryRequest request,
@Auth(permit = {ADMIN}) Integer adminId
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "200"),
@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))),
}
)
@Operation(summary = "혜택 카테고리를 수정한다.")
@PutMapping("/categories/{id}")
ResponseEntity<AdminModifyBenefitCategoryResponse> modifyBenefitCategory(
@PathVariable("id") Integer categoryId,
@RequestBody AdminModifyBenefitCategoryRequest request,
@Auth(permit = {ADMIN}) Integer adminId
);

@ApiResponses(
Expand All @@ -72,8 +91,8 @@ ResponseEntity<AdminCreateBenefitCategoryResponse> createBenefitCategory(
@Operation(summary = "혜택 카테고리를 삭제한다.")
@DeleteMapping("/categories/{id}")
ResponseEntity<Void> deleteBenefitCategory(
@Auth(permit = {ADMIN}) Integer adminId,
@PathVariable("id") Integer categoryId
@PathVariable("id") Integer categoryId,
@Auth(permit = {ADMIN}) Integer adminId
);

@ApiResponses(
Expand All @@ -87,8 +106,8 @@ ResponseEntity<Void> deleteBenefitCategory(
@Operation(summary = "특정 혜택 카테고리에 속하는 상점을 모두 조회한다.")
@GetMapping("/{id}/shops")
ResponseEntity<AdminBenefitShopsResponse> getBenefitShops(
@Auth(permit = {ADMIN}) Integer adminId,
@PathVariable("id") Integer benefitId
@PathVariable("id") Integer benefitId,
@Auth(permit = {ADMIN}) Integer adminId
);

@ApiResponses(
Expand All @@ -103,9 +122,9 @@ ResponseEntity<AdminBenefitShopsResponse> getBenefitShops(
@Operation(summary = "특정 혜택을 제공하는 상점을 추가한다.")
@PostMapping("/{id}/shops")
ResponseEntity<AdminCreateBenefitShopsResponse> createBenefitShops(
@Auth(permit = {ADMIN}) Integer adminId,
@PathVariable("id") Integer benefitId,
@RequestBody AdminCreateBenefitShopsRequest request
@RequestBody AdminCreateBenefitShopsRequest request,
@Auth(permit = {ADMIN}) Integer adminId
);

@ApiResponses(
Expand All @@ -120,9 +139,9 @@ ResponseEntity<AdminCreateBenefitShopsResponse> createBenefitShops(
@Operation(summary = "특정 혜택을 제공하는 상점을 삭제한다.")
@DeleteMapping("/{id}/shops")
ResponseEntity<Void> deleteBenefitShops(
@Auth(permit = {ADMIN}) Integer adminId,
@PathVariable("id") Integer benefitId,
@RequestBody AdminDeleteShopsRequest request
@RequestBody AdminDeleteShopsRequest request,
@Auth(permit = {ADMIN}) Integer adminId
);

@ApiResponses(
Expand All @@ -137,8 +156,8 @@ ResponseEntity<Void> deleteBenefitShops(
@Operation(summary = "혜택 상점을 추가하기 위해 상점을 검색한다.")
@GetMapping("/{id}/shops/search")
ResponseEntity<AdminSearchBenefitShopsResponse> searchShops(
@Auth(permit = {ADMIN}) Integer adminId,
@PathVariable("id") Integer benefitId,
@RequestParam("search_keyword") String searchKeyword
@RequestParam("search_keyword") String searchKeyword,
@Auth(permit = {ADMIN}) Integer adminId
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,28 @@

import static in.koreatech.koin.domain.user.model.UserType.ADMIN;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import in.koreatech.koin.admin.benefit.dto.*;
import in.koreatech.koin.admin.benefit.dto.AdminBenefitCategoryResponse;
import in.koreatech.koin.admin.benefit.dto.AdminBenefitShopsResponse;
import in.koreatech.koin.admin.benefit.dto.AdminCreateBenefitCategoryRequest;
import in.koreatech.koin.admin.benefit.dto.AdminCreateBenefitCategoryResponse;
import in.koreatech.koin.admin.benefit.dto.AdminCreateBenefitShopsRequest;
import in.koreatech.koin.admin.benefit.dto.AdminCreateBenefitShopsResponse;
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.AdminSearchBenefitShopsResponse;
import in.koreatech.koin.admin.benefit.service.AdminBenefitService;
import in.koreatech.koin.global.auth.Auth;
import lombok.RequiredArgsConstructor;
Expand All @@ -27,56 +45,66 @@ public ResponseEntity<AdminBenefitCategoryResponse> getBenefitCategories(

@PostMapping("/categories")
public ResponseEntity<AdminCreateBenefitCategoryResponse> createBenefitCategory(
@Auth(permit = {ADMIN}) Integer adminId,
@RequestBody AdminCreateBenefitCategoryRequest request
@RequestBody AdminCreateBenefitCategoryRequest request,
@Auth(permit = {ADMIN}) Integer adminId
) {
AdminCreateBenefitCategoryResponse response = adminBenefitService.createBenefitCategory(request);
return ResponseEntity.status(201).body(response);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}

@PutMapping("/categories/{id}")
public ResponseEntity<AdminModifyBenefitCategoryResponse> modifyBenefitCategory(
@PathVariable("id") Integer categoryId,
@RequestBody AdminModifyBenefitCategoryRequest request,
@Auth(permit = {ADMIN}) Integer adminId
) {
AdminModifyBenefitCategoryResponse response = adminBenefitService.modifyBenefitCategory(categoryId, request);
return ResponseEntity.ok(response);
}

@DeleteMapping("/categories/{id}")
public ResponseEntity<Void> deleteBenefitCategory(
@Auth(permit = {ADMIN}) Integer adminId,
@PathVariable("id") Integer categoryId
@PathVariable("id") Integer categoryId,
@Auth(permit = {ADMIN}) Integer adminId
) {
adminBenefitService.deleteBenefitCategory(categoryId);
return ResponseEntity.noContent().build();
}

@GetMapping("/{id}/shops")
public ResponseEntity<AdminBenefitShopsResponse> getBenefitShops(
@Auth(permit = {ADMIN}) Integer adminId,
@PathVariable("id") Integer benefitId
@PathVariable("id") Integer benefitId,
@Auth(permit = {ADMIN}) Integer adminId
) {
AdminBenefitShopsResponse response = adminBenefitService.getBenefitShops(benefitId);
return ResponseEntity.ok(response);
}

@PostMapping("/{id}/shops")
public ResponseEntity<AdminCreateBenefitShopsResponse> createBenefitShops(
@Auth(permit = {ADMIN}) Integer adminId,
@PathVariable("id") Integer benefitId,
@RequestBody AdminCreateBenefitShopsRequest request
@RequestBody AdminCreateBenefitShopsRequest request,
@Auth(permit = {ADMIN}) Integer adminId
) {
AdminCreateBenefitShopsResponse response = adminBenefitService.createBenefitShops(benefitId, request);
return ResponseEntity.status(201).body(response);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}

@DeleteMapping("/{id}/shops")
public ResponseEntity<Void> deleteBenefitShops(
@Auth(permit = {ADMIN}) Integer adminId,
@PathVariable("id") Integer benefitId,
@RequestBody AdminDeleteShopsRequest request
@RequestBody AdminDeleteShopsRequest request,
@Auth(permit = {ADMIN}) Integer adminId
) {
adminBenefitService.deleteBenefitShops(benefitId, request);
return ResponseEntity.noContent().build();
}

@GetMapping("/{id}/shops/search")
public ResponseEntity<AdminSearchBenefitShopsResponse> searchShops(
@Auth(permit = {ADMIN}) Integer adminId,
@PathVariable("id") Integer benefitId,
@RequestParam("search_keyword") String searchKeyword
@RequestParam("search_keyword") String searchKeyword,
@Auth(permit = {ADMIN}) Integer adminId
) {
AdminSearchBenefitShopsResponse response = adminBenefitService.searchShops(benefitId, searchKeyword);
return ResponseEntity.ok(response);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package in.koreatech.koin.admin.benefit.dto;

import static io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED;

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

import in.koreatech.koin.domain.benefit.model.BenefitCategory;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

@JsonNaming(SnakeCaseStrategy.class)
public record AdminModifyBenefitCategoryRequest(
@Schema(description = "혜택 카테고리 제목", example = "배달비 아끼기", requiredMode = REQUIRED)
@NotBlank(message = "혜택 카테고리 제목은 필수입니다.")
String title,

@Schema(description = "혜택 카테고리 설명", example = "계좌이체하면 배달비가 무료(할인)인 상점들을 모아뒀어요.", requiredMode = REQUIRED)
@NotBlank(message = "혜택 카테고리 설명은 필수입니다.")
String detail,

@Schema(description = "혜택 카테고리 ON 이미지 URL", example = "https://example.com/button_on.jpg", requiredMode = REQUIRED)
@NotBlank(message = "혜택 카테고리 ON 이미지 URL은 필수입니다.")
String onImageUrl,

@Schema(description = "혜택 카테고리 OFF 이미지 URL", example = "https://example.com/button_off.jpg", requiredMode = REQUIRED)
@NotBlank(message = "혜택 카테고리 OFF 이미지 URL은 필수입니다.")
String offImageUrl
) {

public BenefitCategory toBenefitCategory() {
return BenefitCategory.builder()
.title(title)
.detail(detail)
.onImageUrl(onImageUrl)
.offImageUrl(offImageUrl)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package in.koreatech.koin.admin.benefit.dto;

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

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

@JsonNaming(SnakeCaseStrategy.class)
public record AdminModifyBenefitCategoryResponse(
@Schema(description = "혜택 카테고리 ID", example = "1")
Integer id,

@Schema(description = "혜택 카테고리 제목", example = "배달비 아끼기")
String title,

@Schema(description = "혜택 카테고리 설명", example = "계좌이체하면 배달비가 무료(할인)인 상점들을 모아뒀어요.")
String detail,

@Schema(description = "혜택 카테고리 ON 이미지 URL", example = "https://example.com/button_on.jpg")
String onImageUrl,

@Schema(description = "혜택 카테고리 OFF 이미지 URL", example = "https://example.com/button_off.jpg")
String offImageUrl
) {

public static AdminModifyBenefitCategoryResponse from(BenefitCategory benefitCategory) {
return new AdminModifyBenefitCategoryResponse(
benefitCategory.getId(),
benefitCategory.getTitle(),
benefitCategory.getDetail(),
benefitCategory.getOnImageUrl(),
benefitCategory.getOffImageUrl()
);
}
}
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.DuplicationException;

public class BenefitDuplicationException extends DuplicationException {

private static final String DEFAULT_MESSAGE = "이미 존재하는 혜택 카테고리입니다.";

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

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

public static BenefitDuplicationException withDetail(String detail) {
return new BenefitDuplicationException(DEFAULT_MESSAGE, detail);
}
}
Original file line number Diff line number Diff line change
@@ -1,27 +1,39 @@
package in.koreatech.koin.admin.benefit.repository;

import java.util.Collection;
import java.util.List;

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

import in.koreatech.koin.domain.benefit.model.BenefitCategory;
import in.koreatech.koin.domain.benefit.model.BenefitCategoryMap;

public interface AdminBenefitCategoryMapRepository extends Repository<BenefitCategoryMap, Integer> {

void save(BenefitCategoryMap benefitCategoryMap);

@Query("SELECT bcm FROM BenefitCategoryMap bcm WHERE bcm.benefitCategory.id = :benefitId")
List<BenefitCategoryMap> findAllByBenefitCategoryId(Integer benefitId);
@Query("""
SELECT bcm
FROM BenefitCategoryMap bcm
JOIN FETCH bcm.shop
WHERE bcm.benefitCategory.id = :benefitId
ORDER BY bcm.shop.name ASC
""")
List<BenefitCategoryMap> findAllByBenefitCategoryIdOrderByShopName(Integer benefitId);

@Modifying
@Query("DELETE FROM BenefitCategoryMap bcm WHERE bcm.benefitCategory.id = :benefitId AND bcm.shop.id IN :shopIds")
@Query("""
DELETE FROM BenefitCategoryMap bcm
WHERE bcm.benefitCategory.id = :benefitId
AND bcm.shop.id IN :shopIds
""")
void deleteByBenefitCategoryIdAndShopIds(Integer benefitId, List<Integer> shopIds);

@Modifying
@Query("DELETE FROM BenefitCategoryMap bcm WHERE bcm.benefitCategory.id = :categoryId")
void deleteByBenefitCategoryId(Integer categoryId);
@Query("""
DELETE FROM BenefitCategoryMap bcm
WHERE bcm.benefitCategory.id = :benefitId
""")
void deleteByBenefitCategoryId(Integer benefitId);

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ public interface AdminBenefitCategoryRepository extends Repository<BenefitCatego

Optional<BenefitCategory> findById(Integer benefitId);

Optional<BenefitCategory> findByTitle(String title);

List<BenefitCategory> findAllByOrderByTitleAsc();

void deleteById(Integer id);

int count();

boolean existsByTitle(String title);

default BenefitCategory getById(Integer benefitId) {
return findById(benefitId)
.orElseThrow(() -> BenefitNotFoundException.withDetail("해당 ID의 혜택 카테고리가 존재하지 않습니다."));
Expand Down
Loading
Loading