517 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			517 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| 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
 | ||
| }
 |