externalize business constants
This commit is contained in:
@@ -1,13 +1,16 @@
|
|||||||
package com.petshop.backend;
|
package com.petshop.backend;
|
||||||
|
|
||||||
|
import com.petshop.backend.config.BusinessProperties;
|
||||||
import com.petshop.backend.config.FlywayContextInitializer;
|
import com.petshop.backend.config.FlywayContextInitializer;
|
||||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
import org.springframework.data.web.config.EnableSpringDataWebSupport;
|
import org.springframework.data.web.config.EnableSpringDataWebSupport;
|
||||||
import org.springframework.scheduling.annotation.EnableAsync;
|
import org.springframework.scheduling.annotation.EnableAsync;
|
||||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
@SpringBootApplication
|
@SpringBootApplication
|
||||||
|
@EnableConfigurationProperties(BusinessProperties.class)
|
||||||
@EnableScheduling
|
@EnableScheduling
|
||||||
@EnableAsync
|
@EnableAsync
|
||||||
@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
|
@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package com.petshop.backend.config;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalTime;
|
||||||
|
|
||||||
|
@ConfigurationProperties(prefix = "petshop.business")
|
||||||
|
public record BusinessProperties(
|
||||||
|
LocalTime openTime,
|
||||||
|
LocalTime closeTime,
|
||||||
|
int slotIntervalMinutes,
|
||||||
|
long maxImageSizeBytes,
|
||||||
|
BigDecimal employeeDiscountPercent,
|
||||||
|
int loyaltyPointsPerDollar
|
||||||
|
) {
|
||||||
|
public BusinessProperties {
|
||||||
|
if (openTime == null) openTime = LocalTime.of(9, 0);
|
||||||
|
if (closeTime == null) closeTime = LocalTime.of(17, 0);
|
||||||
|
if (slotIntervalMinutes <= 0) slotIntervalMinutes = 30;
|
||||||
|
if (maxImageSizeBytes <= 0) maxImageSizeBytes = 5 * 1024 * 1024;
|
||||||
|
if (employeeDiscountPercent == null) employeeDiscountPercent = new BigDecimal("0.10");
|
||||||
|
if (loyaltyPointsPerDollar <= 0) loyaltyPointsPerDollar = 20;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,7 @@ import com.petshop.backend.repository.PetRepository;
|
|||||||
import com.petshop.backend.repository.ServiceRepository;
|
import com.petshop.backend.repository.ServiceRepository;
|
||||||
import com.petshop.backend.repository.StoreRepository;
|
import com.petshop.backend.repository.StoreRepository;
|
||||||
import com.petshop.backend.repository.UserRepository;
|
import com.petshop.backend.repository.UserRepository;
|
||||||
|
import com.petshop.backend.config.BusinessProperties;
|
||||||
import com.petshop.backend.util.AuthenticationHelper;
|
import com.petshop.backend.util.AuthenticationHelper;
|
||||||
import com.petshop.backend.util.StringUtils;
|
import com.petshop.backend.util.StringUtils;
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
@@ -47,8 +48,9 @@ public class AppointmentService {
|
|||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final AdoptionRepository adoptionRepository;
|
private final AdoptionRepository adoptionRepository;
|
||||||
private final ApplicationEventPublisher eventPublisher;
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
private final BusinessProperties businessProperties;
|
||||||
|
|
||||||
public AppointmentService(AppointmentRepository appointmentRepository, ServiceRepository serviceRepository, PetRepository petRepository, StoreRepository storeRepository, UserRepository userRepository, AdoptionRepository adoptionRepository, ApplicationEventPublisher eventPublisher) {
|
public AppointmentService(AppointmentRepository appointmentRepository, ServiceRepository serviceRepository, PetRepository petRepository, StoreRepository storeRepository, UserRepository userRepository, AdoptionRepository adoptionRepository, ApplicationEventPublisher eventPublisher, BusinessProperties businessProperties) {
|
||||||
this.appointmentRepository = appointmentRepository;
|
this.appointmentRepository = appointmentRepository;
|
||||||
this.serviceRepository = serviceRepository;
|
this.serviceRepository = serviceRepository;
|
||||||
this.petRepository = petRepository;
|
this.petRepository = petRepository;
|
||||||
@@ -56,6 +58,7 @@ public class AppointmentService {
|
|||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
this.adoptionRepository = adoptionRepository;
|
this.adoptionRepository = adoptionRepository;
|
||||||
this.eventPublisher = eventPublisher;
|
this.eventPublisher = eventPublisher;
|
||||||
|
this.businessProperties = businessProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
@@ -240,8 +243,8 @@ public class AppointmentService {
|
|||||||
.collect(Collectors.groupingBy(a -> a.getEmployee().getId()));
|
.collect(Collectors.groupingBy(a -> a.getEmployee().getId()));
|
||||||
|
|
||||||
List<String> availableSlots = new ArrayList<>();
|
List<String> availableSlots = new ArrayList<>();
|
||||||
LocalTime startTime = LocalTime.of(9, 0);
|
LocalTime startTime = businessProperties.openTime();
|
||||||
LocalTime endTime = LocalTime.of(17, 0);
|
LocalTime endTime = businessProperties.closeTime();
|
||||||
LocalTime latestStart = endTime.minusMinutes(service.getServiceDuration());
|
LocalTime latestStart = endTime.minusMinutes(service.getServiceDuration());
|
||||||
|
|
||||||
LocalTime currentTime = startTime;
|
LocalTime currentTime = startTime;
|
||||||
@@ -255,7 +258,7 @@ public class AppointmentService {
|
|||||||
if (anyEmployeeAvailable) {
|
if (anyEmployeeAvailable) {
|
||||||
availableSlots.add(currentTime.toString());
|
availableSlots.add(currentTime.toString());
|
||||||
}
|
}
|
||||||
currentTime = currentTime.plusMinutes(30);
|
currentTime = currentTime.plusMinutes(businessProperties.slotIntervalMinutes());
|
||||||
}
|
}
|
||||||
|
|
||||||
return availableSlots;
|
return availableSlots;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import com.petshop.backend.entity.*;
|
|||||||
import com.petshop.backend.exception.BusinessException;
|
import com.petshop.backend.exception.BusinessException;
|
||||||
import com.petshop.backend.exception.ResourceNotFoundException;
|
import com.petshop.backend.exception.ResourceNotFoundException;
|
||||||
import com.petshop.backend.repository.*;
|
import com.petshop.backend.repository.*;
|
||||||
import com.petshop.backend.util.BusinessConstants;
|
import com.petshop.backend.config.BusinessProperties;
|
||||||
import com.stripe.Stripe;
|
import com.stripe.Stripe;
|
||||||
import com.stripe.exception.StripeException;
|
import com.stripe.exception.StripeException;
|
||||||
import com.stripe.model.PaymentIntent;
|
import com.stripe.model.PaymentIntent;
|
||||||
@@ -25,8 +25,6 @@ import java.util.List;
|
|||||||
@Service
|
@Service
|
||||||
public class CartService {
|
public class CartService {
|
||||||
|
|
||||||
private static final int LOYALTY_POINTS_PER_DOLLAR = BusinessConstants.LOYALTY_POINTS_PER_DOLLAR;
|
|
||||||
|
|
||||||
private final CartRepository cartRepository;
|
private final CartRepository cartRepository;
|
||||||
private final CartItemRepository cartItemRepository;
|
private final CartItemRepository cartItemRepository;
|
||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
@@ -36,6 +34,7 @@ public class CartService {
|
|||||||
private final CouponService couponService;
|
private final CouponService couponService;
|
||||||
private final SaleRepository saleRepository;
|
private final SaleRepository saleRepository;
|
||||||
private final SaleService saleService;
|
private final SaleService saleService;
|
||||||
|
private final BusinessProperties businessProperties;
|
||||||
|
|
||||||
@Value("${stripe.secret-key:}")
|
@Value("${stripe.secret-key:}")
|
||||||
private String stripeSecretKey;
|
private String stripeSecretKey;
|
||||||
@@ -48,7 +47,8 @@ public class CartService {
|
|||||||
CouponRepository couponRepository,
|
CouponRepository couponRepository,
|
||||||
CouponService couponService,
|
CouponService couponService,
|
||||||
SaleRepository saleRepository,
|
SaleRepository saleRepository,
|
||||||
SaleService saleService) {
|
SaleService saleService,
|
||||||
|
BusinessProperties businessProperties) {
|
||||||
this.cartRepository = cartRepository;
|
this.cartRepository = cartRepository;
|
||||||
this.cartItemRepository = cartItemRepository;
|
this.cartItemRepository = cartItemRepository;
|
||||||
this.userRepository = userRepository;
|
this.userRepository = userRepository;
|
||||||
@@ -58,6 +58,7 @@ public class CartService {
|
|||||||
this.couponService = couponService;
|
this.couponService = couponService;
|
||||||
this.saleRepository = saleRepository;
|
this.saleRepository = saleRepository;
|
||||||
this.saleService = saleService;
|
this.saleService = saleService;
|
||||||
|
this.businessProperties = businessProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
@@ -473,7 +474,7 @@ public class CartService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int availablePoints = user.getLoyaltyPoints() != null ? user.getLoyaltyPoints() : 0;
|
int availablePoints = user.getLoyaltyPoints() != null ? user.getLoyaltyPoints() : 0;
|
||||||
int wholeDollars = availablePoints / LOYALTY_POINTS_PER_DOLLAR;
|
int wholeDollars = availablePoints / businessProperties.loyaltyPointsPerDollar();
|
||||||
if (wholeDollars <= 0) {
|
if (wholeDollars <= 0) {
|
||||||
return BigDecimal.ZERO;
|
return BigDecimal.ZERO;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import com.petshop.backend.exception.ResourceNotFoundException;
|
|||||||
import com.petshop.backend.event.SaleReceiptEvent;
|
import com.petshop.backend.event.SaleReceiptEvent;
|
||||||
import com.petshop.backend.repository.*;
|
import com.petshop.backend.repository.*;
|
||||||
import com.petshop.backend.util.AuthenticationHelper;
|
import com.petshop.backend.util.AuthenticationHelper;
|
||||||
import com.petshop.backend.util.BusinessConstants;
|
import com.petshop.backend.config.BusinessProperties;
|
||||||
import com.petshop.backend.util.StringUtils;
|
import com.petshop.backend.util.StringUtils;
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.data.domain.Page;
|
import org.springframework.data.domain.Page;
|
||||||
@@ -25,9 +25,6 @@ import java.util.List;
|
|||||||
@Service
|
@Service
|
||||||
public class SaleService {
|
public class SaleService {
|
||||||
|
|
||||||
private static final BigDecimal EMPLOYEE_DISCOUNT_PERCENT = BusinessConstants.EMPLOYEE_DISCOUNT_PERCENT;
|
|
||||||
private static final int LOYALTY_POINTS_PER_DOLLAR = BusinessConstants.LOYALTY_POINTS_PER_DOLLAR;
|
|
||||||
|
|
||||||
private final SaleRepository saleRepository;
|
private final SaleRepository saleRepository;
|
||||||
private final ProductRepository productRepository;
|
private final ProductRepository productRepository;
|
||||||
private final StoreRepository storeRepository;
|
private final StoreRepository storeRepository;
|
||||||
@@ -37,8 +34,9 @@ public class SaleService {
|
|||||||
private final CouponService couponService;
|
private final CouponService couponService;
|
||||||
private final CartRepository cartRepository;
|
private final CartRepository cartRepository;
|
||||||
private final ApplicationEventPublisher eventPublisher;
|
private final ApplicationEventPublisher eventPublisher;
|
||||||
|
private final BusinessProperties businessProperties;
|
||||||
|
|
||||||
public SaleService(SaleRepository saleRepository, ProductRepository productRepository, StoreRepository storeRepository, InventoryRepository inventoryRepository, UserRepository userRepository, CouponRepository couponRepository, CouponService couponService, CartRepository cartRepository, ApplicationEventPublisher eventPublisher) {
|
public SaleService(SaleRepository saleRepository, ProductRepository productRepository, StoreRepository storeRepository, InventoryRepository inventoryRepository, UserRepository userRepository, CouponRepository couponRepository, CouponService couponService, CartRepository cartRepository, ApplicationEventPublisher eventPublisher, BusinessProperties businessProperties) {
|
||||||
this.saleRepository = saleRepository;
|
this.saleRepository = saleRepository;
|
||||||
this.productRepository = productRepository;
|
this.productRepository = productRepository;
|
||||||
this.storeRepository = storeRepository;
|
this.storeRepository = storeRepository;
|
||||||
@@ -48,6 +46,7 @@ public class SaleService {
|
|||||||
this.couponService = couponService;
|
this.couponService = couponService;
|
||||||
this.cartRepository = cartRepository;
|
this.cartRepository = cartRepository;
|
||||||
this.eventPublisher = eventPublisher;
|
this.eventPublisher = eventPublisher;
|
||||||
|
this.businessProperties = businessProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
@@ -251,7 +250,7 @@ public class SaleService {
|
|||||||
int pointsDeducted;
|
int pointsDeducted;
|
||||||
if (request.getPointsUsed() != null && request.getPointsUsed() > 0) {
|
if (request.getPointsUsed() != null && request.getPointsUsed() > 0) {
|
||||||
loyaltyDiscount = BigDecimal.valueOf(request.getPointsUsed())
|
loyaltyDiscount = BigDecimal.valueOf(request.getPointsUsed())
|
||||||
.divide(BigDecimal.valueOf(LOYALTY_POINTS_PER_DOLLAR), 2, RoundingMode.HALF_UP)
|
.divide(BigDecimal.valueOf(businessProperties.loyaltyPointsPerDollar()), 2, RoundingMode.HALF_UP)
|
||||||
.min(remainingAfterDiscounts.max(BigDecimal.ZERO))
|
.min(remainingAfterDiscounts.max(BigDecimal.ZERO))
|
||||||
.setScale(2, RoundingMode.HALF_UP);
|
.setScale(2, RoundingMode.HALF_UP);
|
||||||
pointsDeducted = request.getPointsUsed();
|
pointsDeducted = request.getPointsUsed();
|
||||||
@@ -292,7 +291,7 @@ public class SaleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (customer.getRole() == User.Role.STAFF || customer.getRole() == User.Role.ADMIN) {
|
if (customer.getRole() == User.Role.STAFF || customer.getRole() == User.Role.ADMIN) {
|
||||||
return remainingAmount.multiply(EMPLOYEE_DISCOUNT_PERCENT).setScale(2, RoundingMode.HALF_UP);
|
return remainingAmount.multiply(businessProperties.employeeDiscountPercent()).setScale(2, RoundingMode.HALF_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
return BigDecimal.ZERO;
|
return BigDecimal.ZERO;
|
||||||
@@ -304,7 +303,7 @@ public class SaleService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int availablePoints = customer.getLoyaltyPoints() != null ? customer.getLoyaltyPoints() : 0;
|
int availablePoints = customer.getLoyaltyPoints() != null ? customer.getLoyaltyPoints() : 0;
|
||||||
int wholeDollars = availablePoints / LOYALTY_POINTS_PER_DOLLAR;
|
int wholeDollars = availablePoints / businessProperties.loyaltyPointsPerDollar();
|
||||||
if (wholeDollars <= 0) {
|
if (wholeDollars <= 0) {
|
||||||
return BigDecimal.ZERO;
|
return BigDecimal.ZERO;
|
||||||
}
|
}
|
||||||
@@ -319,7 +318,7 @@ public class SaleService {
|
|||||||
if (loyaltyDiscount == null || loyaltyDiscount.compareTo(BigDecimal.ZERO) <= 0) {
|
if (loyaltyDiscount == null || loyaltyDiscount.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return loyaltyDiscount.setScale(0, RoundingMode.DOWN).intValue() * LOYALTY_POINTS_PER_DOLLAR;
|
return loyaltyDiscount.setScale(0, RoundingMode.DOWN).intValue() * businessProperties.loyaltyPointsPerDollar();
|
||||||
}
|
}
|
||||||
|
|
||||||
private User resolveWebsiteSaleEmployee(Long storeId) {
|
private User resolveWebsiteSaleEmployee(Long storeId) {
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
package com.petshop.backend.util;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
public final class BusinessConstants {
|
|
||||||
|
|
||||||
private BusinessConstants() {}
|
|
||||||
|
|
||||||
public static final int LOYALTY_POINTS_PER_DOLLAR = 20;
|
|
||||||
public static final BigDecimal EMPLOYEE_DISCOUNT_PERCENT = new BigDecimal("0.10");
|
|
||||||
}
|
|
||||||
@@ -61,6 +61,15 @@ app:
|
|||||||
frontend-url: ${FRONTEND_URL:http://localhost:3000}
|
frontend-url: ${FRONTEND_URL:http://localhost:3000}
|
||||||
allowed-origins: ${ALLOWED_ORIGINS:http://localhost:3000,http://localhost:3001,http://127.0.0.1:3000,https://petshop-web.nicepond-c7280126.westus2.azurecontainerapps.io}
|
allowed-origins: ${ALLOWED_ORIGINS:http://localhost:3000,http://localhost:3001,http://127.0.0.1:3000,https://petshop-web.nicepond-c7280126.westus2.azurecontainerapps.io}
|
||||||
|
|
||||||
|
petshop:
|
||||||
|
business:
|
||||||
|
open-time: "09:00"
|
||||||
|
close-time: "17:00"
|
||||||
|
slot-interval-minutes: 30
|
||||||
|
max-image-size-bytes: 5242880
|
||||||
|
employee-discount-percent: 0.10
|
||||||
|
loyalty-points-per-dollar: 20
|
||||||
|
|
||||||
azure:
|
azure:
|
||||||
storage:
|
storage:
|
||||||
connection-string: ${AZURE_STORAGE_CONNECTION_STRING:}
|
connection-string: ${AZURE_STORAGE_CONNECTION_STRING:}
|
||||||
|
|||||||
Reference in New Issue
Block a user