A wrong password or an unknown phone number was answered 900 (HTTP 500), like a server error, so clients could only tell them apart by the cause text. Both are now 304 (HTTP 400) from customer_auth_service with one cause, "invalid phone number or password", so a login does not tell which numbers have an account. A customer who never set a password gets 304 "customer not properly registered". Anything else stays 900. Login is limited per phone number: 5 attempts in 15 minutes, counted in Redis before the password is checked, so attempts sent at once all count, and for numbers without a customer too. The sixth is refused with 429 and data.locked_until, even with the right password, until the window ends. A successful login starts the count again. Since the number is normalized first, 0812… and 62812… count as one. When Redis fails, logins go on unlimited and the error is logged. There is no limit per IP: the client IP comes from X-Forwarded-For, which anyone can set while no trusted proxies are configured. Tested over HTTP with fakes; the Redis commands were checked against miniredis outside the repo, not against a real Redis. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
776 lines
45 KiB
Go
776 lines
45 KiB
Go
package app
|
|
|
|
import (
|
|
"apskel-pos-be/internal/client"
|
|
"apskel-pos-be/internal/transformer"
|
|
"context"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"apskel-pos-be/config"
|
|
"apskel-pos-be/internal/handler"
|
|
"apskel-pos-be/internal/middleware"
|
|
"apskel-pos-be/internal/processor"
|
|
"apskel-pos-be/internal/repository"
|
|
"apskel-pos-be/internal/router"
|
|
"apskel-pos-be/internal/service"
|
|
"apskel-pos-be/internal/validator"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type App struct {
|
|
server *http.Server
|
|
db *gorm.DB
|
|
redisClient *redis.Client
|
|
router *router.Router
|
|
shutdown chan os.Signal
|
|
omsetScheduler *service.OmsetMilestoneScheduler
|
|
walletRecon *service.WalletReconciliationJob
|
|
earningRetry *service.EarningBackfillJob
|
|
walletExpiry *service.WalletExpiryJob
|
|
gameSessions *service.GameSessionJob
|
|
gameBudgets *service.GameBudgetPeriodJob
|
|
voucherCodes *service.VoucherCodeExpiryJob
|
|
voucherRecover *service.VoucherRedemptionRecoveryJob
|
|
}
|
|
|
|
func NewApp(db *gorm.DB, redisClient *redis.Client) *App {
|
|
return &App{
|
|
db: db,
|
|
redisClient: redisClient,
|
|
shutdown: make(chan os.Signal, 1),
|
|
}
|
|
}
|
|
|
|
func (a *App) Initialize(cfg *config.Config) error {
|
|
repos := a.initRepositories()
|
|
processors := a.initProcessors(cfg, repos)
|
|
|
|
// Initialize omset milestone scheduler
|
|
a.omsetScheduler = service.NewOmsetMilestoneScheduler(
|
|
repos.organizationRepo,
|
|
repos.outletRepo,
|
|
repos.userRepo,
|
|
processors.notificationProcessor,
|
|
)
|
|
|
|
// Checks that wallet balances, ledger and lots agree (docs/prd-point-coin.md §7.5)
|
|
a.walletRecon = service.NewWalletReconciliationJob(
|
|
repository.NewWalletReconciliationRepository(a.db),
|
|
repos.userRepo,
|
|
processors.notificationProcessor,
|
|
)
|
|
// Earns for paid orders whose earning failed at payment time (docs/prd-point-coin.md F3)
|
|
a.earningRetry = service.NewEarningBackfillJob(processors.earningProcessor)
|
|
// Expires balances whose time is up and reminds customers before (docs/prd-point-coin.md F12)
|
|
a.walletExpiry = service.NewWalletExpiryJob(processor.NewWalletExpiryProcessor(
|
|
repository.NewWalletExpiryRepository(a.db), processors.loyaltySettingsProcessor, processor.NewWalletProcessor(repos.walletRepo), repos.txManager, processors.customerDeviceProcessor))
|
|
// Refunds or expires EnakGame sessions left open (docs/rfc-enakgame.md §7.3)
|
|
a.gameSessions = service.NewGameSessionJob(processors.gameSessionProcessor)
|
|
// Opens next month's global EnakGame budget (docs/rfc-enakgame.md §12)
|
|
a.gameBudgets = service.NewGameBudgetPeriodJob(processors.gameBudgetProcessor)
|
|
// Expires voucher codes past their date (docs/rfc-enakgame.md §12)
|
|
a.voucherCodes = service.NewVoucherCodeExpiryJob(processors.voucherAdminProcessor)
|
|
// Settles EXTERNAL voucher redemptions left PENDING (docs/rfc-enakgame.md §7.5)
|
|
a.voucherRecover = service.NewVoucherRedemptionRecoveryJob(processors.voucherRedemptionProcessor)
|
|
|
|
services := a.initServices(processors, repos, cfg)
|
|
validators := a.initValidators()
|
|
middleware := a.initMiddleware(services, cfg)
|
|
healthHandler := handler.NewHealthHandler()
|
|
selfOrderHandler := handler.NewSelfOrderHandler(
|
|
services.orderService,
|
|
services.categoryService,
|
|
services.productService,
|
|
repos.tableRepo,
|
|
repos.outletRepo,
|
|
repos.userRepo,
|
|
repos.sessionRepo,
|
|
repos.orderRepo,
|
|
services.productOutletPriceService,
|
|
)
|
|
|
|
a.router = router.NewRouter(
|
|
cfg,
|
|
healthHandler,
|
|
services.authService,
|
|
middleware.authMiddleware,
|
|
services.userService,
|
|
validators.userValidator,
|
|
services.organizationService,
|
|
validators.organizationValidator,
|
|
services.outletService,
|
|
validators.outletValidator,
|
|
services.outletSettingService,
|
|
services.categoryService,
|
|
validators.categoryValidator,
|
|
services.productService,
|
|
validators.productValidator,
|
|
services.productVariantService,
|
|
validators.productVariantValidator,
|
|
services.inventoryService,
|
|
validators.inventoryValidator,
|
|
services.orderService,
|
|
validators.orderValidator,
|
|
services.fileService,
|
|
validators.fileValidator,
|
|
services.customerService,
|
|
validators.customerValidator,
|
|
services.paymentMethodService,
|
|
validators.paymentMethodValidator,
|
|
services.analyticsService,
|
|
services.reportService,
|
|
services.tableService,
|
|
validators.tableValidator,
|
|
services.unitService,
|
|
services.ingredientService,
|
|
services.productRecipeService,
|
|
services.vendorService,
|
|
validators.vendorValidator,
|
|
services.purchaseOrderService,
|
|
validators.purchaseOrderValidator,
|
|
services.purchaseCategoryService,
|
|
validators.purchaseCategoryValidator,
|
|
services.unitConverterService,
|
|
validators.unitConverterValidator,
|
|
services.chartOfAccountTypeService,
|
|
validators.chartOfAccountTypeValidator,
|
|
services.chartOfAccountService,
|
|
validators.chartOfAccountValidator,
|
|
services.accountService,
|
|
validators.accountValidator,
|
|
*services.orderIngredientTransactionService,
|
|
validators.orderIngredientTransactionValidator,
|
|
services.gamificationService,
|
|
validators.gamificationValidator,
|
|
services.campaignService,
|
|
validators.campaignValidator,
|
|
services.customerAuthService,
|
|
validators.customerAuthValidator,
|
|
services.customerPointsService,
|
|
middleware.customerAuthMiddleware,
|
|
services.userDeviceService,
|
|
validators.userDeviceValidator,
|
|
services.notificationService,
|
|
validators.notificationValidator,
|
|
services.productOutletPriceService,
|
|
validators.productOutletPriceValidator,
|
|
selfOrderHandler,
|
|
services.expenseService,
|
|
validators.expenseValidator,
|
|
services.cashAdvanceService,
|
|
validators.cashAdvanceValidator,
|
|
services.walletAdminService,
|
|
validators.walletValidator,
|
|
services.loyaltySettingsService,
|
|
services.customerPinService,
|
|
services.customerWalletService,
|
|
services.customerDeviceService,
|
|
services.customerOutletService,
|
|
services.customerOrderService,
|
|
services.enakGameAdminService,
|
|
services.enakGameCustomerService,
|
|
a.redisClient,
|
|
)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (a *App) Start(port string) error {
|
|
// Start the omset milestone scheduler (checks every 5 minutes for daily omset milestones)
|
|
if a.omsetScheduler != nil {
|
|
a.omsetScheduler.Start(5 * time.Minute)
|
|
}
|
|
if a.walletRecon != nil {
|
|
a.walletRecon.Start(6 * time.Hour)
|
|
}
|
|
if a.earningRetry != nil {
|
|
a.earningRetry.Start(30 * time.Minute)
|
|
}
|
|
if a.walletExpiry != nil {
|
|
a.walletExpiry.Start(15 * time.Minute)
|
|
}
|
|
if a.gameSessions != nil {
|
|
a.gameSessions.Start(time.Minute)
|
|
}
|
|
if a.gameBudgets != nil {
|
|
a.gameBudgets.Start(24 * time.Hour)
|
|
}
|
|
if a.voucherCodes != nil {
|
|
a.voucherCodes.Start(time.Hour)
|
|
}
|
|
if a.voucherRecover != nil {
|
|
a.voucherRecover.Start(time.Minute)
|
|
}
|
|
|
|
engine := a.router.Init()
|
|
|
|
a.server = &http.Server{
|
|
Addr: ":" + port,
|
|
Handler: engine,
|
|
ReadTimeout: 15 * time.Second,
|
|
WriteTimeout: 15 * time.Second,
|
|
IdleTimeout: 60 * time.Second,
|
|
}
|
|
|
|
signal.Notify(a.shutdown, os.Interrupt, syscall.SIGTERM)
|
|
|
|
go func() {
|
|
log.Printf("Server starting on port %s", port)
|
|
if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Fatalf("Failed to start server: %v", err)
|
|
}
|
|
}()
|
|
|
|
<-a.shutdown
|
|
log.Println("Shutting down server...")
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
defer cancel()
|
|
|
|
if err := a.server.Shutdown(ctx); err != nil {
|
|
log.Printf("Server forced to shutdown: %v", err)
|
|
return err
|
|
}
|
|
|
|
log.Println("Server exited gracefully")
|
|
return nil
|
|
}
|
|
|
|
func (a *App) Shutdown() {
|
|
if a.omsetScheduler != nil {
|
|
a.omsetScheduler.Stop()
|
|
}
|
|
if a.walletRecon != nil {
|
|
a.walletRecon.Stop()
|
|
}
|
|
if a.earningRetry != nil {
|
|
a.earningRetry.Stop()
|
|
}
|
|
if a.walletExpiry != nil {
|
|
a.walletExpiry.Stop()
|
|
}
|
|
if a.gameSessions != nil {
|
|
a.gameSessions.Stop()
|
|
}
|
|
if a.gameBudgets != nil {
|
|
a.gameBudgets.Stop()
|
|
}
|
|
if a.voucherCodes != nil {
|
|
a.voucherCodes.Stop()
|
|
}
|
|
if a.voucherRecover != nil {
|
|
a.voucherRecover.Stop()
|
|
}
|
|
close(a.shutdown)
|
|
}
|
|
|
|
type repositories struct {
|
|
userRepo *repository.UserRepositoryImpl
|
|
organizationRepo *repository.OrganizationRepositoryImpl
|
|
outletRepo *repository.OutletRepositoryImpl
|
|
outletSettingRepo *repository.OutletSettingRepositoryImpl
|
|
categoryRepo *repository.CategoryRepositoryImpl
|
|
productRepo *repository.ProductRepositoryImpl
|
|
productVariantRepo *repository.ProductVariantRepositoryImpl
|
|
inventoryRepo *repository.InventoryRepositoryImpl
|
|
inventoryMovementRepo *repository.InventoryMovementRepositoryImpl
|
|
orderRepo *repository.OrderRepositoryImpl
|
|
orderItemRepo *repository.OrderItemRepositoryImpl
|
|
paymentRepo *repository.PaymentRepositoryImpl
|
|
paymentOrderItemRepo *repository.PaymentOrderItemRepositoryImpl
|
|
paymentMethodRepo *repository.PaymentMethodRepositoryImpl
|
|
fileRepo *repository.FileRepositoryImpl
|
|
customerRepo *repository.CustomerRepository
|
|
analyticsRepo *repository.AnalyticsRepositoryImpl
|
|
tableRepo *repository.TableRepository
|
|
unitRepo *repository.UnitRepository
|
|
ingredientRepo *repository.IngredientRepository
|
|
ingredientCompositionRepo *repository.IngredientCompositionRepository
|
|
productRecipeRepo *repository.ProductRecipeRepository
|
|
vendorRepo *repository.VendorRepositoryImpl
|
|
purchaseOrderRepo *repository.PurchaseOrderRepositoryImpl
|
|
purchaseCategoryRepo *repository.PurchaseCategoryRepositoryImpl
|
|
unitConverterRepo *repository.IngredientUnitConverterRepositoryImpl
|
|
chartOfAccountTypeRepo *repository.ChartOfAccountTypeRepositoryImpl
|
|
chartOfAccountRepo *repository.ChartOfAccountRepositoryImpl
|
|
accountRepo *repository.AccountRepositoryImpl
|
|
orderIngredientTransactionRepo *repository.OrderIngredientTransactionRepositoryImpl
|
|
tierRepo *repository.TierRepository
|
|
omsetTrackerRepo *repository.OmsetTrackerRepository
|
|
campaignRepo repository.CampaignRepository
|
|
campaignRuleRepo repository.CampaignRuleRepository
|
|
customerAuthRepo repository.CustomerAuthRepository
|
|
customerLoginAttemptRepo repository.CustomerLoginAttemptRepository
|
|
otpRepo repository.OtpRepository
|
|
sessionRepo repository.SessionRepository
|
|
txManager *repository.TxManager
|
|
userDeviceRepo *repository.UserDeviceRepositoryImpl
|
|
notificationRepo *repository.NotificationRepositoryImpl
|
|
notificationReceiverRepo *repository.NotificationReceiverRepositoryImpl
|
|
notificationDeliveryRepo *repository.NotificationDeliveryRepositoryImpl
|
|
productOutletPriceRepo *repository.ProductOutletPriceRepositoryImpl
|
|
expenseRepo *repository.ExpenseRepositoryImpl
|
|
cashAdvanceRepo *repository.CashAdvanceRepositoryImpl
|
|
walletRepo repository.WalletRepository
|
|
walletQueryRepo repository.WalletQueryRepository
|
|
loyaltySettingsRepo repository.LoyaltySettingsRepository
|
|
}
|
|
|
|
func (a *App) initRepositories() *repositories {
|
|
return &repositories{
|
|
userRepo: repository.NewUserRepository(a.db),
|
|
organizationRepo: repository.NewOrganizationRepositoryImpl(a.db),
|
|
outletRepo: repository.NewOutletRepositoryImpl(a.db),
|
|
outletSettingRepo: repository.NewOutletSettingRepositoryImpl(a.db),
|
|
categoryRepo: repository.NewCategoryRepositoryImpl(a.db),
|
|
productRepo: repository.NewProductRepositoryImpl(a.db),
|
|
productVariantRepo: repository.NewProductVariantRepositoryImpl(a.db),
|
|
inventoryRepo: repository.NewInventoryRepositoryImpl(a.db),
|
|
inventoryMovementRepo: repository.NewInventoryMovementRepositoryImpl(a.db),
|
|
orderRepo: repository.NewOrderRepositoryImpl(a.db),
|
|
orderItemRepo: repository.NewOrderItemRepositoryImpl(a.db),
|
|
paymentRepo: repository.NewPaymentRepositoryImpl(a.db),
|
|
paymentOrderItemRepo: repository.NewPaymentOrderItemRepositoryImpl(a.db),
|
|
paymentMethodRepo: repository.NewPaymentMethodRepositoryImpl(a.db),
|
|
fileRepo: repository.NewFileRepositoryImpl(a.db),
|
|
customerRepo: repository.NewCustomerRepository(a.db),
|
|
analyticsRepo: repository.NewAnalyticsRepositoryImpl(a.db),
|
|
tableRepo: repository.NewTableRepository(a.db),
|
|
unitRepo: repository.NewUnitRepository(a.db),
|
|
ingredientRepo: repository.NewIngredientRepository(a.db),
|
|
ingredientCompositionRepo: repository.NewIngredientCompositionRepository(a.db),
|
|
productRecipeRepo: repository.NewProductRecipeRepository(a.db),
|
|
vendorRepo: repository.NewVendorRepositoryImpl(a.db),
|
|
purchaseOrderRepo: repository.NewPurchaseOrderRepositoryImpl(a.db),
|
|
purchaseCategoryRepo: repository.NewPurchaseCategoryRepositoryImpl(a.db),
|
|
unitConverterRepo: repository.NewIngredientUnitConverterRepositoryImpl(a.db).(*repository.IngredientUnitConverterRepositoryImpl),
|
|
chartOfAccountTypeRepo: repository.NewChartOfAccountTypeRepositoryImpl(a.db),
|
|
chartOfAccountRepo: repository.NewChartOfAccountRepositoryImpl(a.db),
|
|
accountRepo: repository.NewAccountRepositoryImpl(a.db),
|
|
orderIngredientTransactionRepo: repository.NewOrderIngredientTransactionRepositoryImpl(a.db).(*repository.OrderIngredientTransactionRepositoryImpl),
|
|
tierRepo: repository.NewTierRepository(a.db),
|
|
omsetTrackerRepo: repository.NewOmsetTrackerRepository(a.db),
|
|
campaignRepo: repository.NewCampaignRepository(a.db),
|
|
campaignRuleRepo: repository.NewCampaignRuleRepository(a.db),
|
|
customerAuthRepo: repository.NewCustomerAuthRepository(a.db),
|
|
customerLoginAttemptRepo: repository.NewCustomerLoginAttemptRepository(a.redisClient),
|
|
otpRepo: repository.NewOtpRepository(a.db),
|
|
sessionRepo: repository.NewSessionRepository(a.redisClient),
|
|
txManager: repository.NewTxManager(a.db),
|
|
userDeviceRepo: repository.NewUserDeviceRepositoryImpl(a.db),
|
|
notificationRepo: repository.NewNotificationRepository(a.db),
|
|
notificationReceiverRepo: repository.NewNotificationReceiverRepository(a.db),
|
|
notificationDeliveryRepo: repository.NewNotificationDeliveryRepository(a.db),
|
|
productOutletPriceRepo: repository.NewProductOutletPriceRepositoryImpl(a.db),
|
|
expenseRepo: repository.NewExpenseRepositoryImpl(a.db),
|
|
cashAdvanceRepo: repository.NewCashAdvanceRepositoryImpl(a.db),
|
|
walletRepo: repository.NewWalletRepository(a.db),
|
|
walletQueryRepo: repository.NewWalletQueryRepository(a.db),
|
|
loyaltySettingsRepo: repository.NewLoyaltySettingsRepository(a.db),
|
|
}
|
|
}
|
|
|
|
type processors struct {
|
|
userProcessor *processor.UserProcessorImpl
|
|
organizationProcessor processor.OrganizationProcessor
|
|
outletProcessor processor.OutletProcessor
|
|
outletSettingProcessor *processor.OutletSettingProcessorImpl
|
|
categoryProcessor processor.CategoryProcessor
|
|
productProcessor processor.ProductProcessor
|
|
productVariantProcessor processor.ProductVariantProcessor
|
|
inventoryProcessor processor.InventoryProcessor
|
|
orderProcessor processor.OrderProcessor
|
|
paymentMethodProcessor processor.PaymentMethodProcessor
|
|
fileProcessor processor.FileProcessor
|
|
customerProcessor *processor.CustomerProcessor
|
|
analyticsProcessor *processor.AnalyticsProcessorImpl
|
|
tableProcessor *processor.TableProcessor
|
|
unitProcessor *processor.UnitProcessorImpl
|
|
ingredientProcessor *processor.IngredientProcessorImpl
|
|
productRecipeProcessor *processor.ProductRecipeProcessorImpl
|
|
vendorProcessor *processor.VendorProcessorImpl
|
|
purchaseOrderProcessor *processor.PurchaseOrderProcessorImpl
|
|
purchaseCategoryProcessor *processor.PurchaseCategoryProcessorImpl
|
|
unitConverterProcessor *processor.IngredientUnitConverterProcessorImpl
|
|
chartOfAccountTypeProcessor *processor.ChartOfAccountTypeProcessorImpl
|
|
chartOfAccountProcessor *processor.ChartOfAccountProcessorImpl
|
|
accountProcessor *processor.AccountProcessorImpl
|
|
orderIngredientTransactionProcessor *processor.OrderIngredientTransactionProcessorImpl
|
|
tierProcessor *processor.TierProcessor
|
|
omsetTrackerProcessor *processor.OmsetTrackerProcessor
|
|
campaignProcessor processor.CampaignProcessor
|
|
campaignRuleProcessor processor.CampaignRuleProcessor
|
|
customerAuthProcessor processor.CustomerAuthProcessor
|
|
customerPointsProcessor *processor.CustomerPointsProcessor
|
|
otpProcessor processor.OtpProcessor
|
|
fileClient processor.FileClient
|
|
inventoryMovementService service.InventoryMovementService
|
|
userDeviceProcessor *processor.UserDeviceProcessorImpl
|
|
notificationProcessor *processor.NotificationProcessorImpl
|
|
productOutletPriceProcessor processor.ProductOutletPriceProcessor
|
|
expenseProcessor *processor.ExpenseProcessorImpl
|
|
cashAdvanceProcessor *processor.CashAdvanceProcessorImpl
|
|
walletProcessor *processor.WalletProcessor
|
|
walletAdminProcessor *processor.WalletAdminProcessor
|
|
loyaltySettingsProcessor *processor.LoyaltySettingsProcessor
|
|
earningProcessor *processor.EarningProcessor
|
|
customerPinProcessor *processor.CustomerPinProcessor
|
|
walletExchangeProcessor *processor.WalletExchangeProcessor
|
|
walletTransferProcessor *processor.WalletTransferProcessor
|
|
walletTraceProcessor *processor.WalletTraceProcessor
|
|
customerDeviceProcessor *processor.CustomerDeviceProcessor
|
|
customerOutletProcessor *processor.CustomerOutletProcessor
|
|
customerOrderProcessor *processor.CustomerOrderProcessor
|
|
enakGameAdminProcessor *processor.EnakGameAdminProcessor
|
|
gameBudgetProcessor *processor.GameBudgetProcessor
|
|
gameSessionProcessor *processor.GameSessionProcessor
|
|
voucherAdminProcessor *processor.VoucherAdminProcessor
|
|
voucherRedemptionProcessor *processor.VoucherRedemptionProcessor
|
|
}
|
|
|
|
func (a *App) initProcessors(cfg *config.Config, repos *repositories) *processors {
|
|
fileClient := client.NewFileClient(cfg.S3Config)
|
|
fcmClient := buildFCMClient(cfg)
|
|
// Devices of the customer app, for pushes through FCM
|
|
customerDeviceProcessor := processor.NewCustomerDeviceProcessor(repository.NewCustomerDeviceRepository(a.db), fcmClient)
|
|
fonnteClient := client.NewFonnteClient(cfg.GetFonnte())
|
|
otpProcessor := processor.NewOtpProcessor(fonnteClient, repos.otpRepo)
|
|
// Customer PIN (docs/prd-point-coin.md F11)
|
|
customerPinProcessor := processor.NewCustomerPinProcessor(repository.NewCustomerPinRepository(a.db), otpProcessor, customerDeviceProcessor)
|
|
inventoryMovementService := service.NewInventoryMovementService(repos.inventoryMovementRepo, repos.ingredientRepo)
|
|
|
|
orderProcessor := processor.NewOrderProcessorImpl(repos.orderRepo, repos.orderItemRepo, repos.paymentRepo, repos.paymentOrderItemRepo, repos.productRepo, repos.paymentMethodRepo, repos.inventoryRepo, repos.inventoryMovementRepo, repos.productVariantRepo, repos.outletRepo, repos.customerRepo, repos.txManager, repos.productRecipeRepo, repos.ingredientRepo, inventoryMovementService, repos.productOutletPriceRepo)
|
|
loyaltySettingsProcessor := processor.NewLoyaltySettingsProcessor(repos.loyaltySettingsRepo, repos.txManager)
|
|
auditLogger := processor.NewAuditLogger(repository.NewAuditLogRepository(a.db))
|
|
enakGameRepo := repository.NewEnakGameRepository(a.db)
|
|
gameBudgetRepo := repository.NewGameBudgetRepository(a.db)
|
|
voucherRepo := repository.NewVoucherRepository(a.db)
|
|
// Earn EnakPoint and EnakCoin when an order becomes fully paid (docs/prd-point-coin.md F3)
|
|
earningProcessor := processor.NewEarningProcessor(repository.NewEarningRepository(a.db), loyaltySettingsProcessor, processor.NewWalletProcessor(repos.walletRepo), repos.txManager)
|
|
orderProcessor.SetLoyalty(earningProcessor)
|
|
// Exchange EnakCoin into EnakPoint, approved by the customer's PIN (docs/prd-point-coin.md F4)
|
|
walletExchangeProcessor := processor.NewWalletExchangeProcessor(repository.NewWalletMoveRepository(a.db), loyaltySettingsProcessor, repos.walletQueryRepo, customerPinProcessor, processor.NewWalletProcessor(repos.walletRepo), repos.txManager)
|
|
// Send EnakPoint or EnakCoin to another customer; the recipient gets a push through FCM (docs/prd-point-coin.md F5)
|
|
walletTransferProcessor := processor.NewWalletTransferProcessor(repository.NewWalletMoveRepository(a.db), loyaltySettingsProcessor, repos.walletQueryRepo, customerPinProcessor, processor.NewWalletProcessor(repos.walletRepo), repos.txManager, customerDeviceProcessor)
|
|
|
|
return &processors{
|
|
userProcessor: processor.NewUserProcessor(repos.userRepo, repos.organizationRepo, repos.outletRepo),
|
|
organizationProcessor: processor.NewOrganizationProcessorImpl(repos.organizationRepo, repos.outletRepo, repos.userRepo),
|
|
outletProcessor: processor.NewOutletProcessorImpl(repos.outletRepo),
|
|
outletSettingProcessor: processor.NewOutletSettingProcessorImpl(repos.outletSettingRepo, repos.outletRepo),
|
|
categoryProcessor: processor.NewCategoryProcessorImpl(repos.categoryRepo),
|
|
productProcessor: processor.NewProductProcessorImpl(repos.productRepo, repos.categoryRepo, repos.productVariantRepo, repos.inventoryRepo, repos.outletRepo, repos.productOutletPriceRepo),
|
|
productVariantProcessor: processor.NewProductVariantProcessorImpl(repos.productVariantRepo, repos.productRepo),
|
|
inventoryProcessor: processor.NewInventoryProcessorImpl(repos.inventoryRepo, repos.productRepo, repos.outletRepo, repos.ingredientRepo, repos.inventoryMovementRepo),
|
|
orderProcessor: orderProcessor,
|
|
paymentMethodProcessor: processor.NewPaymentMethodProcessorImpl(repos.paymentMethodRepo),
|
|
fileProcessor: processor.NewFileProcessorImpl(repos.fileRepo, fileClient),
|
|
customerProcessor: processor.NewCustomerProcessor(repos.customerRepo),
|
|
analyticsProcessor: processor.NewAnalyticsProcessorImpl(repos.analyticsRepo, repos.expenseRepo),
|
|
tableProcessor: processor.NewTableProcessor(repos.tableRepo, repos.orderRepo),
|
|
unitProcessor: processor.NewUnitProcessor(repos.unitRepo),
|
|
ingredientProcessor: processor.NewIngredientProcessor(repos.ingredientRepo, repos.unitRepo, repos.ingredientCompositionRepo),
|
|
productRecipeProcessor: processor.NewProductRecipeProcessor(repos.productRecipeRepo, repos.productRepo, repos.ingredientRepo),
|
|
vendorProcessor: processor.NewVendorProcessorImpl(repos.vendorRepo),
|
|
purchaseOrderProcessor: processor.NewPurchaseOrderProcessorImpl(repos.purchaseOrderRepo, repos.vendorRepo, repos.ingredientRepo, repos.purchaseCategoryRepo, repos.categoryRepo, repos.cashAdvanceRepo, repos.unitRepo, repos.fileRepo, inventoryMovementService, repos.unitConverterRepo),
|
|
purchaseCategoryProcessor: processor.NewPurchaseCategoryProcessorImpl(repos.purchaseCategoryRepo),
|
|
unitConverterProcessor: processor.NewIngredientUnitConverterProcessorImpl(repos.unitConverterRepo, repos.ingredientRepo, repos.unitRepo),
|
|
chartOfAccountTypeProcessor: processor.NewChartOfAccountTypeProcessorImpl(repos.chartOfAccountTypeRepo),
|
|
chartOfAccountProcessor: processor.NewChartOfAccountProcessorImpl(repos.chartOfAccountRepo, repos.chartOfAccountTypeRepo),
|
|
accountProcessor: processor.NewAccountProcessorImpl(repos.accountRepo, repos.chartOfAccountRepo),
|
|
orderIngredientTransactionProcessor: processor.NewOrderIngredientTransactionProcessorImpl(repos.orderIngredientTransactionRepo, repos.productRecipeRepo, repos.ingredientRepo, repos.unitRepo).(*processor.OrderIngredientTransactionProcessorImpl),
|
|
tierProcessor: processor.NewTierProcessor(repos.tierRepo),
|
|
omsetTrackerProcessor: processor.NewOmsetTrackerProcessor(repos.omsetTrackerRepo),
|
|
campaignProcessor: processor.NewCampaignProcessor(repos.campaignRepo),
|
|
campaignRuleProcessor: processor.NewCampaignRuleProcessor(repos.campaignRuleRepo),
|
|
customerAuthProcessor: processor.NewCustomerAuthProcessor(repos.customerAuthRepo, repos.customerLoginAttemptRepo, otpProcessor, repos.otpRepo, cfg.GetCustomerJWTSecret(), cfg.GetCustomerJWTExpiresTTL()),
|
|
customerPointsProcessor: processor.NewCustomerPointsProcessor(processor.NewWalletQueryProcessor(repos.walletQueryRepo, processor.NewLoyaltySettingsProcessor(repos.loyaltySettingsRepo, repos.txManager))),
|
|
otpProcessor: otpProcessor,
|
|
fileClient: fileClient,
|
|
inventoryMovementService: inventoryMovementService,
|
|
userDeviceProcessor: processor.NewUserDeviceProcessorImpl(repos.userDeviceRepo),
|
|
notificationProcessor: buildNotificationProcessor(repos, fcmClient),
|
|
productOutletPriceProcessor: processor.NewProductOutletPriceProcessorImpl(repos.productOutletPriceRepo, repos.productRepo, repos.outletRepo),
|
|
expenseProcessor: processor.NewExpenseProcessorImpl(repos.expenseRepo, repos.purchaseCategoryRepo, repos.cashAdvanceRepo),
|
|
cashAdvanceProcessor: processor.NewCashAdvanceProcessorImpl(repos.cashAdvanceRepo, repos.categoryRepo),
|
|
walletProcessor: processor.NewWalletProcessor(repos.walletRepo),
|
|
loyaltySettingsProcessor: loyaltySettingsProcessor,
|
|
earningProcessor: earningProcessor,
|
|
customerPinProcessor: customerPinProcessor,
|
|
walletExchangeProcessor: walletExchangeProcessor,
|
|
walletTransferProcessor: walletTransferProcessor,
|
|
walletTraceProcessor: processor.NewWalletTraceProcessor(repository.NewWalletTraceRepository(a.db)),
|
|
customerDeviceProcessor: customerDeviceProcessor,
|
|
customerOutletProcessor: processor.NewCustomerOutletProcessor(repository.NewCustomerOutletRepository(a.db), loyaltySettingsProcessor),
|
|
customerOrderProcessor: processor.NewCustomerOrderProcessor(repository.NewCustomerOrderRepository(a.db), earningProcessor),
|
|
walletAdminProcessor: processor.NewWalletAdminProcessor(repository.NewWalletAdminRepository(a.db), repos.walletQueryRepo, processor.NewWalletProcessor(repos.walletRepo), loyaltySettingsProcessor, repos.txManager),
|
|
// EnakGame (docs/rfc-enakgame.md)
|
|
enakGameAdminProcessor: processor.NewEnakGameAdminProcessor(enakGameRepo, auditLogger, repos.txManager),
|
|
gameBudgetProcessor: processor.NewGameBudgetProcessor(gameBudgetRepo, auditLogger, repos.txManager),
|
|
gameSessionProcessor: processor.NewGameSessionProcessor(repository.NewWalletMoveRepository(a.db), enakGameRepo,
|
|
repository.NewGameSessionRepository(a.db), gameBudgetRepo, repository.NewGameEventRepository(a.db), repository.NewGameRewardCounterRepository(a.db), loyaltySettingsProcessor, repos.walletQueryRepo, processor.NewWalletProcessor(repos.walletRepo), auditLogger, repos.txManager),
|
|
voucherAdminProcessor: processor.NewVoucherAdminProcessor(voucherRepo, auditLogger, repos.txManager),
|
|
voucherRedemptionProcessor: processor.NewVoucherRedemptionProcessor(repository.NewWalletMoveRepository(a.db), voucherRepo,
|
|
repository.NewVoucherRedemptionRepository(a.db), customerPinProcessor, repos.walletQueryRepo, processor.NewWalletProcessor(repos.walletRepo),
|
|
// No provider has an adapter yet (EG-801): EXTERNAL vouchers stay out of the
|
|
// catalog and cannot be redeemed until one is added here.
|
|
processor.VoucherProviders{}, repos.txManager),
|
|
}
|
|
}
|
|
|
|
type services struct {
|
|
userService *service.UserServiceImpl
|
|
authService service.AuthService
|
|
organizationService service.OrganizationService
|
|
outletService service.OutletService
|
|
outletSettingService service.OutletSettingService
|
|
categoryService service.CategoryService
|
|
productService service.ProductService
|
|
productVariantService service.ProductVariantService
|
|
inventoryService service.InventoryService
|
|
orderService service.OrderService
|
|
paymentMethodService service.PaymentMethodService
|
|
fileService service.FileService
|
|
customerService service.CustomerService
|
|
analyticsService *service.AnalyticsServiceImpl
|
|
reportService service.ReportService
|
|
tableService *service.TableServiceImpl
|
|
unitService *service.UnitServiceImpl
|
|
ingredientService *service.IngredientServiceImpl
|
|
productRecipeService *service.ProductRecipeServiceImpl
|
|
vendorService *service.VendorServiceImpl
|
|
purchaseOrderService *service.PurchaseOrderServiceImpl
|
|
purchaseCategoryService service.PurchaseCategoryService
|
|
unitConverterService *service.IngredientUnitConverterServiceImpl
|
|
chartOfAccountTypeService service.ChartOfAccountTypeService
|
|
chartOfAccountService service.ChartOfAccountService
|
|
accountService service.AccountService
|
|
orderIngredientTransactionService *service.OrderIngredientTransactionService
|
|
gamificationService service.GamificationService
|
|
campaignService service.CampaignService
|
|
customerAuthService service.CustomerAuthService
|
|
customerPointsService service.CustomerPointsService
|
|
userDeviceService service.UserDeviceService
|
|
notificationService service.NotificationService
|
|
productOutletPriceService service.ProductOutletPriceService
|
|
expenseService *service.ExpenseServiceImpl
|
|
cashAdvanceService *service.CashAdvanceServiceImpl
|
|
walletAdminService *service.WalletAdminServiceImpl
|
|
loyaltySettingsService *service.LoyaltySettingsServiceImpl
|
|
customerPinService *service.CustomerPinServiceImpl
|
|
customerWalletService *service.CustomerWalletServiceImpl
|
|
customerDeviceService *service.CustomerDeviceServiceImpl
|
|
customerOutletService *service.CustomerOutletServiceImpl
|
|
customerOrderService *service.CustomerOrderServiceImpl
|
|
enakGameAdminService *service.EnakGameAdminServiceImpl
|
|
enakGameCustomerService *service.EnakGameCustomerServiceImpl
|
|
}
|
|
|
|
func (a *App) initServices(processors *processors, repos *repositories, cfg *config.Config) *services {
|
|
authConfig := cfg.Auth()
|
|
authService := service.NewAuthService(processors.userProcessor, processors.userDeviceProcessor, authConfig)
|
|
organizationService := service.NewOrganizationService(processors.organizationProcessor)
|
|
outletService := service.NewOutletService(processors.outletProcessor)
|
|
outletSettingService := service.NewOutletSettingService(processors.outletSettingProcessor)
|
|
categoryService := service.NewCategoryService(processors.categoryProcessor)
|
|
productService := service.NewProductService(processors.productProcessor)
|
|
productVariantService := service.NewProductVariantService(processors.productVariantProcessor)
|
|
inventoryService := service.NewInventoryService(processors.inventoryProcessor)
|
|
orderService := service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, nil, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager, repos.sessionRepo, processors.notificationProcessor, repos.userRepo) // Will be updated after orderIngredientTransactionService is created
|
|
paymentMethodService := service.NewPaymentMethodService(processors.paymentMethodProcessor)
|
|
enakGameAudit := processor.NewAuditLogger(repository.NewAuditLogRepository(a.db))
|
|
gameBudgetMetrics := processor.NewGameBudgetMetricsProcessor(repository.NewGameBudgetRepository(a.db), repository.NewGameBudgetMetricsRepository(a.db))
|
|
fileService := service.NewFileServiceImpl(processors.fileProcessor)
|
|
var customerService service.CustomerService = service.NewCustomerService(processors.customerProcessor)
|
|
analyticsService := service.NewAnalyticsServiceImpl(processors.analyticsProcessor)
|
|
reportService := service.NewReportService(analyticsService, repos.organizationRepo, repos.outletRepo, processors.fileClient)
|
|
tableService := service.NewTableService(processors.tableProcessor, transformer.NewTableTransformer())
|
|
unitService := service.NewUnitService(processors.unitProcessor)
|
|
ingredientService := service.NewIngredientService(processors.ingredientProcessor)
|
|
productRecipeService := service.NewProductRecipeService(processors.productRecipeProcessor)
|
|
vendorService := service.NewVendorService(processors.vendorProcessor)
|
|
purchaseOrderService := service.NewPurchaseOrderService(processors.purchaseOrderProcessor)
|
|
purchaseCategoryService := service.NewPurchaseCategoryService(processors.purchaseCategoryProcessor)
|
|
unitConverterService := service.NewIngredientUnitConverterService(processors.unitConverterProcessor)
|
|
chartOfAccountTypeService := service.NewChartOfAccountTypeService(processors.chartOfAccountTypeProcessor)
|
|
chartOfAccountService := service.NewChartOfAccountService(processors.chartOfAccountProcessor)
|
|
accountService := service.NewAccountService(processors.accountProcessor)
|
|
orderIngredientTransactionService := service.NewOrderIngredientTransactionService(processors.orderIngredientTransactionProcessor, repos.txManager)
|
|
gamificationService := service.NewGamificationService(processors.tierProcessor, processors.omsetTrackerProcessor)
|
|
campaignService := service.NewCampaignService(processors.campaignProcessor, processors.campaignRuleProcessor)
|
|
customerAuthService := service.NewCustomerAuthService(processors.customerAuthProcessor)
|
|
customerPointsService := service.NewCustomerPointsService(processors.customerPointsProcessor)
|
|
userDeviceService := service.NewUserDeviceService(processors.userDeviceProcessor)
|
|
notificationService := service.NewNotificationService(processors.notificationProcessor)
|
|
|
|
// Update order service with order ingredient transaction service
|
|
orderService = service.NewOrderServiceImpl(processors.orderProcessor, repos.tableRepo, orderIngredientTransactionService, processors.orderIngredientTransactionProcessor, *repos.productRecipeRepo, repos.txManager, repos.sessionRepo, processors.notificationProcessor, repos.userRepo)
|
|
|
|
return &services{
|
|
userService: service.NewUserService(processors.userProcessor),
|
|
authService: authService,
|
|
organizationService: organizationService,
|
|
outletService: outletService,
|
|
outletSettingService: outletSettingService,
|
|
categoryService: categoryService,
|
|
productService: productService,
|
|
productVariantService: productVariantService,
|
|
inventoryService: inventoryService,
|
|
orderService: orderService,
|
|
paymentMethodService: paymentMethodService,
|
|
fileService: fileService,
|
|
customerService: customerService,
|
|
analyticsService: analyticsService,
|
|
reportService: reportService,
|
|
tableService: tableService,
|
|
unitService: unitService,
|
|
ingredientService: ingredientService,
|
|
productRecipeService: productRecipeService,
|
|
vendorService: vendorService,
|
|
purchaseOrderService: purchaseOrderService,
|
|
purchaseCategoryService: purchaseCategoryService,
|
|
unitConverterService: unitConverterService,
|
|
chartOfAccountTypeService: chartOfAccountTypeService,
|
|
chartOfAccountService: chartOfAccountService,
|
|
accountService: accountService,
|
|
orderIngredientTransactionService: orderIngredientTransactionService,
|
|
gamificationService: gamificationService,
|
|
campaignService: campaignService,
|
|
customerAuthService: customerAuthService,
|
|
customerPointsService: customerPointsService,
|
|
userDeviceService: userDeviceService,
|
|
notificationService: notificationService,
|
|
productOutletPriceService: service.NewProductOutletPriceService(processors.productOutletPriceProcessor),
|
|
expenseService: service.NewExpenseService(processors.expenseProcessor),
|
|
cashAdvanceService: service.NewCashAdvanceService(processors.cashAdvanceProcessor),
|
|
walletAdminService: service.NewWalletAdminService(processors.walletAdminProcessor, processors.walletTraceProcessor),
|
|
loyaltySettingsService: service.NewLoyaltySettingsService(processors.loyaltySettingsProcessor, repos.walletQueryRepo),
|
|
customerPinService: service.NewCustomerPinService(processors.customerPinProcessor),
|
|
customerWalletService: service.NewCustomerWalletService(processors.walletExchangeProcessor, processors.walletTransferProcessor),
|
|
customerDeviceService: service.NewCustomerDeviceService(processors.customerDeviceProcessor),
|
|
customerOutletService: service.NewCustomerOutletService(processors.customerOutletProcessor),
|
|
customerOrderService: service.NewCustomerOrderService(processors.customerOrderProcessor),
|
|
enakGameAdminService: service.NewEnakGameAdminService(processors.enakGameAdminProcessor, processors.gameBudgetProcessor,
|
|
gameBudgetMetrics,
|
|
processor.NewGameBudgetControllerProcessor(repository.NewGameBudgetRepository(a.db), gameBudgetMetrics, repository.NewEnakGameRepository(a.db),
|
|
enakGameAudit, repos.txManager),
|
|
processor.NewGameEventProcessor(repository.NewGameEventRepository(a.db), repository.NewEnakGameRepository(a.db), repository.NewGameBudgetRepository(a.db),
|
|
enakGameAudit, repos.txManager),
|
|
processors.voucherAdminProcessor,
|
|
processor.NewEnakGameAnalyticsProcessor(repository.NewEnakGameAnalyticsRepository(a.db))),
|
|
enakGameCustomerService: service.NewEnakGameCustomerService(processors.gameSessionProcessor, processors.voucherRedemptionProcessor),
|
|
}
|
|
}
|
|
|
|
type middlewares struct {
|
|
authMiddleware *middleware.AuthMiddleware
|
|
customerAuthMiddleware *middleware.CustomerAuthMiddleware
|
|
}
|
|
|
|
func (a *App) initMiddleware(services *services, cfg *config.Config) *middlewares {
|
|
return &middlewares{
|
|
authMiddleware: middleware.NewAuthMiddleware(services.authService),
|
|
customerAuthMiddleware: middleware.NewCustomerAuthMiddleware(cfg.GetCustomerJWTSecret()),
|
|
}
|
|
}
|
|
|
|
type validators struct {
|
|
userValidator *validator.UserValidatorImpl
|
|
organizationValidator validator.OrganizationValidator
|
|
outletValidator validator.OutletValidator
|
|
categoryValidator validator.CategoryValidator
|
|
productValidator validator.ProductValidator
|
|
productVariantValidator validator.ProductVariantValidator
|
|
inventoryValidator validator.InventoryValidator
|
|
orderValidator validator.OrderValidator
|
|
paymentMethodValidator validator.PaymentMethodValidator
|
|
fileValidator validator.FileValidator
|
|
customerValidator validator.CustomerValidator
|
|
tableValidator *validator.TableValidator
|
|
vendorValidator *validator.VendorValidatorImpl
|
|
purchaseOrderValidator *validator.PurchaseOrderValidatorImpl
|
|
purchaseCategoryValidator *validator.PurchaseCategoryValidatorImpl
|
|
unitConverterValidator *validator.IngredientUnitConverterValidatorImpl
|
|
chartOfAccountTypeValidator *validator.ChartOfAccountTypeValidatorImpl
|
|
chartOfAccountValidator *validator.ChartOfAccountValidatorImpl
|
|
accountValidator *validator.AccountValidatorImpl
|
|
orderIngredientTransactionValidator *validator.OrderIngredientTransactionValidatorImpl
|
|
gamificationValidator *validator.GamificationValidatorImpl
|
|
campaignValidator validator.CampaignValidator
|
|
customerAuthValidator validator.CustomerAuthValidator
|
|
userDeviceValidator *validator.UserDeviceValidatorImpl
|
|
notificationValidator *validator.NotificationValidatorImpl
|
|
productOutletPriceValidator *validator.ProductOutletPriceValidatorImpl
|
|
expenseValidator *validator.ExpenseValidatorImpl
|
|
cashAdvanceValidator *validator.CashAdvanceValidatorImpl
|
|
walletValidator *validator.WalletValidatorImpl
|
|
}
|
|
|
|
func (a *App) initValidators() *validators {
|
|
return &validators{
|
|
userValidator: validator.NewUserValidator(),
|
|
organizationValidator: validator.NewOrganizationValidator(),
|
|
outletValidator: validator.NewOutletValidator(),
|
|
categoryValidator: validator.NewCategoryValidator(),
|
|
productValidator: validator.NewProductValidator(),
|
|
productVariantValidator: validator.NewProductVariantValidator(),
|
|
inventoryValidator: validator.NewInventoryValidator(),
|
|
orderValidator: validator.NewOrderValidator(),
|
|
paymentMethodValidator: validator.NewPaymentMethodValidator(),
|
|
fileValidator: validator.NewFileValidatorImpl(),
|
|
customerValidator: validator.NewCustomerValidator(),
|
|
tableValidator: validator.NewTableValidator(),
|
|
vendorValidator: validator.NewVendorValidator(),
|
|
purchaseOrderValidator: validator.NewPurchaseOrderValidator(),
|
|
purchaseCategoryValidator: validator.NewPurchaseCategoryValidator(),
|
|
unitConverterValidator: validator.NewIngredientUnitConverterValidator().(*validator.IngredientUnitConverterValidatorImpl),
|
|
chartOfAccountTypeValidator: validator.NewChartOfAccountTypeValidator().(*validator.ChartOfAccountTypeValidatorImpl),
|
|
chartOfAccountValidator: validator.NewChartOfAccountValidator().(*validator.ChartOfAccountValidatorImpl),
|
|
accountValidator: validator.NewAccountValidator().(*validator.AccountValidatorImpl),
|
|
orderIngredientTransactionValidator: validator.NewOrderIngredientTransactionValidator().(*validator.OrderIngredientTransactionValidatorImpl),
|
|
gamificationValidator: validator.NewGamificationValidator(),
|
|
campaignValidator: validator.NewCampaignValidator(),
|
|
customerAuthValidator: validator.NewCustomerAuthValidator(),
|
|
userDeviceValidator: validator.NewUserDeviceValidator(),
|
|
notificationValidator: validator.NewNotificationValidator(),
|
|
productOutletPriceValidator: validator.NewProductOutletPriceValidator(),
|
|
expenseValidator: validator.NewExpenseValidator(),
|
|
cashAdvanceValidator: validator.NewCashAdvanceValidator(),
|
|
walletValidator: validator.NewWalletValidator(),
|
|
}
|
|
}
|
|
|
|
// buildFCMClient connects to FCM, or returns nil when it is not configured or fails to
|
|
// start. FCM failing is non-fatal: pushes are skipped and notifications are still
|
|
// persisted.
|
|
func buildFCMClient(cfg *config.Config) client.FCMClient {
|
|
if cfg.FCM.CredentialsFile == "" {
|
|
return nil
|
|
}
|
|
fcmClient, err := client.NewFCMClient(&cfg.FCM)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
return fcmClient
|
|
}
|
|
|
|
// buildNotificationProcessor creates the notification processor with FCM integration.
|
|
// If FCM is not configured, it returns a processor with a nil FCM client (FCM dispatch will be skipped).
|
|
func buildNotificationProcessor(repos *repositories, fcmClient client.FCMClient) *processor.NotificationProcessorImpl {
|
|
return processor.NewNotificationProcessor(
|
|
repos.notificationRepo,
|
|
repos.notificationReceiverRepo,
|
|
repos.notificationDeliveryRepo,
|
|
repos.userDeviceRepo,
|
|
repos.userRepo,
|
|
fcmClient,
|
|
)
|
|
}
|