diff --git a/api/src/main/java/com/pinback/api/category/controller/CategoryControllerV3.java b/api/src/main/java/com/pinback/api/category/controller/CategoryControllerV3.java new file mode 100644 index 00000000..9a3dc395 --- /dev/null +++ b/api/src/main/java/com/pinback/api/category/controller/CategoryControllerV3.java @@ -0,0 +1,54 @@ +package com.pinback.api.category.controller; + +import org.springframework.web.bind.annotation.PatchMapping; +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.RestController; + +import com.pinback.api.category.dto.request.CreateCategoryRequestV3; +import com.pinback.api.category.dto.request.UpdateCategoryRequestV3; +import com.pinback.application.category.dto.response.CreateCategoryResponseV3; +import com.pinback.application.category.dto.response.UpdateCategoryResponseV3; +import com.pinback.application.category.port.in.CreateCategoryPort; +import com.pinback.application.category.port.in.UpdateCategoryPort; +import com.pinback.domain.user.entity.User; +import com.pinback.shared.annotation.CurrentUser; +import com.pinback.shared.dto.ResponseDto; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping("/api/v3/categories") +@RequiredArgsConstructor +@Tag(name = "CategoryV3", description = "카테고리 관리 API V3") +public class CategoryControllerV3 { + private final CreateCategoryPort createCategoryPort; + private final UpdateCategoryPort updateCategoryPort; + + @Operation(summary = "카테고리 생성 V3", description = "공개여부 설정을 추가하여 새로운 카테고리를 생성합니다") + @PostMapping + public ResponseDto createCategoryV3( + @Parameter(hidden = true) @CurrentUser User user, + @Valid @RequestBody CreateCategoryRequestV3 request + ) { + CreateCategoryResponseV3 response = createCategoryPort.createCategoryV3(user, request.toCommand()); + return ResponseDto.ok(response); + } + + @Operation(summary = "카테고리 수정 V3", description = "공개여부 설정을 추가하여 카테고리 정보를 수정합니다") + @PatchMapping("/{categoryId}") + public ResponseDto updateCategoryV3( + @Parameter(hidden = true) @CurrentUser User user, + @Parameter(description = "카테고리 ID") @PathVariable Long categoryId, + @Valid @RequestBody UpdateCategoryRequestV3 request + ) { + UpdateCategoryResponseV3 response = updateCategoryPort.updateCategoryV3(user, categoryId, request.toCommand()); + return ResponseDto.ok(response); + } +} diff --git a/api/src/main/java/com/pinback/api/category/dto/request/CreateCategoryRequestV3.java b/api/src/main/java/com/pinback/api/category/dto/request/CreateCategoryRequestV3.java new file mode 100644 index 00000000..700c6e74 --- /dev/null +++ b/api/src/main/java/com/pinback/api/category/dto/request/CreateCategoryRequestV3.java @@ -0,0 +1,22 @@ +package com.pinback.api.category.dto.request; + +import com.pinback.application.category.dto.command.CreateCategoryCommandV3; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +@Schema(description = "카테고리 생성 요청 V3") +public record CreateCategoryRequestV3( + @Schema(description = "카테고리 이름", example = "개발") + @NotBlank(message = "카테고리 이름은 필수입니다") + @Size(max = 50, message = "카테고리 이름은 50자 이하로 입력해주세요") + String categoryName, + @Schema(description = "카테고리 공개여부", example = "true") + Boolean isPublic +) { + public CreateCategoryCommandV3 toCommand() { + return new CreateCategoryCommandV3(categoryName, isPublic); + } + +} diff --git a/api/src/main/java/com/pinback/api/category/dto/request/UpdateCategoryRequestV3.java b/api/src/main/java/com/pinback/api/category/dto/request/UpdateCategoryRequestV3.java new file mode 100644 index 00000000..f60c40d8 --- /dev/null +++ b/api/src/main/java/com/pinback/api/category/dto/request/UpdateCategoryRequestV3.java @@ -0,0 +1,21 @@ +package com.pinback.api.category.dto.request; + +import com.pinback.application.category.dto.command.UpdateCategoryCommandV3; + +import io.swagger.v3.oas.annotations.media.Schema; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Size; + +@Schema(description = "카테고리 수정 요청 V3") +public record UpdateCategoryRequestV3( + @Schema(description = "카테고리 이름", example = "수정된 개발") + @NotBlank(message = "카테고리 이름은 필수입니다") + @Size(max = 50, message = "카테고리 이름은 50자 이하로 입력해주세요") + String categoryName, + @Schema(description = "카테고리 공개여부", example = "false") + Boolean isPublic +) { + public UpdateCategoryCommandV3 toCommand() { + return new UpdateCategoryCommandV3(categoryName, isPublic); + } +} diff --git a/application/src/main/java/com/pinback/application/category/dto/command/CreateCategoryCommandV3.java b/application/src/main/java/com/pinback/application/category/dto/command/CreateCategoryCommandV3.java new file mode 100644 index 00000000..0d603966 --- /dev/null +++ b/application/src/main/java/com/pinback/application/category/dto/command/CreateCategoryCommandV3.java @@ -0,0 +1,7 @@ +package com.pinback.application.category.dto.command; + +public record CreateCategoryCommandV3( + String categoryName, + Boolean isPublic +) { +} diff --git a/application/src/main/java/com/pinback/application/category/dto/command/UpdateCategoryCommandV3.java b/application/src/main/java/com/pinback/application/category/dto/command/UpdateCategoryCommandV3.java new file mode 100644 index 00000000..e2759574 --- /dev/null +++ b/application/src/main/java/com/pinback/application/category/dto/command/UpdateCategoryCommandV3.java @@ -0,0 +1,7 @@ +package com.pinback.application.category.dto.command; + +public record UpdateCategoryCommandV3( + String categoryName, + Boolean isPublic +) { +} diff --git a/application/src/main/java/com/pinback/application/category/dto/response/CreateCategoryResponseV3.java b/application/src/main/java/com/pinback/application/category/dto/response/CreateCategoryResponseV3.java new file mode 100644 index 00000000..af90a20f --- /dev/null +++ b/application/src/main/java/com/pinback/application/category/dto/response/CreateCategoryResponseV3.java @@ -0,0 +1,19 @@ +package com.pinback.application.category.dto.response; + +import com.pinback.domain.category.enums.CategoryColor; + +public record CreateCategoryResponseV3( + Long categoryId, + String categoryName, + String categoryColor, + Boolean isPublic +) { + public static CreateCategoryResponseV3 of(Long categoryId, String categoryName, CategoryColor categoryColor) { + return new CreateCategoryResponseV3(categoryId, categoryName, categoryColor.toString(), false); + } + + public static CreateCategoryResponseV3 of(Long categoryId, String categoryName, CategoryColor categoryColor, + boolean isPublic) { + return new CreateCategoryResponseV3(categoryId, categoryName, categoryColor.toString(), isPublic); + } +} diff --git a/application/src/main/java/com/pinback/application/category/dto/response/UpdateCategoryResponseV3.java b/application/src/main/java/com/pinback/application/category/dto/response/UpdateCategoryResponseV3.java new file mode 100644 index 00000000..16d7c8d4 --- /dev/null +++ b/application/src/main/java/com/pinback/application/category/dto/response/UpdateCategoryResponseV3.java @@ -0,0 +1,13 @@ +package com.pinback.application.category.dto.response; + +import com.pinback.domain.category.entity.Category; + +public record UpdateCategoryResponseV3( + long categoryId, + String categoryName, + Boolean isPublic +) { + public static UpdateCategoryResponseV3 from(Category category) { + return new UpdateCategoryResponseV3(category.getId(), category.getName(), category.getIsPublic()); + } +} diff --git a/application/src/main/java/com/pinback/application/category/port/in/CreateCategoryPort.java b/application/src/main/java/com/pinback/application/category/port/in/CreateCategoryPort.java index 8d80bad1..504a1421 100644 --- a/application/src/main/java/com/pinback/application/category/port/in/CreateCategoryPort.java +++ b/application/src/main/java/com/pinback/application/category/port/in/CreateCategoryPort.java @@ -1,9 +1,13 @@ package com.pinback.application.category.port.in; import com.pinback.application.category.dto.command.CreateCategoryCommand; +import com.pinback.application.category.dto.command.CreateCategoryCommandV3; import com.pinback.application.category.dto.response.CreateCategoryResponse; +import com.pinback.application.category.dto.response.CreateCategoryResponseV3; import com.pinback.domain.user.entity.User; public interface CreateCategoryPort { CreateCategoryResponse createCategory(User user, CreateCategoryCommand command); + + CreateCategoryResponseV3 createCategoryV3(User user, CreateCategoryCommandV3 command); } diff --git a/application/src/main/java/com/pinback/application/category/port/in/UpdateCategoryPort.java b/application/src/main/java/com/pinback/application/category/port/in/UpdateCategoryPort.java index ad19b74f..7b850917 100644 --- a/application/src/main/java/com/pinback/application/category/port/in/UpdateCategoryPort.java +++ b/application/src/main/java/com/pinback/application/category/port/in/UpdateCategoryPort.java @@ -1,9 +1,13 @@ package com.pinback.application.category.port.in; import com.pinback.application.category.dto.command.UpdateCategoryCommand; +import com.pinback.application.category.dto.command.UpdateCategoryCommandV3; import com.pinback.application.category.dto.response.UpdateCategoryResponse; +import com.pinback.application.category.dto.response.UpdateCategoryResponseV3; import com.pinback.domain.user.entity.User; public interface UpdateCategoryPort { UpdateCategoryResponse updateCategory(User user, Long categoryId, UpdateCategoryCommand command); + + UpdateCategoryResponseV3 updateCategoryV3(User user, Long categoryId, UpdateCategoryCommandV3 command); } diff --git a/application/src/main/java/com/pinback/application/category/usecase/command/CreateCategoryUsecase.java b/application/src/main/java/com/pinback/application/category/usecase/command/CreateCategoryUsecase.java index 2898a7f2..32047c20 100644 --- a/application/src/main/java/com/pinback/application/category/usecase/command/CreateCategoryUsecase.java +++ b/application/src/main/java/com/pinback/application/category/usecase/command/CreateCategoryUsecase.java @@ -7,7 +7,9 @@ import org.springframework.transaction.annotation.Transactional; import com.pinback.application.category.dto.command.CreateCategoryCommand; +import com.pinback.application.category.dto.command.CreateCategoryCommandV3; import com.pinback.application.category.dto.response.CreateCategoryResponse; +import com.pinback.application.category.dto.response.CreateCategoryResponseV3; import com.pinback.application.category.port.in.CreateCategoryPort; import com.pinback.application.category.port.out.CategoryColorServicePort; import com.pinback.application.category.port.out.CategoryGetServicePort; @@ -44,6 +46,26 @@ public CreateCategoryResponse createCategory(User user, CreateCategoryCommand co } } + @Override + public CreateCategoryResponseV3 createCategoryV3(User user, CreateCategoryCommandV3 command) { + validateCategoryCreationV3(user, command); + + CategoryColor availableColor = getNextAvailableColor(user); + try { + Category category = Category.createWithIsPublic(command.categoryName(), user, availableColor, + command.isPublic()); + Category savedCategory = categorySaveService.save(category); + return CreateCategoryResponseV3.of( + savedCategory.getId(), + savedCategory.getName(), + savedCategory.getColor(), + savedCategory.getIsPublic() + ); + } catch (CategoryNameLengthOverException e) { + throw new CategoryNameLengthOverException(); + } + } + private void validateCategoryCreation(User user, CreateCategoryCommand command) { if (categoryGetService.countCategoriesByUser(user) >= 10) { throw new CategoryLimitOverException(); @@ -61,4 +83,13 @@ private CategoryColor getNextAvailableColor(User user) { .findFirst() .orElse(CategoryColor.COLOR1); } + + private void validateCategoryCreationV3(User user, CreateCategoryCommandV3 command) { + if (categoryGetService.countCategoriesByUser(user) >= 10) { + throw new CategoryLimitOverException(); + } + if (categoryGetService.checkExistsByCategoryNameAndUser(command.categoryName(), user)) { + throw new CategoryAlreadyExistException(); + } + } } diff --git a/application/src/main/java/com/pinback/application/category/usecase/command/UpdateCategoryUsecase.java b/application/src/main/java/com/pinback/application/category/usecase/command/UpdateCategoryUsecase.java index 3e09c31d..d57bcf91 100644 --- a/application/src/main/java/com/pinback/application/category/usecase/command/UpdateCategoryUsecase.java +++ b/application/src/main/java/com/pinback/application/category/usecase/command/UpdateCategoryUsecase.java @@ -4,7 +4,9 @@ import org.springframework.transaction.annotation.Transactional; import com.pinback.application.category.dto.command.UpdateCategoryCommand; +import com.pinback.application.category.dto.command.UpdateCategoryCommandV3; import com.pinback.application.category.dto.response.UpdateCategoryResponse; +import com.pinback.application.category.dto.response.UpdateCategoryResponseV3; import com.pinback.application.category.port.in.UpdateCategoryPort; import com.pinback.application.category.port.out.CategoryGetServicePort; import com.pinback.domain.category.entity.Category; @@ -31,4 +33,17 @@ public UpdateCategoryResponse updateCategory(User user, Long categoryId, UpdateC return UpdateCategoryResponse.from(category); } + + @Override + public UpdateCategoryResponseV3 updateCategoryV3(User user, Long categoryId, UpdateCategoryCommandV3 command) { + Category category = categoryGetService.getCategoryAndUser(categoryId, user); + try { + category.updateName(command.categoryName()); + } catch (CategoryNameLengthOverException e) { + throw new CategoryNameLengthOverException(); + } + category.updateIsPublic(command.isPublic()); + + return UpdateCategoryResponseV3.from(category); + } } diff --git a/domain/src/main/java/com/pinback/domain/category/entity/Category.java b/domain/src/main/java/com/pinback/domain/category/entity/Category.java index 02b7bec4..5e896a4d 100644 --- a/domain/src/main/java/com/pinback/domain/category/entity/Category.java +++ b/domain/src/main/java/com/pinback/domain/category/entity/Category.java @@ -53,6 +53,9 @@ public class Category extends BaseEntity { @Enumerated(EnumType.STRING) private CategoryColor color; + @Column(name = "is_public", nullable = false) + private Boolean isPublic; + public static Category create(String name, User user, CategoryColor color) { validateName(name); return Category.builder() @@ -62,6 +65,16 @@ public static Category create(String name, User user, CategoryColor color) { .build(); } + public static Category createWithIsPublic(String name, User user, CategoryColor color, Boolean isPublic) { + validateName(name); + return Category.builder() + .name(name) + .user(user) + .color(color) + .isPublic(isPublic) + .build(); + } + private static void validateName(String name) { int characterCount = TextUtil.countGraphemeClusters(name); if (characterCount > 300) { @@ -77,4 +90,8 @@ public void updateName(String name) { public boolean isOwnedBy(User user) { return this.user.equals(user); } + + public void updateIsPublic(Boolean isPublic) { + this.isPublic = isPublic; + } }