package entities import ( "time" "github.com/google/uuid" "github.com/shopspring/decimal" "gorm.io/gorm" "tyapi-server/internal/domains/finance/value_objects" ) // ApplicationStatus 申请状态枚举 type ApplicationStatus string const ( ApplicationStatusPending ApplicationStatus = "pending" // 待处理 ApplicationStatusCompleted ApplicationStatus = "completed" // 已完成(已上传发票) ApplicationStatusRejected ApplicationStatus = "rejected" // 已拒绝 ) // InvoiceApplication 发票申请聚合根 type InvoiceApplication struct { // 基础标识 ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"申请唯一标识"` UserID string `gorm:"type:varchar(36);not null;index" json:"user_id" comment:"申请用户ID"` // 申请信息 InvoiceType value_objects.InvoiceType `gorm:"type:varchar(20);not null" json:"invoice_type" comment:"发票类型"` Amount decimal.Decimal `gorm:"type:decimal(20,8);not null" json:"amount" comment:"申请金额"` Status ApplicationStatus `gorm:"type:varchar(20);not null;default:'pending';index" json:"status" comment:"申请状态"` // 开票信息快照(申请时的信息,用于历史记录追踪) CompanyName string `gorm:"type:varchar(200);not null" json:"company_name" comment:"公司名称"` TaxpayerID string `gorm:"type:varchar(50);not null" json:"taxpayer_id" comment:"纳税人识别号"` BankName string `gorm:"type:varchar(100)" json:"bank_name" comment:"开户银行"` BankAccount string `gorm:"type:varchar(50)" json:"bank_account" comment:"银行账号"` CompanyAddress string `gorm:"type:varchar(500)" json:"company_address" comment:"企业地址"` CompanyPhone string `gorm:"type:varchar(20)" json:"company_phone" comment:"企业电话"` ReceivingEmail string `gorm:"type:varchar(100);not null" json:"receiving_email" comment:"发票接收邮箱"` // 开票信息引用(关联到用户开票信息表,用于模板功能) UserInvoiceInfoID string `gorm:"type:varchar(36);not null" json:"user_invoice_info_id" comment:"用户开票信息ID"` // 文件信息(申请通过后才有) FileID *string `gorm:"type:varchar(200)" json:"file_id,omitempty" comment:"文件ID"` FileName *string `gorm:"type:varchar(200)" json:"file_name,omitempty" comment:"文件名"` FileSize *int64 `json:"file_size,omitempty" comment:"文件大小"` FileURL *string `gorm:"type:varchar(500)" json:"file_url,omitempty" comment:"文件URL"` // 处理信息 ProcessedBy *string `gorm:"type:varchar(36)" json:"processed_by,omitempty" comment:"处理人ID"` ProcessedAt *time.Time `json:"processed_at,omitempty" comment:"处理时间"` RejectReason *string `gorm:"type:varchar(500)" json:"reject_reason,omitempty" comment:"拒绝原因"` AdminNotes *string `gorm:"type:varchar(500)" json:"admin_notes,omitempty" 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:"软删除时间"` } // TableName 指定数据库表名 func (InvoiceApplication) TableName() string { return "invoice_applications" } // BeforeCreate GORM钩子:创建前自动生成UUID func (ia *InvoiceApplication) BeforeCreate(tx *gorm.DB) error { if ia.ID == "" { ia.ID = uuid.New().String() } return nil } // IsPending 检查是否为待处理状态 func (ia *InvoiceApplication) IsPending() bool { return ia.Status == ApplicationStatusPending } // IsCompleted 检查是否为已完成状态 func (ia *InvoiceApplication) IsCompleted() bool { return ia.Status == ApplicationStatusCompleted } // IsRejected 检查是否为已拒绝状态 func (ia *InvoiceApplication) IsRejected() bool { return ia.Status == ApplicationStatusRejected } // CanProcess 检查是否可以处理 func (ia *InvoiceApplication) CanProcess() bool { return ia.IsPending() } // CanReject 检查是否可以拒绝 func (ia *InvoiceApplication) CanReject() bool { return ia.IsPending() } // MarkCompleted 标记为已完成 func (ia *InvoiceApplication) MarkCompleted(processedBy string) { ia.Status = ApplicationStatusCompleted ia.ProcessedBy = &processedBy now := time.Now() ia.ProcessedAt = &now } // MarkRejected 标记为已拒绝 func (ia *InvoiceApplication) MarkRejected(reason string, processedBy string) { ia.Status = ApplicationStatusRejected ia.RejectReason = &reason ia.ProcessedBy = &processedBy now := time.Now() ia.ProcessedAt = &now } // SetFileInfo 设置文件信息 func (ia *InvoiceApplication) SetFileInfo(fileID, fileName, fileURL string, fileSize int64) { ia.FileID = &fileID ia.FileName = &fileName ia.FileURL = &fileURL ia.FileSize = &fileSize } // NewInvoiceApplication 工厂方法 func NewInvoiceApplication(userID string, invoiceType value_objects.InvoiceType, amount decimal.Decimal, userInvoiceInfoID string) *InvoiceApplication { return &InvoiceApplication{ UserID: userID, InvoiceType: invoiceType, Amount: amount, Status: ApplicationStatusPending, UserInvoiceInfoID: userInvoiceInfoID, } } // SetInvoiceInfoSnapshot 设置开票信息快照 func (ia *InvoiceApplication) SetInvoiceInfoSnapshot(info *value_objects.InvoiceInfo) { ia.CompanyName = info.CompanyName ia.TaxpayerID = info.TaxpayerID ia.BankName = info.BankName ia.BankAccount = info.BankAccount ia.CompanyAddress = info.CompanyAddress ia.CompanyPhone = info.CompanyPhone ia.ReceivingEmail = info.ReceivingEmail } // GetInvoiceInfoSnapshot 获取开票信息快照 func (ia *InvoiceApplication) GetInvoiceInfoSnapshot() *value_objects.InvoiceInfo { return value_objects.NewInvoiceInfo( ia.CompanyName, ia.TaxpayerID, ia.BankName, ia.BankAccount, ia.CompanyAddress, ia.CompanyPhone, ia.ReceivingEmail, ) }