package entities import ( "fmt" "math/rand" "time" "github.com/google/uuid" "gorm.io/gorm" ) // 初始化随机数种子 func init() { rand.Seed(time.Now().UnixNano()) } // ContractType 合同类型枚举 type ContractType string const ( ContractTypeCooperation ContractType = "cooperation" // 合作协议 ContractTypeReSign ContractType = "resign" // 补签协议 ) // ContractInfo 合同信息聚合根 // 存储企业签署的合同信息,一个企业可以有多个合同 type ContractInfo struct { // 基础标识 ID string `gorm:"primaryKey;type:varchar(36)" json:"id" comment:"合同信息唯一标识"` EnterpriseInfoID string `gorm:"type:varchar(36);not null;index" json:"enterprise_info_id" comment:"关联企业信息ID"` UserID string `gorm:"type:varchar(36);not null;index" json:"user_id" comment:"关联用户ID"` // 合同基本信息 ContractCode string `gorm:"type:varchar(255);not null;uniqueIndex" json:"contract_code" comment:"合同编号"` ContractName string `gorm:"type:varchar(255);not null" json:"contract_name" comment:"合同名称"` ContractType ContractType `gorm:"type:varchar(50);not null;index" json:"contract_type" comment:"合同类型"` ContractFileID string `gorm:"type:varchar(100);not null" json:"contract_file_id" comment:"合同文件ID"` ContractFileURL string `gorm:"type:varchar(500);not null" json:"contract_file_url" 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:"软删除时间"` // 关联关系 EnterpriseInfo *EnterpriseInfo `gorm:"foreignKey:EnterpriseInfoID" json:"enterprise_info,omitempty" comment:"关联的企业信息"` // 领域事件 (不持久化) domainEvents []interface{} `gorm:"-" json:"-"` } // TableName 指定数据库表名 func (ContractInfo) TableName() string { return "contract_infos" } // BeforeCreate GORM钩子:创建前自动生成UUID func (c *ContractInfo) BeforeCreate(tx *gorm.DB) error { if c.ID == "" { c.ID = uuid.New().String() } return nil } // ================ 工厂方法 ================ // NewContractInfo 创建新的合同信息 func NewContractInfo(enterpriseInfoID, userID, contractName string, contractType ContractType, contractFileID, contractFileURL string) (*ContractInfo, error) { if enterpriseInfoID == "" { return nil, fmt.Errorf("企业信息ID不能为空") } if userID == "" { return nil, fmt.Errorf("用户ID不能为空") } if contractName == "" { return nil, fmt.Errorf("合同名称不能为空") } if contractType == "" { return nil, fmt.Errorf("合同类型不能为空") } if contractFileID == "" { return nil, fmt.Errorf("合同文件ID不能为空") } if contractFileURL == "" { return nil, fmt.Errorf("合同文件URL不能为空") } // 验证合同类型 if !isValidContractType(contractType) { return nil, fmt.Errorf("无效的合同类型: %s", contractType) } // 生成合同编码 contractCode := GenerateContractCode(contractType) contractInfo := &ContractInfo{ ID: uuid.New().String(), EnterpriseInfoID: enterpriseInfoID, UserID: userID, ContractCode: contractCode, ContractName: contractName, ContractType: contractType, ContractFileID: contractFileID, ContractFileURL: contractFileURL, domainEvents: make([]interface{}, 0), } // 添加领域事件 contractInfo.addDomainEvent(&ContractInfoCreatedEvent{ ContractInfoID: contractInfo.ID, EnterpriseInfoID: enterpriseInfoID, UserID: userID, ContractCode: contractCode, ContractName: contractName, ContractType: string(contractType), CreatedAt: time.Now(), }) return contractInfo, nil } // ================ 聚合根核心方法 ================ // UpdateContractInfo 更新合同信息 func (c *ContractInfo) UpdateContractInfo(contractName, contractFileID, contractFileURL string) error { // 验证输入参数 if contractName == "" { return fmt.Errorf("合同名称不能为空") } if contractFileID == "" { return fmt.Errorf("合同文件ID不能为空") } if contractFileURL == "" { return fmt.Errorf("合同文件URL不能为空") } // 记录原始值用于事件 oldContractName := c.ContractName oldContractFileID := c.ContractFileID // 更新字段 c.ContractName = contractName c.ContractFileID = contractFileID c.ContractFileURL = contractFileURL // 添加领域事件 c.addDomainEvent(&ContractInfoUpdatedEvent{ ContractInfoID: c.ID, EnterpriseInfoID: c.EnterpriseInfoID, UserID: c.UserID, OldContractName: oldContractName, NewContractName: contractName, OldContractFileID: oldContractFileID, NewContractFileID: contractFileID, UpdatedAt: time.Now(), }) return nil } // DeleteContract 删除合同 func (c *ContractInfo) DeleteContract() error { // 添加领域事件 c.addDomainEvent(&ContractInfoDeletedEvent{ ContractInfoID: c.ID, EnterpriseInfoID: c.EnterpriseInfoID, UserID: c.UserID, ContractName: c.ContractName, ContractType: string(c.ContractType), DeletedAt: time.Now(), }) return nil } // ================ 业务规则验证 ================ // ValidateBusinessRules 验证业务规则 func (c *ContractInfo) ValidateBusinessRules() error { // 基础字段验证 if err := c.validateBasicFields(); err != nil { return fmt.Errorf("基础字段验证失败: %w", err) } // 业务规则验证 if err := c.validateBusinessLogic(); err != nil { return fmt.Errorf("业务规则验证失败: %w", err) } return nil } // validateBasicFields 验证基础字段 func (c *ContractInfo) validateBasicFields() error { if c.EnterpriseInfoID == "" { return fmt.Errorf("企业信息ID不能为空") } if c.UserID == "" { return fmt.Errorf("用户ID不能为空") } if c.ContractCode == "" { return fmt.Errorf("合同编码不能为空") } if c.ContractName == "" { return fmt.Errorf("合同名称不能为空") } if c.ContractType == "" { return fmt.Errorf("合同类型不能为空") } if c.ContractFileID == "" { return fmt.Errorf("合同文件ID不能为空") } if c.ContractFileURL == "" { return fmt.Errorf("合同文件URL不能为空") } // 合同类型验证 if !isValidContractType(c.ContractType) { return fmt.Errorf("无效的合同类型: %s", c.ContractType) } return nil } // validateBusinessLogic 验证业务逻辑 func (c *ContractInfo) validateBusinessLogic() error { // 合同名称长度限制 if len(c.ContractName) > 255 { return fmt.Errorf("合同名称长度不能超过255个字符") } // 合同文件URL格式验证 if !isValidURL(c.ContractFileURL) { return fmt.Errorf("合同文件URL格式无效") } return nil } // ================ 查询方法 ================ // GetContractTypeName 获取合同类型名称 func (c *ContractInfo) GetContractTypeName() string { switch c.ContractType { case ContractTypeCooperation: return "合作协议" case ContractTypeReSign: return "补签协议" default: return "未知类型" } } // IsCooperationContract 检查是否为合作协议 func (c *ContractInfo) IsCooperationContract() bool { return c.ContractType == ContractTypeCooperation } // ================ 领域事件管理 ================ // addDomainEvent 添加领域事件 func (c *ContractInfo) addDomainEvent(event interface{}) { if c.domainEvents == nil { c.domainEvents = make([]interface{}, 0) } c.domainEvents = append(c.domainEvents, event) } // GetDomainEvents 获取领域事件 func (c *ContractInfo) GetDomainEvents() []interface{} { return c.domainEvents } // ClearDomainEvents 清除领域事件 func (c *ContractInfo) ClearDomainEvents() { c.domainEvents = make([]interface{}, 0) } // ================ 私有验证方法 ================ // isValidContractType 验证合同类型 func isValidContractType(contractType ContractType) bool { switch contractType { case ContractTypeCooperation: return true case ContractTypeReSign: return true default: return false } } // isValidURL 验证URL格式 func isValidURL(url string) bool { // 简单的URL格式验证 if len(url) < 10 { return false } if url[:7] != "http://" && url[:8] != "https://" { return false } return true } // ================ 领域事件定义 ================ // ContractInfoCreatedEvent 合同信息创建事件 type ContractInfoCreatedEvent struct { ContractInfoID string `json:"contract_info_id"` EnterpriseInfoID string `json:"enterprise_info_id"` UserID string `json:"user_id"` ContractCode string `json:"contract_code"` ContractName string `json:"contract_name"` ContractType string `json:"contract_type"` CreatedAt time.Time `json:"created_at"` } // ContractInfoUpdatedEvent 合同信息更新事件 type ContractInfoUpdatedEvent struct { ContractInfoID string `json:"contract_info_id"` EnterpriseInfoID string `json:"enterprise_info_id"` UserID string `json:"user_id"` OldContractName string `json:"old_contract_name"` NewContractName string `json:"new_contract_name"` OldContractFileID string `json:"old_contract_file_id"` NewContractFileID string `json:"new_contract_file_id"` UpdatedAt time.Time `json:"updated_at"` } // ContractInfoDeletedEvent 合同信息删除事件 type ContractInfoDeletedEvent struct { ContractInfoID string `json:"contract_info_id"` EnterpriseInfoID string `json:"enterprise_info_id"` UserID string `json:"user_id"` ContractName string `json:"contract_name"` ContractType string `json:"contract_type"` DeletedAt time.Time `json:"deleted_at"` } // GenerateContractCode 生成合同编码 func GenerateContractCode(contractType ContractType) string { prefix := "CON" switch contractType { case ContractTypeCooperation: prefix += "01" case ContractTypeReSign: prefix += "02" } // 获取当前日期,格式为YYYYMMDD now := time.Now() dateStr := now.Format("20060102") // YYYYMMDD格式 // 生成一个随机的6位数字 randNum := fmt.Sprintf("%06d", rand.Intn(1000000)) // 格式:CON + 类型标识 + YYYYMMDD + 6位随机数 return fmt.Sprintf("%s%s%s", prefix, dateStr, randNum) }