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: 어드민 혜택 상점 및 자동완성 검색기능 추가 #906

Merged
merged 19 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from 8 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package in.koreatech.koin.admin.benefit.controller;

import org.springframework.http.ResponseEntity;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

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.AdminSearchBenefitShopsResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;

@Tag(name = "(Admin) ShopBenefit: 상점 혜택", description = "어드민 상점 혜택 정보를 관리한다")
@RequestMapping("/admin/benefit")
public interface AdminBenefitApi {

@ApiResponses(
value = {
@ApiResponse(responseCode = "200"),
@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 = "상점 혜택 카테고리를 모두 조회한다.")
@GetMapping("/categories")
ResponseEntity<AdminBenefitCategoryResponse> getBenefitCategories();

@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))),
}
)
@Operation(summary = "혜택 카테고리를 추가한다.")
@PostMapping("/categories")
ResponseEntity<AdminCreateBenefitCategoryResponse> createBenefitCategory(
@RequestBody AdminCreateBenefitCategoryRequest request
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "204"),
@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 = "혜택 카테고리를 삭제한다.")
@DeleteMapping("/categories/{id}")
ResponseEntity<Void> deleteBenefitCategory(
@PathVariable("id") Integer categoryId
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "200"),
@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 = "특정 혜택 카테고리에 속하는 상점을 모두 조회한다.")
@GetMapping("/{id}/shops")
ResponseEntity<AdminBenefitShopsResponse> getBenefitShops(
@PathVariable("id") Integer benefitId
);

@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 = "특정 혜택을 제공하는 상점을 추가한다.")
@PostMapping("/{id}/shops")
ResponseEntity<AdminCreateBenefitShopsResponse> createBenefitShops(
@PathVariable("id") Integer benefitId,
@RequestBody AdminCreateBenefitShopsRequest request
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "204"),
@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 = "특정 혜택을 제공하는 상점을 삭제한다.")
@DeleteMapping("/{id}/shops")
ResponseEntity<Void> deleteBenefitShops(
@PathVariable("id") Integer benefitId,
@RequestBody AdminDeleteShopsRequest request
);

@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))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true))),
}
)
@Operation(summary = "혜택 상점을 추가하기 위해 상점을 검색한다.")
@GetMapping("/{id}/shops/search")
ResponseEntity<AdminSearchBenefitShopsResponse> searchShops(
@PathVariable("id") Integer benefitId,
@RequestParam("query") String query
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package in.koreatech.koin.admin.benefit.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import in.koreatech.koin.admin.benefit.dto.*;
import in.koreatech.koin.admin.benefit.service.AdminBenefitService;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/admin/benefit")
public class AdminBenefitController implements AdminBenefitApi {

private final AdminBenefitService adminBenefitService;

@Override
@GetMapping("/categories")
public ResponseEntity<AdminBenefitCategoryResponse> getBenefitCategories() {
AdminBenefitCategoryResponse response = adminBenefitService.getBenefitCategories();
return ResponseEntity.ok(response);
}

@Override
@PostMapping("/categories")
public ResponseEntity<AdminCreateBenefitCategoryResponse> createBenefitCategory(
@RequestBody AdminCreateBenefitCategoryRequest request
) {
AdminCreateBenefitCategoryResponse response = adminBenefitService.createBenefitCategory(request);
return ResponseEntity.status(201).body(response);
}

@Override
@DeleteMapping("/categories/{id}")
public ResponseEntity<Void> deleteBenefitCategory(
@PathVariable("id") Integer categoryId
) {
adminBenefitService.deleteBenefitCategory(categoryId);
return ResponseEntity.noContent().build();
}

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

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

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

@Override
@GetMapping("/{id}/shops/search")
public ResponseEntity<AdminSearchBenefitShopsResponse> searchShops(
@PathVariable("id") Integer benefitId,
@RequestParam("query") String query
) {
AdminSearchBenefitShopsResponse response = adminBenefitService.searchShops(benefitId, query);
return ResponseEntity.ok(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package in.koreatech.koin.admin.benefit.dto;

import java.util.List;

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 AdminBenefitCategoryResponse(
@Schema(description = "혜택 카테고리 리스트")
List<InnerBenefitResponse> benefits
) {

public static AdminBenefitCategoryResponse from(List<BenefitCategory> benefitCategories) {
return new AdminBenefitCategoryResponse(
benefitCategories.stream().map(InnerBenefitResponse::from).toList()
);
}

@JsonNaming(SnakeCaseStrategy.class)
public record InnerBenefitResponse(
@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 InnerBenefitResponse from(BenefitCategory benefitCategory) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C

외부에서 InnerBenefitResponse 를 만들어서 사용할 일이 있나요?
없다면 접근제어자를 private으로 설정하는건 어떨까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

잊고있었네요. 좋은 지적 감사합니다! 아래도 동일하게 수정하겠습니다!

return new InnerBenefitResponse(
benefitCategory.getId(),
benefitCategory.getTitle(),
benefitCategory.getDetail(),
benefitCategory.getOnImageUrl(),
benefitCategory.getOffImageUrl()
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package in.koreatech.koin.admin.benefit.dto;

import java.util.List;

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

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,

@Schema(description = "상점 정보")
List<InnerShopResponse> shops
) {

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

@JsonNaming(SnakeCaseStrategy.class)
BaeJinho4028 marked this conversation as resolved.
Show resolved Hide resolved
public record InnerShopResponse(
@Schema(example = "1", description = "고유 id")
Integer id,

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

public static InnerShopResponse from(Shop shop) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C

외부에서 InnerBenefitResponse 를 만들어서 사용할 일이 있나요?
없다면 접근제어자를 private으로 설정하는건 어떨까요?

ditto

return new InnerShopResponse(
shop.getId(),
shop.getName()
);
}
}
}
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 AdminCreateBenefitCategoryRequest(
@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 AdminCreateBenefitCategoryResponse(
@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 AdminCreateBenefitCategoryResponse from(BenefitCategory benefitCategory) {
return new AdminCreateBenefitCategoryResponse(
benefitCategory.getId(),
benefitCategory.getTitle(),
benefitCategory.getDetail(),
benefitCategory.getOnImageUrl(),
benefitCategory.getOffImageUrl()
);
}
}
Loading
Loading