-
Notifications
You must be signed in to change notification settings - Fork 1
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
Changes from 8 commits
Commits
Show all changes
19 commits
Select commit
Hold shift + click to select a range
327fd08
feat: API문서 & DTO 작성
BaeJinho4028 2115dec
feat: controller 작성
BaeJinho4028 da7b4d0
feat: service & repository 작성
BaeJinho4028 2293e72
Merge branch 'develop' into feature/885-admin-benefit-shops
BaeJinho4028 052cb0f
feat: 자동완성 검색 기능 구현
BaeJinho4028 1d81ade
feat: 혜택 카테고리 on/off 이미지 필드 수정
BaeJinho4028 e82740b
feat: 예외 처리
BaeJinho4028 eb7a820
test: 테스트 추가
BaeJinho4028 8a27189
Merge branch 'develop' into feature/885-admin-benefit-shops
BaeJinho4028 d669cf2
fix: 피드백 반영 1
BaeJinho4028 3c2e0d1
fix: 피드백 반영 2
BaeJinho4028 de9eda3
fix: 테스트 수정
BaeJinho4028 0bf344d
chore: 불필요한 코드 삭제
BaeJinho4028 f8294ad
Merge branch 'develop' into feature/885-admin-benefit-shops
BaeJinho4028 040a155
fix: flyway 버전 수정
BaeJinho4028 1f051e1
fix: 피드백 반영 3
BaeJinho4028 2c62f45
Merge branch 'develop' into feature/885-admin-benefit-shops
BaeJinho4028 83743f7
fix: flyway 버전 수정
BaeJinho4028 723fb68
test: 삭제시 검증 추가
BaeJinho4028 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
133 changes: 133 additions & 0 deletions
133
src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitApi.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
); | ||
} |
78 changes: 78 additions & 0 deletions
78
src/main/java/in/koreatech/koin/admin/benefit/controller/AdminBenefitController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitCategoryResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
return new InnerBenefitResponse( | ||
benefitCategory.getId(), | ||
benefitCategory.getTitle(), | ||
benefitCategory.getDetail(), | ||
benefitCategory.getOnImageUrl(), | ||
benefitCategory.getOffImageUrl() | ||
); | ||
} | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
src/main/java/in/koreatech/koin/admin/benefit/dto/AdminBenefitShopsResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. C
ditto |
||
return new InnerShopResponse( | ||
shop.getId(), | ||
shop.getName() | ||
); | ||
} | ||
} | ||
} |
39 changes: 39 additions & 0 deletions
39
src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitCategoryRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
src/main/java/in/koreatech/koin/admin/benefit/dto/AdminCreateBenefitCategoryResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() | ||
); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C
외부에서 InnerBenefitResponse 를 만들어서 사용할 일이 있나요?
없다면 접근제어자를 private으로 설정하는건 어떨까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
잊고있었네요. 좋은 지적 감사합니다! 아래도 동일하게 수정하겠습니다!