Notifications added

This commit is contained in:
Sidharth Prabhu
2026-04-21 14:15:55 +05:30
parent 3e35bacc14
commit 21c581b7eb
9 changed files with 373 additions and 8 deletions

View File

@@ -13,6 +13,7 @@ import org.springframework.data.domain.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import com.rit.canteen.sales.service.SystemNotificationService;
import java.util.*;
@@ -29,6 +30,9 @@ public class FeedbackController {
@Autowired
private ItemRatingRepository itemRatingRepository;
@Autowired
private SystemNotificationService notificationService;
@GetMapping
public Page<Feedback> getAllFeedback(
@RequestParam(defaultValue = "0") int page,
@@ -188,6 +192,14 @@ public class FeedbackController {
order.setHasFeedback(true);
orderRepository.save(order);
// Notify Admins
notificationService.createNotification(
"New Feedback Received",
"A new feedback has been submitted by " + (feedback.getUserName() != null ? feedback.getUserName() : "a customer") + " for Order #" + order.getDisplayOrderId(),
"FEEDBACK",
"/feedback"
);
return ResponseEntity.ok(savedFeedback);
}
}

View File

@@ -6,6 +6,7 @@ import jakarta.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.rit.canteen.sales.service.SystemNotificationService;
import java.util.List;
@@ -22,6 +23,9 @@ public class ProductController {
@Autowired
private StockUpdateController stockUpdateController;
@Autowired
private SystemNotificationService notificationService;
@GetMapping
public org.springframework.data.domain.Page<Product> getAllProducts(
@RequestParam(required = false) String search,
@@ -53,6 +57,16 @@ public class ProductController {
public Product createProduct(@Valid @RequestBody Product product) {
Product savedProduct = productRepository.save(product);
updateStallAssociations(savedProduct, product.getStalls());
if (savedProduct.isDraft()) {
notificationService.createNotification(
"Draft Product Created",
"A new product draft '" + savedProduct.getName() + "' has been created and requires review before publishing.",
"PRODUCT",
"/inventory/products"
);
}
return productRepository.findById(savedProduct.getId()).orElse(savedProduct);
}
@@ -249,6 +263,13 @@ public class ProductController {
Product updated = productRepository.save(product);
updateStallAssociations(updated, productDetails.getStalls());
notificationService.createNotification(
"Product Published",
"Product '" + productDetails.getName() + "' is now live in the inventory with ID: " + finalProductId,
"PRODUCT",
"/inventory/products"
);
return ResponseEntity.ok(productRepository.findById(updated.getId()).orElse(updated));
}
})

View File

@@ -10,6 +10,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import com.rit.canteen.sales.service.SystemNotificationService;
import java.util.List;
import java.util.Map;
@@ -22,6 +24,9 @@ public class PurchaseController {
@Autowired
private PurchaseService purchaseService;
@Autowired
private SystemNotificationService notificationService;
@GetMapping("/orders")
public List<PurchaseOrder> getAllOrders() {
return purchaseService.getAllOrders();
@@ -30,7 +35,17 @@ public class PurchaseController {
@PostMapping("/orders")
public PurchaseOrder createOrder(@RequestBody PurchaseOrder order) {
logger.info("Received request to create/update order: {}", order.getPurchaseId());
return purchaseService.createOrder(order);
PurchaseOrder saved = purchaseService.createOrder(order);
// Notify Admins
notificationService.createNotification(
"Procurement Update",
"A new purchase order (" + saved.getPurchaseId() + ") has been created/updated for vendor: " + (saved.getVendor() != null ? saved.getVendor().getName() : "Unknown"),
"PURCHASE",
"/purchases/orders"
);
return saved;
}
@GetMapping("/orders/{id}/history")

View File

@@ -0,0 +1,37 @@
package com.rit.canteen.sales.controller;
import com.rit.canteen.sales.model.SystemNotification;
import com.rit.canteen.sales.service.SystemNotificationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/notifications")
@CrossOrigin(origins = "*")
public class SystemNotificationController {
@Autowired
private SystemNotificationService notificationService;
@GetMapping
public List<SystemNotification> getUnread() {
return notificationService.getUnreadNotifications();
}
@GetMapping("/all")
public List<SystemNotification> getAll() {
return notificationService.getAllNotifications();
}
@PostMapping("/mark-read/{id}")
public void markRead(@PathVariable Long id) {
notificationService.markAsRead(id);
}
@PostMapping("/mark-all-read")
public void markAllRead() {
notificationService.markAllAsRead();
}
}

View File

@@ -0,0 +1,68 @@
package com.rit.canteen.sales.model;
import jakarta.persistence.*;
import java.time.LocalDateTime;
@Entity
@Table(name = "system_notifications")
public class SystemNotification {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false)
private String title;
@Column(nullable = false, length = 1000)
private String message;
@Column(nullable = false)
private String type; // FEEDBACK, PURCHASE, PRODUCT, COUPON
@Column(nullable = false)
private boolean isRead = false;
@Column(nullable = true)
private String link; // URL to navigate to
@Column(nullable = false)
private LocalDateTime createdAt;
@PrePersist
protected void onCreate() {
this.createdAt = LocalDateTime.now();
}
public SystemNotification() {}
public SystemNotification(String title, String message, String type, String link) {
this.title = title;
this.message = message;
this.type = type;
this.link = link;
this.createdAt = LocalDateTime.now();
}
// Getters and Setters
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public boolean isRead() { return isRead; }
public void setRead(boolean read) { isRead = read; }
public String getLink() { return link; }
public void setLink(String link) { this.link = link; }
public LocalDateTime getCreatedAt() { return createdAt; }
public void setCreatedAt(LocalDateTime createdAt) { this.createdAt = createdAt; }
}

View File

@@ -5,7 +5,11 @@ import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Optional;
import java.util.List;
import java.time.LocalDateTime;
@Repository
public interface CouponRepository extends JpaRepository<CouponCode, Long> {
Optional<CouponCode> findByCode(String code);
List<CouponCode> findByExpiryDateBeforeAndIsActiveTrue(LocalDateTime threshold);
}

View File

@@ -0,0 +1,12 @@
package com.rit.canteen.sales.repository;
import com.rit.canteen.sales.model.SystemNotification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface SystemNotificationRepository extends JpaRepository<SystemNotification, Long> {
List<SystemNotification> findByIsReadFalseOrderByCreatedAtDesc();
List<SystemNotification> findAllByOrderByCreatedAtDesc();
}

View File

@@ -0,0 +1,78 @@
package com.rit.canteen.sales.service;
import com.rit.canteen.sales.model.CouponCode;
import com.rit.canteen.sales.model.SystemNotification;
import com.rit.canteen.sales.repository.CouponRepository;
import com.rit.canteen.sales.repository.SystemNotificationRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class SystemNotificationService {
@Autowired
private SystemNotificationRepository notificationRepository;
@Autowired
private CouponRepository couponRepository;
@Transactional
public void createNotification(String title, String message, String type, String link) {
SystemNotification notification = new SystemNotification(title, message, type, link);
notificationRepository.save(notification);
}
public List<SystemNotification> getUnreadNotifications() {
return notificationRepository.findByIsReadFalseOrderByCreatedAtDesc();
}
public List<SystemNotification> getAllNotifications() {
return notificationRepository.findAllByOrderByCreatedAtDesc();
}
@Transactional
public void markAsRead(Long id) {
notificationRepository.findById(id).ifPresent(n -> {
n.setRead(true);
notificationRepository.save(n);
});
}
@Transactional
public void markAllAsRead() {
List<SystemNotification> unread = notificationRepository.findByIsReadFalseOrderByCreatedAtDesc();
for (SystemNotification n : unread) {
n.setRead(true);
}
notificationRepository.saveAll(unread);
}
// Triggered every 6 hours to check for expiring coupons
@Scheduled(fixedRate = 21600000)
@Transactional
public void checkExpiringCoupons() {
LocalDateTime threshold = LocalDateTime.now().plusHours(48);
List<CouponCode> expiringSoon = couponRepository.findByExpiryDateBeforeAndIsActiveTrue(threshold);
for (CouponCode coupon : expiringSoon) {
String title = "Coupon Expiring Soon: " + coupon.getCode();
// Avoid duplicate notifications for the same coupon within the last 24 hours
boolean exists = notificationRepository.findAllByOrderByCreatedAtDesc().stream()
.anyMatch(n -> n.getTitle().equals(title) && n.getCreatedAt().isAfter(LocalDateTime.now().minusDays(1)));
if (!exists) {
createNotification(
title,
"Ritz Coupon code '" + coupon.getCode() + "' is set to expire on " + coupon.getExpiryDate() + ". Consider extending or updating it.",
"COUPON",
"/ritz/coupons"
);
}
}
}
}