From 540128da865f9faead9f03a0e2fc1541cbca36ce Mon Sep 17 00:00:00 2001 From: keke125 Date: Fri, 22 Dec 2023 14:17:17 +0800 Subject: [PATCH 1/5] refactor: remove unused test code. --- .../spring/NtouAuctionJavaApplicationTests.java | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 src/test/java/ntou/auction/spring/NtouAuctionJavaApplicationTests.java diff --git a/src/test/java/ntou/auction/spring/NtouAuctionJavaApplicationTests.java b/src/test/java/ntou/auction/spring/NtouAuctionJavaApplicationTests.java deleted file mode 100644 index d6b285b..0000000 --- a/src/test/java/ntou/auction/spring/NtouAuctionJavaApplicationTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package ntou.auction.spring; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class NtouAuctionJavaApplicationTests { - - @Test - void contextLoads() { - } - -} From e69626cb162943d06e9d3daca6f41145129dfa93 Mon Sep 17 00:00:00 2001 From: keke125 Date: Fri, 22 Dec 2023 15:35:46 +0800 Subject: [PATCH 2/5] feat: Introduce Email Notification functionality. --- .../auction/spring/mail/EmailService.java | 129 +++++++++++++++++- .../order/controller/OrderController.java | 17 ++- .../spring/order/service/OrderService.java | 7 +- .../product/controller/ProductController.java | 97 +++++++------ .../spring/product/service/TimerTask.java | 22 +-- 5 files changed, 203 insertions(+), 69 deletions(-) diff --git a/src/main/java/ntou/auction/spring/mail/EmailService.java b/src/main/java/ntou/auction/spring/mail/EmailService.java index a807948..afa1752 100644 --- a/src/main/java/ntou/auction/spring/mail/EmailService.java +++ b/src/main/java/ntou/auction/spring/mail/EmailService.java @@ -2,10 +2,11 @@ import jakarta.mail.Message; import jakarta.mail.internet.InternetAddress; -import ntou.auction.spring.util.AppConfig; -import ntou.auction.spring.product.entity.Product; import ntou.auction.spring.account.entity.User; import ntou.auction.spring.account.service.UserService; +import ntou.auction.spring.order.entity.Order; +import ntou.auction.spring.product.entity.Product; +import ntou.auction.spring.util.AppConfig; import org.springframework.mail.MailException; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.MimeMessagePreparator; @@ -25,7 +26,7 @@ public EmailService(JavaMailSender mailSender, UserService userService, AppConfi this.appConfig = appConfig; } - public void sendMailBid(Long userId, Product product) { + public void sendMailBidSuccess(Long userId, Product product) { if (userService.get(userId).isEmpty()) { System.err.println("找不到ID為 " + userId + " 的使用者,無法寄出得標成功通知"); @@ -56,4 +57,126 @@ public void sendMailBid(Long userId, Product product) { } } + /* + public void sendMailBidFailed(Long userId, Product product) { + + if (userService.get(userId).isEmpty()) { + System.err.println("找不到ID為 " + userId + " 的使用者,無法寄出商品下架通知"); + return; + } + User customer = userService.get(userId).get(); + + MimeMessagePreparator preparator = mimeMessage -> { + mimeMessage.setSubject("[NTOU Auction] 商品下架通知", "UTF-8"); + mimeMessage.setRecipient(Message.RecipientType.TO, + new InternetAddress(customer.getEmail())); + mimeMessage.setFrom(new InternetAddress(appConfig.getMailUsername())); + mimeMessage.setText("親愛的 " + customer.getName() + + " (@" + customer.getUsername() + ") 您好:" + "\n" + + "您之前參加競標的 " + product.getProductName() + " 商品" + + "目前已由賣家下架,造成您的不便還請見諒。" + "\n\n" + + "感謝您使用 NTOU Auction,歡迎選購其他商品!" + "\n\n" + + "此為系統自動發送之郵件,請勿回覆!", "UTF-8" + + ); + mimeMessage.setSentDate(new Date()); + }; + + try { + this.mailSender.send(preparator); + } catch (MailException ex) { + System.err.println(ex.getMessage()); + } + } + */ + public void sendMailOrderEstablished(Long userId, Order order) { + + if (userService.get(userId).isEmpty() || userService.get(order.getSellerid()).isEmpty()) { + System.err.println("找不到ID為 " + userId + " 的使用者,或查無賣家,無法寄出訂單成立通知"); + return; + } + User customer = userService.get(userId).get(); + User seller = userService.get(order.getSellerid()).get(); + + MimeMessagePreparator buyerPreparator = mimeMessage -> { + mimeMessage.setSubject("[NTOU Auction] 訂單成立通知", "UTF-8"); + mimeMessage.setRecipient(Message.RecipientType.TO, + new InternetAddress(customer.getEmail())); + mimeMessage.setFrom(new InternetAddress(appConfig.getMailUsername())); + mimeMessage.setText("親愛的 " + customer.getName() + + " (@" + customer.getUsername() + ") 您好:" + "\n" + + "您已成功購買賣家為 @" + seller.getUsername() + " 的商品," + + "您這次購買了 " + order.getProductAddAmountList().size() + " 個品項的商品" + "\n" + + "目前訂單狀態為等待賣家確認,訂單詳細資訊請上NTOU Auction確認。" + "\n\n" + + "感謝您使用 NTOU Auction,祝您購物愉快!" + "\n\n" + + "此為系統自動發送之郵件,請勿回覆!", "UTF-8" + + ); + mimeMessage.setSentDate(new Date()); + }; + + MimeMessagePreparator sellerPreparator = mimeMessage -> { + mimeMessage.setSubject("[NTOU Auction] 訂單成立通知", "UTF-8"); + mimeMessage.setRecipient(Message.RecipientType.TO, + new InternetAddress(seller.getEmail())); + mimeMessage.setFrom(new InternetAddress(appConfig.getMailUsername())); + mimeMessage.setText("親愛的 " + seller.getName() + + " (@" + seller.getUsername() + ") 您好:" + "\n" + + "買家 @" + customer.getUsername() + " 已下訂您的商品," + + "目前訂單狀態為等待確認,請您盡快上NTOU Auction更新訂單狀態。" + "\n\n" + + "感謝您使用 NTOU Auction,祝您交易愉快!" + "\n\n" + + "此為系統自動發送之郵件,請勿回覆!", "UTF-8" + + ); + mimeMessage.setSentDate(new Date()); + }; + + try { + this.mailSender.send(buyerPreparator); + this.mailSender.send(sellerPreparator); + } catch (MailException ex) { + System.err.println(ex.getMessage()); + } + } + + public void sendMailOrderUpdate(Long userId, Order order) { + + if (userService.get(userId).isEmpty() || userService.get(order.getSellerid()).isEmpty()) { + System.err.println("找不到ID為 " + userId + " 的使用者,或查無賣家,無法寄出訂單狀態更新通知"); + return; + } + User customer = userService.get(userId).get(); + User seller = userService.get(order.getSellerid()).get(); + String status; + if (order.getStatus() == 0L) { + status = "賣家拒絕您的訂單"; + } else if (order.getStatus() == 2L) { + status = "賣家同意您的訂單"; + } else { + status = "未知"; + } + + MimeMessagePreparator buyerPreparator = mimeMessage -> { + mimeMessage.setSubject("[NTOU Auction] 訂單狀態更新通知", "UTF-8"); + mimeMessage.setRecipient(Message.RecipientType.TO, + new InternetAddress(customer.getEmail())); + mimeMessage.setFrom(new InternetAddress(appConfig.getMailUsername())); + mimeMessage.setText("親愛的 " + customer.getName() + + " (@" + customer.getUsername() + ") 您好:" + "\n" + + "您之前購買賣家為 @" + seller.getUsername() + " 的商品," + + "目前訂單狀態為 " + status + " ,訂單詳細資訊請上NTOU Auction確認。" + "\n\n" + + "感謝您使用 NTOU Auction,祝您購物愉快!" + "\n\n" + + "此為系統自動發送之郵件,請勿回覆!", "UTF-8" + + ); + mimeMessage.setSentDate(new Date()); + }; + + try { + this.mailSender.send(buyerPreparator); + } catch (MailException ex) { + System.err.println(ex.getMessage()); + } + } + } diff --git a/src/main/java/ntou/auction/spring/order/controller/OrderController.java b/src/main/java/ntou/auction/spring/order/controller/OrderController.java index 06f051f..afe469c 100644 --- a/src/main/java/ntou/auction/spring/order/controller/OrderController.java +++ b/src/main/java/ntou/auction/spring/order/controller/OrderController.java @@ -3,6 +3,7 @@ import jakarta.validation.Valid; import ntou.auction.spring.account.response.UserIdentity; import ntou.auction.spring.account.service.UserService; +import ntou.auction.spring.mail.EmailService; import ntou.auction.spring.order.entity.Order; import ntou.auction.spring.order.request.AddOrderRequest; import ntou.auction.spring.order.request.OperateOrderRequest; @@ -28,7 +29,7 @@ public class OrderController { private final ShoppingcartService shoppingcartService; private final UserService userService; - + private final EmailService emailService; private final UserIdentity userIdentity; private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); @@ -49,11 +50,12 @@ public class OrderController { private static final Map selfBuyingError = Collections.singletonMap("message", "不可以購買自己的商品"); - public OrderController(OrderService orderService, ProductService productService, ShoppingcartService shoppingcartService, UserService userService, UserIdentity userIdentity) { + public OrderController(OrderService orderService, ProductService productService, ShoppingcartService shoppingcartService, UserService userService, EmailService emailService, UserIdentity userIdentity) { this.orderService = orderService; this.productService = productService; this.shoppingcartService = shoppingcartService; this.userService = userService; + this.emailService = emailService; this.userIdentity = userIdentity; } @@ -137,8 +139,8 @@ ResponseEntity> addOrder(@Valid @RequestBody AddOrderRequest // checkInShoppingCart -> -1: format error, 0: false, 1: true Long checkInShoppingCart = shoppingcartService.checkIsProductAllInShoppingCart(getrequest, userId); - if(checkInShoppingCart.equals(-1L)) return ResponseEntity.badRequest().body(formatError); - if(checkInShoppingCart.equals(0L)) return ResponseEntity.badRequest().body(notFoundInShoppingCartError); + if (checkInShoppingCart.equals(-1L)) return ResponseEntity.badRequest().body(formatError); + if (checkInShoppingCart.equals(0L)) return ResponseEntity.badRequest().body(notFoundInShoppingCartError); for (List eachProductAddAmount : getrequest) { Long productId = eachProductAddAmount.get(0); @@ -153,11 +155,11 @@ ResponseEntity> addOrder(@Valid @RequestBody AddOrderRequest // Same seller boolean checkSameSeller = orderService.checkIsSameSeller(getrequest); - if(!checkSameSeller) return ResponseEntity.badRequest().body(tooManySellerMessage); + if (!checkSameSeller) return ResponseEntity.badRequest().body(tooManySellerMessage); // Self buying boolean checkSelfBuying = shoppingcartService.checkIsViolateSelfBuying(getrequest, userId); - if(checkSelfBuying) return ResponseEntity.badRequest().body(selfBuyingError); + if (checkSelfBuying) return ResponseEntity.badRequest().body(selfBuyingError); // order status -> 0: reject, 1: waiting for submit, 2: submitted but not paid, 3: order done Order order = new Order(); @@ -186,6 +188,7 @@ ResponseEntity> addOrder(@Valid @RequestBody AddOrderRequest shoppingcartService.decreaseProductByUserId(userId, productId, amount); } orderService.addOrder(order); + emailService.sendMailOrderEstablished(order.getBuyerid(),order); return ResponseEntity.ok(successMessage); } @@ -244,7 +247,7 @@ ResponseEntity> makeCancel(@Valid @RequestBody OperateOrderR if (result.equals(-1L)) return ResponseEntity.badRequest().body(expiredError); Order thisOrder = orderService.findOrderById(orderId); boolean check = orderService.addAmountToProduct(thisOrder); - if(!check) return ResponseEntity.badRequest().body(orderNotFound); // this may not be happened + if (!check) return ResponseEntity.badRequest().body(orderNotFound); // this may not be happened return ResponseEntity.ok(successMessage); } } diff --git a/src/main/java/ntou/auction/spring/order/service/OrderService.java b/src/main/java/ntou/auction/spring/order/service/OrderService.java index a459b86..0584e4a 100644 --- a/src/main/java/ntou/auction/spring/order/service/OrderService.java +++ b/src/main/java/ntou/auction/spring/order/service/OrderService.java @@ -1,5 +1,6 @@ package ntou.auction.spring.order.service; +import ntou.auction.spring.mail.EmailService; import ntou.auction.spring.order.entity.Order; import ntou.auction.spring.order.response.OrderWithProductDetail; import ntou.auction.spring.order.repository.OrderRepository; @@ -20,13 +21,15 @@ public class OrderService { private final ProductService productService; private final ShoppingcartService shoppingcartService; + private final EmailService emailService; private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - public OrderService(OrderRepository repository, ProductService productService, ShoppingcartService shoppingcartService) { + public OrderService(OrderRepository repository, ProductService productService, ShoppingcartService shoppingcartService, EmailService emailService) { this.repository = repository; this.productService = productService; this.shoppingcartService = shoppingcartService; + this.emailService = emailService; } public Order findOrderById(Long Id) { return repository.findById(Id).orElse(null); @@ -68,6 +71,7 @@ public Long submitOrder(Long orderId, Long userId) { if(!Objects.equals(findOrderById(orderId).getSellerid(), userId)) return 2L; getorder.setStatus(2L); repository.save(getorder); + emailService.sendMailOrderUpdate(getorder.getBuyerid(),getorder); return 3L; } @@ -80,6 +84,7 @@ public Long rejectOrder(Long orderId, Long userId) { if(!Objects.equals(findOrderById(orderId).getSellerid(), userId)) return 2L; getorder.setStatus(0L); repository.save(getorder); + emailService.sendMailOrderUpdate(getorder.getBuyerid(),getorder); return 3L; } diff --git a/src/main/java/ntou/auction/spring/product/controller/ProductController.java b/src/main/java/ntou/auction/spring/product/controller/ProductController.java index 7b93a31..b79fe13 100644 --- a/src/main/java/ntou/auction/spring/product/controller/ProductController.java +++ b/src/main/java/ntou/auction/spring/product/controller/ProductController.java @@ -28,7 +28,6 @@ public class ProductController { private final UserService userService; private final ShoppingcartService shoppingcartService; - public ProductController(ProductService productService, UserIdentity userIdentity, UserService userService, ShoppingcartService shoppingcartService) { this.productService = productService; this.userIdentity = userIdentity; @@ -39,13 +38,13 @@ public ProductController(ProductService productService, UserIdentity userIdentit @GetMapping("/product/name/{name}") @ResponseBody - public ListgetProductName(@PathVariable String name ) { - return productService.findByProductName(name); + public List getProductName(@PathVariable String name) { + return productService.findByProductName(name); } @GetMapping("/product/classification/{classification}") @ResponseBody - public ListgetProductClassification(@PathVariable String classification) { + public List getProductClassification(@PathVariable String classification) { return productService.findByProductClassification(classification); } @@ -62,9 +61,9 @@ Product getProduct(@PathVariable long ID) { } @PostMapping("/fixedproduct") - ResponseEntity> postProduct(@Valid @RequestBody PostFixedPriceProductRequest request){ //productrequest的限制 + ResponseEntity> postProduct(@Valid @RequestBody PostFixedPriceProductRequest request) { //productrequest的限制 - Map successMessage = Collections.singletonMap("message","成功上架"); + Map successMessage = Collections.singletonMap("message", "成功上架"); Product product = new Product(); @@ -91,10 +90,10 @@ ResponseEntity> postProduct(@Valid @RequestBody PostFixedPric } @PostMapping("/nonfixedproduct") - ResponseEntity> postProduct(@Valid @RequestBody PostNonFixedPriceProductRequest request){ //productrequest的限制 + ResponseEntity> postProduct(@Valid @RequestBody PostNonFixedPriceProductRequest request) { //productrequest的限制 - Map successMessage = Collections.singletonMap("message","成功上架"); - Map fail = Collections.singletonMap("message","截止時間錯誤"); + Map successMessage = Collections.singletonMap("message", "成功上架"); + Map fail = Collections.singletonMap("message", "截止時間錯誤"); Product product = new Product(); @@ -119,7 +118,7 @@ ResponseEntity> postProduct(@Valid @RequestBody PostNonFixedP DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime dateTime = LocalDateTime.parse(request.getFinishTime(), formatter); - if(!now.isBefore(dateTime)){ + if (!now.isBefore(dateTime)) { return ResponseEntity.badRequest().body(fail); } product.setFinishTime(dateTime); @@ -128,45 +127,45 @@ ResponseEntity> postProduct(@Valid @RequestBody PostNonFixedP return ResponseEntity.ok(successMessage); } - @PatchMapping("/bid") //商品ID 出價。出價也需傳入token - ResponseEntity> bidProduct(@Valid @RequestBody BidRequest request){ + @PatchMapping("/bid") + //商品ID 出價。出價也需傳入token + ResponseEntity> bidProduct(@Valid @RequestBody BidRequest request) { - Map successMessage = Collections.singletonMap("message","成功出價"); - Map failMessage = Collections.singletonMap("message","出價不合理,出價需比當前最高價高" + productService.getID(request.getProductID()).getBidIncrement()); - Map expired = Collections.singletonMap("message","競標已結束"); + Map successMessage = Collections.singletonMap("message", "成功出價"); + Map failMessage = Collections.singletonMap("message", "出價不合理,出價需比當前最高價高" + productService.getID(request.getProductID()).getBidIncrement()); + Map expired = Collections.singletonMap("message", "競標已結束"); LocalDateTime now = LocalDateTime.now(); - if(!now.isBefore(productService.getID(request.getProductID()).getFinishTime())){ + if (!now.isBefore(productService.getID(request.getProductID()).getFinishTime())) { return ResponseEntity.badRequest().body(expired); } - if(!productService.isBidReasonable(request.getBid(), request.getProductID())) { + if (!productService.isBidReasonable(request.getBid(), request.getProductID())) { return ResponseEntity.badRequest().body(failMessage); } System.out.println(userIdentity.getUsername()); - productService.bid(request.getBid(), request.getProductID(),userService.findByUsername(userIdentity.getUsername()).getId()); + productService.bid(request.getBid(), request.getProductID(), userService.findByUsername(userIdentity.getUsername()).getId()); return ResponseEntity.ok(successMessage); } - @PostMapping("/buy") - ResponseEntity> buyProduct(@Valid @RequestBody BuyProductRequest request){ + ResponseEntity> buyProduct(@Valid @RequestBody BuyProductRequest request) { - Map successMessage = Collections.singletonMap("message","成功加入購物車"); - Map notEnoughMessage = Collections.singletonMap("message","商品剩餘數量不足"); - Map errorMessage = Collections.singletonMap("message","只能將不二價商品加入購物車"); + Map successMessage = Collections.singletonMap("message", "成功加入購物車"); + Map notEnoughMessage = Collections.singletonMap("message", "商品剩餘數量不足"); + Map errorMessage = Collections.singletonMap("message", "只能將不二價商品加入購物車"); Map productNotExistMessage = Collections.singletonMap("message", "商品不存在或無法購買"); // 商品是否存在 - if( productService.getID(request.getProductID())==null){ + if (productService.getID(request.getProductID()) == null) { return ResponseEntity.badRequest().body(productNotExistMessage); } // 購物車是空的 // 只檢查request送來的加入數量 - if (shoppingcartService.getByUserId(userService.findByUsername(userIdentity.getUsername()).getId())==null){ + if (shoppingcartService.getByUserId(userService.findByUsername(userIdentity.getUsername()).getId()) == null) { if (request.getProductAmount() > productService.getID(request.getProductID()).getProductAmount()) { return ResponseEntity.badRequest().body(notEnoughMessage); } else { @@ -176,7 +175,7 @@ ResponseEntity> buyProduct(@Valid @RequestBody BuyProductRequ } // 購物車裡面還沒有要加入的商品 // 只檢查request送來的加入數量 - if(shoppingcartService.getByUserId(userService.findByUsername(userIdentity.getUsername()).getId()).getProductItems().get(request.getProductID())==null){ + if (shoppingcartService.getByUserId(userService.findByUsername(userIdentity.getUsername()).getId()).getProductItems().get(request.getProductID()) == null) { if (request.getProductAmount() > productService.getID(request.getProductID()).getProductAmount()) { return ResponseEntity.badRequest().body(notEnoughMessage); } else { @@ -190,7 +189,7 @@ ResponseEntity> buyProduct(@Valid @RequestBody BuyProductRequ if (request.getProductAmount() + shoppingcartService.getByUserId(userService.findByUsername(userIdentity.getUsername()).getId()).getProductItems().get(request.getProductID()) > productService.getID(request.getProductID()).getProductAmount()) { //要買的數量 > 商品剩餘數量 return ResponseEntity.badRequest().body(notEnoughMessage); } - if(!productService.getID(request.getProductID()).getIsFixedPrice()){ + if (!productService.getID(request.getProductID()).getIsFixedPrice()) { return ResponseEntity.badRequest().body(errorMessage); } //public void addProductByUserId(Long userId, Long productId, Long amount) { @@ -205,13 +204,13 @@ List getProductInSellerCenter() { } @DeleteMapping("/{ID}") - ResponseEntity> deleteProduct(@PathVariable long ID){ - Map successMessage = Collections.singletonMap("message","成功刪除"); - Map failMessage = Collections.singletonMap("message","刪錯商品嚕"); + ResponseEntity> deleteProduct(@PathVariable long ID) { + Map successMessage = Collections.singletonMap("message", "成功刪除"); + Map failMessage = Collections.singletonMap("message", "刪錯商品嚕"); Product p = productService.getID(ID); - if(!Objects.equals(userService.findByUsername(userIdentity.getUsername()).getId(), p.getSellerID())){ + if (!Objects.equals(userService.findByUsername(userIdentity.getUsername()).getId(), p.getSellerID())) { return ResponseEntity.badRequest().body(failMessage); } p.setProductAmount(0L); @@ -222,13 +221,13 @@ ResponseEntity> deleteProduct(@PathVariable long ID){ } @PutMapping("/fixedproduct/{ID}") - ResponseEntity> putFixedProduct(@PathVariable long ID , @Valid @RequestBody UpdateFixedPriceProductRequest request){ + ResponseEntity> putFixedProduct(@PathVariable long ID, @Valid @RequestBody UpdateFixedPriceProductRequest request) { - Map successMessage = Collections.singletonMap("message","成功更新不二價商品"); - Map failMessage = Collections.singletonMap("message","更新錯商品嚕"); + Map successMessage = Collections.singletonMap("message", "成功更新不二價商品"); + Map failMessage = Collections.singletonMap("message", "更新錯商品嚕"); Product product = productService.getID(ID); - if(!Objects.equals(userService.findByUsername(userIdentity.getUsername()).getId(), product.getSellerID())){ + if (!Objects.equals(userService.findByUsername(userIdentity.getUsername()).getId(), product.getSellerID())) { return ResponseEntity.badRequest().body(failMessage); } product.setProductName(request.getProductName()); @@ -243,17 +242,17 @@ ResponseEntity> putFixedProduct(@PathVariable long ID , @Vali } @PutMapping("/nonfixedproduct/{ID}") - ResponseEntity> putNonFixedProduct(@PathVariable long ID , @Valid @RequestBody UpdateNonFixedPriceProductRequest request){ - - Map successMessage = Collections.singletonMap("message","成功更新競標商品"); - Map failToPostponeAuction = Collections.singletonMap("message","延長競標截止時間失敗,因為有人得標嚕"); - Map fail = Collections.singletonMap("message","截止時間錯誤"); - Map failToSetUpsetPrice = Collections.singletonMap("message","底價不得更改,因為有人出價了"); - Map failToSetBidIncrement = Collections.singletonMap("message","每次增加金額不得更改,因為有人出價了"); - Map failMessage = Collections.singletonMap("message","更新錯商品嚕阿"); + ResponseEntity> putNonFixedProduct(@PathVariable long ID, @Valid @RequestBody UpdateNonFixedPriceProductRequest request) { + + Map successMessage = Collections.singletonMap("message", "成功更新競標商品"); + Map failToPostponeAuction = Collections.singletonMap("message", "延長競標截止時間失敗,因為有人得標嚕"); + Map fail = Collections.singletonMap("message", "截止時間錯誤"); + Map failToSetUpsetPrice = Collections.singletonMap("message", "底價不得更改,因為有人出價了"); + Map failToSetBidIncrement = Collections.singletonMap("message", "每次增加金額不得更改,因為有人出價了"); + Map failMessage = Collections.singletonMap("message", "更新錯商品嚕阿"); Product product = productService.getID(ID); - if(!Objects.equals(userService.findByUsername(userIdentity.getUsername()).getId(), product.getSellerID())){ + if (!Objects.equals(userService.findByUsername(userIdentity.getUsername()).getId(), product.getSellerID())) { return ResponseEntity.badRequest().body(failMessage); } product.setProductName(request.getProductName()); @@ -262,13 +261,13 @@ ResponseEntity> putNonFixedProduct(@PathVariable long ID , @V product.setProductType(request.getProductType()); - Map productMap= product.getBidInfo(); - if(!productMap.isEmpty() && !Objects.equals(request.getUpsetPrice(), product.getUpsetPrice())){ //map不為空,有人出價過了。且更改的底價 != 原本底價 + Map productMap = product.getBidInfo(); + if (!productMap.isEmpty() && !Objects.equals(request.getUpsetPrice(), product.getUpsetPrice())) { //map不為空,有人出價過了。且更改的底價 != 原本底價 return ResponseEntity.badRequest().body(failToSetUpsetPrice); } product.setUpsetPrice(request.getUpsetPrice()); - if(!productMap.isEmpty() && !Objects.equals(request.getBidIncrement(), product.getBidIncrement())){ //map不為空,有人出價過了。且被更改每口叫價 + if (!productMap.isEmpty() && !Objects.equals(request.getBidIncrement(), product.getBidIncrement())) { //map不為空,有人出價過了。且被更改每口叫價 return ResponseEntity.badRequest().body(failToSetBidIncrement); } product.setBidIncrement(request.getBidIncrement()); @@ -276,10 +275,10 @@ ResponseEntity> putNonFixedProduct(@PathVariable long ID , @V LocalDateTime now = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); LocalDateTime dateTime = LocalDateTime.parse(request.getFinishTime(), formatter); - if(!now.isBefore(dateTime)){ + if (!now.isBefore(dateTime)) { return ResponseEntity.badRequest().body(fail); } - if(product.getIsAuction()) { //代表競標結束且有被加入購物車 + if (product.getIsAuction()) { //代表競標結束且有被加入購物車 return ResponseEntity.badRequest().body(failToPostponeAuction); } diff --git a/src/main/java/ntou/auction/spring/product/service/TimerTask.java b/src/main/java/ntou/auction/spring/product/service/TimerTask.java index c776882..1d739c5 100644 --- a/src/main/java/ntou/auction/spring/product/service/TimerTask.java +++ b/src/main/java/ntou/auction/spring/product/service/TimerTask.java @@ -1,8 +1,7 @@ package ntou.auction.spring.product.service; +import ntou.auction.spring.mail.EmailService; import ntou.auction.spring.shoppingcart.service.ShoppingcartService; -import ntou.auction.spring.account.response.UserIdentity; -import ntou.auction.spring.account.service.UserService; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import ntou.auction.spring.product.entity.Product; @@ -11,33 +10,38 @@ import java.util.List; import java.util.Map; import java.util.Optional; + @Component public class TimerTask { - private final ProductService productService; + private final ProductService productService; private final ShoppingcartService shoppingcartService; + private final EmailService emailService; - public TimerTask(ProductService productService, ShoppingcartService shoppingcartService, UserIdentity userIdentity, UserService userService) { + public TimerTask(ProductService productService, ShoppingcartService shoppingcartService, EmailService emailService) { this.productService = productService; this.shoppingcartService = shoppingcartService; + this.emailService = emailService; } + @Transactional @Scheduled(cron = "0 * * * * ?") //每分鐘的第0秒 public void execute() { - List productList = productService.findByProductNonFixed(); + List productList = productService.findByProductNonFixed(); for (Product product : productList) { System.out.println(product.getId()); if (product.isExpired()) { //競標結束 - Map productMap= product.getBidInfo(); + Map productMap = product.getBidInfo(); - Optional> max0 = productMap.entrySet() + Optional> max0 = productMap.entrySet() .stream().max(Map.Entry.comparingByValue()); - if(max0.isPresent()) { - shoppingcartService.addProductByUserId(max0.get().getKey(),product.getId(),1L); + if (max0.isPresent()) { + shoppingcartService.addProductByUserId(max0.get().getKey(), product.getId(), 1L); product.setIsAuction(true); productService.store(product); + emailService.sendMailBidSuccess(max0.get().getKey(), product); } } } From 1c41f7b5848eb49a1aed5d7d14d198bd3c230df0 Mon Sep 17 00:00:00 2001 From: keke125 Date: Fri, 22 Dec 2023 15:45:22 +0800 Subject: [PATCH 3/5] fix: Strengthen security by replacing JWT Key and email configuration with environment variables. --- src/main/java/ntou/auction/spring/util/AppConfig.java | 2 ++ src/main/resources/application.properties | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/ntou/auction/spring/util/AppConfig.java b/src/main/java/ntou/auction/spring/util/AppConfig.java index 9998a2c..dc1479f 100644 --- a/src/main/java/ntou/auction/spring/util/AppConfig.java +++ b/src/main/java/ntou/auction/spring/util/AppConfig.java @@ -35,4 +35,6 @@ public class AppConfig { private String mailUsername; + private String JWTKey; + } \ No newline at end of file diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7128d7a..7a94aab 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -13,10 +13,11 @@ spring.jpa.defer-datasource-initialization=true spring.sql.init.mode=always # mail -spring.mail.host=smtp.gmail.com +spring.mail.host=${NA_MAIL_PROVIDER} spring.mail.port=587 spring.mail.username=${NA_MAIL_USER} spring.mail.password=${NA_MAIL_PASSWORD} +app.mail-username=${NA_MAIL_USER} spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.smtp.starttls.enable=true @@ -40,6 +41,6 @@ app.newSignupImageSizeLimit=30 # password encoder # BCrypt pbkdf2 argon2 app.idForEncode=argon2 -app.mail-username=${NA_MAIL_USER} +app.JWTKey=${NA_JWT_KEY} From e549c21ae29d153189911df2c3309d452c4f4749 Mon Sep 17 00:00:00 2001 From: keke125 Date: Fri, 22 Dec 2023 16:11:41 +0800 Subject: [PATCH 4/5] chore: Adjust database DDL auto mode. --- src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 7a94aab..1fd37a7 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,7 +4,7 @@ server.port=${PORT:8080} # mariadb database # For persistent storage without dropping the database when the app stops, choose the "update" option. # For development mode where the database is created when the app starts and dropped when it stops, choose the "create-drop" option. -spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.hibernate.ddl-auto=update spring.datasource.url=${NA_DB_URL} spring.datasource.username=${NA_DB_USER} spring.datasource.password=${NA_DB_PASSWORD} From 09f391d8d113c9c45d8ea4bd30dc996b1f18e796 Mon Sep 17 00:00:00 2001 From: keke125 Date: Fri, 22 Dec 2023 16:24:54 +0800 Subject: [PATCH 5/5] fix: Codacy CI. --- .../auction/spring/product/controller/ProductController.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/ntou/auction/spring/product/controller/ProductController.java b/src/main/java/ntou/auction/spring/product/controller/ProductController.java index b79fe13..7a9a7d1 100644 --- a/src/main/java/ntou/auction/spring/product/controller/ProductController.java +++ b/src/main/java/ntou/auction/spring/product/controller/ProductController.java @@ -128,7 +128,6 @@ ResponseEntity> postProduct(@Valid @RequestBody PostNonFixed } @PatchMapping("/bid") - //商品ID 出價。出價也需傳入token ResponseEntity> bidProduct(@Valid @RequestBody BidRequest request) { Map successMessage = Collections.singletonMap("message", "成功出價");