diff --git a/internal/application/certification/certification_application_service_impl.go b/internal/application/certification/certification_application_service_impl.go index 7cbc214..0fc6ca8 100644 --- a/internal/application/certification/certification_application_service_impl.go +++ b/internal/application/certification/certification_application_service_impl.go @@ -1388,13 +1388,28 @@ func (s *CertificationApplicationServiceImpl) generateAndAddContractFile( s.logger.Info("合同生成-步骤1-开始填充合同模板", zap.String("user_id", cert.UserID), zap.String("company_name", companyName)) + + // 协议编号:已有则复用,否则新生成 + if cert.ContractCode == "" { + cert.SetContractCode(user_entities.GenerateContractCode(user_entities.ContractTypeCooperation)) + } + agreementNo := cert.ContractCode + + signDate := time.Now().Format("2006年01月02日") + // 控件 key 与 e 签宝合同模板中控件名一致(新合同) fileComponent := map[string]string{ - "jfqym": companyName, - "jfqym2": companyName, - "jfsqdb": authorizedRepName, - "jftyshxydm": unifiedSocialCode, - "jflxdz": enterpriseAddress, + "jfqym": companyName, + "jfqym2": companyName, + "jfsqdb": authorizedRepName, + "jftyshxydm": unifiedSocialCode, + "jflxdz": enterpriseAddress, + // 甲方 + "xybh": agreementNo, + "qsrq1": signDate, + "qsrq3": signDate, + // 乙方 + "qsrq2": signDate, } fillTemplateResp, err := s.esignClient.FillTemplate(fileComponent) if err != nil { @@ -1403,7 +1418,8 @@ func (s *CertificationApplicationServiceImpl) generateAndAddContractFile( } s.logger.Info("合同生成-步骤1-模板填充成功", zap.String("user_id", cert.UserID), - zap.String("file_id", fillTemplateResp.FileID)) + zap.String("file_id", fillTemplateResp.FileID), + zap.String("contract_code", agreementNo)) err = cert.AddContractFileID(fillTemplateResp.FileID, fillTemplateResp.FileDownloadUrl) if err != nil { s.logger.Error("加入合同文件ID链接失败", zap.Error(err)) @@ -1566,8 +1582,8 @@ func (s *CertificationApplicationServiceImpl) handleContractAfterSignComplete(ct s.logger.Info("合同文件已上传七牛云", zap.String("file_name", fileName), zap.String("qiniu_url", qiniuURL)) - // 4. 保存到合同聚合根 - _, err = s.contractAggregateService.CreateContract( + // 4. 保存到合同聚合根(复用认证阶段的合同编号) + _, err = s.contractAggregateService.CreateContractWithCode( ctx, user.EnterpriseInfo.ID, cert.UserID, @@ -1575,6 +1591,7 @@ func (s *CertificationApplicationServiceImpl) handleContractAfterSignComplete(ct user_entities.ContractTypeCooperation, fileId, qiniuURL, + cert.ContractCode, ) if err != nil { s.logger.Error("保存合同信息到聚合根失败", zap.String("file_name", fileName), zap.Error(err)) diff --git a/internal/domains/certification/entities/certification.go b/internal/domains/certification/entities/certification.go index c32e82e..f104cef 100644 --- a/internal/domains/certification/entities/certification.go +++ b/internal/domains/certification/entities/certification.go @@ -31,6 +31,7 @@ type Certification struct { // === e签宝相关信息 === AuthFlowID string `gorm:"type:varchar(500)" json:"auth_flow_id,omitempty" comment:"企业认证流程ID"` AuthURL string `gorm:"type:varchar(500)" json:"auth_url,omitempty" comment:"企业认证链接"` + ContractCode string `gorm:"type:varchar(100)" json:"contract_code,omitempty" comment:"合同/协议编号"` ContractFileID string `gorm:"type:varchar(500)" json:"contract_file_id,omitempty" comment:"合同文件ID"` EsignFlowID string `gorm:"type:varchar(500)" json:"esign_flow_id,omitempty" comment:"签署流程ID"` ContractURL string `gorm:"type:varchar(500)" json:"contract_url,omitempty" comment:"合同文件访问链接"` @@ -332,6 +333,11 @@ func (c *Certification) AddContractFileID(contractFileID string, contractURL str return nil } +// SetContractCode 设置合同/协议编号 +func (c *Certification) SetContractCode(contractCode string) { + c.ContractCode = contractCode +} + // UpdateContractInfo 更新合同信息 func (c *Certification) UpdateContractInfo(contractInfo *value_objects.ContractInfo) error { // 验证合同信息 diff --git a/internal/domains/user/entities/contract_info.go b/internal/domains/user/entities/contract_info.go index 5bcb751..9aaac72 100644 --- a/internal/domains/user/entities/contract_info.go +++ b/internal/domains/user/entities/contract_info.go @@ -64,8 +64,22 @@ func (c *ContractInfo) BeforeCreate(tx *gorm.DB) error { // ================ 工厂方法 ================ -// NewContractInfo 创建新的合同信息 +// NewContractInfo 创建新的合同信息(自动生成合同编号) func NewContractInfo(enterpriseInfoID, userID, contractName string, contractType ContractType, contractFileID, contractFileURL string) (*ContractInfo, error) { + contractCode := GenerateContractCode(contractType) + return newContractInfo(enterpriseInfoID, userID, contractName, contractType, contractFileID, contractFileURL, contractCode) +} + +// NewContractInfoWithCode 创建新的合同信息(使用指定的合同编号) +func NewContractInfoWithCode(enterpriseInfoID, userID, contractName string, contractType ContractType, contractFileID, contractFileURL, contractCode string) (*ContractInfo, error) { + if contractCode == "" { + return nil, fmt.Errorf("合同编号不能为空") + } + return newContractInfo(enterpriseInfoID, userID, contractName, contractType, contractFileID, contractFileURL, contractCode) +} + +// newContractInfo 创建合同信息的内部实现 +func newContractInfo(enterpriseInfoID, userID, contractName string, contractType ContractType, contractFileID, contractFileURL, contractCode string) (*ContractInfo, error) { if enterpriseInfoID == "" { return nil, fmt.Errorf("企业信息ID不能为空") } @@ -90,9 +104,6 @@ func NewContractInfo(enterpriseInfoID, userID, contractName string, contractType return nil, fmt.Errorf("无效的合同类型: %s", contractType) } - // 生成合同编码 - contractCode := GenerateContractCode(contractType) - contractInfo := &ContractInfo{ ID: uuid.New().String(), EnterpriseInfoID: enterpriseInfoID, @@ -338,22 +349,24 @@ type ContractInfoDeletedEvent struct { } // GenerateContractCode 生成合同编码 +// 格式:HYDATA-YYYYMMDD-R{6位随机数}(R=入驻) func GenerateContractCode(contractType ContractType) string { - prefix := "CON" + now := time.Now() + dateStr := now.Format("20060102") + + prefix := "R" switch contractType { case ContractTypeCooperation: - prefix += "01" + prefix = "R" // 入驻 case ContractTypeReSign: - prefix += "02" + prefix = "B" // 补签 } - // 获取当前日期,格式为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) + return fmt.Sprintf("HYDATA-%s-%s%s", dateStr, prefix, randNum) +} + +// GenerateContractCodeWithPrefix 使用指定前缀生成合同编码 +func GenerateContractCodeWithPrefix(contractType ContractType) string { + return GenerateContractCode(contractType) } diff --git a/internal/domains/user/services/contract_aggregate_service.go b/internal/domains/user/services/contract_aggregate_service.go index 820281c..c7f7a50 100644 --- a/internal/domains/user/services/contract_aggregate_service.go +++ b/internal/domains/user/services/contract_aggregate_service.go @@ -14,6 +14,7 @@ import ( type ContractAggregateService interface { // 聚合根生命周期管理 CreateContract(ctx context.Context, enterpriseInfoID, userID, contractName string, contractType entities.ContractType, contractFileID, contractFileURL string) (*entities.ContractInfo, error) + CreateContractWithCode(ctx context.Context, enterpriseInfoID, userID, contractName string, contractType entities.ContractType, contractFileID, contractFileURL, contractCode string) (*entities.ContractInfo, error) LoadContract(ctx context.Context, contractID string) (*entities.ContractInfo, error) SaveContract(ctx context.Context, contract *entities.ContractInfo) error DeleteContract(ctx context.Context, contractID string) error @@ -94,6 +95,48 @@ func (s *ContractAggregateServiceImpl) CreateContract( return contract, nil } +// CreateContractWithCode 使用指定编号创建合同信息 +func (s *ContractAggregateServiceImpl) CreateContractWithCode( + ctx context.Context, + enterpriseInfoID, userID, contractName string, + contractType entities.ContractType, + contractFileID, contractFileURL, contractCode string, +) (*entities.ContractInfo, error) { + s.logger.Debug("使用指定编号创建合同信息", + zap.String("enterprise_info_id", enterpriseInfoID), + zap.String("user_id", userID), + zap.String("contract_code", contractCode)) + + exists, err := s.ExistsByContractFileID(ctx, contractFileID) + if err != nil { + return nil, fmt.Errorf("检查合同文件ID失败: %w", err) + } + if exists { + return nil, fmt.Errorf("合同文件ID已存在") + } + + contract, err := entities.NewContractInfoWithCode(enterpriseInfoID, userID, contractName, contractType, contractFileID, contractFileURL, contractCode) + if err != nil { + return nil, fmt.Errorf("创建合同信息失败: %w", err) + } + + if err := s.ValidateBusinessRules(ctx, contract); err != nil { + return nil, fmt.Errorf("业务规则验证失败: %w", err) + } + + err = s.SaveContract(ctx, contract) + if err != nil { + return nil, fmt.Errorf("保存合同信息失败: %w", err) + } + + s.logger.Info("合同信息创建成功(指定编号)", + zap.String("contract_id", contract.ID), + zap.String("contract_code", contractCode), + zap.String("enterprise_info_id", enterpriseInfoID)) + + return contract, nil +} + // LoadContract 加载合同信息 func (s *ContractAggregateServiceImpl) LoadContract(ctx context.Context, contractID string) (*entities.ContractInfo, error) { s.logger.Debug("加载合同信息", zap.String("contract_id", contractID))