Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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,52 @@
package com.example.green.domain.pointshop.admin.controller;

import static com.example.green.domain.pointshop.admin.controller.message.PointShopAdminResponseMessage.*;

import org.springframework.web.bind.annotation.DeleteMapping;
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.RestController;

import com.example.green.domain.pointshop.admin.controller.docs.PointShopAdminControllerDocs;
import com.example.green.domain.pointshop.admin.dto.request.AdminCreatePointShopRequest;
import com.example.green.domain.pointshop.admin.dto.request.AdminUpdatePointShopRequest;
import com.example.green.domain.pointshop.admin.service.PointShopAdminService;
import com.example.green.global.api.ApiTemplate;
import com.example.green.global.api.NoContent;
import com.example.green.global.security.annotation.AdminApi;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/admin/point-shop")
@AdminApi
public class PointShopAdminController implements PointShopAdminControllerDocs {

private final PointShopAdminService pointShopAdminService;

@PostMapping("/create")
public ApiTemplate<Long> create(@RequestBody @Valid AdminCreatePointShopRequest createPointShopRequest) {
Long result = pointShopAdminService.create(createPointShopRequest);
return ApiTemplate.ok(POINT_ITEM_CREATION_SUCCESS, result);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use type-agnostic message constants or conditional messages.

The endpoint uses POINT_ITEM_CREATION_SUCCESS for all creations, but this controller handles both items and products. Consider either:

  1. Renaming the constants to be generic (e.g., POINT_SHOP_CREATION_SUCCESS)
  2. Using conditional messages based on the request type

The same issue applies to lines 44 and 50 with POINT_ITEM_UPDATE_SUCCESS and POINT_ITEM_DELETE_SUCCESS.

🤖 Prompt for AI Agents
In
src/main/java/com/example/green/domain/pointshop/admin/controller/PointShopAdminController.java
around lines 35, 44 and 50, the controller currently returns
POINT_ITEM_CREATION_SUCCESS, POINT_ITEM_UPDATE_SUCCESS and
POINT_ITEM_DELETE_SUCCESS for both item and product operations; change to use
type-agnostic constants (e.g., POINT_SHOP_CREATION_SUCCESS,
POINT_SHOP_UPDATE_SUCCESS, POINT_SHOP_DELETE_SUCCESS) or select the message
conditionally based on the incoming DTO/type before calling ApiTemplate.ok, and
update usages/imports accordingly so responses reflect the correct generic or
conditional message.

}

@PutMapping("/update/{id}")
public NoContent update(
@PathVariable Long id,
@RequestBody @Valid AdminUpdatePointShopRequest updatePointShopRequest
) {
pointShopAdminService.update(updatePointShopRequest, id);
return NoContent.ok(POINT_ITEM_UPDATE_SUCCESS);
}

@DeleteMapping("/delete/{id}")
public NoContent delete(@PathVariable Long id) {
pointShopAdminService.delete(id);
return NoContent.ok(POINT_ITEM_DELETE_SUCCESS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.example.green.domain.pointshop.admin.controller.docs;

import com.example.green.domain.pointshop.admin.dto.request.AdminCreatePointShopRequest;
import com.example.green.domain.pointshop.admin.dto.request.AdminUpdatePointShopRequest;
import com.example.green.global.api.ApiTemplate;
import com.example.green.global.api.NoContent;
import com.example.green.global.error.dto.ExceptionResponse;

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.tags.Tag;

@Tag(name = "포인트 상점 상품 / 아이템 어드민 API", description = "포인트 상점 상품 / 아이템 어드민 API 모음 입니다.")
public interface PointShopAdminControllerDocs {

@Operation(summary = "포인트 아이템 / 상품 생성 (관리자)", description = "포인트 아이템 / 상품 을 생성합니다.")
@ApiResponse(responseCode = "200", description = "포인트 아이템 / 상품 생성에 성공했습니다.")
@ApiResponse(
responseCode = "400", description = "포인트 아이템 / 상품 생성 시 잘못된 정보 기입하면 발생",
content = @Content(schema = @Schema(implementation = ExceptionResponse.class))
)
ApiTemplate<Long> create(AdminCreatePointShopRequest createPointShopRequest);

@Operation(summary = "포인트 상점 아이템 / 상품 수정(관리자)", description = "포인트 상점 아이템 / 상품 정보를 수정합니다.")
@ApiResponse(responseCode = "200", description = "포인트 상점 아이템 / 상품 수정에 성공했습니다")
@ApiResponse(
responseCode = "400", description = "중복된 코드 또는 잘못된 데이터",
content = @Content(schema = @Schema(implementation = ExceptionResponse.class))
)
@ApiResponse(
responseCode = "404", description = "아이템 / 상품 을 찾을 수 없습니다.",
content = @Content(schema = @Schema(implementation = ExceptionResponse.class))
)
NoContent update(Long id, AdminUpdatePointShopRequest updatePointShopRequest);

@Operation(summary = "포인트 상점 아이템 / 상품 삭제(관리자)", description = "포인트 상점 아이템 / 상품 을 삭제합니다.")
@ApiResponse(responseCode = "200", description = "아이템 / 상품 삭제에 성공했습니다")
@ApiResponse(
responseCode = "404", description = "아이템 / 상품 을 찾을 수 없습니다.",
content = @Content(schema = @Schema(implementation = ExceptionResponse.class))
)
NoContent delete(Long id);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.green.domain.pointshop.admin.controller.message;

import com.example.green.global.api.ResponseMessage;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Getter
public enum PointShopAdminResponseMessage implements ResponseMessage {

POINT_ITEM_CREATION_SUCCESS("포인트 상점 아이템 / 상품 생성에 성공했습니다."),
POINT_ITEM_UPDATE_SUCCESS("포인트 상점 아이템 / 상품 수정에 성공했습니다."),
POINT_ITEM_DELETE_SUCCESS("아이템 / 상품수정 삭제에 성공했습니다.");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix typo in delete success message.

The message contains "상품수정 삭제" where "수정" (modification) appears incorrectly. It should be "상품 삭제" (product deletion).

Apply this diff to correct the message:

 	POINT_ITEM_CREATION_SUCCESS("포인트 상점 아이템 / 상품 생성에 성공했습니다."),
 	POINT_ITEM_UPDATE_SUCCESS("포인트 상점 아이템 / 상품 수정에 성공했습니다."),
-	POINT_ITEM_DELETE_SUCCESS("아이템 / 상품수정 삭제에 성공했습니다.");
+	POINT_ITEM_DELETE_SUCCESS("아이템 / 상품 삭제에 성공했습니다.");
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
POINT_ITEM_DELETE_SUCCESS("아이템 / 상품수정 삭제에 성공했습니다.");
POINT_ITEM_CREATION_SUCCESS("포인트 상점 아이템 / 상품 생성에 성공했습니다."),
POINT_ITEM_UPDATE_SUCCESS("포인트 상점 아이템 / 상품 수정에 성공했습니다."),
POINT_ITEM_DELETE_SUCCESS("아이템 / 상품 삭제에 성공했습니다.");
🤖 Prompt for AI Agents
In
src/main/java/com/example/green/domain/pointshop/admin/controller/message/PointShopAdminResponseMessage.java
around line 14, the enum constant message contains a typo "아이템 / 상품수정 삭제에
성공했습니다." — replace "상품수정" with "상품" so the message reads "아이템 / 상품 삭제에 성공했습니다."
and save the file.


private final String message;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.example.green.domain.pointshop.admin.dto.request;

import java.math.BigDecimal;

import com.example.green.domain.pointshop.admin.service.PointShopType;

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

@Schema(description = "상품 생성 요청")
@Builder
public record AdminCreatePointShopRequest(
PointShopType type,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add @NotNull validation to the type field.

The type field is essential for determining whether to create an item or a product, but it lacks validation. Without @NotNull, the field could be null, causing routing failures in the service layer.

Apply this diff to add validation:

 public record AdminCreatePointShopRequest(
+	@NotNull(message = "상품 타입은 필수입니다.")
 	PointShopType type,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
PointShopType type,
@NotNull(message = "상품 타입은 필수입니다.")
PointShopType type,
🤖 Prompt for AI Agents
In
src/main/java/com/example/green/domain/pointshop/admin/dto/request/AdminCreatePointShopRequest.java
around line 16, the PointShopType type field lacks validation; add the
javax.validation constraint @NotNull to the type field declaration (and import
javax.validation.constraints.NotNull if missing) so the DTO enforces non-null
input and prevents null routing to the service layer.


@NotBlank(message = "상품 코드는 비어 있을 수 없습니다.")
@Schema(description = "상품 코드", example = "ITM-AA-001")
String code,

@NotBlank(message = "상품명은 비어있을 수 없습니다.")
@Size(min = 2, max = 15, message = "상품명은 2글자 ~ 15글자 사이입니다.")
@Schema(description = "상품명", example = "맑은 뭉게 구름")
String name,

@NotNull(message = "상품 설명은 필수입니다.")
@Size(max = 100, message = "상품 설명은 최대 100글자입니다.")
@Schema(description = "상품 설명", example = "하늘에서 포근한 구름이 내려와 식물을 감싸요. 몽글몽글 기분 좋은 하루!")
String description,

@NotBlank(message = "상품 썸네일 이미지는 비어있을 수 없습니다.")
@Schema(description = "상품 썸네일", example = "https://example.com/image.png")
String thumbnailUrl,

@NotNull(message = "상품 가격은 필수입니다.")
@Schema(description = "상품 가격", example = "10000")
BigDecimal price,

@Schema(description = "상품 재고", example = "10")
Integer stock
) {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.example.green.domain.pointshop.admin.dto.request;

import java.math.BigDecimal;

import com.example.green.domain.pointshop.admin.service.PointShopType;

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

@Schema(description = "상품 수정 요청")
@Builder
public record AdminUpdatePointShopRequest(
PointShopType type,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add @NotNull validation to the type field.

The type field is essential for determining whether the request updates an item or a product, but it lacks validation. Without @NotNull, the field could be null, leading to potential NullPointerException in the service layer.

Apply this diff to add validation:

 public record AdminUpdatePointShopRequest(
+	@NotNull(message = "상품 타입은 필수입니다.")
 	PointShopType type,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
PointShopType type,
@NotNull(message = "상품 타입은 필수입니다.")
PointShopType type,
🤖 Prompt for AI Agents
In
src/main/java/com/example/green/domain/pointshop/admin/dto/request/AdminUpdatePointShopRequest.java
around line 16, the PointShopType type field lacks validation; add the
javax.validation constraint @NotNull to the type field declaration and import
the annotation so the field is validated on request binding, ensuring null
values are rejected before reaching the service layer.


@NotBlank(message = "상품 코드는 비어 있을 수 없습니다.")
@Schema(description = "상품 코드", example = "ITM-AA-001")
String code,

@NotBlank(message = "상품명은 비어있을 수 없습니다.")
@Size(min = 2, max = 15, message = "상품명은 2글자 ~ 15글자 사이입니다.")
@Schema(description = "상품명", example = "맑은 뭉게 구름")
String name,

@NotNull(message = "상품 설명은 필수입니다.")
@Size(max = 100, message = "상품 설명은 최대 100글자입니다.")
@Schema(description = "상품 설명", example = "하늘에서 포근한 구름이 내려와 식물을 감싸요. 몽글몽글 기분 좋은 하루!")
String description,

@NotBlank(message = "상품 썸네일 이미지는 비어있을 수 없습니다.")
@Schema(description = "상품 썸네일", example = "https://example.com/image.png")
String thumbnailUrl,

@NotNull(message = "상품 가격은 필수입니다.")
@Schema(description = "상품 가격", example = "10000")
BigDecimal price,

@Schema(description = "상품 재고", example = "10")
Integer stock
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.example.green.domain.pointshop.admin.exception;

import com.example.green.global.error.exception.BusinessException;
import com.example.green.global.error.exception.ExceptionMessage;

public class PointShopAdminException extends BusinessException {

public PointShopAdminException(ExceptionMessage message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.example.green.domain.pointshop.admin.exception;

import static org.springframework.http.HttpStatus.*;

import org.springframework.http.HttpStatus;

import com.example.green.global.error.exception.ExceptionMessage;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Getter
public enum PointShopAdminExceptionMessage implements ExceptionMessage {

NOT_FOUND_CATEGORY(NOT_FOUND, "카테고리를 찾을 수가 없습니다"),
NOT_FOUND_PRODUCT(NOT_FOUND, "등록된 상품 / 아이템을 찾을 수 없습니다");

private final HttpStatus httpStatus;
private final String message;

}
Comment on lines +12 to +22

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Polish error message Korean phrasing for consistency

"카테고리를 찾을 수가 없습니다" (Line 16) reads unnatural; usually "카테고리를 찾을 수 없습니다". Same suggestion if other enums standardize on that phrasing.

-	NOT_FOUND_CATEGORY(NOT_FOUND, "카테고리를 찾을 수가 없습니다"),
+	NOT_FOUND_CATEGORY(NOT_FOUND, "카테고리를 찾을 수 없습니다"),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@RequiredArgsConstructor
@Getter
public enum PointShopAdminExceptionMessage implements ExceptionMessage {
NOT_FOUND_CATEGORY(NOT_FOUND, "카테고리를 찾을 수가 없습니다"),
NOT_FOUND_PRODUCT(NOT_FOUND, "등록된 상품 / 아이템을 찾을 수 없습니다");
private final HttpStatus httpStatus;
private final String message;
}
@RequiredArgsConstructor
@Getter
public enum PointShopAdminExceptionMessage implements ExceptionMessage {
NOT_FOUND_CATEGORY(NOT_FOUND, "카테고리를 찾을 없습니다"),
NOT_FOUND_PRODUCT(NOT_FOUND, "등록된 상품 / 아이템을 찾을 수 없습니다");
private final HttpStatus httpStatus;
private final String message;
}
🤖 Prompt for AI Agents
In
src/main/java/com/example/green/domain/pointshop/admin/exception/PointShopAdminExceptionMessage.java
around lines 12 to 22, the Korean message for NOT_FOUND_CATEGORY uses the less
natural phrasing "카테고리를 찾을 수가 없습니다"; update it to the more natural "카테고리를 찾을 수
없습니다" and ensure other enum entries follow the same phrasing convention for
consistency (adjust strings accordingly).

Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package com.example.green.domain.pointshop.admin.service;

import java.util.Optional;

import org.springframework.stereotype.Service;

import com.example.green.domain.pointshop.admin.dto.request.AdminCreatePointShopRequest;
import com.example.green.domain.pointshop.admin.dto.request.AdminUpdatePointShopRequest;
import com.example.green.domain.pointshop.admin.exception.PointShopAdminException;
import com.example.green.domain.pointshop.admin.exception.PointShopAdminExceptionMessage;
import com.example.green.domain.pointshop.item.entity.PointItem;
import com.example.green.domain.pointshop.item.entity.vo.Category;
import com.example.green.domain.pointshop.item.entity.vo.ItemBasicInfo;
import com.example.green.domain.pointshop.item.entity.vo.ItemCode;
import com.example.green.domain.pointshop.item.entity.vo.ItemMedia;
import com.example.green.domain.pointshop.item.entity.vo.ItemPrice;
import com.example.green.domain.pointshop.item.repository.PointItemRepository;
import com.example.green.domain.pointshop.item.service.PointItemService;
import com.example.green.domain.pointshop.item.service.command.PointItemCreateCommand;
import com.example.green.domain.pointshop.item.service.command.PointItemUpdateCommand;
import com.example.green.domain.pointshop.product.entity.PointProduct;
import com.example.green.domain.pointshop.product.entity.vo.BasicInfo;
import com.example.green.domain.pointshop.product.entity.vo.Code;
import com.example.green.domain.pointshop.product.entity.vo.Media;
import com.example.green.domain.pointshop.product.entity.vo.Price;
import com.example.green.domain.pointshop.product.entity.vo.Stock;
import com.example.green.domain.pointshop.product.repository.PointProductRepository;
import com.example.green.domain.pointshop.product.service.PointProductService;
import com.example.green.domain.pointshop.product.service.command.PointProductCreateCommand;
import com.example.green.domain.pointshop.product.service.command.PointProductUpdateCommand;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class PointShopAdminService {
Comment on lines +34 to +36

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Missing @Transactional annotation.

Both PointItemService and PointProductService use @Transactional at the class level. For consistency and to ensure atomicity of operations (especially for update and delete which perform lookups before delegating), add @Transactional to this service.

+import org.springframework.transaction.annotation.Transactional;
+
 @Service
 @RequiredArgsConstructor
+@Transactional
 public class PointShopAdminService {
🤖 Prompt for AI Agents
In
src/main/java/com/example/green/domain/pointshop/admin/service/PointShopAdminService.java
around lines 34 to 36, the class is missing the @Transactional annotation which
the other services use; add @Transactional at the class level (import
org.springframework.transaction.annotation.Transactional) so all public methods
run within a transaction for atomicity, matching PointItemService and
PointProductService; no method-level changes required unless specific
readOnly/propagation behavior is needed.


private final PointItemService pointItemService;
private final PointProductService pointProductService;
private final PointItemRepository pointItemRepository;
private final PointProductRepository pointProductRepository;

//생성
public Long create(AdminCreatePointShopRequest request) {

if (request.type() == PointShopType.ITEM) {
PointItemCreateCommand command = new PointItemCreateCommand(
new ItemCode(request.code()),
new ItemBasicInfo(request.name(), request.description()),
new ItemMedia(request.thumbnailUrl()),
new ItemPrice(request.price()),
Category.ITEM
);
return pointItemService.create(command);
}

if (request.type() == PointShopType.PRODUCT) {
PointProductCreateCommand command = new PointProductCreateCommand(
new Code(request.code()),
new BasicInfo(request.name(), request.description()),
new Media(request.thumbnailUrl()),
new Price(request.price()),
new Stock(request.stock())
);
return pointProductService.create(command);
}

throw new PointShopAdminException(PointShopAdminExceptionMessage.NOT_FOUND_CATEGORY);
}

//수정
public void update(AdminUpdatePointShopRequest request, Long productId) {
//item 먼저 찾기
Optional<PointItem> pointItem = pointItemRepository.findById(productId);
if (pointItem.isPresent()) {
PointItemUpdateCommand command = new PointItemUpdateCommand(
new ItemCode(request.code()),
new ItemBasicInfo(request.name(), request.description()),
new ItemMedia(request.thumbnailUrl()),
new ItemPrice(request.price()),
Category.ITEM
);
pointItemService.updatePointItem(command, productId);
return;
}
//product 찾기
Optional<PointProduct> pointProduct = pointProductRepository.findById(productId);
if (pointProduct.isPresent()) {
PointProductUpdateCommand command = new PointProductUpdateCommand(
new Code(request.code()),
new BasicInfo(request.name(), request.description()),
new Media(request.thumbnailUrl()),
new Price(request.price()),
new Stock(request.stock()),
Category.PRODUCT
);
pointProductService.update(command, productId);
return;
}
throw new PointShopAdminException(PointShopAdminExceptionMessage.NOT_FOUND_PRODUCT);
Comment on lines +72 to +100

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

ID collision risk: probing both repositories by ID is unreliable.

PointItem and PointProduct likely have separate auto-increment ID sequences. If both tables have an entity with ID=5, this method will always update the PointItem and never reach the PointProduct.

Unlike create() which uses request.type() to determine the target, update() guesses by probing repositories. Consider requiring the caller to specify PointShopType in the request and use it to route directly to the correct service, mirroring the create() approach.

-public void update(AdminUpdatePointShopRequest request, Long productId) {
-    Optional<PointItem> pointItem = pointItemRepository.findById(productId);
-    if (pointItem.isPresent()) {
+public void update(AdminUpdatePointShopRequest request, Long productId) {
+    if (request.type() == PointShopType.ITEM) {
         PointItemUpdateCommand command = new PointItemUpdateCommand(
             ...
         );
         pointItemService.updatePointItem(command, productId);
         return;
     }
-    Optional<PointProduct> pointProduct = pointProductRepository.findById(productId);
-    if (pointProduct.isPresent()) {
+    if (request.type() == PointShopType.PRODUCT) {
         PointProductUpdateCommand command = new PointProductUpdateCommand(
             ...
         );
         pointProductService.update(command, productId);
         return;
     }
-    throw new PointShopAdminException(PointShopAdminExceptionMessage.NOT_FOUND_PRODUCT);
+    throw new PointShopAdminException(PointShopAdminExceptionMessage.NOT_FOUND_CATEGORY);
 }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In
src/main/java/com/example/green/domain/pointshop/admin/service/PointShopAdminService.java
around lines 72 to 100, the update method currently probes both
pointItemRepository and pointProductRepository by ID which can misroute updates
when IDs overlap; change the API and implementation to accept a PointShopType
(or reuse request.type()) so the caller explicitly indicates whether the target
is ITEM or PRODUCT, then route directly to pointItemService.updatePointItem(...)
or pointProductService.update(...) based on that type (validate the type, map
request fields to the correct command objects, and throw NOT_FOUND_PRODUCT only
if the specified type’s repository lookup fails).

}

//삭제
public void delete(Long productId) {
Optional<PointItem> pointItem = pointItemRepository.findById(productId);
if (pointItem.isPresent()) {
pointItemService.delete(productId);
return;
}
Optional<PointProduct> pointProduct = pointProductRepository.findById(productId);
if (pointProduct.isPresent()) {
pointProductService.delete(productId);
return;
}
throw new PointShopAdminException(PointShopAdminExceptionMessage.NOT_FOUND_PRODUCT);
}
Comment on lines +104 to +116

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Same ID collision issue as update(); also inefficient existence check.

This method has the same problem: if both tables have an entity with the same ID, the PointItem is always deleted. Consider adding a PointShopType parameter to explicitly route to the correct service.

Additionally, findById fetches the entire entity just to check existence. Use existsById for efficiency if you retain the probing approach:

 public void delete(Long productId) {
-    Optional<PointItem> pointItem = pointItemRepository.findById(productId);
-    if (pointItem.isPresent()) {
+    if (pointItemRepository.existsById(productId)) {
         pointItemService.delete(productId);
         return;
     }
-    Optional<PointProduct> pointProduct = pointProductRepository.findById(productId);
-    if (pointProduct.isPresent()) {
+    if (pointProductRepository.existsById(productId)) {
         pointProductService.delete(productId);
         return;
     }
     throw new PointShopAdminException(PointShopAdminExceptionMessage.NOT_FOUND_PRODUCT);
 }

However, the preferred fix is to require a type discriminator like create() does.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.example.green.domain.pointshop.admin.service;

public enum PointShopType {
ITEM,
PRODUCT
}
Loading