package value_objects import ( "errors" "fmt" "regexp" "strings" "time" ) // ContractInfo 合同信息值对象 // 封装电子合同相关的核心信息,包含合同状态和签署流程管理 type ContractInfo struct { // 合同基本信息 ContractFileID string `json:"contract_file_id"` // 合同文件ID EsignFlowID string `json:"esign_flow_id"` // e签宝签署流程ID ContractURL string `json:"contract_url"` // 合同文件访问链接 ContractSignURL string `json:"contract_sign_url"` // 合同签署链接 // 合同元数据 ContractTitle string `json:"contract_title"` // 合同标题 ContractVersion string `json:"contract_version"` // 合同版本 TemplateID string `json:"template_id"` // 模板ID // 签署相关信息 SignerAccount string `json:"signer_account"` // 签署人账号 SignerName string `json:"signer_name"` // 签署人姓名 TransactorPhone string `json:"transactor_phone"` // 经办人手机号 TransactorName string `json:"transactor_name"` // 经办人姓名 TransactorIDCardNum string `json:"transactor_id_card_num"` // 经办人身份证号 // 时间信息 GeneratedAt *time.Time `json:"generated_at,omitempty"` // 合同生成时间 SignFlowCreatedAt *time.Time `json:"sign_flow_created_at,omitempty"` // 签署流程创建时间 SignedAt *time.Time `json:"signed_at,omitempty"` // 签署完成时间 ExpiresAt *time.Time `json:"expires_at,omitempty"` // 签署链接过期时间 // 状态信息 Status string `json:"status"` // 合同状态 SignProgress int `json:"sign_progress"` // 签署进度 // 附加信息 Metadata map[string]interface{} `json:"metadata,omitempty"` // 元数据 } // ContractStatus 合同状态常量 const ( ContractStatusDraft = "draft" // 草稿 ContractStatusGenerated = "generated" // 已生成 ContractStatusSigning = "signing" // 签署中 ContractStatusSigned = "signed" // 已签署 ContractStatusExpired = "expired" // 已过期 ContractStatusRejected = "rejected" // 被拒绝 ContractStatusCancelled = "cancelled" // 已取消 ) // NewContractInfo 创建合同信息值对象 func NewContractInfo(contractFileID, esignFlowID, contractURL, contractSignURL string) (*ContractInfo, error) { info := &ContractInfo{ ContractFileID: strings.TrimSpace(contractFileID), EsignFlowID: strings.TrimSpace(esignFlowID), ContractURL: strings.TrimSpace(contractURL), ContractSignURL: strings.TrimSpace(contractSignURL), Status: ContractStatusGenerated, SignProgress: 0, Metadata: make(map[string]interface{}), } if err := info.Validate(); err != nil { return nil, fmt.Errorf("合同信息验证失败: %w", err) } return info, nil } // Validate 验证合同信息的完整性和格式 func (c *ContractInfo) Validate() error { if err := c.validateContractFileID(); err != nil { return err } if err := c.validateEsignFlowID(); err != nil { return err } if err := c.validateContractURL(); err != nil { return err } if err := c.validateContractSignURL(); err != nil { return err } if err := c.validateSignerInfo(); err != nil { return err } if err := c.validateStatus(); err != nil { return err } return nil } // validateContractFileID 验证合同文件ID func (c *ContractInfo) validateContractFileID() error { if c.ContractFileID == "" { return errors.New("合同文件ID不能为空") } // 简单的格式验证 if len(c.ContractFileID) < 10 { return errors.New("合同文件ID格式不正确") } return nil } // validateEsignFlowID 验证e签宝流程ID func (c *ContractInfo) validateEsignFlowID() error { if c.EsignFlowID == "" { return errors.New("e签宝流程ID不能为空") } // 简单的格式验证 if len(c.EsignFlowID) < 10 { return errors.New("e签宝流程ID格式不正确") } return nil } // validateContractURL 验证合同访问链接 func (c *ContractInfo) validateContractURL() error { if c.ContractURL == "" { return errors.New("合同访问链接不能为空") } // URL格式验证 urlPattern := `^https?://.*` matched, err := regexp.MatchString(urlPattern, c.ContractURL) if err != nil { return fmt.Errorf("合同访问链接格式验证错误: %w", err) } if !matched { return errors.New("合同访问链接格式不正确,必须以http://或https://开头") } return nil } // validateContractSignURL 验证合同签署链接 func (c *ContractInfo) validateContractSignURL() error { if c.ContractSignURL == "" { return errors.New("合同签署链接不能为空") } // URL格式验证 urlPattern := `^https?://.*` matched, err := regexp.MatchString(urlPattern, c.ContractSignURL) if err != nil { return fmt.Errorf("合同签署链接格式验证错误: %w", err) } if !matched { return errors.New("合同签署链接格式不正确,必须以http://或https://开头") } return nil } // validateSignerInfo 验证签署人信息 func (c *ContractInfo) validateSignerInfo() error { // 如果有签署人信息,进行验证 if c.SignerAccount != "" || c.SignerName != "" || c.TransactorPhone != "" { if c.SignerAccount == "" { return errors.New("签署人账号不能为空") } if c.SignerName == "" { return errors.New("签署人姓名不能为空") } if c.TransactorPhone != "" { // 手机号格式验证 phonePattern := `^1[3-9]\d{9}$` matched, err := regexp.MatchString(phonePattern, c.TransactorPhone) if err != nil { return fmt.Errorf("经办人手机号格式验证错误: %w", err) } if !matched { return errors.New("经办人手机号格式不正确") } } if c.TransactorIDCardNum != "" { // 身份证号格式验证 idPattern := `^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dXx]$` matched, err := regexp.MatchString(idPattern, c.TransactorIDCardNum) if err != nil { return fmt.Errorf("经办人身份证号格式验证错误: %w", err) } if !matched { return errors.New("经办人身份证号格式不正确") } } } return nil } // validateStatus 验证合同状态 func (c *ContractInfo) validateStatus() error { validStatuses := []string{ ContractStatusDraft, ContractStatusGenerated, ContractStatusSigning, ContractStatusSigned, ContractStatusExpired, ContractStatusRejected, ContractStatusCancelled, } for _, status := range validStatuses { if c.Status == status { return nil } } return fmt.Errorf("无效的合同状态: %s", c.Status) } // SetSignerInfo 设置签署人信息 func (c *ContractInfo) SetSignerInfo(signerAccount, signerName, transactorPhone, transactorName, transactorIDCardNum string) error { c.SignerAccount = strings.TrimSpace(signerAccount) c.SignerName = strings.TrimSpace(signerName) c.TransactorPhone = strings.TrimSpace(transactorPhone) c.TransactorName = strings.TrimSpace(transactorName) c.TransactorIDCardNum = strings.TrimSpace(transactorIDCardNum) return c.validateSignerInfo() } // UpdateStatus 更新合同状态 func (c *ContractInfo) UpdateStatus(status string) error { oldStatus := c.Status c.Status = status if err := c.validateStatus(); err != nil { c.Status = oldStatus // 回滚 return err } // 根据状态更新进度 c.updateProgressByStatus() return nil } // updateProgressByStatus 根据状态更新进度 func (c *ContractInfo) updateProgressByStatus() { progressMap := map[string]int{ ContractStatusDraft: 0, ContractStatusGenerated: 25, ContractStatusSigning: 50, ContractStatusSigned: 100, ContractStatusExpired: 50, ContractStatusRejected: 50, ContractStatusCancelled: 0, } if progress, exists := progressMap[c.Status]; exists { c.SignProgress = progress } } // MarkAsSigning 标记为签署中 func (c *ContractInfo) MarkAsSigning() error { c.Status = ContractStatusSigning c.SignProgress = 50 now := time.Now() c.SignFlowCreatedAt = &now return nil } // MarkAsSigned 标记为已签署 func (c *ContractInfo) MarkAsSigned() error { c.Status = ContractStatusSigned c.SignProgress = 100 now := time.Now() c.SignedAt = &now return nil } // MarkAsExpired 标记为已过期 func (c *ContractInfo) MarkAsExpired() error { c.Status = ContractStatusExpired now := time.Now() c.ExpiresAt = &now return nil } // MarkAsRejected 标记为被拒绝 func (c *ContractInfo) MarkAsRejected() error { c.Status = ContractStatusRejected c.SignProgress = 50 return nil } // IsExpired 检查合同是否已过期 func (c *ContractInfo) IsExpired() bool { if c.ExpiresAt == nil { return false } return time.Now().After(*c.ExpiresAt) } // IsSigned 检查合同是否已签署 func (c *ContractInfo) IsSigned() bool { return c.Status == ContractStatusSigned } // CanSign 检查是否可以签署 func (c *ContractInfo) CanSign() bool { return c.Status == ContractStatusGenerated || c.Status == ContractStatusSigning } // GetStatusName 获取状态的中文名称 func (c *ContractInfo) GetStatusName() string { statusNames := map[string]string{ ContractStatusDraft: "草稿", ContractStatusGenerated: "已生成", ContractStatusSigning: "签署中", ContractStatusSigned: "已签署", ContractStatusExpired: "已过期", ContractStatusRejected: "被拒绝", ContractStatusCancelled: "已取消", } if name, exists := statusNames[c.Status]; exists { return name } return c.Status } // GetDisplayTitle 获取显示用的合同标题 func (c *ContractInfo) GetDisplayTitle() string { if c.ContractTitle != "" { return c.ContractTitle } return "企业认证服务合同" } // GetMaskedSignerAccount 获取脱敏的签署人账号 func (c *ContractInfo) GetMaskedSignerAccount() string { if len(c.SignerAccount) <= 6 { return c.SignerAccount } // 保留前3位和后3位,中间用*替代 return c.SignerAccount[:3] + "***" + c.SignerAccount[len(c.SignerAccount)-3:] } // GetMaskedTransactorPhone 获取脱敏的经办人手机号 func (c *ContractInfo) GetMaskedTransactorPhone() string { if len(c.TransactorPhone) != 11 { return c.TransactorPhone } // 保留前3位和后4位,中间用*替代 return c.TransactorPhone[:3] + "****" + c.TransactorPhone[7:] } // GetMaskedTransactorIDCardNum 获取脱敏的经办人身份证号 func (c *ContractInfo) GetMaskedTransactorIDCardNum() string { if len(c.TransactorIDCardNum) != 18 { return c.TransactorIDCardNum } // 保留前6位和后4位,中间用*替代 return c.TransactorIDCardNum[:6] + "********" + c.TransactorIDCardNum[14:] } // AddMetadata 添加元数据 func (c *ContractInfo) AddMetadata(key string, value interface{}) { if c.Metadata == nil { c.Metadata = make(map[string]interface{}) } c.Metadata[key] = value } // GetMetadata 获取元数据 func (c *ContractInfo) GetMetadata(key string) (interface{}, bool) { if c.Metadata == nil { return nil, false } value, exists := c.Metadata[key] return value, exists } // Equals 比较两个合同信息是否相等 func (c *ContractInfo) Equals(other *ContractInfo) bool { if other == nil { return false } return c.ContractFileID == other.ContractFileID && c.EsignFlowID == other.EsignFlowID && c.Status == other.Status } // Clone 创建合同信息的副本 func (c *ContractInfo) Clone() *ContractInfo { cloned := &ContractInfo{ ContractFileID: c.ContractFileID, EsignFlowID: c.EsignFlowID, ContractURL: c.ContractURL, ContractSignURL: c.ContractSignURL, ContractTitle: c.ContractTitle, ContractVersion: c.ContractVersion, TemplateID: c.TemplateID, SignerAccount: c.SignerAccount, SignerName: c.SignerName, TransactorPhone: c.TransactorPhone, TransactorName: c.TransactorName, TransactorIDCardNum: c.TransactorIDCardNum, Status: c.Status, SignProgress: c.SignProgress, } // 复制时间字段 if c.GeneratedAt != nil { generatedAt := *c.GeneratedAt cloned.GeneratedAt = &generatedAt } if c.SignFlowCreatedAt != nil { signFlowCreatedAt := *c.SignFlowCreatedAt cloned.SignFlowCreatedAt = &signFlowCreatedAt } if c.SignedAt != nil { signedAt := *c.SignedAt cloned.SignedAt = &signedAt } if c.ExpiresAt != nil { expiresAt := *c.ExpiresAt cloned.ExpiresAt = &expiresAt } // 复制元数据 if c.Metadata != nil { cloned.Metadata = make(map[string]interface{}) for k, v := range c.Metadata { cloned.Metadata[k] = v } } return cloned } // String 返回合同信息的字符串表示 func (c *ContractInfo) String() string { return fmt.Sprintf("合同信息[文件ID:%s, 流程ID:%s, 状态:%s, 进度:%d%%]", c.ContractFileID, c.EsignFlowID, c.GetStatusName(), c.SignProgress) } // ToMap 转换为map格式(用于序列化) func (c *ContractInfo) ToMap() map[string]interface{} { result := map[string]interface{}{ "contract_file_id": c.ContractFileID, "esign_flow_id": c.EsignFlowID, "contract_url": c.ContractURL, "contract_sign_url": c.ContractSignURL, "contract_title": c.ContractTitle, "contract_version": c.ContractVersion, "template_id": c.TemplateID, "signer_account": c.SignerAccount, "signer_name": c.SignerName, "transactor_phone": c.TransactorPhone, "transactor_name": c.TransactorName, "transactor_id_card_num": c.TransactorIDCardNum, "status": c.Status, "sign_progress": c.SignProgress, } // 添加时间字段 if c.GeneratedAt != nil { result["generated_at"] = c.GeneratedAt } if c.SignFlowCreatedAt != nil { result["sign_flow_created_at"] = c.SignFlowCreatedAt } if c.SignedAt != nil { result["signed_at"] = c.SignedAt } if c.ExpiresAt != nil { result["expires_at"] = c.ExpiresAt } // 添加元数据 if c.Metadata != nil { result["metadata"] = c.Metadata } return result }