344 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			344 lines
		
	
	
		
			8.8 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | package entities | |||
|  | 
 | |||
|  | import ( | |||
|  | 	"errors" | |||
|  | 	"time" | |||
|  | 
 | |||
|  | 	"github.com/google/uuid" | |||
|  | 	"gorm.io/gorm" | |||
|  | ) | |||
|  | 
 | |||
|  | // StatisticsReport 统计报告实体 | |||
|  | // 用于存储生成的统计报告数据 | |||
|  | type StatisticsReport struct { | |||
|  | 	// 基础标识 | |||
|  | 	ID         string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"报告唯一标识"` | |||
|  | 	ReportType string `gorm:"type:varchar(50);not null;index" json:"report_type" comment:"报告类型"` | |||
|  | 	Title      string `gorm:"type:varchar(200);not null" json:"title" comment:"报告标题"` | |||
|  | 	Content    string `gorm:"type:json" json:"content" comment:"报告内容"` | |||
|  | 	Period     string `gorm:"type:varchar(20)" json:"period" comment:"统计周期"` | |||
|  | 	UserRole   string `gorm:"type:varchar(20)" json:"user_role" comment:"用户角色"` | |||
|  | 	Status     string `gorm:"type:varchar(20);default:'draft'" json:"status" comment:"报告状态"` | |||
|  | 
 | |||
|  | 	// 报告元数据 | |||
|  | 	GeneratedBy string     `gorm:"type:varchar(36)" json:"generated_by" comment:"生成者ID"` | |||
|  | 	GeneratedAt *time.Time `json:"generated_at" comment:"生成时间"` | |||
|  | 	ExpiresAt   *time.Time `json:"expires_at" comment:"过期时间"` | |||
|  | 
 | |||
|  | 	// 时间戳字段 | |||
|  | 	CreatedAt time.Time      `gorm:"autoCreateTime" json:"created_at" comment:"创建时间"` | |||
|  | 	UpdatedAt time.Time      `gorm:"autoUpdateTime" json:"updated_at" comment:"更新时间"` | |||
|  | 	DeletedAt gorm.DeletedAt `gorm:"index" json:"-" comment:"软删除时间"` | |||
|  | 
 | |||
|  | 	// 领域事件 (不持久化) | |||
|  | 	domainEvents []interface{} `gorm:"-" json:"-"` | |||
|  | } | |||
|  | 
 | |||
|  | // TableName 指定数据库表名 | |||
|  | func (StatisticsReport) TableName() string { | |||
|  | 	return "statistics_reports" | |||
|  | } | |||
|  | 
 | |||
|  | // BeforeCreate GORM钩子:创建前自动生成UUID | |||
|  | func (s *StatisticsReport) BeforeCreate(tx *gorm.DB) error { | |||
|  | 	if s.ID == "" { | |||
|  | 		s.ID = uuid.New().String() | |||
|  | 	} | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // 实现 Entity 接口 - 提供统一的实体管理接口 | |||
|  | // GetID 获取实体唯一标识 | |||
|  | func (s *StatisticsReport) GetID() string { | |||
|  | 	return s.ID | |||
|  | } | |||
|  | 
 | |||
|  | // GetCreatedAt 获取创建时间 | |||
|  | func (s *StatisticsReport) GetCreatedAt() time.Time { | |||
|  | 	return s.CreatedAt | |||
|  | } | |||
|  | 
 | |||
|  | // GetUpdatedAt 获取更新时间 | |||
|  | func (s *StatisticsReport) GetUpdatedAt() time.Time { | |||
|  | 	return s.UpdatedAt | |||
|  | } | |||
|  | 
 | |||
|  | // Validate 验证统计报告信息 | |||
|  | // 检查统计报告必填字段是否完整,确保数据的有效性 | |||
|  | func (s *StatisticsReport) Validate() error { | |||
|  | 	if s.ReportType == "" { | |||
|  | 		return NewValidationError("报告类型不能为空") | |||
|  | 	} | |||
|  | 	if s.Title == "" { | |||
|  | 		return NewValidationError("报告标题不能为空") | |||
|  | 	} | |||
|  | 	if s.Period == "" { | |||
|  | 		return NewValidationError("统计周期不能为空") | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 验证报告类型 | |||
|  | 	if !s.IsValidReportType() { | |||
|  | 		return NewValidationError("无效的报告类型") | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 验证报告状态 | |||
|  | 	if !s.IsValidStatus() { | |||
|  | 		return NewValidationError("无效的报告状态") | |||
|  | 	} | |||
|  | 
 | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // IsValidReportType 检查报告类型是否有效 | |||
|  | func (s *StatisticsReport) IsValidReportType() bool { | |||
|  | 	validTypes := []string{ | |||
|  | 		"dashboard",  // 仪表板报告 | |||
|  | 		"summary",    // 汇总报告 | |||
|  | 		"detailed",   // 详细报告 | |||
|  | 		"custom",     // 自定义报告 | |||
|  | 	} | |||
|  | 
 | |||
|  | 	for _, validType := range validTypes { | |||
|  | 		if s.ReportType == validType { | |||
|  | 			return true | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return false | |||
|  | } | |||
|  | 
 | |||
|  | // IsValidStatus 检查报告状态是否有效 | |||
|  | func (s *StatisticsReport) IsValidStatus() bool { | |||
|  | 	validStatuses := []string{ | |||
|  | 		"draft",     // 草稿 | |||
|  | 		"generating", // 生成中 | |||
|  | 		"completed", // 已完成 | |||
|  | 		"failed",    // 生成失败 | |||
|  | 		"expired",   // 已过期 | |||
|  | 	} | |||
|  | 
 | |||
|  | 	for _, validStatus := range validStatuses { | |||
|  | 		if s.Status == validStatus { | |||
|  | 			return true | |||
|  | 		} | |||
|  | 	} | |||
|  | 	return false | |||
|  | } | |||
|  | 
 | |||
|  | // GetReportTypeName 获取报告类型的中文名称 | |||
|  | func (s *StatisticsReport) GetReportTypeName() string { | |||
|  | 	typeNames := map[string]string{ | |||
|  | 		"dashboard": "仪表板报告", | |||
|  | 		"summary":   "汇总报告", | |||
|  | 		"detailed":  "详细报告", | |||
|  | 		"custom":    "自定义报告", | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if name, exists := typeNames[s.ReportType]; exists { | |||
|  | 		return name | |||
|  | 	} | |||
|  | 	return s.ReportType | |||
|  | } | |||
|  | 
 | |||
|  | // GetStatusName 获取报告状态的中文名称 | |||
|  | func (s *StatisticsReport) GetStatusName() string { | |||
|  | 	statusNames := map[string]string{ | |||
|  | 		"draft":      "草稿", | |||
|  | 		"generating": "生成中", | |||
|  | 		"completed":  "已完成", | |||
|  | 		"failed":     "生成失败", | |||
|  | 		"expired":    "已过期", | |||
|  | 	} | |||
|  | 
 | |||
|  | 	if name, exists := statusNames[s.Status]; exists { | |||
|  | 		return name | |||
|  | 	} | |||
|  | 	return s.Status | |||
|  | } | |||
|  | 
 | |||
|  | // NewStatisticsReport 工厂方法 - 创建统计报告 | |||
|  | func NewStatisticsReport(reportType, title, period, userRole string) (*StatisticsReport, error) { | |||
|  | 	if reportType == "" { | |||
|  | 		return nil, errors.New("报告类型不能为空") | |||
|  | 	} | |||
|  | 	if title == "" { | |||
|  | 		return nil, errors.New("报告标题不能为空") | |||
|  | 	} | |||
|  | 	if period == "" { | |||
|  | 		return nil, errors.New("统计周期不能为空") | |||
|  | 	} | |||
|  | 
 | |||
|  | 	report := &StatisticsReport{ | |||
|  | 		ReportType: reportType, | |||
|  | 		Title:      title, | |||
|  | 		Period:     period, | |||
|  | 		UserRole:   userRole, | |||
|  | 		Status:     "draft", | |||
|  | 		domainEvents: make([]interface{}, 0), | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 验证报告 | |||
|  | 	if err := report.Validate(); err != nil { | |||
|  | 		return nil, err | |||
|  | 	} | |||
|  | 
 | |||
|  | 	// 添加领域事件 | |||
|  | 	report.addDomainEvent(&StatisticsReportCreatedEvent{ | |||
|  | 		ReportID:   report.ID, | |||
|  | 		ReportType: reportType, | |||
|  | 		Title:      title, | |||
|  | 		Period:     period, | |||
|  | 		CreatedAt:  time.Now(), | |||
|  | 	}) | |||
|  | 
 | |||
|  | 	return report, nil | |||
|  | } | |||
|  | 
 | |||
|  | // StartGeneration 开始生成报告 | |||
|  | func (s *StatisticsReport) StartGeneration(generatedBy string) error { | |||
|  | 	if s.Status != "draft" { | |||
|  | 		return NewValidationError("只有草稿状态的报告才能开始生成") | |||
|  | 	} | |||
|  | 
 | |||
|  | 	s.Status = "generating" | |||
|  | 	s.GeneratedBy = generatedBy | |||
|  | 	now := time.Now() | |||
|  | 	s.GeneratedAt = &now | |||
|  | 
 | |||
|  | 	// 添加领域事件 | |||
|  | 	s.addDomainEvent(&StatisticsReportGenerationStartedEvent{ | |||
|  | 		ReportID:     s.ID, | |||
|  | 		GeneratedBy:  generatedBy, | |||
|  | 		StartedAt:    now, | |||
|  | 	}) | |||
|  | 
 | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // CompleteGeneration 完成报告生成 | |||
|  | func (s *StatisticsReport) CompleteGeneration(content string) error { | |||
|  | 	if s.Status != "generating" { | |||
|  | 		return NewValidationError("只有生成中状态的报告才能完成生成") | |||
|  | 	} | |||
|  | 
 | |||
|  | 	s.Status = "completed" | |||
|  | 	s.Content = content | |||
|  | 
 | |||
|  | 	// 设置过期时间(默认7天) | |||
|  | 	expiresAt := time.Now().Add(7 * 24 * time.Hour) | |||
|  | 	s.ExpiresAt = &expiresAt | |||
|  | 
 | |||
|  | 	// 添加领域事件 | |||
|  | 	s.addDomainEvent(&StatisticsReportCompletedEvent{ | |||
|  | 		ReportID:    s.ID, | |||
|  | 		CompletedAt: time.Now(), | |||
|  | 	}) | |||
|  | 
 | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // FailGeneration 报告生成失败 | |||
|  | func (s *StatisticsReport) FailGeneration(reason string) error { | |||
|  | 	if s.Status != "generating" { | |||
|  | 		return NewValidationError("只有生成中状态的报告才能标记为失败") | |||
|  | 	} | |||
|  | 
 | |||
|  | 	s.Status = "failed" | |||
|  | 
 | |||
|  | 	// 添加领域事件 | |||
|  | 	s.addDomainEvent(&StatisticsReportFailedEvent{ | |||
|  | 		ReportID:   s.ID, | |||
|  | 		Reason:     reason, | |||
|  | 		FailedAt:   time.Now(), | |||
|  | 	}) | |||
|  | 
 | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // IsExpired 检查报告是否已过期 | |||
|  | func (s *StatisticsReport) IsExpired() bool { | |||
|  | 	if s.ExpiresAt == nil { | |||
|  | 		return false | |||
|  | 	} | |||
|  | 	return time.Now().After(*s.ExpiresAt) | |||
|  | } | |||
|  | 
 | |||
|  | // MarkAsExpired 标记报告为过期 | |||
|  | func (s *StatisticsReport) MarkAsExpired() error { | |||
|  | 	if s.Status != "completed" { | |||
|  | 		return NewValidationError("只有已完成状态的报告才能标记为过期") | |||
|  | 	} | |||
|  | 
 | |||
|  | 	s.Status = "expired" | |||
|  | 
 | |||
|  | 	// 添加领域事件 | |||
|  | 	s.addDomainEvent(&StatisticsReportExpiredEvent{ | |||
|  | 		ReportID:  s.ID, | |||
|  | 		ExpiredAt: time.Now(), | |||
|  | 	}) | |||
|  | 
 | |||
|  | 	return nil | |||
|  | } | |||
|  | 
 | |||
|  | // CanBeRegenerated 检查报告是否可以重新生成 | |||
|  | func (s *StatisticsReport) CanBeRegenerated() bool { | |||
|  | 	return s.Status == "failed" || s.Status == "expired" | |||
|  | } | |||
|  | 
 | |||
|  | // ================ 领域事件管理 ================ | |||
|  | 
 | |||
|  | // addDomainEvent 添加领域事件 | |||
|  | func (s *StatisticsReport) addDomainEvent(event interface{}) { | |||
|  | 	if s.domainEvents == nil { | |||
|  | 		s.domainEvents = make([]interface{}, 0) | |||
|  | 	} | |||
|  | 	s.domainEvents = append(s.domainEvents, event) | |||
|  | } | |||
|  | 
 | |||
|  | // GetDomainEvents 获取领域事件 | |||
|  | func (s *StatisticsReport) GetDomainEvents() []interface{} { | |||
|  | 	return s.domainEvents | |||
|  | } | |||
|  | 
 | |||
|  | // ClearDomainEvents 清除领域事件 | |||
|  | func (s *StatisticsReport) ClearDomainEvents() { | |||
|  | 	s.domainEvents = make([]interface{}, 0) | |||
|  | } | |||
|  | 
 | |||
|  | // ================ 领域事件定义 ================ | |||
|  | 
 | |||
|  | // StatisticsReportCreatedEvent 统计报告创建事件 | |||
|  | type StatisticsReportCreatedEvent struct { | |||
|  | 	ReportID   string    `json:"report_id"` | |||
|  | 	ReportType string    `json:"report_type"` | |||
|  | 	Title      string    `json:"title"` | |||
|  | 	Period     string    `json:"period"` | |||
|  | 	CreatedAt  time.Time `json:"created_at"` | |||
|  | } | |||
|  | 
 | |||
|  | // StatisticsReportGenerationStartedEvent 统计报告生成开始事件 | |||
|  | type StatisticsReportGenerationStartedEvent struct { | |||
|  | 	ReportID    string    `json:"report_id"` | |||
|  | 	GeneratedBy string    `json:"generated_by"` | |||
|  | 	StartedAt   time.Time `json:"started_at"` | |||
|  | } | |||
|  | 
 | |||
|  | // StatisticsReportCompletedEvent 统计报告完成事件 | |||
|  | type StatisticsReportCompletedEvent struct { | |||
|  | 	ReportID    string    `json:"report_id"` | |||
|  | 	CompletedAt time.Time `json:"completed_at"` | |||
|  | } | |||
|  | 
 | |||
|  | // StatisticsReportFailedEvent 统计报告失败事件 | |||
|  | type StatisticsReportFailedEvent struct { | |||
|  | 	ReportID string    `json:"report_id"` | |||
|  | 	Reason   string    `json:"reason"` | |||
|  | 	FailedAt time.Time `json:"failed_at"` | |||
|  | } | |||
|  | 
 | |||
|  | // StatisticsReportExpiredEvent 统计报告过期事件 | |||
|  | type StatisticsReportExpiredEvent struct { | |||
|  | 	ReportID  string    `json:"report_id"` | |||
|  | 	ExpiredAt time.Time `json:"expired_at"` | |||
|  | } | |||
|  | 
 |