Skip to content

Commit

Permalink
Merge pull request #45 from SSapingMall/product-crud
Browse files Browse the repository at this point in the history
Product crud
  • Loading branch information
wintiger98 authored Apr 24, 2024
2 parents 071158b + 8d6a8e2 commit c6ab786
Show file tree
Hide file tree
Showing 17 changed files with 586 additions and 24 deletions.
3 changes: 0 additions & 3 deletions spring-boot-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,6 @@ dependencies {
implementation 'org.mapstruct:mapstruct:1.4.2.Final'
annotationProcessor 'org.mapstruct:mapstruct-processor:1.4.2.Final'

// for domain -> dto auto change
implementation 'org.modelmapper:modelmapper:2.3.8'

}

tasks.named('test') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaAuditing;

// 날짜 관련 속성 자동 채우기를 위한 어노테이션
@EnableJpaAuditing
@SpringBootApplication
public class SpringBootApiApplication {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.ssafy.springbootapi.domain.product.api;

import java.util.List;

import com.ssafy.springbootapi.domain.product.application.ProductService;
import com.ssafy.springbootapi.domain.product.dto.ProductInput;
import com.ssafy.springbootapi.domain.product.dto.ProductListOutput;
import com.ssafy.springbootapi.domain.product.dto.ProductOutput;
import com.ssafy.springbootapi.domain.product.dto.ProductUpdate;
import com.ssafy.springbootapi.global.aop.annotation.ToException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@Tag(name = "Product", description = "Product 관련 API 입니다.")
@RestController
@RequiredArgsConstructor
//@CrossOrigin // spring security 디펜던시 삭제로 인한 주석처리
@RequestMapping("/api/v1/products")
public class ProductController {
private final ProductService productService;

@Operation(summary = "모든 제품 조회")
@GetMapping("")
public ResponseEntity<List<ProductListOutput>> getAllProducts() {
List<ProductListOutput> products = productService.getAllProducts();
return ResponseEntity.status(HttpStatus.OK).body(products);
}

@Operation(summary = "제품 ID로 조회")
@GetMapping("/{id}")
public ResponseEntity<ProductOutput> getProductById(@PathVariable Long id) {
ProductOutput product = productService.getProductById(id);
return ResponseEntity.status(HttpStatus.OK).body(product);
}

@Operation(summary = "새 제품 추가")
@PostMapping("")
public ResponseEntity<ProductOutput> createProduct(@RequestBody ProductInput productInput) {
// 서비스 레이어를 통해 비즈니스 로직 처리
ProductOutput newProduct = productService.insertProduct(productInput);

// 생성된 Product 객체 반환
return ResponseEntity.status(HttpStatus.CREATED).body(newProduct);
}

@Operation(summary = "제품 정보 업데이트")
@PatchMapping("/{id}")
public ResponseEntity<ProductOutput> updateProduct(@PathVariable Long id, @RequestBody ProductUpdate productDetails) {
// 서비스 레이어를 통해 비즈니스 로직 처리
ProductOutput updatedProduct = productService.updateProduct(id, productDetails);

// 업데이트된 Product 객체 반환
return ResponseEntity.ok().body(updatedProduct);
}
@Operation(summary = "제품 삭제")
@DeleteMapping("/{id}")
public ResponseEntity<ProductOutput> deleteProduct(@PathVariable Long id) {
ProductOutput removedProduct = productService.removeProduct(id);
return ResponseEntity.ok().body(removedProduct);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.ssafy.springbootapi.domain.product.application;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import com.ssafy.springbootapi.domain.product.dao.ProductRepository;
import com.ssafy.springbootapi.domain.product.domain.Product;
import com.ssafy.springbootapi.domain.product.domain.ProductMapper;
import com.ssafy.springbootapi.domain.product.dto.ProductInput;
import com.ssafy.springbootapi.domain.product.dto.ProductListOutput;
import com.ssafy.springbootapi.domain.product.dto.ProductOutput;
import com.ssafy.springbootapi.domain.product.dto.ProductUpdate;
import com.ssafy.springbootapi.domain.product.exception.NotFoundProductException;
import com.ssafy.springbootapi.domain.user.dao.UserRepository;
import com.ssafy.springbootapi.domain.user.domain.User;
import com.ssafy.springbootapi.domain.user.exception.UserNotFoundException;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;

@Service
@RequiredArgsConstructor
public class ProductService {
private final ProductRepository productRepository;
private final UserRepository userRepository;
private final ProductMapper productMapper;


/**
* Product 전체 조회
* @return List(ProductListOutput)
*/
public List<ProductListOutput> getAllProducts() {
List<Product> products = productRepository.findAll();
return products.stream()
.map(productMapper::toProductListOutput)
.collect(Collectors.toList());
}

/**
* Product ID로 조회
* @param id : 조회할 상품 id
* @return ProductOutput(DTO)
*/
public ProductOutput getProductById(Long id) {
Product product = findProductByIdOrThrow(id);
return productMapper.toProductOutput(product);
}

/**
* Product 삽입 메서드
* @param productInput : Product 생성 데이터
* @return ProductOutput(DTO)
*/
public ProductOutput insertProduct(ProductInput productInput) {
// user_id로 User 객체 찾기
Optional<User> userOptional = userRepository.findById(productInput.getUser_id());
User user = userOptional.orElseThrow(() ->
new UserNotFoundException("User not found with id: " + productInput.getUser_id())
);

// ProductInput에서 Product 엔티티로 변환
Product product = productMapper.toEntity(productInput);

// User 객체를 Product 엔티티에 설정
product.setUser(user);

// Product 엔티티를 DB에 저장하고, 저장된 엔티티를 ProductOutput DTO로 변환하여 반환
return productMapper.toProductOutput(productRepository.save(product));
}


/**
* Product 삭제 메서드
* @param id : 삭제할 product id
* @return ProductOutput(DTO)
*/
public ProductOutput removeProduct(Long id) {
Product toRemove = findProductByIdOrThrow(id);
productRepository.delete(toRemove);
return productMapper.toProductOutput(toRemove);
}

/**
* Product 수정 메서드
* @param id : 수정할 product id
* @param productUpdate : 수정할 product 데이터
* @return ProductOutput(DTO)
*/
@Transactional
public ProductOutput updateProduct(Long id, ProductUpdate productUpdate) {
Product productToUpdate = findProductByIdOrThrow(id);
productMapper.updateProduct(productUpdate, productToUpdate);
return productMapper.toProductOutput(productRepository.save(productToUpdate));
}

/**
* id로 product 찾기 또는 없을 경우 Exception 발생
* @param id : product id
* @return Product(Entity)
*/
private Product findProductByIdOrThrow(Long id) {
return productRepository.findById(id)
.orElseThrow(() -> new NotFoundProductException("Product not found with id: " + id));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.ssafy.springbootapi.domain.product.dao;

import com.ssafy.springbootapi.domain.product.domain.Product;
import org.springframework.data.jpa.repository.JpaRepository;

public interface ProductRepository extends JpaRepository<Product, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package com.ssafy.springbootapi.domain.product.domain;


import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.ssafy.springbootapi.domain.user.domain.User;
import jakarta.persistence.*;
import lombok.*;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Entity
@EntityListeners(AuditingEntityListener.class)
@JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
@Table(name = "product")
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Builder
public class Product {
@Id
@GeneratedValue
@Column(name = "id", unique = true)
private Long id;

@Column(name = "image_url")
private String imageUrl;

@CreatedDate
@Column(name = "created_at")
private LocalDateTime createdAt;

@LastModifiedDate
@Column(name = "updated_at")
private LocalDateTime updatedAt;

@Column(name = "name", nullable = false)
private String name;

@Column(name = "price", nullable = false)
private int price;

@Column(name = "description", nullable = false)
private String description;

@Column(nullable = false)
private int category;

@Column(nullable = false)
private int stock;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
// @Column(name = "user_id")
// private int userId;
// test code를 위해 추가
public void updateInfo(int category, int stock, String imageUrl) {
this.category = category;
this.stock = stock;
this.imageUrl = imageUrl;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.ssafy.springbootapi.domain.product.domain;

import com.ssafy.springbootapi.domain.product.dto.ProductInput;
import com.ssafy.springbootapi.domain.product.dto.ProductListOutput;
import com.ssafy.springbootapi.domain.product.dto.ProductOutput;
import com.ssafy.springbootapi.domain.product.dto.ProductUpdate;
import org.mapstruct.BeanMapping;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import org.mapstruct.NullValuePropertyMappingStrategy;

@Mapper(componentModel = "spring")
public interface ProductMapper {
@BeanMapping(nullValuePropertyMappingStrategy = NullValuePropertyMappingStrategy.IGNORE)
void updateProduct(ProductUpdate dto, @MappingTarget Product entity);

Product toEntity(ProductUpdate dto);
Product toEntity(ProductInput dto);

ProductOutput toProductOutput(Product entity);
ProductListOutput toProductListOutput(Product entity);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.ssafy.springbootapi.domain.product.dto;

import lombok.*;
import lombok.experimental.SuperBuilder;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@SuperBuilder
public class ProductBase {
private String imageUrl;
private String name;
private int price;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.ssafy.springbootapi.domain.product.dto;

import jakarta.validation.constraints.NotBlank;
import lombok.*;
import lombok.experimental.SuperBuilder;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@SuperBuilder
public class ProductInput extends ProductBase {
private String description;
private int category;
private int stock;
@NotBlank(message = "user id is required")
private Long user_id;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.ssafy.springbootapi.domain.product.dto;

import lombok.*;
import lombok.experimental.SuperBuilder;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@SuperBuilder
public class ProductListOutput extends ProductBase{
private Long id;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.ssafy.springbootapi.domain.product.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

@NoArgsConstructor
@AllArgsConstructor
@Getter
@SuperBuilder
public class ProductOutput extends ProductListOutput{
private String description;
private int category;
private int stock;
// private int user_id;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.ssafy.springbootapi.domain.product.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.experimental.SuperBuilder;

@AllArgsConstructor
@NoArgsConstructor
@Getter
@SuperBuilder
public class ProductUpdate extends ProductBase {
private String description;
private int category;
private int stock;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.ssafy.springbootapi.domain.product.exception;

public class NotFoundProductException extends RuntimeException{
public NotFoundProductException(String message) {
super(message);
}
}
Loading

0 comments on commit c6ab786

Please sign in to comment.