Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 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
Expand Up @@ -36,32 +36,85 @@ public class AdminController {
@GetMapping("/tool")
@Operation(summary = "관리자용 툴 조회", description = "관리자용 전체 툴 조회 API입니다.")
public ResponseEntity<SuccessResponse<AdminToolPageRes>> fetchAllTool(
@Parameter(description = "정렬 기준", example = "createdAt")
@RequestParam(defaultValue = "createdAt", value = "criteria") String criteria,
@Parameter(description = "정렬 순서 (ASC/DESC)", example = "DESC")
@RequestParam(defaultValue = "DESC", value = "direction") String direction,
@Min(0)
@Parameter(description = "페이지 인덱스", example = "0")
@RequestParam(value = "page", defaultValue = "0") int page,
@Min(1) @Max(100)
@Parameter(description = "페이지 크기", example = "20")
@RequestParam(value = "size", defaultValue = "20") int size
@Parameter(description = "페이지 크기", example = "10")
@RequestParam(value = "size", defaultValue = "10") int size
) {
AdminToolPageRes res = adminService.fetchAllTool(page, size);
AdminToolPageRes res = adminService.fetchAllTool(criteria, direction, page, size);
Comment thread
yechan-kim marked this conversation as resolved.
return ResponseEntity.ok(SuccessResponse.of(SuccessCode.SUCCESS_FETCH, res));
}

@PostMapping
@Operation(summary = "관리자용 툴 추가", description = "관리자용 툴 추가 API입니다.")
@Operation(
summary = "관리자용 툴 추가",
description = """
관리자용 툴 추가 API입니다.
**플랜(plans) 필드 사용 가이드:**
- 플랜은 여러 개를 추가할 수 있습니다 (배열 형태)
- 각 플랜은 planName(플랜명), planPrice(가격), planDescription(설명)을 포함합니다
- planType과 plans는 함께 사용됩니다:
* FREE: 무료 플랜인 경우 plans는 빈 배열이거나 무료 플랜 1개만 포함
* MONTHLY: 월간 구독 플랜들 (예: Basic, Pro, Enterprise 등)
* PURCHASE: 일회성 구매 플랜들 (예: Starter, Professional 등)
* MONTHLY_ANNUAL: 월간 & 연간 플랜 혼합 (예: 월간 Basic, 연간 Basic 등)
**플랜 케이스 예시:**
1. 무료 툴: planType="무료", plans=[]
또는 plans=[{planName:"무료", planPrice:0, planDescription:"기본 기능 무료 제공"}]
2. 월간 구독: planType="월간",
plans=[{planName:"Basic", planPrice:10000, ...}, {planName:"Pro", planPrice:30000, ...}]
3. 일회성 구매: planType="구매",
plans=[{planName:"Starter", planPrice:50000, ...},
{planName:"Professional", planPrice:150000, ...}]
4. 혼합: planType="월간 & 연간",
plans=[{planName:"Basic 월간", planPrice:10000, ...},
{planName:"Basic 연간", planPrice:100000, ...}]
**주의사항:**
- planPrice는 원화 단위로 입력합니다 (예: 10000 = 1만원)
- planName, planPrice, planDescription은 모두 필수입니다
- planDescription은 최대 500자까지 입력 가능합니다
- plans가 null이거나 빈 배열일 수 있지만, planType이 설정된 경우 적절한 플랜 정보를 제공하는 것을 권장합니다
"""
)
Comment thread
yechan-kim marked this conversation as resolved.
public ResponseEntity<SuccessResponse<Void>> createTool(
@Parameter(description = "추가할 툴")
@Parameter(description = "추가할 툴 정보 (플랜 포함)")
@RequestBody @Valid CreateToolRequest createToolRequest) {

adminService.createTool(createToolRequest);
return ResponseEntity.ok(SuccessResponse.of(SuccessCode.SUCCESS_CREATE));
}

@PatchMapping("/tools/{toolId}")
@Operation(summary = "관리자용 툴 수정", description = "관리자용 툴 수정 API입니다.")
@Operation(
summary = "관리자용 툴 수정",
description = """
관리자용 툴 수정 API입니다.
**플랜(plans) 필드 수정 가이드:**
- plans 필드를 전송하면 기존 플랜들이 모두 삭제되고 새로운 플랜들로 교체됩니다
- plans를 null로 보내면 플랜 정보는 변경되지 않습니다
- plans를 빈 배열([])로 보내면 모든 플랜이 삭제됩니다
**플랜 수정 케이스:**
1. 플랜 추가/변경: plans=[{planName:"New Plan", planPrice:20000, planDescription:"새로운 플랜"}]
2. 플랜 삭제: plans=[] (모든 플랜 삭제)
3. 플랜 유지: plans=null (기존 플랜 그대로 유지)
4. 플랜 수정: plans=[수정된 플랜 목록] (기존 플랜 삭제 후 새로 추가)
**주의사항:**
- 플랜 수정 시 planName, planPrice, planDescription은 모두 필수입니다 (null이면 해당 플랜은 저장되지 않습니다)
- planDescription은 최대 500자까지 입력 가능합니다
- planType도 함께 수정하려면 planType 필드도 함께 전송하세요
- 기존 플랜을 유지하면서 일부만 수정하려면, 유지할 플랜도 포함하여 전체 플랜 목록을 다시 전송해야 합니다
"""
)
public ResponseEntity<SuccessResponse<Void>> updateTool(
@Parameter(description = "수정할 툴 ID") @PathVariable Long toolId,
@Parameter(description = "수정 요청 DTO") @RequestBody @Valid UpdateToolRequest request
@Parameter(description = "수정 요청 DTO (플랜 정보 포함)")
@RequestBody @Valid UpdateToolRequest request
) {
adminService.updateTool(toolId, request);
return ResponseEntity.ok(SuccessResponse.of(SuccessCode.SUCCESS_UPDATE));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
package com.daruda.darudaserver.domain.admin.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.PositiveOrZero;
import jakarta.validation.constraints.Size;

public record CreateToolPlanRequest(
@Schema(description = "플랜명")
@Schema(
description = "플랜명",
example = "Basic",
requiredMode = Schema.RequiredMode.REQUIRED
)
@NotBlank(message = "플랜명은 필수입니다.")
String planName,
@Schema(description = "플랜 가격(원화)")
@Schema(
description = "플랜 가격(원화)",
example = "10000",
requiredMode = Schema.RequiredMode.REQUIRED
)
@NotNull(message = "플랜 가격은 필수입니다.")
@PositiveOrZero(message = "플랜 가격은 0 이상이어야 합니다.")
Long planPrice,
@Schema(description = "플랜 상세 설명")
@Schema(
description = "플랜 상세 설명 (최대 500자, 필수)",
example = "기본 기능을 포함한 플랜입니다. 월 10,000원으로 이용 가능합니다.",
requiredMode = Schema.RequiredMode.REQUIRED
)
@NotBlank(message = "플랜 상세 설명은 필수입니다.")
@Size(max = 500, message = "플랜 상세 설명은 500자 이내로 작성해주세요.")
String planDescription
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.fasterxml.jackson.annotation.JsonInclude;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Size;

@JsonIgnoreProperties(ignoreUnknown = true)
Expand Down Expand Up @@ -39,9 +40,33 @@ public record CreateToolRequest(
List<String> keywords,
@Schema(description = "핵심 기능 목록")
List<CreateToolCoreRequest> cores,
@Schema(description = "플랜 유형")
@Schema(
description = "플랜 유형",
example = "무료",
allowableValues = {"무료", "월간", "구매", "월간 & 연간"},
requiredMode = Schema.RequiredMode.NOT_REQUIRED
)
String planType,
@Schema(description = "플랜 목록")
@Schema(
description = """
플랜 목록 (배열)
**사용 가이드:**
- planType에 따라 플랜 구조가 달라집니다
- 무료: 빈 배열 또는 무료 플랜 1개
- 월간: 여러 월간 구독 플랜 (예: Basic, Pro, Enterprise)
- 구매: 여러 일회성 구매 플랜 (예: Starter, Professional)
- 월간 & 연간: 월간과 연간 플랜 혼합
**예시:**
- 무료: [] 또는 [{"planName":"무료", "planPrice":0, "planDescription":"기본 기능 제공"}]
- 월간: [{"planName":"Basic", "planPrice":10000, "planDescription":"기본 기능"},
{"planName":"Pro", "planPrice":30000, "planDescription":"고급 기능}]
- 구매: [{"planName":"Starter", "planPrice":50000, "planDescription":"시작 패키지"},
{"planName":"Professional", "planPrice":150000, "planDescription":"전문가 패키지"}]
**주의:** 각 플랜의 planName, planPrice, planDescription은 모두 필수입니다.
""",
requiredMode = Schema.RequiredMode.NOT_REQUIRED
)
@Valid
List<CreateToolPlanRequest> plans,
Comment thread
yechan-kim marked this conversation as resolved.
@Schema(description = "블로그 링크 목록")
List<String> blogLinks,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.annotation.Nullable;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Size;

@JsonIgnoreProperties(ignoreUnknown = true)
Expand Down Expand Up @@ -40,9 +41,36 @@ public record UpdateToolRequest(
List<String> keywords,
@Schema(description = "핵심 기능 목록")
List<CreateToolCoreRequest> cores,
@Schema(description = "플랜 유형")
@Schema(
description = "플랜 유형",
example = "월간",
allowableValues = {"무료", "월간", "구매", "월간 & 연간"},
requiredMode = Schema.RequiredMode.NOT_REQUIRED
)
String planType,
@Schema(description = "플랜 목록")
@Schema(
description = """
플랜 목록 (배열)

**수정 동작:**
- 이 필드를 전송하면 기존 플랜들이 모두 삭제되고 새로운 플랜들로 교체됩니다
- null로 보내면 플랜 정보는 변경되지 않습니다
- 빈 배열([])로 보내면 모든 플랜이 삭제됩니다

**주의사항:**
- 플랜을 수정할 때는 유지할 플랜도 포함하여 전체 플랜 목록을 다시 전송해야 합니다
- planName, planPrice, planDescription이 null인 플랜은 저장되지 않습니다 (모두 필수)
- planDescription은 최대 500자까지 입력 가능합니다

**예시:**
- 플랜 추가: [{"planName":"New Plan", "planPrice":20000, "planDescription":"새로운 플랜"}]
- 플랜 삭제: [] (모든 플랜 삭제)
- 플랜 유지: null (기존 플랜 그대로 유지)
- 플랜 수정: [수정된 전체 플랜 목록]
""",
requiredMode = Schema.RequiredMode.NOT_REQUIRED
)
@Valid
Comment thread
yechan-kim marked this conversation as resolved.
List<CreateToolPlanRequest> plans,
@Schema(description = "블로그 링크 목록")
List<String> blogLinks,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -363,8 +364,9 @@ public void deleteTool(final Long toolId) {
toolRepository.delete(tool);
}

public AdminToolPageRes fetchAllTool(int page, int size) {
Pageable pageable = PageRequest.of(page, size);
public AdminToolPageRes fetchAllTool(String criteria, String direction, int page, int size) {
Sort.Direction dir = Sort.Direction.fromString(direction);
Pageable pageable = PageRequest.of(page, size, Sort.by(dir, criteria));
Page<Tool> toolPage = toolRepository.findAll(pageable);

return AdminToolPageRes.of(toolPage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import com.daruda.darudaserver.domain.community.entity.Board;
import com.daruda.darudaserver.domain.tool.entity.Tool;
import com.daruda.darudaserver.domain.user.entity.UserEntity;

import jakarta.transaction.Transactional;

Expand Down Expand Up @@ -43,6 +44,21 @@ List<Board> findBoards(

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Transactional
@Query(value = "UPDATE board SET tool_id = 0 WHERE tool_id = :#{#tool.toolId}", nativeQuery = true)
@Query(value = """
UPDATE board
SET tool_id = 0, updated_at = CURRENT_TIMESTAMP
WHERE tool_id = :#{#tool.toolId}
""",
nativeQuery = true)
void clearTool(Tool tool);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Transactional
@Query(value = """
UPDATE board
SET user_id = 0, updated_at = CURRENT_TIMESTAMP
WHERE user_id = :#{#userEntity.id}
""",
nativeQuery = true)
void clearUser(UserEntity userEntity);
Comment thread
yechan-kim marked this conversation as resolved.
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,6 @@ public class ToolDocument {
@Field(type = FieldType.Text)
private String planLink;

@Field(type = FieldType.Keyword)
private String bgColor;

@Field(type = FieldType.Boolean)
private boolean fontColor;

@Field(type = FieldType.Integer)
private int viewCount;

Expand All @@ -85,8 +79,6 @@ public static ToolDocument from(Tool tool) {
.supportKorea(tool.getSupportKorea())
.detailDescription(tool.getDetailDescription())
.planLink(tool.getPlanLink())
.bgColor(tool.getBgColor())
.fontColor(tool.isFontColor())
.viewCount(tool.getViewCount())
.popular(tool.getPopular())
.toolLogo(tool.getToolLogo())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,7 @@ public record ToolSearchResponse(
String description,
String license,
List<String> keywords,
boolean isScraped,
String bgColor,
boolean fontColor
boolean isScraped
) {
public static ToolSearchResponse from(ToolDocument document, List<String> keywords, boolean isScrapped) {
return new ToolSearchResponse(
Expand All @@ -23,9 +21,7 @@ public static ToolSearchResponse from(ToolDocument document, List<String> keywor
document.getDescription(),
document.getLicense(),
keywords,
isScrapped,
document.getBgColor(),
document.isFontColor()
isScrapped
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,11 @@ public record ToolDetailGetRes(
String detailDescription,
List<String> videos,
List<String> images,
String bgColor,
Boolean fontColor,
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd", timezone = "Asia/Seoul")
Timestamp updatedAt,
Boolean isScrapped
) {
public static ToolDetailGetRes of(Tool tool, List<PlatformRes> platform, String toolLogo, List<String> keywords,
public static ToolDetailGetRes of(Tool tool, List<PlatformRes> platform, List<String> keywords,
List<String> images, List<String> videos, Boolean isScrapped) {

return ToolDetailGetRes.builder()
Expand All @@ -49,8 +47,6 @@ public static ToolDetailGetRes of(Tool tool, List<PlatformRes> platform, String
.updatedAt(tool.getUpdatedAt())
.images(images)
.videos(videos)
.bgColor(tool.getBgColor())
.fontColor(tool.isFontColor())
.isScrapped(isScrapped)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,7 @@ public record ToolResponse(
String description,
String license,
List<String> keywords,
Boolean isScraped,
String bgColor,
boolean fontColor
Boolean isScraped
) {
public static ToolResponse of(Tool tool, List<String> keywords, Boolean isScraped) {
return ToolResponse.builder()
Expand All @@ -27,8 +25,6 @@ public static ToolResponse of(Tool tool, List<String> keywords, Boolean isScrape
.license(tool.getLicense().getKoreanName())
.keywords(keywords)
.isScraped(isScraped)
.bgColor(tool.getBgColor())
.fontColor(tool.isFontColor())
.build();
}
}
Loading