386 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			386 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package core
 | 
						||
 | 
						||
import (
 | 
						||
	"fmt"
 | 
						||
	"time"
 | 
						||
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/app/webhook"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/audit"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/device"
 | 
						||
	domain_notify "git.huangwc.com/pig/pig-farm-controller/internal/domain/notify"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/pig"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/plan"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/task"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/token"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/config"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/database"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/notify"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport"
 | 
						||
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport/lora"
 | 
						||
	"gorm.io/gorm"
 | 
						||
)
 | 
						||
 | 
						||
// Infrastructure 聚合了所有基础设施层的组件。
 | 
						||
type Infrastructure struct {
 | 
						||
	storage       database.Storage
 | 
						||
	repos         *Repositories
 | 
						||
	lora          *LoraComponents
 | 
						||
	notifyService domain_notify.Service
 | 
						||
	tokenService  token.Service
 | 
						||
}
 | 
						||
 | 
						||
// initInfrastructure 初始化所有基础设施层组件。
 | 
						||
func initInfrastructure(cfg *config.Config, logger *logs.Logger) (*Infrastructure, error) {
 | 
						||
	storage, err := initStorage(cfg.Database, logger)
 | 
						||
	if err != nil {
 | 
						||
		return nil, err
 | 
						||
	}
 | 
						||
 | 
						||
	repos := initRepositories(storage.GetDB(), logger)
 | 
						||
 | 
						||
	lora, err := initLora(cfg, logger, repos)
 | 
						||
	if err != nil {
 | 
						||
		return nil, err
 | 
						||
	}
 | 
						||
 | 
						||
	notifyService, err := initNotifyService(cfg.Notify, logger, repos.userRepo, repos.notificationRepo)
 | 
						||
	if err != nil {
 | 
						||
		return nil, fmt.Errorf("初始化通知服务失败: %w", err)
 | 
						||
	}
 | 
						||
 | 
						||
	tokenService := token.NewTokenService([]byte(cfg.App.JWTSecret))
 | 
						||
 | 
						||
	return &Infrastructure{
 | 
						||
		storage:       storage,
 | 
						||
		repos:         repos,
 | 
						||
		lora:          lora,
 | 
						||
		notifyService: notifyService,
 | 
						||
		tokenService:  tokenService,
 | 
						||
	}, nil
 | 
						||
}
 | 
						||
 | 
						||
// Repositories 聚合了所有的仓库实例。
 | 
						||
type Repositories struct {
 | 
						||
	userRepo              repository.UserRepository
 | 
						||
	deviceRepo            repository.DeviceRepository
 | 
						||
	areaControllerRepo    repository.AreaControllerRepository
 | 
						||
	deviceTemplateRepo    repository.DeviceTemplateRepository
 | 
						||
	planRepo              repository.PlanRepository
 | 
						||
	pendingTaskRepo       repository.PendingTaskRepository
 | 
						||
	executionLogRepo      repository.ExecutionLogRepository
 | 
						||
	sensorDataRepo        repository.SensorDataRepository
 | 
						||
	deviceCommandLogRepo  repository.DeviceCommandLogRepository
 | 
						||
	pendingCollectionRepo repository.PendingCollectionRepository
 | 
						||
	userActionLogRepo     repository.UserActionLogRepository
 | 
						||
	pigBatchRepo          repository.PigBatchRepository
 | 
						||
	pigBatchLogRepo       repository.PigBatchLogRepository
 | 
						||
	pigFarmRepo           repository.PigFarmRepository
 | 
						||
	pigPenRepo            repository.PigPenRepository
 | 
						||
	pigTransferLogRepo    repository.PigTransferLogRepository
 | 
						||
	pigTradeRepo          repository.PigTradeRepository
 | 
						||
	pigSickPigLogRepo     repository.PigSickLogRepository
 | 
						||
	medicationLogRepo     repository.MedicationLogRepository
 | 
						||
	rawMaterialRepo       repository.RawMaterialRepository
 | 
						||
	notificationRepo      repository.NotificationRepository
 | 
						||
	unitOfWork            repository.UnitOfWork
 | 
						||
}
 | 
						||
 | 
						||
// initRepositories 初始化所有的仓库。
 | 
						||
func initRepositories(db *gorm.DB, logger *logs.Logger) *Repositories {
 | 
						||
	return &Repositories{
 | 
						||
		userRepo:              repository.NewGormUserRepository(db),
 | 
						||
		deviceRepo:            repository.NewGormDeviceRepository(db),
 | 
						||
		areaControllerRepo:    repository.NewGormAreaControllerRepository(db),
 | 
						||
		deviceTemplateRepo:    repository.NewGormDeviceTemplateRepository(db),
 | 
						||
		planRepo:              repository.NewGormPlanRepository(db),
 | 
						||
		pendingTaskRepo:       repository.NewGormPendingTaskRepository(db),
 | 
						||
		executionLogRepo:      repository.NewGormExecutionLogRepository(db),
 | 
						||
		sensorDataRepo:        repository.NewGormSensorDataRepository(db),
 | 
						||
		deviceCommandLogRepo:  repository.NewGormDeviceCommandLogRepository(db),
 | 
						||
		pendingCollectionRepo: repository.NewGormPendingCollectionRepository(db),
 | 
						||
		userActionLogRepo:     repository.NewGormUserActionLogRepository(db),
 | 
						||
		pigBatchRepo:          repository.NewGormPigBatchRepository(db),
 | 
						||
		pigBatchLogRepo:       repository.NewGormPigBatchLogRepository(db),
 | 
						||
		pigFarmRepo:           repository.NewGormPigFarmRepository(db),
 | 
						||
		pigPenRepo:            repository.NewGormPigPenRepository(db),
 | 
						||
		pigTransferLogRepo:    repository.NewGormPigTransferLogRepository(db),
 | 
						||
		pigTradeRepo:          repository.NewGormPigTradeRepository(db),
 | 
						||
		pigSickPigLogRepo:     repository.NewGormPigSickLogRepository(db),
 | 
						||
		medicationLogRepo:     repository.NewGormMedicationLogRepository(db),
 | 
						||
		rawMaterialRepo:       repository.NewGormRawMaterialRepository(db),
 | 
						||
		notificationRepo:      repository.NewGormNotificationRepository(db),
 | 
						||
		unitOfWork:            repository.NewGormUnitOfWork(db, logger),
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// DomainServices 聚合了所有的领域服务实例。
 | 
						||
type DomainServices struct {
 | 
						||
	pigPenTransferManager   pig.PigPenTransferManager
 | 
						||
	pigTradeManager         pig.PigTradeManager
 | 
						||
	pigSickManager          pig.SickPigManager
 | 
						||
	pigBatchDomain          pig.PigBatchService
 | 
						||
	generalDeviceService    device.Service
 | 
						||
	taskFactory             plan.TaskFactory
 | 
						||
	planExecutionManager    plan.ExecutionManager
 | 
						||
	analysisPlanTaskManager plan.AnalysisPlanTaskManager
 | 
						||
	planService             plan.Service
 | 
						||
}
 | 
						||
 | 
						||
// initDomainServices 初始化所有的领域服务。
 | 
						||
func initDomainServices(cfg *config.Config, infra *Infrastructure, logger *logs.Logger) *DomainServices {
 | 
						||
	// 猪群管理相关
 | 
						||
	pigPenTransferManager := pig.NewPigPenTransferManager(infra.repos.pigPenRepo, infra.repos.pigTransferLogRepo, infra.repos.pigBatchRepo)
 | 
						||
	pigTradeManager := pig.NewPigTradeManager(infra.repos.pigTradeRepo)
 | 
						||
	pigSickManager := pig.NewSickPigManager(infra.repos.pigSickPigLogRepo, infra.repos.medicationLogRepo)
 | 
						||
	pigBatchDomain := pig.NewPigBatchService(infra.repos.pigBatchRepo, infra.repos.pigBatchLogRepo, infra.repos.unitOfWork,
 | 
						||
		pigPenTransferManager, pigTradeManager, pigSickManager)
 | 
						||
 | 
						||
	// 通用设备服务
 | 
						||
	generalDeviceService := device.NewGeneralDeviceService(
 | 
						||
		infra.repos.deviceRepo,
 | 
						||
		infra.repos.deviceCommandLogRepo,
 | 
						||
		infra.repos.pendingCollectionRepo,
 | 
						||
		logger,
 | 
						||
		infra.lora.comm,
 | 
						||
	)
 | 
						||
 | 
						||
	// 任务工厂
 | 
						||
	taskFactory := task.NewTaskFactory(logger, infra.repos.sensorDataRepo, infra.repos.deviceRepo, generalDeviceService)
 | 
						||
 | 
						||
	// 计划任务管理器
 | 
						||
	analysisPlanTaskManager := plan.NewAnalysisPlanTaskManager(infra.repos.planRepo, infra.repos.pendingTaskRepo, infra.repos.executionLogRepo, logger)
 | 
						||
 | 
						||
	// 任务执行器
 | 
						||
	planExecutionManager := plan.NewPlanExecutionManager(
 | 
						||
		infra.repos.pendingTaskRepo,
 | 
						||
		infra.repos.executionLogRepo,
 | 
						||
		infra.repos.deviceRepo,
 | 
						||
		infra.repos.sensorDataRepo,
 | 
						||
		infra.repos.planRepo,
 | 
						||
		analysisPlanTaskManager,
 | 
						||
		taskFactory,
 | 
						||
		logger,
 | 
						||
		generalDeviceService,
 | 
						||
		time.Duration(cfg.Task.Interval)*time.Second,
 | 
						||
		cfg.Task.NumWorkers,
 | 
						||
	)
 | 
						||
 | 
						||
	// 计划管理器
 | 
						||
	planService := plan.NewPlanService(
 | 
						||
		planExecutionManager,
 | 
						||
		analysisPlanTaskManager,
 | 
						||
		infra.repos.planRepo,
 | 
						||
		infra.repos.deviceRepo,
 | 
						||
		infra.repos.unitOfWork,
 | 
						||
		taskFactory,
 | 
						||
		logger)
 | 
						||
 | 
						||
	return &DomainServices{
 | 
						||
		pigPenTransferManager:   pigPenTransferManager,
 | 
						||
		pigTradeManager:         pigTradeManager,
 | 
						||
		pigSickManager:          pigSickManager,
 | 
						||
		pigBatchDomain:          pigBatchDomain,
 | 
						||
		generalDeviceService:    generalDeviceService,
 | 
						||
		analysisPlanTaskManager: analysisPlanTaskManager,
 | 
						||
		taskFactory:             taskFactory,
 | 
						||
		planExecutionManager:    planExecutionManager,
 | 
						||
		planService:             planService,
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// AppServices 聚合了所有的应用服务实例。
 | 
						||
type AppServices struct {
 | 
						||
	pigFarmService  service.PigFarmService
 | 
						||
	pigBatchService service.PigBatchService
 | 
						||
	monitorService  service.MonitorService
 | 
						||
	deviceService   service.DeviceService
 | 
						||
	planService     service.PlanService
 | 
						||
	userService     service.UserService
 | 
						||
	auditService    audit.Service
 | 
						||
}
 | 
						||
 | 
						||
// initAppServices 初始化所有的应用服务。
 | 
						||
func initAppServices(infra *Infrastructure, domainServices *DomainServices, logger *logs.Logger) *AppServices {
 | 
						||
	pigFarmService := service.NewPigFarmService(infra.repos.pigFarmRepo, infra.repos.pigPenRepo, infra.repos.pigBatchRepo, domainServices.pigBatchDomain, infra.repos.unitOfWork, logger)
 | 
						||
	pigBatchService := service.NewPigBatchService(domainServices.pigBatchDomain, logger)
 | 
						||
	monitorService := service.NewMonitorService(
 | 
						||
		infra.repos.sensorDataRepo,
 | 
						||
		infra.repos.deviceCommandLogRepo,
 | 
						||
		infra.repos.executionLogRepo,
 | 
						||
		infra.repos.planRepo,
 | 
						||
		infra.repos.pendingCollectionRepo,
 | 
						||
		infra.repos.userActionLogRepo,
 | 
						||
		infra.repos.rawMaterialRepo,
 | 
						||
		infra.repos.medicationLogRepo,
 | 
						||
		infra.repos.pigBatchRepo,
 | 
						||
		infra.repos.pigBatchLogRepo,
 | 
						||
		infra.repos.pigTransferLogRepo,
 | 
						||
		infra.repos.pigSickPigLogRepo,
 | 
						||
		infra.repos.pigTradeRepo,
 | 
						||
		infra.repos.notificationRepo,
 | 
						||
	)
 | 
						||
	deviceService := service.NewDeviceService(
 | 
						||
		infra.repos.deviceRepo,
 | 
						||
		infra.repos.areaControllerRepo,
 | 
						||
		infra.repos.deviceTemplateRepo,
 | 
						||
		domainServices.generalDeviceService,
 | 
						||
	)
 | 
						||
	auditService := audit.NewService(infra.repos.userActionLogRepo, logger)
 | 
						||
	planService := service.NewPlanService(logger, domainServices.planService)
 | 
						||
	userService := service.NewUserService(infra.repos.userRepo, infra.tokenService, infra.notifyService, logger)
 | 
						||
 | 
						||
	return &AppServices{
 | 
						||
		pigFarmService:  pigFarmService,
 | 
						||
		pigBatchService: pigBatchService,
 | 
						||
		monitorService:  monitorService,
 | 
						||
		deviceService:   deviceService,
 | 
						||
		auditService:    auditService,
 | 
						||
		planService:     planService,
 | 
						||
		userService:     userService,
 | 
						||
	}
 | 
						||
}
 | 
						||
 | 
						||
// LoraComponents 聚合了所有 LoRa 相关组件。
 | 
						||
type LoraComponents struct {
 | 
						||
	listenHandler webhook.ListenHandler
 | 
						||
	comm          transport.Communicator
 | 
						||
	loraListener  transport.Listener
 | 
						||
}
 | 
						||
 | 
						||
// initLora 根据配置初始化 LoRa 相关组件。
 | 
						||
func initLora(
 | 
						||
	cfg *config.Config,
 | 
						||
	logger *logs.Logger,
 | 
						||
	repos *Repositories,
 | 
						||
) (*LoraComponents, error) {
 | 
						||
	var listenHandler webhook.ListenHandler
 | 
						||
	var comm transport.Communicator
 | 
						||
	var loraListener transport.Listener
 | 
						||
 | 
						||
	if cfg.Lora.Mode == config.LoraMode_LoRaWAN {
 | 
						||
		logger.Info("当前运行模式: lora_wan。初始化 ChirpStack 监听器和传输层。")
 | 
						||
		listenHandler = webhook.NewChirpStackListener(logger, repos.sensorDataRepo, repos.deviceRepo, repos.areaControllerRepo, repos.deviceCommandLogRepo, repos.pendingCollectionRepo)
 | 
						||
		comm = lora.NewChirpStackTransport(cfg.ChirpStack, logger)
 | 
						||
		loraListener = lora.NewPlaceholderTransport(logger)
 | 
						||
	} else {
 | 
						||
		logger.Info("当前运行模式: lora_mesh。初始化 LoRa Mesh 传输层和占位符监听器。")
 | 
						||
		listenHandler = webhook.NewPlaceholderListener(logger)
 | 
						||
		tp, err := lora.NewLoRaMeshUartPassthroughTransport(cfg.LoraMesh, logger, repos.areaControllerRepo, repos.pendingCollectionRepo, repos.deviceRepo, repos.sensorDataRepo)
 | 
						||
		if err != nil {
 | 
						||
			return nil, fmt.Errorf("无法初始化 LoRa Mesh 模块: %w", err)
 | 
						||
		}
 | 
						||
		loraListener = tp
 | 
						||
		comm = tp
 | 
						||
	}
 | 
						||
 | 
						||
	return &LoraComponents{
 | 
						||
		listenHandler: listenHandler,
 | 
						||
		comm:          comm,
 | 
						||
		loraListener:  loraListener,
 | 
						||
	}, nil
 | 
						||
}
 | 
						||
 | 
						||
// initNotifyService 根据配置初始化并返回一个通知领域服务。
 | 
						||
// 它确保至少有一个 LogNotifier 总是可用,并根据配置启用其他通知器。
 | 
						||
func initNotifyService(
 | 
						||
	cfg config.NotifyConfig,
 | 
						||
	log *logs.Logger,
 | 
						||
	userRepo repository.UserRepository,
 | 
						||
	notificationRepo repository.NotificationRepository,
 | 
						||
) (domain_notify.Service, error) {
 | 
						||
	var availableNotifiers []notify.Notifier
 | 
						||
 | 
						||
	// 1. 总是创建 LogNotifier 作为所有告警的最终记录渠道
 | 
						||
	logNotifier := notify.NewLogNotifier(log)
 | 
						||
	availableNotifiers = append(availableNotifiers, logNotifier)
 | 
						||
	log.Info("Log通知器已启用 (作为所有告警的最终记录渠道)")
 | 
						||
 | 
						||
	// 2. 根据配置,按需创建并收集所有启用的其他 Notifier 实例
 | 
						||
	if cfg.SMTP.Enabled {
 | 
						||
		smtpNotifier := notify.NewSMTPNotifier(
 | 
						||
			cfg.SMTP.Host,
 | 
						||
			cfg.SMTP.Port,
 | 
						||
			cfg.SMTP.Username,
 | 
						||
			cfg.SMTP.Password,
 | 
						||
			cfg.SMTP.Sender,
 | 
						||
		)
 | 
						||
		availableNotifiers = append(availableNotifiers, smtpNotifier)
 | 
						||
		log.Info("SMTP通知器已启用")
 | 
						||
	}
 | 
						||
 | 
						||
	if cfg.WeChat.Enabled {
 | 
						||
		wechatNotifier := notify.NewWechatNotifier(
 | 
						||
			cfg.WeChat.CorpID,
 | 
						||
			cfg.WeChat.AgentID,
 | 
						||
			cfg.WeChat.Secret,
 | 
						||
		)
 | 
						||
		availableNotifiers = append(availableNotifiers, wechatNotifier)
 | 
						||
		log.Info("企业微信通知器已启用")
 | 
						||
	}
 | 
						||
 | 
						||
	if cfg.Lark.Enabled {
 | 
						||
		larkNotifier := notify.NewLarkNotifier(
 | 
						||
			cfg.Lark.AppID,
 | 
						||
			cfg.Lark.AppSecret,
 | 
						||
		)
 | 
						||
		availableNotifiers = append(availableNotifiers, larkNotifier)
 | 
						||
		log.Info("飞书通知器已启用")
 | 
						||
	}
 | 
						||
 | 
						||
	// 3. 动态确定首选通知器
 | 
						||
	var primaryNotifier notify.Notifier
 | 
						||
	primaryNotifierType := notify.NotifierType(cfg.Primary)
 | 
						||
 | 
						||
	// 检查用户指定的主渠道是否已启用
 | 
						||
	for _, n := range availableNotifiers {
 | 
						||
		if n.Type() == primaryNotifierType {
 | 
						||
			primaryNotifier = n
 | 
						||
			break
 | 
						||
		}
 | 
						||
	}
 | 
						||
 | 
						||
	// 如果用户指定的主渠道未启用或未指定,则自动选择第一个可用的 (这将是 LogNotifier,如果其他都未启用)
 | 
						||
	if primaryNotifier == nil {
 | 
						||
		primaryNotifier = availableNotifiers[0] // 确保总能找到一个,因为 LogNotifier 总是存在的
 | 
						||
		log.Warnf("配置的首选渠道 '%s' 未启用或未指定,已自动降级使用 '%s' 作为首选渠道。", cfg.Primary, primaryNotifier.Type())
 | 
						||
	}
 | 
						||
 | 
						||
	// 4. 使用创建的 Notifier 列表和 notificationRepo 来组装领域服务
 | 
						||
	notifyService, err := domain_notify.NewFailoverService(
 | 
						||
		log,
 | 
						||
		userRepo,
 | 
						||
		availableNotifiers,
 | 
						||
		primaryNotifier.Type(),
 | 
						||
		cfg.FailureThreshold,
 | 
						||
		notificationRepo,
 | 
						||
	)
 | 
						||
	if err != nil {
 | 
						||
		return nil, fmt.Errorf("创建故障转移通知服务失败: %w", err)
 | 
						||
	}
 | 
						||
 | 
						||
	log.Infof("通知服务初始化成功,首选渠道: %s, 故障阈值: %d", primaryNotifier.Type(), cfg.FailureThreshold)
 | 
						||
	return notifyService, nil
 | 
						||
}
 | 
						||
 | 
						||
// initStorage 封装了数据库的初始化、连接和迁移逻辑。
 | 
						||
func initStorage(cfg config.DatabaseConfig, logger *logs.Logger) (database.Storage, error) {
 | 
						||
	// 创建存储实例
 | 
						||
	storage := database.NewStorage(cfg, logger)
 | 
						||
	if err := storage.Connect(); err != nil {
 | 
						||
		// 错误已在 Connect 内部被记录,这里只需包装并返回
 | 
						||
		return nil, fmt.Errorf("数据库连接失败: %w", err)
 | 
						||
	}
 | 
						||
 | 
						||
	// 执行数据库迁移
 | 
						||
	if err := storage.Migrate(models.GetAllModels()...); err != nil {
 | 
						||
		return nil, fmt.Errorf("数据库迁移失败: %w", err)
 | 
						||
	}
 | 
						||
 | 
						||
	logger.Info("数据库初始化完成。")
 | 
						||
	return storage, nil
 | 
						||
}
 |