diff --git a/pom.xml b/pom.xml index c590c1d..8dda94c 100644 --- a/pom.xml +++ b/pom.xml @@ -44,6 +44,17 @@ spring-boot-starter-web + + org.springframework.boot + spring-boot-starter-data-redis + + + + org.apache.rocketmq + rocketmq-spring-boot-starter + 2.3.4 + + org.json json diff --git a/src/main/java/com/ljc42/product/Controller/ProductController.java b/src/main/java/com/ljc42/product/Controller/ProductController.java index 6aef2c6..627eb21 100644 --- a/src/main/java/com/ljc42/product/Controller/ProductController.java +++ b/src/main/java/com/ljc42/product/Controller/ProductController.java @@ -1,8 +1,9 @@ package com.ljc42.product.Controller; +import com.ljc42.product.DTO.ProductDTO; +import com.ljc42.product.DTO.ResponseDTO; import com.ljc42.product.Exceptions.ProductExitsException; import com.ljc42.product.Exceptions.ProductNotFoundException; -import com.ljc42.product.Model.Product; import com.ljc42.product.Service.ProductService; import org.json.JSONObject; import org.springframework.http.HttpStatus; @@ -22,40 +23,34 @@ public class ProductController { } @GetMapping("/api/v1/product/{name}") - public ResponseEntity> getProducts(@PathVariable String name) { + public ResponseEntity getProducts(@PathVariable String name) { try { - Product product = this.productService.getProductByName(name); - JSONObject jsonProduct = new JSONObject(); - jsonProduct.put("name", product.getName()); - jsonProduct.put("price", product.getPrice()); - jsonProduct.put("stock", product.getStock()); - JSONObject responseBody = new JSONObject(); - responseBody.put("code", 200); - responseBody.put("product", jsonProduct); - responseBody.put("message", "Product retrieved"); - return new ResponseEntity<>(responseBody.toMap(), HttpStatus.OK); + ProductDTO productDTO = this.productService.getProductByName(name); + return new ResponseEntity<>(new ResponseDTO(200,"Product retrieved", productDTO), HttpStatus.OK); } catch (ProductNotFoundException e) { - JSONObject responseBody = new JSONObject(); - responseBody.put("code", 404); - responseBody.put("message", e.getMessage()); - return new ResponseEntity<>(responseBody.toMap(), HttpStatus.NOT_FOUND); + return new ResponseEntity<>(new ResponseDTO(404,e.getMessage()), HttpStatus.NOT_FOUND); } } @PostMapping("/api/v1/product/create") - public ResponseEntity> createProducts(@RequestBody Product product) { + public ResponseEntity createProducts(@RequestBody ProductDTO productDTO) { try { - this.productService.createProduct(product); - JSONObject responseBody = new JSONObject(); - responseBody.put("code", 201); - responseBody.put("name", product.getName()); - responseBody.put("message", "Product created"); - return new ResponseEntity<>(responseBody.toMap(),HttpStatus.CREATED); + ProductDTO createdDTO = this.productService.createProduct(productDTO); + return new ResponseEntity<>(new ResponseDTO(201,"Product created",createdDTO),HttpStatus.CREATED); } catch (ProductExitsException e) { - JSONObject responseBody = new JSONObject(); - responseBody.put("code", 409); - responseBody.put("message", e.getMessage()); - return new ResponseEntity<>(responseBody.toMap(),HttpStatus.CONFLICT); + return new ResponseEntity<>(new ResponseDTO(409,e.getMessage()),HttpStatus.CONFLICT); } } + + @PutMapping("/api/v1/product/update") + public ResponseEntity updateProducts(@RequestBody ProductDTO productDTO){ + try { + ProductDTO updatedDTO = this.productService.updateProduct(productDTO); + return new ResponseEntity<>(new ResponseDTO(200,"Product updated",updatedDTO),HttpStatus.OK); + } catch (ProductNotFoundException e) { + return new ResponseEntity<>(new ResponseDTO(404,e.getMessage()),HttpStatus.NOT_FOUND); + } + + } + } diff --git a/src/main/java/com/ljc42/product/DTO/DTO.java b/src/main/java/com/ljc42/product/DTO/DTO.java new file mode 100644 index 0000000..7ce8c3a --- /dev/null +++ b/src/main/java/com/ljc42/product/DTO/DTO.java @@ -0,0 +1,4 @@ +package com.ljc42.product.DTO; + +public class DTO { +} diff --git a/src/main/java/com/ljc42/product/DTO/ProductDTO.java b/src/main/java/com/ljc42/product/DTO/ProductDTO.java new file mode 100644 index 0000000..7851b72 --- /dev/null +++ b/src/main/java/com/ljc42/product/DTO/ProductDTO.java @@ -0,0 +1,40 @@ +package com.ljc42.product.DTO; + +public class ProductDTO extends DTO{ + private String name; + private Double price; + private Integer stock; + + public ProductDTO() { + } + + public ProductDTO(String name, Double price, Integer stock) { + this.name = name; + this.price = price; + this.stock = stock; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public Integer getStock() { + return stock; + } + + public void setStock(Integer stock) { + this.stock = stock; + } +} diff --git a/src/main/java/com/ljc42/product/DTO/ResponseDTO.java b/src/main/java/com/ljc42/product/DTO/ResponseDTO.java new file mode 100644 index 0000000..604e4f3 --- /dev/null +++ b/src/main/java/com/ljc42/product/DTO/ResponseDTO.java @@ -0,0 +1,36 @@ +package com.ljc42.product.DTO; + +public class ResponseDTO extends DTO{ + private int code; + private String message; + private DTO data; + public ResponseDTO(int code, String message, DTO data) { + this.code = code; + this.message = message; + this.data = data; + } + public ResponseDTO(int code, String message) { + this.code = code; + this.message = message; + this.data = null; + } + public int getCode() { + return code; + } + public String getMessage() { + return message; + } + public Object getData() { + return data; + } + + public void setCode(int code) { + this.code = code; + } + public void setMessage(String message) { + this.message = message; + } + public void setData(DTO data) { + this.data = data; + } +} diff --git a/src/main/java/com/ljc42/product/Listener.java b/src/main/java/com/ljc42/product/Listener.java new file mode 100644 index 0000000..3786196 --- /dev/null +++ b/src/main/java/com/ljc42/product/Listener.java @@ -0,0 +1,17 @@ +package com.ljc42.product; + +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Service; + +@Service +@RocketMQMessageListener( + topic = "PRODUCT_CREATE_TOPIC", + consumerGroup = "PRODUCT_CREATE_GROUP" +) +public class Listener implements RocketMQListener { + @Override + public void onMessage(String s) { + System.out.println("downstream service is creating index for product: " + s); + } +} diff --git a/src/main/java/com/ljc42/product/Mapper/ProductMapper.java b/src/main/java/com/ljc42/product/Mapper/ProductMapper.java new file mode 100644 index 0000000..48a52cb --- /dev/null +++ b/src/main/java/com/ljc42/product/Mapper/ProductMapper.java @@ -0,0 +1,22 @@ +package com.ljc42.product.Mapper; + +import com.ljc42.product.DTO.ProductDTO; +import com.ljc42.product.Model.Product; + +public class ProductMapper { + public static ProductDTO ProductToDTO(Product product) { + ProductDTO productDTO = new ProductDTO(); + productDTO.setName(product.getName()); + productDTO.setPrice(product.getPrice()); + productDTO.setStock(product.getStock()); + return productDTO; + } + + public static Product DTOtoProduct(ProductDTO productDTO) { + Product product = new Product(); + product.setName(productDTO.getName()); + product.setPrice(productDTO.getPrice()); + product.setStock(productDTO.getStock()); + return product; + } +} diff --git a/src/main/java/com/ljc42/product/Model/Product.java b/src/main/java/com/ljc42/product/Model/Product.java index 6b7daef..f9dbe75 100644 --- a/src/main/java/com/ljc42/product/Model/Product.java +++ b/src/main/java/com/ljc42/product/Model/Product.java @@ -4,19 +4,21 @@ import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import java.io.Serializable; + @Entity -public class Product { +public class Product implements Serializable { private @Id @GeneratedValue Long id; private String name; - private double price; - private int stock; + private Double price; + private Integer stock; public Product() { } - public Product(String name, double price, int stock) { + public Product(String name, Double price, Integer stock) { this.name = name; this.price = price; this.stock = stock; @@ -30,19 +32,19 @@ public class Product { this.name = name; } - public double getPrice() { + public Double getPrice() { return price; } - public void setPrice(double price) { + public void setPrice(Double price) { this.price = price; } - public int getStock() { + public Integer getStock() { return stock; } - public void setStock(int stock) { + public void setStock(Integer stock) { this.stock = stock; } } diff --git a/src/main/java/com/ljc42/product/RedisConfig.java b/src/main/java/com/ljc42/product/RedisConfig.java new file mode 100644 index 0000000..da3c85e --- /dev/null +++ b/src/main/java/com/ljc42/product/RedisConfig.java @@ -0,0 +1,23 @@ +package com.ljc42.product; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; + +@Configuration +public class RedisConfig { + @Bean + LettuceConnectionFactory connectionFactory() { + return new LettuceConnectionFactory(); + } + + @Bean + RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) { + + RedisTemplate template = new RedisTemplate<>(); + template.setConnectionFactory(connectionFactory); + return template; + } +} diff --git a/src/main/java/com/ljc42/product/RocketMQConfig.java b/src/main/java/com/ljc42/product/RocketMQConfig.java new file mode 100644 index 0000000..c082fde --- /dev/null +++ b/src/main/java/com/ljc42/product/RocketMQConfig.java @@ -0,0 +1,9 @@ +package com.ljc42.product; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class RocketMQConfig { + +} diff --git a/src/main/java/com/ljc42/product/Service/ProductService.java b/src/main/java/com/ljc42/product/Service/ProductService.java index b556f79..39e275f 100644 --- a/src/main/java/com/ljc42/product/Service/ProductService.java +++ b/src/main/java/com/ljc42/product/Service/ProductService.java @@ -1,31 +1,72 @@ package com.ljc42.product.Service; +import com.ljc42.product.DTO.ProductDTO; import com.ljc42.product.Exceptions.ProductNotFoundException; +import com.ljc42.product.Mapper.ProductMapper; import com.ljc42.product.Model.Product; import com.ljc42.product.Repository.ProductRepository; import com.ljc42.product.Exceptions.ProductExitsException; +import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Service; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + @Service public class ProductService { @Autowired private ProductRepository productRepository; - public void createProduct(Product product) throws ProductExitsException{ - if(productRepository.existsByName(product.getName())) { + @Autowired + private RedisTemplate redisTemplate; + + @Autowired + private RocketMQTemplate rocketMQTemplate; + + public ProductDTO createProduct(ProductDTO productDTO) throws ProductExitsException{ + if(productRepository.existsByName(productDTO.getName())) { throw new ProductExitsException("Product with the same name already exists"); } else { - this.productRepository.save(product); + redisTemplate.delete(productDTO.getName()); + this.productRepository.save(ProductMapper.DTOtoProduct(productDTO)); + rocketMQTemplate.getProducer().setSendMsgTimeout(10000); + rocketMQTemplate.convertAndSend("PRODUCT_CREATE_TOPIC", productDTO.getName()); + return productDTO; } } - public Product getProductByName(String name) throws ProductNotFoundException { + public ProductDTO getProductByName(String name) throws ProductNotFoundException { + if(redisTemplate.hasKey(name)) { + return ProductMapper.ProductToDTO((Product) Objects.requireNonNull(redisTemplate.opsForValue().get(name))); + } if(productRepository.existsByName(name)) { - return productRepository.findByName(name); + Product product = productRepository.findByName(name); + redisTemplate.opsForValue().set(name, product); + redisTemplate.expire(name,5, TimeUnit.MINUTES); + return ProductMapper.ProductToDTO(product); } else { throw new ProductNotFoundException("Product not found"); } } + + public ProductDTO updateProduct(ProductDTO productDTO) throws ProductNotFoundException { + if(productRepository.existsByName(productDTO.getName())) { + redisTemplate.delete(productDTO.getName()); + Product product = productRepository.findByName(productDTO.getName()); + if(productDTO.getPrice() != null) { + product.setPrice(productDTO.getPrice()); + } + if(productDTO.getStock() != null) { + product.setStock(productDTO.getStock()); + } + productRepository.save(product); + return ProductMapper.ProductToDTO(product); + } else { + throw new ProductNotFoundException("Product not found"); + } + + } } diff --git a/src/main/java/com/ljc42/product/DemoApplication.java b/src/main/resources/DemoApplication.java similarity index 100% rename from src/main/java/com/ljc42/product/DemoApplication.java rename to src/main/resources/DemoApplication.java diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a02a06a..8e5aacb 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,4 +4,10 @@ spring.datasource.url=jdbc:h2:mem:app spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa spring.datasource.password=password -spring.jpa.database-platform=org.hibernate.dialect.H2Dialect \ No newline at end of file +spring.jpa.database-platform=org.hibernate.dialect.H2Dialect + +spring.redis.host=localhost +spring.redis.port=6379 + +rocketmq.name-server=localhost:9876 +rocketmq.producer.group=my_producer_group \ No newline at end of file