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"`
|
|||
|
|
}
|
|||
|
|
|