diff --git a/src/main/generated/kw/zeropick/correction/domain/QCorrection.java b/src/main/generated/kw/zeropick/correction/domain/QCorrection.java new file mode 100644 index 0000000..0553f45 --- /dev/null +++ b/src/main/generated/kw/zeropick/correction/domain/QCorrection.java @@ -0,0 +1,70 @@ +package kw.zeropick.correction.domain; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; +import com.querydsl.core.types.dsl.PathInits; + + +/** + * QCorrection is a Querydsl query type for Correction + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QCorrection extends EntityPathBase { + + private static final long serialVersionUID = 1655451227L; + + private static final PathInits INITS = PathInits.DIRECT2; + + public static final QCorrection correction = new QCorrection("correction"); + + public final kw.zeropick.common.domain.QBaseEntity _super = new kw.zeropick.common.domain.QBaseEntity(this); + + public final StringPath additional = createString("additional"); + + public final EnumPath correctionStatus = createEnum("correctionStatus", CorrectionStatus.class); + + //inherited + public final DateTimePath createdAt = _super.createdAt; + + public final NumberPath id = createNumber("id", Long.class); + + public final ListPath imageUrls = this.createList("imageUrls", String.class, StringPath.class, PathInits.DIRECT2); + + public final kw.zeropick.member.domain.QMember member; + + public final kw.zeropick.product.domain.QProduct product; + + public final StringPath rejectionReason = createString("rejectionReason"); + + //inherited + public final DateTimePath updatedAt = _super.updatedAt; + + public QCorrection(String variable) { + this(Correction.class, forVariable(variable), INITS); + } + + public QCorrection(Path path) { + this(path.getType(), path.getMetadata(), PathInits.getFor(path.getMetadata(), INITS)); + } + + public QCorrection(PathMetadata metadata) { + this(metadata, PathInits.getFor(metadata, INITS)); + } + + public QCorrection(PathMetadata metadata, PathInits inits) { + this(Correction.class, metadata, inits); + } + + public QCorrection(Class type, PathMetadata metadata, PathInits inits) { + super(type, metadata, inits); + this.member = inits.isInitialized("member") ? new kw.zeropick.member.domain.QMember(forProperty("member")) : null; + this.product = inits.isInitialized("product") ? new kw.zeropick.product.domain.QProduct(forProperty("product"), inits.get("product")) : null; + } + +} + diff --git a/src/main/java/kw/zeropick/correction/controller/CorrectionController.java b/src/main/java/kw/zeropick/correction/controller/CorrectionController.java new file mode 100644 index 0000000..4fb972a --- /dev/null +++ b/src/main/java/kw/zeropick/correction/controller/CorrectionController.java @@ -0,0 +1,90 @@ +package kw.zeropick.correction.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import java.util.List; +import kw.zeropick.common.LoginUser; +import kw.zeropick.correction.dto.request.CorrectionRequest; +import kw.zeropick.correction.dto.response.CorrectionResponse; +import kw.zeropick.correction.service.CorrectionService; +import kw.zeropick.member.domain.Member; +import kw.zeropick.member.service.MemberService; +import kw.zeropick.payload.ApiResponse; +import kw.zeropick.registration.controller.response.RegistrationResponse; +import kw.zeropick.registration.dto.RegistrationRequestDto; +import kw.zeropick.registration.service.RegistrationService; +import lombok.RequiredArgsConstructor; +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.RequestMapping; +import org.springframework.web.bind.annotation.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +@Tag(name = "correction", description = "상품 수정 요청 API") +@RestController +@RequestMapping("/correction") +@RequiredArgsConstructor +public class CorrectionController { + private final CorrectionService correctionService; + private final MemberService memberService; + + @PostMapping + @Operation(summary = "제품 수정 요청 생성") + public ResponseEntity correctionProduct( + @RequestPart(value = "correction") CorrectionRequest request, + @RequestPart(value = "files", required = false) List files) { + + Long memberId = 1L; + + CorrectionResponse response = correctionService.correctionProduct(memberId, request.getProductId(), request, files); + + return ResponseEntity.ok( + ApiResponse.builder() + .check(true) + .information(response) + .build() + ); + } + + @GetMapping + @Operation(summary = "제품 수정 요청 조회") + public ResponseEntity getAllCorrections() { + Long memberId = 1L; + Member requestMember = memberService.getById(memberId); + List correctionResponses = correctionService.getAllCorrections(requestMember); + + return ResponseEntity.ok( + ApiResponse.builder() + .check(true) + .information(correctionResponses) + .build() + ); + } + + @DeleteMapping("/{correctionId}") + @Operation(summary = "제품 수정 요청 철회") + public ResponseEntity productBookmarkUndo(@PathVariable Long correctionId) { + Long memberId = LoginUser.get().getId(); + try { + correctionService.deleteCorrection(correctionId, memberId); + return ResponseEntity.ok( + ApiResponse.builder() + .check(true) + .information("Product correction deleted successfully") + .build() + ); + } catch (Exception e) { + return ResponseEntity.internalServerError().body( + ApiResponse.builder() + .check(false) + .information(e.getMessage()) + .build() + ); + } + } + +} diff --git a/src/main/java/kw/zeropick/correction/domain/Correction.java b/src/main/java/kw/zeropick/correction/domain/Correction.java new file mode 100644 index 0000000..4520fa7 --- /dev/null +++ b/src/main/java/kw/zeropick/correction/domain/Correction.java @@ -0,0 +1,50 @@ +package kw.zeropick.correction.domain; + +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import java.util.ArrayList; +import java.util.List; +import kw.zeropick.common.converter.StringListToStringConverter; +import kw.zeropick.common.domain.BaseEntity; +import kw.zeropick.member.domain.Member; +import kw.zeropick.product.domain.Product; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Correction extends BaseEntity { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "correction_id") + private Long id; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + private Member member; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "product_id", nullable = false) + private Product product; + + @Convert(converter = StringListToStringConverter.class) + private List imageUrls = new ArrayList<>(); + + private String additional; + + private CorrectionStatus correctionStatus; // 등록 요청 처리 상태 + + private String rejectionReason; // 반려 사유 +} diff --git a/src/main/java/kw/zeropick/correction/domain/CorrectionStatus.java b/src/main/java/kw/zeropick/correction/domain/CorrectionStatus.java new file mode 100644 index 0000000..35bb2c7 --- /dev/null +++ b/src/main/java/kw/zeropick/correction/domain/CorrectionStatus.java @@ -0,0 +1,7 @@ +package kw.zeropick.correction.domain; + +public enum CorrectionStatus { + PENDING, // 대기중 + REJECTED, // 반려 + COMPLETED // 수정 완료 +} diff --git a/src/main/java/kw/zeropick/correction/dto/request/CorrectionRequest.java b/src/main/java/kw/zeropick/correction/dto/request/CorrectionRequest.java new file mode 100644 index 0000000..e03a9a9 --- /dev/null +++ b/src/main/java/kw/zeropick/correction/dto/request/CorrectionRequest.java @@ -0,0 +1,11 @@ +package kw.zeropick.correction.dto.request; + +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class CorrectionRequest { + private Long productId; + private String additional; +} diff --git a/src/main/java/kw/zeropick/correction/dto/response/CorrectionResponse.java b/src/main/java/kw/zeropick/correction/dto/response/CorrectionResponse.java new file mode 100644 index 0000000..00b7b5a --- /dev/null +++ b/src/main/java/kw/zeropick/correction/dto/response/CorrectionResponse.java @@ -0,0 +1,42 @@ +package kw.zeropick.correction.dto.response; + + +import jakarta.persistence.Convert; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import kw.zeropick.common.converter.StringListToStringConverter; +import kw.zeropick.correction.domain.CorrectionStatus; +import kw.zeropick.member.domain.Member; +import kw.zeropick.product.domain.Product; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class CorrectionResponse { + private Long id; + + private Long memberId; + + private Long productId; + + private String productName; + + private String brand; + + private String productImageUrl; + + private List imageUrls; + + private String additional; + + private CorrectionStatus correctionStatus; // 등록 요청 처리 상태 + + private String rejectionReason; // 반려 사유 + + private LocalDateTime createdAt; +} diff --git a/src/main/java/kw/zeropick/correction/repository/CorrectionRepository.java b/src/main/java/kw/zeropick/correction/repository/CorrectionRepository.java new file mode 100644 index 0000000..32d7e92 --- /dev/null +++ b/src/main/java/kw/zeropick/correction/repository/CorrectionRepository.java @@ -0,0 +1,11 @@ +package kw.zeropick.correction.repository; + +import java.util.List; +import kw.zeropick.correction.domain.Correction; +import kw.zeropick.correction.dto.response.CorrectionResponse; +import kw.zeropick.member.domain.Member; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface CorrectionRepository extends JpaRepository { + List findByMember(Member member); +} diff --git a/src/main/java/kw/zeropick/correction/service/CorrectionService.java b/src/main/java/kw/zeropick/correction/service/CorrectionService.java new file mode 100644 index 0000000..6f20ddf --- /dev/null +++ b/src/main/java/kw/zeropick/correction/service/CorrectionService.java @@ -0,0 +1,14 @@ +package kw.zeropick.correction.service; + +import java.util.List; +import kw.zeropick.correction.dto.request.CorrectionRequest; +import kw.zeropick.correction.dto.response.CorrectionResponse; +import kw.zeropick.member.domain.Member; +import kw.zeropick.product.domain.Product; +import org.springframework.web.multipart.MultipartFile; + +public interface CorrectionService { + public CorrectionResponse correctionProduct(Long memberId, Long productId, CorrectionRequest request, List files); + public List getAllCorrections(Member member); + public void deleteCorrection(Long correctionId, Long memberId); +} diff --git a/src/main/java/kw/zeropick/correction/service/CorrectionServiceImpl.java b/src/main/java/kw/zeropick/correction/service/CorrectionServiceImpl.java new file mode 100644 index 0000000..a6e4831 --- /dev/null +++ b/src/main/java/kw/zeropick/correction/service/CorrectionServiceImpl.java @@ -0,0 +1,107 @@ +package kw.zeropick.correction.service; + +import jakarta.persistence.EntityNotFoundException; +import java.util.ArrayList; +import java.util.List; +import kw.zeropick.correction.domain.Correction; +import kw.zeropick.correction.domain.CorrectionStatus; +import kw.zeropick.correction.dto.request.CorrectionRequest; +import kw.zeropick.correction.dto.response.CorrectionResponse; +import kw.zeropick.correction.repository.CorrectionRepository; +import kw.zeropick.member.domain.Member; +import kw.zeropick.member.service.MemberService; +import kw.zeropick.product.service.ProductService; +import kw.zeropick.util.S3Util; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.multipart.MultipartFile; + +@Service +@Transactional(readOnly = true) +@RequiredArgsConstructor +public class CorrectionServiceImpl implements CorrectionService { + private final CorrectionRepository correctionRepository; + private final MemberService memberService; + private final ProductService productService; + private final S3Util s3Util; + + @Override + @Transactional + public CorrectionResponse correctionProduct(Long memberId, Long productId, CorrectionRequest request, List files) { + List images = new ArrayList<>(); + + // 이미지 파일 처리 및 S3 업로드 + if (files != null && !files.isEmpty()) { + for (MultipartFile file : files) { + if (file != null && !file.isEmpty()) { + String imageUrl = s3Util.upload(file); + System.out.println(imageUrl); + images.add(imageUrl); + } + } + } + + Correction correction = Correction.builder() + .member(memberService.getById(memberId)) + .product(productService.getById(productId)) + .correctionStatus(CorrectionStatus.PENDING) + .additional(request.getAdditional()) + .imageUrls(images) + .build(); + + Correction saved = correctionRepository.save(correction); + + return CorrectionResponse.builder() + .id(saved.getId()) + .memberId(saved.getMember().getId()) + .productId(saved.getProduct().getId()) + .productName(saved.getProduct().getProductName()) + .brand(saved.getProduct().getBrand()) + .productImageUrl(saved.getProduct().getImageUrl()) + .imageUrls(saved.getImageUrls()) + .additional(saved.getAdditional()) + .correctionStatus(saved.getCorrectionStatus()) + .rejectionReason(saved.getRejectionReason()) + .createdAt(saved.getCreatedAt()) + .build(); + } + + @Override + public List getAllCorrections(Member member) { + List corrections = correctionRepository.findByMember(member); + + List correctionResponses = new ArrayList<>(); + for (Correction correction : corrections) { + CorrectionResponse response = CorrectionResponse.builder() + .id(correction.getId()) + .memberId(correction.getMember().getId()) + .productId(correction.getProduct().getId()) + .productName(correction.getProduct().getProductName()) + .brand(correction.getProduct().getBrand()) + .productImageUrl(correction.getProduct().getImageUrl()) + .imageUrls(correction.getImageUrls()) + .additional(correction.getAdditional()) + .correctionStatus(correction.getCorrectionStatus()) + .rejectionReason(correction.getRejectionReason()) + .createdAt(correction.getCreatedAt()) + .build(); + correctionResponses.add(response); + } + + return correctionResponses; + } + + @Override + @Transactional + public void deleteCorrection(Long correctionId, Long memberId) { + Correction correction = correctionRepository.findById(correctionId) + .orElseThrow(() -> new EntityNotFoundException("해당 id에 맞는 수정 요청 없음 id: " + correctionId)); + + if(!correction.getMember().getId().equals(memberId)) { + throw new EntityNotFoundException("같은 사용자의 수정 요청만 삭제 가능합니다."); + } + + correctionRepository.delete(correction); + } +} diff --git a/src/main/java/kw/zeropick/product/service/ProductService.java b/src/main/java/kw/zeropick/product/service/ProductService.java index fd8b500..ffedd76 100644 --- a/src/main/java/kw/zeropick/product/service/ProductService.java +++ b/src/main/java/kw/zeropick/product/service/ProductService.java @@ -21,4 +21,6 @@ public interface ProductService { boolean isBookmarkedByUser(Long productId, Long memberId); boolean isComparedByUser(Long productId, Long memberId); List findTopProductsByPopularity(int limit); + + public Product getById(Long productId); } diff --git a/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java b/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java index 5b6bfa4..5b46219 100644 --- a/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java +++ b/src/main/java/kw/zeropick/product/service/ProductServiceImpl.java @@ -3,6 +3,7 @@ import jakarta.persistence.EntityNotFoundException; import java.util.ArrayList; import java.util.List; +import kw.zeropick.common.domain.exception.ResourceNotFoundException; import kw.zeropick.member.domain.Member; import kw.zeropick.member.repository.MemberJpaRepository; import kw.zeropick.product.domain.Bookmark; @@ -296,4 +297,10 @@ public List findTopProductsByPopularity(int limit) { .toList(); } + @Override + public Product getById(Long productId) { + return productJpaRepository.findById(productId) + .orElseThrow(() -> new ResourceNotFoundException("Product", productId)); + } + }