This commit is contained in:
2025-07-21 15:13:26 +08:00
parent 8ad1d7288e
commit b03129667a
41 changed files with 8497 additions and 3968 deletions

View File

@@ -0,0 +1,384 @@
package services
import (
"context"
"fmt"
"tyapi-server/internal/domains/certification/entities"
"tyapi-server/internal/domains/certification/enums"
"tyapi-server/internal/domains/certification/repositories"
"tyapi-server/internal/domains/certification/services/state_machine"
"go.uber.org/zap"
)
// CertificationAggregateService 认证聚合服务接口
// 负责认证聚合根的生命周期管理和状态转换协调
type CertificationAggregateService interface {
// 聚合根管理
CreateCertification(ctx context.Context, userID string) (*entities.Certification, error)
LoadCertification(ctx context.Context, certificationID string) (*entities.Certification, error)
SaveCertification(ctx context.Context, cert *entities.Certification) error
// 状态转换管理
TransitionState(ctx context.Context, certificationID string, targetStatus enums.CertificationStatus, actor enums.ActorType, actorID string, reason string, metadata map[string]interface{}) (*state_machine.StateTransitionResult, error)
ValidateStateTransition(ctx context.Context, certificationID string, targetStatus enums.CertificationStatus, actor enums.ActorType) error
// 业务规则验证
ValidateBusinessRules(ctx context.Context, cert *entities.Certification) error
CheckInvariance(ctx context.Context, cert *entities.Certification) error
// 查询方法
GetStateInfo(status enums.CertificationStatus) *state_machine.StateConfig
GetValidTransitions(ctx context.Context, certificationID string, actor enums.ActorType) ([]*state_machine.StateTransitionRule, error)
}
// CertificationAggregateServiceImpl 认证聚合服务实现
type CertificationAggregateServiceImpl struct {
commandRepo repositories.CertificationCommandRepository
queryRepo repositories.CertificationQueryRepository
stateMachine *state_machine.CertificationStateMachine
logger *zap.Logger
}
// NewCertificationAggregateService 创建认证聚合服务
func NewCertificationAggregateService(
commandRepo repositories.CertificationCommandRepository,
queryRepo repositories.CertificationQueryRepository,
stateMachine *state_machine.CertificationStateMachine,
logger *zap.Logger,
) CertificationAggregateService {
return &CertificationAggregateServiceImpl{
commandRepo: commandRepo,
queryRepo: queryRepo,
stateMachine: stateMachine,
logger: logger,
}
}
// ================ 聚合根管理 ================
// CreateCertification 创建认证申请
func (s *CertificationAggregateServiceImpl) CreateCertification(ctx context.Context, userID string) (*entities.Certification, error) {
s.logger.Info("创建认证申请", zap.String("user_id", userID))
// 1. 检查用户是否已有认证申请
existingCert, err := s.queryRepo.GetByUserID(ctx, userID)
if err == nil && existingCert != nil {
// 检查现有认证的状态
if !existingCert.IsFinalStatus() {
return nil, fmt.Errorf("用户已有进行中的认证申请,请先完成或取消现有申请")
}
s.logger.Info("用户已有完成的认证申请,允许创建新申请",
zap.String("user_id", userID),
zap.String("existing_cert_id", existingCert.ID),
zap.String("existing_status", string(existingCert.Status)))
}
// 2. 创建新的认证聚合根
cert, err := entities.NewCertification(userID)
if err != nil {
s.logger.Error("创建认证实体失败", zap.Error(err), zap.String("user_id", userID))
return nil, fmt.Errorf("创建认证实体失败: %w", err)
}
// 3. 验证业务规则
if err := s.ValidateBusinessRules(ctx, cert); err != nil {
s.logger.Error("认证业务规则验证失败", zap.Error(err))
return nil, fmt.Errorf("业务规则验证失败: %w", err)
}
// 4. 保存聚合根
if err := s.SaveCertification(ctx, cert); err != nil {
s.logger.Error("保存认证申请失败", zap.Error(err))
return nil, fmt.Errorf("保存认证申请失败: %w", err)
}
s.logger.Info("认证申请创建成功",
zap.String("user_id", userID),
zap.String("certification_id", cert.ID))
return cert, nil
}
// LoadCertification 加载认证聚合根
func (s *CertificationAggregateServiceImpl) LoadCertification(ctx context.Context, certificationID string) (*entities.Certification, error) {
s.logger.Debug("加载认证聚合根", zap.String("certification_id", certificationID))
// 从查询仓储加载
cert, err := s.queryRepo.GetByID(ctx, certificationID)
if err != nil {
s.logger.Error("加载认证聚合根失败", zap.Error(err), zap.String("certification_id", certificationID))
return nil, fmt.Errorf("认证申请不存在: %w", err)
}
// 验证聚合根完整性
if err := s.CheckInvariance(ctx, cert); err != nil {
s.logger.Error("认证聚合根完整性验证失败", zap.Error(err))
return nil, fmt.Errorf("认证数据完整性验证失败: %w", err)
}
return cert, nil
}
// SaveCertification 保存认证聚合根
func (s *CertificationAggregateServiceImpl) SaveCertification(ctx context.Context, cert *entities.Certification) error {
s.logger.Debug("保存认证聚合根", zap.String("certification_id", cert.ID))
// 1. 验证业务规则
if err := s.ValidateBusinessRules(ctx, cert); err != nil {
return fmt.Errorf("业务规则验证失败: %w", err)
}
// 2. 检查聚合根是否存在
exists, err := s.queryRepo.Exists(ctx, cert.ID)
if err != nil {
return fmt.Errorf("检查认证存在性失败: %w", err)
}
// 3. 保存到命令仓储
if exists {
err = s.commandRepo.Update(ctx, *cert)
if err != nil {
s.logger.Error("更新认证聚合根失败", zap.Error(err))
return fmt.Errorf("更新认证失败: %w", err)
}
} else {
err = s.commandRepo.Create(ctx, *cert)
if err != nil {
s.logger.Error("创建认证聚合根失败", zap.Error(err))
return fmt.Errorf("创建认证失败: %w", err)
}
}
s.logger.Debug("认证聚合根保存成功", zap.String("certification_id", cert.ID))
return nil
}
// ================ 状态转换管理 ================
// TransitionState 执行状态转换
func (s *CertificationAggregateServiceImpl) TransitionState(
ctx context.Context,
certificationID string,
targetStatus enums.CertificationStatus,
actor enums.ActorType,
actorID string,
reason string,
metadata map[string]interface{},
) (*state_machine.StateTransitionResult, error) {
s.logger.Info("执行状态转换",
zap.String("certification_id", certificationID),
zap.String("target_status", string(targetStatus)),
zap.String("actor", string(actor)),
zap.String("actor_id", actorID))
// 构建状态转换请求
req := &state_machine.StateTransitionRequest{
CertificationID: certificationID,
TargetStatus: targetStatus,
Actor: actor,
ActorID: actorID,
Reason: reason,
Context: metadata,
AllowRollback: true,
}
// 执行状态转换
result, err := s.stateMachine.ExecuteTransition(ctx, req)
if err != nil {
s.logger.Error("状态转换执行失败",
zap.String("certification_id", certificationID),
zap.Error(err))
return result, err
}
s.logger.Info("状态转换执行成功",
zap.String("certification_id", certificationID),
zap.String("from_status", string(result.OldStatus)),
zap.String("to_status", string(result.NewStatus)))
return result, nil
}
// ValidateStateTransition 验证状态转换
func (s *CertificationAggregateServiceImpl) ValidateStateTransition(
ctx context.Context,
certificationID string,
targetStatus enums.CertificationStatus,
actor enums.ActorType,
) error {
// 加载认证聚合根
cert, err := s.LoadCertification(ctx, certificationID)
if err != nil {
return err
}
// 检查是否可以转换
canTransition, message := s.stateMachine.CanTransition(cert, targetStatus, actor)
if !canTransition {
return fmt.Errorf("状态转换验证失败: %s", message)
}
return nil
}
// ================ 业务规则验证 ================
// ValidateBusinessRules 验证业务规则
func (s *CertificationAggregateServiceImpl) ValidateBusinessRules(ctx context.Context, cert *entities.Certification) error {
s.logger.Debug("验证认证业务规则", zap.String("certification_id", cert.ID))
// 1. 实体内部业务规则验证
if err := cert.ValidateBusinessRules(); err != nil {
return fmt.Errorf("实体业务规则验证失败: %w", err)
}
// 2. 跨聚合根业务规则验证
if err := s.validateCrossAggregateRules(ctx, cert); err != nil {
return fmt.Errorf("跨聚合根业务规则验证失败: %w", err)
}
// 3. 领域级业务规则验证
if err := s.validateDomainRules(ctx, cert); err != nil {
return fmt.Errorf("领域业务规则验证失败: %w", err)
}
return nil
}
// CheckInvariance 检查聚合根不变量
func (s *CertificationAggregateServiceImpl) CheckInvariance(ctx context.Context, cert *entities.Certification) error {
s.logger.Debug("检查认证聚合根不变量", zap.String("certification_id", cert.ID))
// 1. 基础不变量检查
if cert.ID == "" {
return fmt.Errorf("认证ID不能为空")
}
if cert.UserID == "" {
return fmt.Errorf("用户ID不能为空")
}
if !enums.IsValidStatus(cert.Status) {
return fmt.Errorf("无效的认证状态: %s", cert.Status)
}
// 2. 状态相关不变量检查
if err := s.validateStatusInvariance(cert); err != nil {
return err
}
// 3. 时间戳不变量检查
if err := s.validateTimestampInvariance(cert); err != nil {
return err
}
return nil
}
// ================ 查询方法 ================
// GetStateInfo 获取状态信息
func (s *CertificationAggregateServiceImpl) GetStateInfo(status enums.CertificationStatus) *state_machine.StateConfig {
return s.stateMachine.GetStateInfo(status)
}
// GetValidTransitions 获取有效的状态转换
func (s *CertificationAggregateServiceImpl) GetValidTransitions(
ctx context.Context,
certificationID string,
actor enums.ActorType,
) ([]*state_machine.StateTransitionRule, error) {
// 加载认证聚合根
cert, err := s.LoadCertification(ctx, certificationID)
if err != nil {
return nil, err
}
// 获取有效转换
transitions := s.stateMachine.GetValidTransitions(cert, actor)
return transitions, nil
}
// ================ 私有方法 ================
// validateCrossAggregateRules 验证跨聚合根业务规则
func (s *CertificationAggregateServiceImpl) validateCrossAggregateRules(ctx context.Context, cert *entities.Certification) error {
// TODO: 实现跨聚合根业务规则验证
// 例如:检查用户是否有权限申请认证、检查企业信息是否已被其他用户使用等
return nil
}
// validateDomainRules 验证领域级业务规则
func (s *CertificationAggregateServiceImpl) validateDomainRules(ctx context.Context, cert *entities.Certification) error {
// TODO: 实现领域级业务规则验证
// 例如:检查认证流程是否符合法规要求、检查时间窗口限制等
return nil
}
// validateStatusInvariance 验证状态相关不变量
func (s *CertificationAggregateServiceImpl) validateStatusInvariance(cert *entities.Certification) error {
switch cert.Status {
case enums.StatusEnterpriseVerified:
if cert.AuthFlowID == "" {
return fmt.Errorf("企业认证状态下必须有认证流程ID")
}
if cert.EnterpriseVerifiedAt == nil {
return fmt.Errorf("企业认证状态下必须有认证完成时间")
}
case enums.StatusContractApplied:
if cert.AuthFlowID == "" {
return fmt.Errorf("合同申请状态下必须有企业认证流程ID")
}
if cert.ContractAppliedAt == nil {
return fmt.Errorf("合同申请状态下必须有合同申请时间")
}
case enums.StatusContractSigned:
if cert.ContractFileID == "" || cert.EsignFlowID == "" {
return fmt.Errorf("合同签署状态下必须有完整的合同信息")
}
if cert.ContractSignedAt == nil {
return fmt.Errorf("合同签署状态下必须有签署完成时间")
}
}
// 失败状态检查
if enums.IsFailureStatus(cert.Status) {
if cert.FailureReason == "" {
return fmt.Errorf("失败状态下必须有失败原因")
}
if !enums.IsValidFailureReason(cert.FailureReason) {
return fmt.Errorf("无效的失败原因: %s", cert.FailureReason)
}
}
return nil
}
// validateTimestampInvariance 验证时间戳不变量
func (s *CertificationAggregateServiceImpl) validateTimestampInvariance(cert *entities.Certification) error {
// 检查时间戳的逻辑顺序
if cert.InfoSubmittedAt != nil && cert.EnterpriseVerifiedAt != nil {
if cert.InfoSubmittedAt.After(*cert.EnterpriseVerifiedAt) {
return fmt.Errorf("企业信息提交时间不能晚于企业认证时间")
}
}
if cert.EnterpriseVerifiedAt != nil && cert.ContractAppliedAt != nil {
if cert.EnterpriseVerifiedAt.After(*cert.ContractAppliedAt) {
return fmt.Errorf("企业认证时间不能晚于合同申请时间")
}
}
if cert.ContractAppliedAt != nil && cert.ContractSignedAt != nil {
if cert.ContractAppliedAt.After(*cert.ContractSignedAt) {
return fmt.Errorf("合同申请时间不能晚于合同签署时间")
}
}
return nil
}

View File

@@ -1,126 +0,0 @@
package services
import (
"context"
"fmt"
"go.uber.org/zap"
cert_entities "tyapi-server/internal/domains/certification/entities"
"tyapi-server/internal/domains/certification/repositories"
user_entities "tyapi-server/internal/domains/user/entities"
"tyapi-server/internal/shared/esign"
)
// CertificationEsignService 负责与e签宝相关的认证业务逻辑
type CertificationEsignService struct {
certRepo repositories.CertificationRepository
esignClient *esign.Client
esignContractGenerateRecordRepo repositories.EsignContractGenerateRecordRepository
esignContractSignRecordRepo repositories.EsignContractSignRecordRepository
logger *zap.Logger
}
// NewCertificationEsignService 创建CertificationEsignService实例
func NewCertificationEsignService(
certRepo repositories.CertificationRepository,
esignClient *esign.Client,
esignContractGenerateRecordRepo repositories.EsignContractGenerateRecordRepository,
logger *zap.Logger,
) *CertificationEsignService {
return &CertificationEsignService{
certRepo: certRepo,
esignClient: esignClient,
esignContractGenerateRecordRepo: esignContractGenerateRecordRepo,
logger: logger,
}
}
// FillTemplate 生成合同文件e签宝模板填充
func (s *CertificationEsignService) FillTemplate(ctx context.Context, certification *cert_entities.Certification, components map[string]string) (*esign.FillTemplate, error) {
resp, err := s.esignClient.FillTemplate(components)
record := &cert_entities.EsignContractGenerateRecord{
CertificationID: certification.ID,
UserID: certification.UserID,
}
if err != nil {
s.logger.Error("生成合同文件失败", zap.Any("components", components), zap.Error(err))
record.Status = "failed"
} else {
record.TemplateID = resp.TemplateID
record.ContractName = resp.FileName
record.ContractFileID = resp.FileID
record.ContractURL = resp.FileDownloadUrl
record.Status = "success"
record.FillTime = &resp.FillTime
}
if _, createErr := s.esignContractGenerateRecordRepo.Create(ctx, *record); createErr != nil {
s.logger.Error("创建合同生成记录失败", zap.Error(createErr))
if err == nil {
return nil, fmt.Errorf("创建合同生成记录失败: %w", createErr)
}
}
if err != nil {
return nil, fmt.Errorf("生成合同文件失败: %w", err)
}
certification.ContractURL = resp.FileDownloadUrl
certification.ContractFileID = resp.FileID
err = s.certRepo.Update(ctx, *certification)
if err != nil {
s.logger.Error("更新认证申请失败", zap.Error(err))
return nil, fmt.Errorf("更新认证申请失败: %w", err)
}
s.logger.Info("生成合同文件成功", zap.String("template_id", resp.TemplateID), zap.String("file_id", resp.FileID))
return resp, nil
}
// 发起签署
func (s *CertificationEsignService) InitiateSign(ctx context.Context, certification *cert_entities.Certification, enterpriseInfo *user_entities.EnterpriseInfo) (*cert_entities.EsignContractSignRecord, error) {
// 发起签署流程
flowID, err := s.esignClient.CreateSignFlow(&esign.CreateSignFlowRequest{
FileID: certification.ContractFileID,
SignerAccount: enterpriseInfo.UnifiedSocialCode,
SignerName: enterpriseInfo.CompanyName,
TransactorPhone: enterpriseInfo.LegalPersonPhone,
TransactorName: enterpriseInfo.LegalPersonName,
TransactorIDCardNum: enterpriseInfo.LegalPersonID,
})
if err != nil {
s.logger.Error("获取签署链接失败",
zap.String("user_id", enterpriseInfo.UserID),
zap.Error(err),
)
return nil, fmt.Errorf("获取签署链接失败: %w", err)
}
signURL, shortURL, err := s.esignClient.GetSignURL(flowID, enterpriseInfo.UnifiedSocialCode, enterpriseInfo.CompanyName)
if err != nil {
s.logger.Error("获取签署链接失败", zap.Error(err))
return nil, fmt.Errorf("获取签署链接失败: %w", err)
}
esignContractSignRecord := cert_entities.NewEsignContractSignRecord(
certification.ID,
enterpriseInfo.UserID,
flowID,
certification.ContractFileID,
enterpriseInfo.UnifiedSocialCode,
enterpriseInfo.LegalPersonPhone,
enterpriseInfo.LegalPersonID,
signURL,
shortURL,
)
signContractSignRecord, err := s.esignContractSignRecordRepo.Create(ctx, *esignContractSignRecord)
if err != nil {
s.logger.Error("创建签署记录失败", zap.Error(err))
return nil, fmt.Errorf("创建签署记录失败: %w", err)
}
certification.EsignFlowID = signContractSignRecord.EsignFlowID
certification.ContractSignURL = signContractSignRecord.SignShortURL // 记录的是短链接
err = s.certRepo.Update(ctx, *certification)
if err != nil {
s.logger.Error("更新认证申请失败", zap.Error(err))
return nil, fmt.Errorf("更新认证申请失败: %w", err)
}
return &signContractSignRecord, nil
}

View File

@@ -1,153 +0,0 @@
package services
import (
"context"
"fmt"
"tyapi-server/internal/domains/certification/entities"
"tyapi-server/internal/domains/certification/enums"
"tyapi-server/internal/domains/certification/repositories"
esign_service "tyapi-server/internal/shared/esign"
"go.uber.org/zap"
)
// CertificationManagementService 认证管理领域服务
// 负责认证申请的生命周期管理,包括创建、状态转换、进度查询等
type CertificationManagementService struct {
certRepo repositories.CertificationRepository
esignService *esign_service.Client
stateMachine *CertificationStateMachine
logger *zap.Logger
}
// NewCertificationManagementService 创建认证管理领域服务
func NewCertificationManagementService(
certRepo repositories.CertificationRepository,
esignService *esign_service.Client,
stateMachine *CertificationStateMachine,
logger *zap.Logger,
) *CertificationManagementService {
return &CertificationManagementService{
certRepo: certRepo,
esignService: esignService,
stateMachine: stateMachine,
logger: logger,
}
}
// CreateCertification 创建认证申请
func (s *CertificationManagementService) CreateCertification(ctx context.Context, userID string) (*entities.Certification, error) {
// 检查用户是否已有认证申请
existingCert, err := s.certRepo.GetByUserID(ctx, userID)
if err == nil && existingCert != nil {
return nil, fmt.Errorf("用户已有认证申请")
}
certification := &entities.Certification{
UserID: userID,
Status: enums.StatusPending,
}
createdCert, err := s.certRepo.Create(ctx, *certification)
if err != nil {
s.logger.Error("创建认证申请失败", zap.Error(err))
return nil, fmt.Errorf("创建认证申请失败: %w", err)
}
certification = &createdCert
s.logger.Info("认证申请创建成功",
zap.String("certification_id", certification.ID),
zap.String("user_id", userID),
)
return certification, nil
}
// GetCertificationByUserID 根据用户ID获取认证申请
func (s *CertificationManagementService) GetCertificationByUserID(ctx context.Context, userID string) (*entities.Certification, error) {
return s.certRepo.GetByUserID(ctx, userID)
}
// GetCertificationByID 根据ID获取认证申请
func (s *CertificationManagementService) GetCertificationByID(ctx context.Context, certificationID string) (*entities.Certification, error) {
cert, err := s.certRepo.GetByID(ctx, certificationID)
if err != nil {
return nil, err
}
return &cert, nil
}
// GetCertificationByAuthFlowID 根据认证流程ID获取认证申请
func (s *CertificationManagementService) GetCertificationByAuthFlowID(ctx context.Context, authFlowID string) (*entities.Certification, error) {
cert, err := s.certRepo.GetByAuthFlowID(ctx, authFlowID)
if err != nil {
return nil, err
}
return &cert, nil
}
// 根据签署流程ID获取认证申请
func (s *CertificationManagementService) GetCertificationByEsignFlowID(ctx context.Context, esignFlowID string) (*entities.Certification, error) {
cert, err := s.certRepo.GetByEsignFlowID(ctx, esignFlowID)
if err != nil {
return nil, err
}
return &cert, nil
}
// GetCertificationProgress 获取认证进度信息
func (s *CertificationManagementService) GetCertificationProgress(ctx context.Context, certificationID string) (map[string]interface{}, error) {
cert, err := s.certRepo.GetByID(ctx, certificationID)
if err != nil {
return nil, fmt.Errorf("认证申请不存在: %w", err)
}
progress := map[string]interface{}{
"certification_id": cert.ID,
"user_id": cert.UserID,
"current_status": cert.Status,
"progress_percentage": cert.GetProgressPercentage(),
"is_user_action_required": cert.IsUserActionRequired(),
"next_valid_statuses": cert.GetNextValidStatuses(),
"created_at": cert.CreatedAt,
"updated_at": cert.UpdatedAt,
}
// 添加时间节点信息
if cert.InfoSubmittedAt != nil {
progress["info_submitted_at"] = cert.InfoSubmittedAt
}
if cert.EnterpriseVerifiedAt != nil {
progress["enterprise_verified_at"] = cert.EnterpriseVerifiedAt
}
if cert.ContractAppliedAt != nil {
progress["contract_applied_at"] = cert.ContractAppliedAt
}
if cert.ContractSignedAt != nil {
progress["contract_signed_at"] = cert.ContractSignedAt
}
if cert.CompletedAt != nil {
progress["completed_at"] = cert.CompletedAt
}
return progress, nil
}
// 通过e签宝检查是否有过认证
func (s *CertificationManagementService) CheckCertification(ctx context.Context, companyName string, unifiedSocialCode string) (bool, error) {
// 查询企业是否已经过认证
queryOrgIdentityInfo := &esign_service.QueryOrgIdentityRequest{
OrgName: companyName,
OrgIDCardNum: unifiedSocialCode,
OrgIDCardType: esign_service.OrgIDCardTypeUSCC,
}
queryOrgIdentityResponse, err := s.esignService.QueryOrgIdentityInfo(queryOrgIdentityInfo)
if err != nil {
return false, fmt.Errorf("查询机构认证信息失败: %w", err)
}
if queryOrgIdentityResponse.Data.RealnameStatus == 1 {
s.logger.Info("该企业已进行过认证", zap.String("company_name", companyName), zap.String("unified_social_code", unifiedSocialCode))
return true, nil
}
return false, nil
}

View File

@@ -0,0 +1,636 @@
package services
import (
"context"
"fmt"
"time"
"tyapi-server/internal/domains/certification/entities"
"tyapi-server/internal/domains/certification/entities/value_objects"
"tyapi-server/internal/domains/certification/enums"
"tyapi-server/internal/domains/certification/services/state_machine"
"go.uber.org/zap"
)
// WorkflowResult 工作流执行结果
type WorkflowResult struct {
Success bool `json:"success"`
CertificationID string `json:"certification_id"`
CurrentStatus enums.CertificationStatus `json:"current_status"`
Message string `json:"message"`
Data map[string]interface{} `json:"data,omitempty"`
StateTransition *state_machine.StateTransitionResult `json:"state_transition,omitempty"`
ExecutedAt time.Time `json:"executed_at"`
}
// SubmitEnterpriseInfoCommand 提交企业信息命令
type SubmitEnterpriseInfoCommand struct {
CertificationID string `json:"certification_id"`
UserID string `json:"user_id"`
EnterpriseInfo *value_objects.EnterpriseInfo `json:"enterprise_info"`
}
// ApplyContractCommand 申请合同命令
type ApplyContractCommand struct {
CertificationID string `json:"certification_id"`
UserID string `json:"user_id"`
}
// EsignCallbackCommand e签宝回调命令
type EsignCallbackCommand struct {
CertificationID string `json:"certification_id"`
CallbackType string `json:"callback_type"` // "auth_result" | "sign_result" | "flow_status"
CallbackData *state_machine.EsignCallbackData `json:"callback_data"`
}
// CertificationWorkflowOrchestrator 认证工作流编排器接口
// 负责编排认证业务流程,协调各个领域服务的协作
type CertificationWorkflowOrchestrator interface {
// 用户操作用例
SubmitEnterpriseInfo(ctx context.Context, cmd *SubmitEnterpriseInfoCommand) (*WorkflowResult, error)
ApplyContract(ctx context.Context, cmd *ApplyContractCommand) (*WorkflowResult, error)
// e签宝回调处理
HandleEnterpriseVerificationCallback(ctx context.Context, cmd *EsignCallbackCommand) (*WorkflowResult, error)
HandleContractSignCallback(ctx context.Context, cmd *EsignCallbackCommand) (*WorkflowResult, error)
// 异常处理
HandleFailure(ctx context.Context, certificationID string, failureType string, reason string) (*WorkflowResult, error)
RetryOperation(ctx context.Context, certificationID string, operation string) (*WorkflowResult, error)
// 查询操作
GetWorkflowStatus(ctx context.Context, certificationID string) (*WorkflowResult, error)
}
// CertificationWorkflowOrchestratorImpl 认证工作流编排器实现
type CertificationWorkflowOrchestratorImpl struct {
aggregateService CertificationAggregateService
callbackHandler *state_machine.EsignCallbackHandler
logger *zap.Logger
}
// NewCertificationWorkflowOrchestrator 创建认证工作流编排器
func NewCertificationWorkflowOrchestrator(
aggregateService CertificationAggregateService,
callbackHandler *state_machine.EsignCallbackHandler,
logger *zap.Logger,
) CertificationWorkflowOrchestrator {
return &CertificationWorkflowOrchestratorImpl{
aggregateService: aggregateService,
callbackHandler: callbackHandler,
logger: logger,
}
}
// ================ 用户操作用例 ================
// SubmitEnterpriseInfo 用户提交企业信息
func (o *CertificationWorkflowOrchestratorImpl) SubmitEnterpriseInfo(
ctx context.Context,
cmd *SubmitEnterpriseInfoCommand,
) (*WorkflowResult, error) {
o.logger.Info("开始处理企业信息提交",
zap.String("certification_id", cmd.CertificationID),
zap.String("user_id", cmd.UserID))
// 1. 验证命令完整性
if err := o.validateSubmitEnterpriseInfoCommand(cmd); err != nil {
return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("命令验证失败: %s", err.Error())), err
}
// 2. 加载认证聚合根
cert, err := o.aggregateService.LoadCertification(ctx, cmd.CertificationID)
if err != nil {
return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
}
// 3. 验证业务前置条件
if err := o.validateEnterpriseInfoSubmissionPreconditions(cert, cmd.UserID); err != nil {
return o.createFailureResult(cmd.CertificationID, cert.Status, err.Error()), err
}
// 4. 执行状态转换
metadata := map[string]interface{}{
"enterprise_info": cmd.EnterpriseInfo,
"user_id": cmd.UserID,
}
result, err := o.aggregateService.TransitionState(
ctx,
cmd.CertificationID,
enums.StatusInfoSubmitted,
enums.ActorTypeUser,
cmd.UserID,
"用户提交企业信息",
metadata,
)
if err != nil {
o.logger.Error("企业信息提交状态转换失败", zap.Error(err))
return o.createFailureResult(cmd.CertificationID, cert.Status, fmt.Sprintf("状态转换失败: %s", err.Error())), err
}
// 5. 执行后续处理如调用e签宝API
if err := o.triggerEnterpriseVerification(ctx, cmd.CertificationID, cmd.EnterpriseInfo); err != nil {
o.logger.Warn("触发企业认证失败", zap.Error(err))
// 这里不返回错误因为状态已经成功转换e签宝调用失败可以通过重试机制处理
}
// 6. 构建成功结果
return o.createSuccessResult(cmd.CertificationID, enums.StatusInfoSubmitted, "企业信息提交成功", map[string]interface{}{
"enterprise_info": cmd.EnterpriseInfo,
"next_action": "等待企业认证结果",
}, result), nil
}
// ApplyContract 用户申请合同签署
func (o *CertificationWorkflowOrchestratorImpl) ApplyContract(
ctx context.Context,
cmd *ApplyContractCommand,
) (*WorkflowResult, error) {
o.logger.Info("开始处理合同申请",
zap.String("certification_id", cmd.CertificationID),
zap.String("user_id", cmd.UserID))
// 1. 验证命令完整性
if err := o.validateApplyContractCommand(cmd); err != nil {
return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("命令验证失败: %s", err.Error())), err
}
// 2. 加载认证聚合根
cert, err := o.aggregateService.LoadCertification(ctx, cmd.CertificationID)
if err != nil {
return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
}
// 3. 验证业务前置条件
if err := o.validateContractApplicationPreconditions(cert, cmd.UserID); err != nil {
return o.createFailureResult(cmd.CertificationID, cert.Status, err.Error()), err
}
// 4. 执行状态转换
result, err := o.aggregateService.TransitionState(
ctx,
cmd.CertificationID,
enums.StatusContractApplied,
enums.ActorTypeUser,
cmd.UserID,
"用户申请合同签署",
map[string]interface{}{},
)
if err != nil {
o.logger.Error("合同申请状态转换失败", zap.Error(err))
return o.createFailureResult(cmd.CertificationID, cert.Status, fmt.Sprintf("状态转换失败: %s", err.Error())), err
}
// 5. 生成合同和签署链接
contractInfo, err := o.generateContractAndSignURL(ctx, cmd.CertificationID, cert)
if err != nil {
o.logger.Error("生成合同失败", zap.Error(err))
// 需要回滚状态
return o.createFailureResult(cmd.CertificationID, cert.Status, fmt.Sprintf("生成合同失败: %s", err.Error())), err
}
// 6. 构建成功结果
return o.createSuccessResult(cmd.CertificationID, enums.StatusContractApplied, "合同申请成功", map[string]interface{}{
"contract_sign_url": contractInfo.ContractSignURL,
"contract_url": contractInfo.ContractURL,
"next_action": "请在规定时间内完成合同签署",
}, result), nil
}
// ================ e签宝回调处理 ================
// HandleEnterpriseVerificationCallback 处理企业认证回调
func (o *CertificationWorkflowOrchestratorImpl) HandleEnterpriseVerificationCallback(
ctx context.Context,
cmd *EsignCallbackCommand,
) (*WorkflowResult, error) {
o.logger.Info("开始处理企业认证回调",
zap.String("certification_id", cmd.CertificationID),
zap.String("callback_type", cmd.CallbackType))
// 1. 验证回调数据
if err := o.callbackHandler.ValidateCallbackData(cmd.CallbackData); err != nil {
return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("回调数据验证失败: %s", err.Error())), err
}
// 2. 加载认证聚合根
cert, err := o.aggregateService.LoadCertification(ctx, cmd.CertificationID)
if err != nil {
return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
}
// 3. 验证回调处理前置条件
if cert.Status != enums.StatusInfoSubmitted {
return o.createFailureResult(cmd.CertificationID, cert.Status,
fmt.Sprintf("当前状态 %s 不允许处理企业认证回调", enums.GetStatusName(cert.Status))),
fmt.Errorf("无效的状态转换")
}
// 4. 处理回调
err = o.callbackHandler.HandleCallback(ctx, cmd.CertificationID, cmd.CallbackData)
if err != nil {
o.logger.Error("处理企业认证回调失败", zap.Error(err))
return o.createFailureResult(cmd.CertificationID, cert.Status, fmt.Sprintf("回调处理失败: %s", err.Error())), err
}
// 5. 重新加载认证信息获取最新状态
updatedCert, err := o.aggregateService.LoadCertification(ctx, cmd.CertificationID)
if err != nil {
return o.createFailureResult(cmd.CertificationID, cert.Status, "加载更新后的认证信息失败"), err
}
// 6. 构建结果
message := "企业认证回调处理成功"
data := map[string]interface{}{
"auth_flow_id": cmd.CallbackData.FlowID,
"status": cmd.CallbackData.Status,
}
if updatedCert.Status == enums.StatusEnterpriseVerified {
message = "企业认证成功"
data["next_action"] = "可以申请合同签署"
} else if updatedCert.Status == enums.StatusInfoRejected {
message = "企业认证失败"
data["next_action"] = "请修正企业信息后重新提交"
data["failure_reason"] = enums.GetFailureReasonName(updatedCert.FailureReason)
data["failure_message"] = updatedCert.FailureMessage
}
return o.createSuccessResult(cmd.CertificationID, updatedCert.Status, message, data, nil), nil
}
// HandleContractSignCallback 处理合同签署回调
func (o *CertificationWorkflowOrchestratorImpl) HandleContractSignCallback(
ctx context.Context,
cmd *EsignCallbackCommand,
) (*WorkflowResult, error) {
o.logger.Info("开始处理合同签署回调",
zap.String("certification_id", cmd.CertificationID),
zap.String("callback_type", cmd.CallbackType))
// 1. 验证回调数据
if err := o.callbackHandler.ValidateCallbackData(cmd.CallbackData); err != nil {
return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("回调数据验证失败: %s", err.Error())), err
}
// 2. 加载认证聚合根
cert, err := o.aggregateService.LoadCertification(ctx, cmd.CertificationID)
if err != nil {
return o.createFailureResult(cmd.CertificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
}
// 3. 验证回调处理前置条件
if cert.Status != enums.StatusContractApplied {
return o.createFailureResult(cmd.CertificationID, cert.Status,
fmt.Sprintf("当前状态 %s 不允许处理合同签署回调", enums.GetStatusName(cert.Status))),
fmt.Errorf("无效的状态转换")
}
// 4. 处理回调
err = o.callbackHandler.HandleCallback(ctx, cmd.CertificationID, cmd.CallbackData)
if err != nil {
o.logger.Error("处理合同签署回调失败", zap.Error(err))
return o.createFailureResult(cmd.CertificationID, cert.Status, fmt.Sprintf("回调处理失败: %s", err.Error())), err
}
// 5. 重新加载认证信息获取最新状态
updatedCert, err := o.aggregateService.LoadCertification(ctx, cmd.CertificationID)
if err != nil {
return o.createFailureResult(cmd.CertificationID, cert.Status, "加载更新后的认证信息失败"), err
}
// 6. 构建结果
message := "合同签署回调处理成功"
data := map[string]interface{}{
"esign_flow_id": cmd.CallbackData.FlowID,
"status": cmd.CallbackData.Status,
}
if updatedCert.Status == enums.StatusContractSigned {
message = "认证完成"
data["next_action"] = "认证流程已完成"
data["contract_url"] = updatedCert.ContractURL
} else if enums.IsFailureStatus(updatedCert.Status) {
message = "合同签署失败"
data["next_action"] = "可以重新申请合同签署"
data["failure_reason"] = enums.GetFailureReasonName(updatedCert.FailureReason)
data["failure_message"] = updatedCert.FailureMessage
}
return o.createSuccessResult(cmd.CertificationID, updatedCert.Status, message, data, nil), nil
}
// ================ 异常处理 ================
// HandleFailure 处理业务失败
func (o *CertificationWorkflowOrchestratorImpl) HandleFailure(
ctx context.Context,
certificationID string,
failureType string,
reason string,
) (*WorkflowResult, error) {
o.logger.Info("开始处理业务失败",
zap.String("certification_id", certificationID),
zap.String("failure_type", failureType),
zap.String("reason", reason))
// 1. 加载认证聚合根
cert, err := o.aggregateService.LoadCertification(ctx, certificationID)
if err != nil {
return o.createFailureResult(certificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
}
// 2. 根据失败类型执行相应处理
var targetStatus enums.CertificationStatus
var failureReason enums.FailureReason
switch failureType {
case "enterprise_verification_failed":
targetStatus = enums.StatusInfoRejected
failureReason = enums.FailureReasonEsignVerificationFailed
case "contract_sign_failed":
targetStatus = enums.StatusContractRejected
failureReason = enums.FailureReasonSignProcessFailed
case "contract_expired":
targetStatus = enums.StatusContractExpired
failureReason = enums.FailureReasonContractExpired
default:
return o.createFailureResult(certificationID, cert.Status, fmt.Sprintf("未知的失败类型: %s", failureType)),
fmt.Errorf("未知的失败类型")
}
// 3. 执行状态转换
metadata := map[string]interface{}{
"failure_reason": failureReason,
"failure_message": reason,
}
result, err := o.aggregateService.TransitionState(
ctx,
certificationID,
targetStatus,
enums.ActorTypeSystem,
"failure_handler",
fmt.Sprintf("系统处理失败: %s", reason),
metadata,
)
if err != nil {
return o.createFailureResult(certificationID, cert.Status, fmt.Sprintf("失败处理状态转换失败: %s", err.Error())), err
}
return o.createSuccessResult(certificationID, targetStatus, "失败处理完成", map[string]interface{}{
"failure_type": failureType,
"failure_reason": enums.GetFailureReasonName(failureReason),
"can_retry": enums.IsRetryable(failureReason),
}, result), nil
}
// RetryOperation 重试操作
func (o *CertificationWorkflowOrchestratorImpl) RetryOperation(
ctx context.Context,
certificationID string,
operation string,
) (*WorkflowResult, error) {
o.logger.Info("开始重试操作",
zap.String("certification_id", certificationID),
zap.String("operation", operation))
// 1. 加载认证聚合根
cert, err := o.aggregateService.LoadCertification(ctx, certificationID)
if err != nil {
return o.createFailureResult(certificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
}
// 2. 检查是否可以重试
if !enums.IsFailureStatus(cert.Status) {
return o.createFailureResult(certificationID, cert.Status, "当前状态不是失败状态,无需重试"),
fmt.Errorf("不需要重试")
}
if !enums.IsRetryable(cert.FailureReason) {
return o.createFailureResult(certificationID, cert.Status,
fmt.Sprintf("失败原因 %s 不支持重试", enums.GetFailureReasonName(cert.FailureReason))),
fmt.Errorf("不支持重试")
}
if cert.RetryCount >= 3 {
return o.createFailureResult(certificationID, cert.Status, "已达到最大重试次数限制"),
fmt.Errorf("超过重试限制")
}
// 3. 根据操作类型执行重试
var targetStatus enums.CertificationStatus
var reason string
switch operation {
case "enterprise_verification":
if cert.Status != enums.StatusInfoRejected {
return o.createFailureResult(certificationID, cert.Status, "当前状态不支持企业认证重试"),
fmt.Errorf("无效的重试操作")
}
targetStatus = enums.StatusInfoSubmitted
reason = "重新提交企业信息"
case "contract_application":
if cert.Status != enums.StatusContractRejected && cert.Status != enums.StatusContractExpired {
return o.createFailureResult(certificationID, cert.Status, "当前状态不支持合同申请重试"),
fmt.Errorf("无效的重试操作")
}
targetStatus = enums.StatusEnterpriseVerified
reason = "重置状态,准备重新申请合同"
default:
return o.createFailureResult(certificationID, cert.Status, fmt.Sprintf("不支持的重试操作: %s", operation)),
fmt.Errorf("不支持的重试操作")
}
// 4. 执行状态转换
result, err := o.aggregateService.TransitionState(
ctx,
certificationID,
targetStatus,
enums.ActorTypeSystem,
"retry_handler",
reason,
map[string]interface{}{},
)
if err != nil {
return o.createFailureResult(certificationID, cert.Status, fmt.Sprintf("重试状态转换失败: %s", err.Error())), err
}
return o.createSuccessResult(certificationID, targetStatus, "重试操作成功", map[string]interface{}{
"retry_operation": operation,
"retry_count": cert.RetryCount + 1,
"next_action": o.getNextActionForStatus(targetStatus),
}, result), nil
}
// ================ 查询操作 ================
// GetWorkflowStatus 获取工作流状态
func (o *CertificationWorkflowOrchestratorImpl) GetWorkflowStatus(
ctx context.Context,
certificationID string,
) (*WorkflowResult, error) {
// 加载认证聚合根
cert, err := o.aggregateService.LoadCertification(ctx, certificationID)
if err != nil {
return o.createFailureResult(certificationID, "", fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
}
// 构建状态信息
data := map[string]interface{}{
"status": cert.Status,
"status_name": enums.GetStatusName(cert.Status),
"progress": cert.GetProgress(),
"is_final": cert.IsFinalStatus(),
"is_completed": cert.IsCompleted(),
"user_action_required": cert.IsUserActionRequired(),
"next_action": o.getNextActionForStatus(cert.Status),
"available_actions": cert.GetAvailableActions(),
}
// 添加失败信息(如果存在)
if enums.IsFailureStatus(cert.Status) {
data["failure_reason"] = enums.GetFailureReasonName(cert.FailureReason)
data["failure_message"] = cert.FailureMessage
data["can_retry"] = enums.IsRetryable(cert.FailureReason)
data["retry_count"] = cert.RetryCount
}
// 添加时间戳信息
if cert.InfoSubmittedAt != nil {
data["info_submitted_at"] = cert.InfoSubmittedAt
}
if cert.EnterpriseVerifiedAt != nil {
data["enterprise_verified_at"] = cert.EnterpriseVerifiedAt
}
if cert.ContractAppliedAt != nil {
data["contract_applied_at"] = cert.ContractAppliedAt
}
if cert.ContractSignedAt != nil {
data["contract_signed_at"] = cert.ContractSignedAt
}
return o.createSuccessResult(certificationID, cert.Status, "工作流状态查询成功", data, nil), nil
}
// ================ 辅助方法 ================
// validateSubmitEnterpriseInfoCommand 验证提交企业信息命令
func (o *CertificationWorkflowOrchestratorImpl) validateSubmitEnterpriseInfoCommand(cmd *SubmitEnterpriseInfoCommand) error {
if cmd.CertificationID == "" {
return fmt.Errorf("认证ID不能为空")
}
if cmd.UserID == "" {
return fmt.Errorf("用户ID不能为空")
}
if cmd.EnterpriseInfo == nil {
return fmt.Errorf("企业信息不能为空")
}
return cmd.EnterpriseInfo.Validate()
}
// validateApplyContractCommand 验证申请合同命令
func (o *CertificationWorkflowOrchestratorImpl) validateApplyContractCommand(cmd *ApplyContractCommand) error {
if cmd.CertificationID == "" {
return fmt.Errorf("认证ID不能为空")
}
if cmd.UserID == "" {
return fmt.Errorf("用户ID不能为空")
}
return nil
}
// validateEnterpriseInfoSubmissionPreconditions 验证企业信息提交前置条件
func (o *CertificationWorkflowOrchestratorImpl) validateEnterpriseInfoSubmissionPreconditions(cert *entities.Certification, userID string) error {
if cert.UserID != userID {
return fmt.Errorf("用户无权限操作此认证申请")
}
if cert.Status != enums.StatusPending && cert.Status != enums.StatusInfoRejected {
return fmt.Errorf("当前状态 %s 不允许提交企业信息", enums.GetStatusName(cert.Status))
}
return nil
}
// validateContractApplicationPreconditions 验证合同申请前置条件
func (o *CertificationWorkflowOrchestratorImpl) validateContractApplicationPreconditions(cert *entities.Certification, userID string) error {
if cert.UserID != userID {
return fmt.Errorf("用户无权限操作此认证申请")
}
if cert.Status != enums.StatusEnterpriseVerified {
return fmt.Errorf("必须先完成企业认证才能申请合同")
}
if cert.AuthFlowID == "" {
return fmt.Errorf("缺少企业认证流程ID")
}
return nil
}
// triggerEnterpriseVerification 触发企业认证
func (o *CertificationWorkflowOrchestratorImpl) triggerEnterpriseVerification(ctx context.Context, certificationID string, enterpriseInfo *value_objects.EnterpriseInfo) error {
// TODO: 调用e签宝API进行企业认证
o.logger.Info("触发企业认证",
zap.String("certification_id", certificationID),
zap.String("company_name", enterpriseInfo.CompanyName))
return nil
}
// generateContractAndSignURL 生成合同和签署链接
func (o *CertificationWorkflowOrchestratorImpl) generateContractAndSignURL(ctx context.Context, certificationID string, cert *entities.Certification) (*value_objects.ContractInfo, error) {
// TODO: 调用e签宝API生成合同和签署链接
o.logger.Info("生成合同和签署链接", zap.String("certification_id", certificationID))
// 临时返回模拟数据
contractInfo, err := value_objects.NewContractInfo(
"contract_file_"+certificationID,
"esign_flow_"+certificationID,
"https://example.com/contract/"+certificationID,
"https://example.com/sign/"+certificationID,
)
if err != nil {
return nil, err
}
return contractInfo, nil
}
// getNextActionForStatus 获取状态对应的下一步操作提示
func (o *CertificationWorkflowOrchestratorImpl) getNextActionForStatus(status enums.CertificationStatus) string {
return enums.GetUserActionHint(status)
}
// createSuccessResult 创建成功结果
func (o *CertificationWorkflowOrchestratorImpl) createSuccessResult(
certificationID string,
status enums.CertificationStatus,
message string,
data map[string]interface{},
stateTransition *state_machine.StateTransitionResult,
) *WorkflowResult {
return &WorkflowResult{
Success: true,
CertificationID: certificationID,
CurrentStatus: status,
Message: message,
Data: data,
StateTransition: stateTransition,
ExecutedAt: time.Now(),
}
}
// createFailureResult 创建失败结果
func (o *CertificationWorkflowOrchestratorImpl) createFailureResult(
certificationID string,
status enums.CertificationStatus,
message string,
) *WorkflowResult {
return &WorkflowResult{
Success: false,
CertificationID: certificationID,
CurrentStatus: status,
Message: message,
Data: map[string]interface{}{},
ExecutedAt: time.Now(),
}
}

View File

@@ -1,162 +0,0 @@
package services
import (
"context"
"fmt"
"go.uber.org/zap"
"tyapi-server/internal/domains/certification/enums"
"tyapi-server/internal/domains/certification/repositories"
)
// CertificationWorkflowService 认证工作流领域服务
// 负责认证流程的状态转换和业务逻辑处理
type CertificationWorkflowService struct {
certRepo repositories.CertificationRepository
stateMachine *CertificationStateMachine
logger *zap.Logger
}
// NewCertificationWorkflowService 创建认证工作流领域服务
func NewCertificationWorkflowService(
certRepo repositories.CertificationRepository,
stateMachine *CertificationStateMachine,
logger *zap.Logger,
) *CertificationWorkflowService {
return &CertificationWorkflowService{
certRepo: certRepo,
stateMachine: stateMachine,
logger: logger,
}
}
// SubmitEnterpriseInfo 提交企业信息
func (s *CertificationWorkflowService) SubmitEnterpriseInfo(ctx context.Context, certificationID string) error {
cert, err := s.certRepo.GetByID(ctx, certificationID)
if err != nil {
return fmt.Errorf("认证申请不存在: %w", err)
}
// 检查当前状态是否可以提交企业信息
if cert.Status != enums.StatusPending && cert.Status != enums.StatusInfoSubmitted {
return fmt.Errorf("当前状态不允许提交企业信息")
}
// 使用状态机转换状态
if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusInfoSubmitted, true, false, nil); err != nil {
return fmt.Errorf("状态转换失败: %w", err)
}
s.logger.Info("企业信息提交成功",
zap.String("certification_id", certificationID),
zap.String("user_id", cert.UserID),
)
return nil
}
// CompleteEnterpriseVerification 完成企业认证
func (s *CertificationWorkflowService) CompleteEnterpriseVerification(ctx context.Context, certificationID string) error {
cert, err := s.certRepo.GetByID(ctx, certificationID)
if err != nil {
return fmt.Errorf("认证申请不存在: %w", err)
}
// 检查当前状态是否可以完成企业认证
if cert.Status != enums.StatusInfoSubmitted {
return fmt.Errorf("当前状态不允许完成企业认证")
}
// 使用状态机转换状态
if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusEnterpriseVerified, true, false, nil); err != nil {
return fmt.Errorf("状态转换失败: %w", err)
}
s.logger.Info("企业认证完成",
zap.String("certification_id", certificationID),
zap.String("user_id", cert.UserID),
)
return nil
}
// ApplyContract 申请签署合同
func (s *CertificationWorkflowService) ApplyContract(ctx context.Context, certificationID string) error {
cert, err := s.certRepo.GetByID(ctx, certificationID)
if err != nil {
return fmt.Errorf("认证申请不存在: %w", err)
}
// 检查当前状态是否可以申请签署合同
if cert.Status != enums.StatusEnterpriseVerified {
return fmt.Errorf("当前状态不允许申请签署合同")
}
// 使用状态机转换状态
if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusContractApplied, true, false, nil); err != nil {
return fmt.Errorf("状态转换失败: %w", err)
}
s.logger.Info("签署合同申请成功",
zap.String("certification_id", certificationID),
zap.String("user_id", cert.UserID),
)
return nil
}
// CompleteContractSign 完成合同签署
func (s *CertificationWorkflowService) CompleteContractSign(ctx context.Context, certificationID, contractURL string) error {
cert, err := s.certRepo.GetByID(ctx, certificationID)
if err != nil {
return fmt.Errorf("认证申请不存在: %w", err)
}
// 检查当前状态是否可以签署
if cert.Status != enums.StatusContractApplied {
return fmt.Errorf("当前状态不允许签署")
}
// 准备签署元数据
metadata := map[string]interface{}{
"contract_url": contractURL,
}
// 使用状态机转换状态
if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusContractSigned, true, false, metadata); err != nil {
return fmt.Errorf("状态转换失败: %w", err)
}
s.logger.Info("合同签署完成",
zap.String("certification_id", certificationID),
zap.String("user_id", cert.UserID),
)
return nil
}
// CompleteCertification 完成认证
func (s *CertificationWorkflowService) CompleteCertification(ctx context.Context, certificationID string) error {
cert, err := s.certRepo.GetByID(ctx, certificationID)
if err != nil {
return fmt.Errorf("认证申请不存在: %w", err)
}
// 检查当前状态是否可以完成
if cert.Status != enums.StatusContractSigned {
return fmt.Errorf("当前状态不允许完成认证")
}
// 使用状态机转换状态
if err := s.stateMachine.TransitionTo(ctx, certificationID, enums.StatusCompleted, false, false, nil); err != nil {
return fmt.Errorf("状态转换失败: %w", err)
}
s.logger.Info("认证完成",
zap.String("certification_id", certificationID),
zap.String("user_id", cert.UserID),
)
return nil
}

View File

@@ -1,180 +0,0 @@
package services
import (
"context"
"fmt"
"go.uber.org/zap"
"tyapi-server/internal/domains/certification/entities"
"tyapi-server/internal/domains/certification/repositories"
)
// EnterpriseInfoSubmitRecordService 企业信息提交记录领域服务
// 负责企业信息提交记录的业务逻辑处理
type EnterpriseInfoSubmitRecordService struct {
enterpriseRecordRepo repositories.EnterpriseInfoSubmitRecordRepository
logger *zap.Logger
}
// NewEnterpriseInfoSubmitRecordService 创建企业信息提交记录领域服务
func NewEnterpriseInfoSubmitRecordService(
enterpriseRecordRepo repositories.EnterpriseInfoSubmitRecordRepository,
logger *zap.Logger,
) *EnterpriseInfoSubmitRecordService {
return &EnterpriseInfoSubmitRecordService{
enterpriseRecordRepo: enterpriseRecordRepo,
logger: logger,
}
}
// CreateEnterpriseInfoSubmitRecord 创建企业信息提交记录
func (s *EnterpriseInfoSubmitRecordService) CreateEnterpriseInfoSubmitRecord(
ctx context.Context,
userID string,
companyName string,
unifiedSocialCode string,
legalPersonName string,
legalPersonID string,
legalPersonPhone string,
) (*entities.EnterpriseInfoSubmitRecord, error) {
// 创建企业信息提交记录实体
record := entities.NewEnterpriseInfoSubmitRecord(
userID,
companyName,
unifiedSocialCode,
legalPersonName,
legalPersonID,
legalPersonPhone,
)
// 保存到仓储
createdRecord, err := s.enterpriseRecordRepo.Create(ctx, *record)
if err != nil {
s.logger.Error("创建企业信息提交记录失败",
zap.String("user_id", userID),
zap.Error(err))
return nil, fmt.Errorf("创建企业信息提交记录失败: %w", err)
}
s.logger.Info("企业信息提交记录创建成功",
zap.String("record_id", createdRecord.ID),
zap.String("user_id", userID),
zap.String("company_name", companyName))
return &createdRecord, nil
}
// GetLatestByUserID 根据用户ID获取最新的企业信息提交记录
func (s *EnterpriseInfoSubmitRecordService) GetLatestByUserID(ctx context.Context, userID string) (*entities.EnterpriseInfoSubmitRecord, error) {
record, err := s.enterpriseRecordRepo.GetLatestByUserID(ctx, userID)
if err != nil {
s.logger.Error("获取企业信息提交记录失败",
zap.String("user_id", userID),
zap.Error(err))
return nil, fmt.Errorf("获取企业信息提交记录失败: %w", err)
}
return record, nil
}
// UpdateEnterpriseInfoSubmitRecord 更新企业信息提交记录
func (s *EnterpriseInfoSubmitRecordService) UpdateEnterpriseInfoSubmitRecord(ctx context.Context, record *entities.EnterpriseInfoSubmitRecord) error {
err := s.enterpriseRecordRepo.Update(ctx, *record)
if err != nil {
s.logger.Error("更新企业信息提交记录失败",
zap.String("record_id", record.ID),
zap.Error(err))
return fmt.Errorf("更新企业信息提交记录失败: %w", err)
}
s.logger.Info("企业信息提交记录更新成功",
zap.String("record_id", record.ID),
zap.String("status", record.Status))
return nil
}
// MarkAsVerified 标记企业信息为已验证
func (s *EnterpriseInfoSubmitRecordService) MarkAsVerified(ctx context.Context, recordID string) error {
record, err := s.enterpriseRecordRepo.GetByID(ctx, recordID)
if err != nil {
return fmt.Errorf("获取企业信息提交记录失败: %w", err)
}
record.MarkAsVerified()
err = s.enterpriseRecordRepo.Update(ctx, record)
if err != nil {
s.logger.Error("标记企业信息为已验证失败",
zap.String("record_id", recordID),
zap.Error(err))
return fmt.Errorf("标记企业信息为已验证失败: %w", err)
}
s.logger.Info("企业信息标记为已验证成功",
zap.String("record_id", recordID))
return nil
}
// UpdateVerificationStatus 更新企业信息验证状态
func (s *EnterpriseInfoSubmitRecordService) UpdateVerificationStatus(ctx context.Context, userID string, isVerified bool, reason string) error {
// 获取用户最新的企业信息提交记录
record, err := s.enterpriseRecordRepo.GetLatestByUserID(ctx, userID)
if err != nil {
return fmt.Errorf("获取企业信息提交记录失败: %w", err)
}
// 更新验证状态
if isVerified {
record.MarkAsVerified()
} else {
record.MarkAsFailed(reason)
}
// 保存更新
err = s.enterpriseRecordRepo.Update(ctx, *record)
if err != nil {
s.logger.Error("更新企业信息验证状态失败",
zap.String("user_id", userID),
zap.Bool("is_verified", isVerified),
zap.Error(err))
return fmt.Errorf("更新企业信息验证状态失败: %w", err)
}
s.logger.Info("企业信息验证状态更新成功",
zap.String("user_id", userID),
zap.Bool("is_verified", isVerified),
zap.String("reason", reason))
return nil
}
// DeleteEnterpriseInfoSubmitRecord 删除企业信息提交记录
func (s *EnterpriseInfoSubmitRecordService) DeleteEnterpriseInfoSubmitRecord(ctx context.Context, recordID string) error {
err := s.enterpriseRecordRepo.Delete(ctx, recordID)
if err != nil {
s.logger.Error("删除企业信息提交记录失败",
zap.String("record_id", recordID),
zap.Error(err))
return fmt.Errorf("删除企业信息提交记录失败: %w", err)
}
s.logger.Info("企业信息提交记录删除成功",
zap.String("record_id", recordID))
return nil
}
// GetByUserID 根据用户ID获取企业信息提交记录列表
func (s *EnterpriseInfoSubmitRecordService) GetByUserID(ctx context.Context, userID string) ([]*entities.EnterpriseInfoSubmitRecord, error) {
records, err := s.enterpriseRecordRepo.GetByUserID(ctx, userID)
if err != nil {
s.logger.Error("获取用户企业信息提交记录失败",
zap.String("user_id", userID),
zap.Error(err))
return nil, fmt.Errorf("获取用户企业信息提交记录失败: %w", err)
}
return records, nil
}

View File

@@ -97,18 +97,9 @@ func (manager *CertificationStateManager) initStateConfigs() {
IsAdminActionRequired: false,
TimestampField: "ContractSignedAt",
Description: "合同已签署",
NextValidStatuses: []enums.CertificationStatus{enums.StatusCompleted},
},
{
Status: enums.StatusCompleted,
Name: "认证完成",
ProgressPercentage: 100,
IsUserActionRequired: false,
IsAdminActionRequired: false,
TimestampField: "CompletedAt",
Description: "认证流程已完成",
NextValidStatuses: []enums.CertificationStatus{},
},
// 已完成状态已合并到StatusContractSigned中
}
// 转换配置
@@ -168,17 +159,7 @@ func (manager *CertificationStateManager) initStateConfigs() {
RequiresValidation: true,
Description: "用户签署合同",
},
// 完成认证
{
From: enums.StatusContractSigned,
To: enums.StatusCompleted,
Action: "complete",
ActionName: "完成认证",
AllowUser: false,
AllowAdmin: false,
RequiresValidation: false,
Description: "系统自动完成认证",
},
// 合同签署即为认证完成,无需额外状态转换
}
// 构建映射

View File

@@ -1,258 +0,0 @@
package services
import (
"context"
"fmt"
"time"
"tyapi-server/internal/domains/certification/entities"
"tyapi-server/internal/domains/certification/enums"
"tyapi-server/internal/domains/certification/repositories"
"go.uber.org/zap"
)
// CertificationStateMachine 认证状态机
type CertificationStateMachine struct {
stateManager *CertificationStateManager
certRepo repositories.CertificationRepository
logger *zap.Logger
}
// NewCertificationStateMachine 创建认证状态机
func NewCertificationStateMachine(
certRepo repositories.CertificationRepository,
logger *zap.Logger,
) *CertificationStateMachine {
return &CertificationStateMachine{
stateManager: NewCertificationStateManager(),
certRepo: certRepo,
logger: logger,
}
}
// CanTransition 检查是否可以转换到指定状态
func (sm *CertificationStateMachine) CanTransition(
from enums.CertificationStatus,
to enums.CertificationStatus,
isUser bool,
isAdmin bool,
) (bool, string) {
return sm.stateManager.CanTransition(from, to, isUser, isAdmin)
}
// TransitionTo 执行状态转换
func (sm *CertificationStateMachine) TransitionTo(
ctx context.Context,
certificationID string,
targetStatus enums.CertificationStatus,
isUser bool,
isAdmin bool,
metadata map[string]interface{},
) error {
// 获取当前认证记录
cert, err := sm.certRepo.GetByID(ctx, certificationID)
if err != nil {
return fmt.Errorf("获取认证记录失败: %w", err)
}
// 检查是否可以转换
canTransition, reason := sm.CanTransition(cert.Status, targetStatus, isUser, isAdmin)
if !canTransition {
return fmt.Errorf("状态转换失败: %s", reason)
}
// 更新状态和时间戳
oldStatus := cert.Status
cert.Status = targetStatus
sm.updateTimestamp(&cert, targetStatus)
// 更新其他字段
sm.updateCertificationFields(&cert, targetStatus, metadata)
// 保存到数据库
if err := sm.certRepo.Update(ctx, cert); err != nil {
return fmt.Errorf("保存状态转换失败: %w", err)
}
sm.logger.Info("认证状态转换成功",
zap.String("certification_id", certificationID),
zap.String("from_status", string(oldStatus)),
zap.String("to_status", string(targetStatus)),
zap.Bool("is_user", isUser),
zap.Bool("is_admin", isAdmin),
)
return nil
}
// updateTimestamp 更新对应的时间戳字段
func (sm *CertificationStateMachine) updateTimestamp(cert *entities.Certification, status enums.CertificationStatus) {
stateConfig := sm.stateManager.GetStateConfig(status)
if stateConfig == nil || stateConfig.TimestampField == "" {
return
}
now := time.Now()
switch stateConfig.TimestampField {
case "InfoSubmittedAt":
cert.InfoSubmittedAt = &now
case "EnterpriseVerifiedAt":
cert.EnterpriseVerifiedAt = &now
case "ContractAppliedAt":
cert.ContractAppliedAt = &now
case "ContractSignedAt":
cert.ContractSignedAt = &now
case "CompletedAt":
cert.CompletedAt = &now
}
}
// updateCertificationFields 根据状态更新认证记录的其他字段
func (sm *CertificationStateMachine) updateCertificationFields(
cert *entities.Certification,
status enums.CertificationStatus,
metadata map[string]interface{},
) {
switch status {
case enums.StatusContractSigned:
if contractURL, ok := metadata["contract_url"].(string); ok {
cert.ContractURL = contractURL
}
}
}
// GetValidNextStatuses 获取当前状态可以转换到的下一个状态列表
func (sm *CertificationStateMachine) GetValidNextStatuses(
currentStatus enums.CertificationStatus,
isUser bool,
isAdmin bool,
) []enums.CertificationStatus {
return sm.stateManager.GetNextValidStatuses(currentStatus)
}
// GetTransitionAction 获取状态转换对应的操作名称
func (sm *CertificationStateMachine) GetTransitionAction(
from enums.CertificationStatus,
to enums.CertificationStatus,
) string {
transitions := sm.stateManager.GetTransitionConfigs(from)
for _, transition := range transitions {
if transition.To == to {
return transition.Action
}
}
return ""
}
// GetTransitionActionName 获取状态转换对应的操作中文名称
func (sm *CertificationStateMachine) GetTransitionActionName(
from enums.CertificationStatus,
to enums.CertificationStatus,
) string {
transitions := sm.stateManager.GetTransitionConfigs(from)
for _, transition := range transitions {
if transition.To == to {
return transition.ActionName
}
}
return ""
}
// GetStateConfig 获取状态配置
func (sm *CertificationStateMachine) GetStateConfig(status enums.CertificationStatus) *StateConfig {
return sm.stateManager.GetStateConfig(status)
}
// GetProgressPercentage 获取进度百分比
func (sm *CertificationStateMachine) GetProgressPercentage(status enums.CertificationStatus) int {
return sm.stateManager.GetProgressPercentage(status)
}
// IsUserActionRequired 检查是否需要用户操作
func (sm *CertificationStateMachine) IsUserActionRequired(status enums.CertificationStatus) bool {
return sm.stateManager.IsUserActionRequired(status)
}
// IsAdminActionRequired 检查是否需要管理员操作
func (sm *CertificationStateMachine) IsAdminActionRequired(status enums.CertificationStatus) bool {
return sm.stateManager.IsAdminActionRequired(status)
}
// GetTransitionHistory 获取状态转换历史
func (sm *CertificationStateMachine) GetTransitionHistory(ctx context.Context, certificationID string) ([]map[string]interface{}, error) {
cert, err := sm.certRepo.GetByID(ctx, certificationID)
if err != nil {
return nil, fmt.Errorf("获取认证记录失败: %w", err)
}
history := []map[string]interface{}{}
// 添加创建时间
history = append(history, map[string]interface{}{
"status": "CREATED",
"timestamp": cert.CreatedAt,
"action": "create",
"performer": "system",
"metadata": map[string]interface{}{},
})
// 添加各个时间节点的状态转换
if cert.InfoSubmittedAt != nil {
history = append(history, map[string]interface{}{
"status": string(enums.StatusInfoSubmitted),
"timestamp": *cert.InfoSubmittedAt,
"action": "submit_info",
"performer": "user",
"metadata": map[string]interface{}{},
})
}
if cert.EnterpriseVerifiedAt != nil {
history = append(history, map[string]interface{}{
"status": string(enums.StatusEnterpriseVerified),
"timestamp": *cert.EnterpriseVerifiedAt,
"action": "enterprise_verify",
"performer": "user",
"metadata": map[string]interface{}{},
})
}
if cert.ContractAppliedAt != nil {
history = append(history, map[string]interface{}{
"status": string(enums.StatusContractApplied),
"timestamp": *cert.ContractAppliedAt,
"action": "apply_contract",
"performer": "user",
"metadata": map[string]interface{}{},
})
}
if cert.ContractSignedAt != nil {
metadata := map[string]interface{}{}
if cert.ContractURL != "" {
metadata["contract_url"] = cert.ContractURL
}
history = append(history, map[string]interface{}{
"status": string(enums.StatusContractSigned),
"timestamp": *cert.ContractSignedAt,
"action": "sign_contract",
"performer": "user",
"metadata": metadata,
})
}
if cert.CompletedAt != nil {
history = append(history, map[string]interface{}{
"status": string(enums.StatusCompleted),
"timestamp": *cert.CompletedAt,
"action": "complete",
"performer": "system",
"metadata": map[string]interface{}{},
})
}
return history, nil
}

View File

@@ -0,0 +1,455 @@
package state_machine
import (
"context"
"fmt"
"time"
"tyapi-server/internal/domains/certification/entities"
"tyapi-server/internal/domains/certification/enums"
"tyapi-server/internal/domains/certification/repositories"
"go.uber.org/zap"
)
// CertificationStateMachine 认证状态机
// 负责管理认证流程的状态转换、业务规则验证和事件发布
type CertificationStateMachine struct {
configManager *StateConfigManager
repository repositories.CertificationCommandRepository
eventPublisher interface{} // TODO: 使用 interfaces.EventPublisher
logger *zap.Logger
}
// NewCertificationStateMachine 创建认证状态机
func NewCertificationStateMachine(
repository repositories.CertificationCommandRepository,
eventPublisher interface{}, // TODO: 使用 interfaces.EventPublisher
logger *zap.Logger,
) *CertificationStateMachine {
return &CertificationStateMachine{
configManager: NewStateConfigManager(),
repository: repository,
eventPublisher: eventPublisher,
logger: logger,
}
}
// StateTransitionRequest 状态转换请求
type StateTransitionRequest struct {
CertificationID string `json:"certification_id"`
TargetStatus enums.CertificationStatus `json:"target_status"`
Actor enums.ActorType `json:"actor"`
ActorID string `json:"actor_id"`
Reason string `json:"reason"`
Context map[string]interface{} `json:"context"`
AllowRollback bool `json:"allow_rollback"`
}
// StateTransitionResult 状态转换结果
type StateTransitionResult struct {
Success bool `json:"success"`
OldStatus enums.CertificationStatus `json:"old_status"`
NewStatus enums.CertificationStatus `json:"new_status"`
Message string `json:"message"`
TransitionedAt time.Time `json:"transitioned_at"`
Events []interface{} `json:"events,omitempty"`
}
// CanTransition 检查是否可以执行状态转换
func (sm *CertificationStateMachine) CanTransition(
cert *entities.Certification,
targetStatus enums.CertificationStatus,
actor enums.ActorType,
) (bool, string) {
// 1. 检查基本状态转换规则
canTransition, message := sm.configManager.CanTransition(cert.Status, targetStatus, actor)
if !canTransition {
return false, message
}
// 2. 检查认证实体的业务规则
if canTransition, message := cert.CanTransitionTo(targetStatus, actor); !canTransition {
return false, message
}
// 3. 检查是否为最终状态
if cert.IsFinalStatus() {
return false, "认证已完成,无法进行状态转换"
}
return true, ""
}
// ExecuteTransition 执行状态转换
func (sm *CertificationStateMachine) ExecuteTransition(
ctx context.Context,
req *StateTransitionRequest,
) (*StateTransitionResult, error) {
sm.logger.Info("开始执行状态转换",
zap.String("certification_id", req.CertificationID),
zap.String("target_status", string(req.TargetStatus)),
zap.String("actor", string(req.Actor)),
zap.String("actor_id", req.ActorID))
// 1. 加载认证聚合根
cert, err := sm.loadCertification(ctx, req.CertificationID)
if err != nil {
return sm.createFailureResult(cert.Status, req.TargetStatus, fmt.Sprintf("加载认证信息失败: %s", err.Error())), err
}
oldStatus := cert.Status
// 2. 验证转换合法性
if canTransition, message := sm.CanTransition(cert, req.TargetStatus, req.Actor); !canTransition {
return sm.createFailureResult(oldStatus, req.TargetStatus, message), fmt.Errorf("状态转换验证失败: %s", message)
}
// 3. 验证业务规则
if err := sm.validateBusinessRules(cert, req); err != nil {
return sm.createFailureResult(oldStatus, req.TargetStatus, fmt.Sprintf("业务规则验证失败: %s", err.Error())), err
}
// 4. 执行状态转换
if err := cert.TransitionTo(req.TargetStatus, req.Actor, req.ActorID, req.Reason); err != nil {
return sm.createFailureResult(oldStatus, req.TargetStatus, fmt.Sprintf("状态转换执行失败: %s", err.Error())), err
}
// 5. 保存到数据库
if err := sm.repository.Update(ctx, *cert); err != nil {
// 如果保存失败,需要回滚状态
sm.logger.Error("状态转换保存失败,尝试回滚",
zap.String("certification_id", req.CertificationID),
zap.Error(err))
if req.AllowRollback {
if rollbackErr := sm.rollbackStateTransition(ctx, cert, oldStatus, req.Actor, req.ActorID); rollbackErr != nil {
sm.logger.Error("状态回滚失败", zap.Error(rollbackErr))
}
}
return sm.createFailureResult(oldStatus, req.TargetStatus, fmt.Sprintf("保存状态转换失败: %s", err.Error())), err
}
// 6. 发布领域事件
events := cert.GetDomainEvents()
for _, event := range events {
// TODO: 实现事件发布
// if err := sm.eventPublisher.PublishEvent(ctx, event); err != nil {
// sm.logger.Error("发布领域事件失败",
// zap.String("certification_id", req.CertificationID),
// zap.Error(err))
// }
sm.logger.Info("领域事件待发布",
zap.String("certification_id", req.CertificationID),
zap.Any("event", event))
}
// 7. 清理领域事件
cert.ClearDomainEvents()
// 8. 记录成功日志
sm.logger.Info("状态转换执行成功",
zap.String("certification_id", req.CertificationID),
zap.String("from_status", string(oldStatus)),
zap.String("to_status", string(req.TargetStatus)),
zap.String("actor", string(req.Actor)))
// 9. 返回成功结果
return &StateTransitionResult{
Success: true,
OldStatus: oldStatus,
NewStatus: req.TargetStatus,
Message: "状态转换成功",
TransitionedAt: time.Now(),
Events: events,
}, nil
}
// GetValidTransitions 获取有效的状态转换
func (sm *CertificationStateMachine) GetValidTransitions(
cert *entities.Certification,
actor enums.ActorType,
) []*StateTransitionRule {
return sm.configManager.GetAllowedTransitions(cert.Status, actor)
}
// GetStateInfo 获取状态信息
func (sm *CertificationStateMachine) GetStateInfo(status enums.CertificationStatus) *StateConfig {
return sm.configManager.GetStateConfig(status)
}
// ValidateBusinessRules 验证业务规则
func (sm *CertificationStateMachine) ValidateBusinessRules(
cert *entities.Certification,
req *StateTransitionRequest,
) error {
return sm.validateBusinessRules(cert, req)
}
// IsUserActionRequired 检查是否需要用户操作
func (sm *CertificationStateMachine) IsUserActionRequired(status enums.CertificationStatus) bool {
return sm.configManager.IsUserActionRequired(status)
}
// GetProgressPercentage 获取进度百分比
func (sm *CertificationStateMachine) GetProgressPercentage(status enums.CertificationStatus) int {
return sm.configManager.GetStateProgress(status)
}
// ================ 私有方法 ================
// loadCertification 加载认证聚合根
func (sm *CertificationStateMachine) loadCertification(ctx context.Context, certificationID string) (*entities.Certification, error) {
// 这里需要通过查询仓储获取认证信息
// 由于当前只有命令仓储,这里使用简单的方法
// 在实际实现中,应该使用查询仓储
cert := &entities.Certification{ID: certificationID}
// TODO: 实现从查询仓储加载认证信息
// cert, err := sm.queryRepository.GetByID(ctx, certificationID)
// if err != nil {
// return nil, fmt.Errorf("认证信息不存在: %w", err)
// }
return cert, nil
}
// validateBusinessRules 验证业务规则
func (sm *CertificationStateMachine) validateBusinessRules(
cert *entities.Certification,
req *StateTransitionRequest,
) error {
// 获取转换规则
rule := sm.configManager.GetTransitionRule(cert.Status, req.TargetStatus)
if rule == nil {
return fmt.Errorf("找不到状态转换规则")
}
// 如果不需要验证,直接返回
if !rule.RequiresValidation {
return nil
}
// 构建验证上下文
context := make(map[string]interface{})
// 添加认证基本信息
context["certification_id"] = cert.ID
context["user_id"] = cert.UserID
context["current_status"] = string(cert.Status)
context["retry_count"] = cert.RetryCount
context["auth_flow_id"] = cert.AuthFlowID
// 添加请求中的上下文信息
for key, value := range req.Context {
context[key] = value
}
// 执行业务规则验证
return sm.configManager.ValidateBusinessRules(rule, context)
}
// rollbackStateTransition 回滚状态转换
func (sm *CertificationStateMachine) rollbackStateTransition(
ctx context.Context,
cert *entities.Certification,
originalStatus enums.CertificationStatus,
actor enums.ActorType,
actorID string,
) error {
sm.logger.Info("开始回滚状态转换",
zap.String("certification_id", cert.ID),
zap.String("original_status", string(originalStatus)),
zap.String("current_status", string(cert.Status)))
// 直接设置回原状态(跳过业务规则验证)
cert.Status = originalStatus
// 更新审计信息
now := time.Now()
cert.LastTransitionAt = &now
cert.LastTransitionBy = actor
cert.LastTransitionActor = actorID
// 保存回滚结果
if err := sm.repository.Update(ctx, *cert); err != nil {
return fmt.Errorf("保存回滚状态失败: %w", err)
}
sm.logger.Info("状态转换回滚成功",
zap.String("certification_id", cert.ID),
zap.String("rollback_to_status", string(originalStatus)))
return nil
}
// createFailureResult 创建失败结果
func (sm *CertificationStateMachine) createFailureResult(
oldStatus, targetStatus enums.CertificationStatus,
message string,
) *StateTransitionResult {
return &StateTransitionResult{
Success: false,
OldStatus: oldStatus,
NewStatus: targetStatus,
Message: message,
TransitionedAt: time.Now(),
Events: []interface{}{},
}
}
// ================ 状态转换快捷方法 ================
// TransitionToInfoSubmitted 转换到已提交企业信息状态
func (sm *CertificationStateMachine) TransitionToInfoSubmitted(
ctx context.Context,
certificationID string,
actor enums.ActorType,
actorID string,
enterpriseInfo interface{},
) (*StateTransitionResult, error) {
req := &StateTransitionRequest{
CertificationID: certificationID,
TargetStatus: enums.StatusInfoSubmitted,
Actor: actor,
ActorID: actorID,
Reason: "用户提交企业信息",
Context: map[string]interface{}{
"enterprise_info": enterpriseInfo,
},
AllowRollback: true,
}
return sm.ExecuteTransition(ctx, req)
}
// TransitionToEnterpriseVerified 转换到已企业认证状态
func (sm *CertificationStateMachine) TransitionToEnterpriseVerified(
ctx context.Context,
certificationID string,
authFlowID string,
) (*StateTransitionResult, error) {
req := &StateTransitionRequest{
CertificationID: certificationID,
TargetStatus: enums.StatusEnterpriseVerified,
Actor: enums.ActorTypeEsign,
ActorID: "esign_system",
Reason: "e签宝企业认证成功",
Context: map[string]interface{}{
"auth_flow_id": authFlowID,
},
AllowRollback: false,
}
return sm.ExecuteTransition(ctx, req)
}
// TransitionToInfoRejected 转换到企业信息被拒绝状态
func (sm *CertificationStateMachine) TransitionToInfoRejected(
ctx context.Context,
certificationID string,
failureReason enums.FailureReason,
failureMessage string,
) (*StateTransitionResult, error) {
req := &StateTransitionRequest{
CertificationID: certificationID,
TargetStatus: enums.StatusInfoRejected,
Actor: enums.ActorTypeEsign,
ActorID: "esign_system",
Reason: "e签宝企业认证失败",
Context: map[string]interface{}{
"failure_reason": failureReason,
"failure_message": failureMessage,
},
AllowRollback: false,
}
return sm.ExecuteTransition(ctx, req)
}
// TransitionToContractApplied 转换到已申请合同状态
func (sm *CertificationStateMachine) TransitionToContractApplied(
ctx context.Context,
certificationID string,
actor enums.ActorType,
actorID string,
) (*StateTransitionResult, error) {
req := &StateTransitionRequest{
CertificationID: certificationID,
TargetStatus: enums.StatusContractApplied,
Actor: actor,
ActorID: actorID,
Reason: "用户申请合同签署",
Context: map[string]interface{}{},
AllowRollback: true,
}
return sm.ExecuteTransition(ctx, req)
}
// TransitionToContractSigned 转换到已签署合同状态(认证完成)
func (sm *CertificationStateMachine) TransitionToContractSigned(
ctx context.Context,
certificationID string,
contractURL string,
) (*StateTransitionResult, error) {
req := &StateTransitionRequest{
CertificationID: certificationID,
TargetStatus: enums.StatusContractSigned,
Actor: enums.ActorTypeEsign,
ActorID: "esign_system",
Reason: "e签宝合同签署成功认证完成",
Context: map[string]interface{}{
"contract_url": contractURL,
},
AllowRollback: false,
}
return sm.ExecuteTransition(ctx, req)
}
// TransitionToContractRejected 转换到合同被拒签状态
func (sm *CertificationStateMachine) TransitionToContractRejected(
ctx context.Context,
certificationID string,
failureReason enums.FailureReason,
failureMessage string,
) (*StateTransitionResult, error) {
req := &StateTransitionRequest{
CertificationID: certificationID,
TargetStatus: enums.StatusContractRejected,
Actor: enums.ActorTypeEsign,
ActorID: "esign_system",
Reason: "合同签署失败",
Context: map[string]interface{}{
"failure_reason": failureReason,
"failure_message": failureMessage,
},
AllowRollback: false,
}
return sm.ExecuteTransition(ctx, req)
}
// TransitionToContractExpired 转换到合同签署超时状态
func (sm *CertificationStateMachine) TransitionToContractExpired(
ctx context.Context,
certificationID string,
failureMessage string,
) (*StateTransitionResult, error) {
req := &StateTransitionRequest{
CertificationID: certificationID,
TargetStatus: enums.StatusContractExpired,
Actor: enums.ActorTypeSystem,
ActorID: "timeout_monitor",
Reason: "合同签署超时",
Context: map[string]interface{}{
"failure_reason": enums.FailureReasonContractExpired,
"failure_message": failureMessage,
},
AllowRollback: false,
}
return sm.ExecuteTransition(ctx, req)
}

View File

@@ -0,0 +1,391 @@
package state_machine
import (
"context"
"encoding/json"
"fmt"
"tyapi-server/internal/domains/certification/enums"
"go.uber.org/zap"
)
// EsignCallbackData e签宝回调数据结构
type EsignCallbackData struct {
// 基础信息
Action string `json:"action"` // 回调动作类型
FlowID string `json:"flow_id"` // 流程ID
AccountID string `json:"account_id"` // 账户ID
Status string `json:"status"` // 状态
Message string `json:"message"` // 消息
Timestamp int64 `json:"timestamp"` // 时间戳
// 扩展数据
Data map[string]interface{} `json:"data,omitempty"` // 扩展数据
OriginalData string `json:"original_data"` // 原始回调数据
}
// EsignCallbackHandler e签宝回调处理器
// 负责处理e签宝的异步回调将外部回调转换为内部状态转换
type EsignCallbackHandler struct {
stateMachine *CertificationStateMachine
logger *zap.Logger
}
// NewEsignCallbackHandler 创建e签宝回调处理器
func NewEsignCallbackHandler(
stateMachine *CertificationStateMachine,
logger *zap.Logger,
) *EsignCallbackHandler {
return &EsignCallbackHandler{
stateMachine: stateMachine,
logger: logger,
}
}
// HandleCallback 处理e签宝回调
func (h *EsignCallbackHandler) HandleCallback(
ctx context.Context,
certificationID string,
callbackData *EsignCallbackData,
) error {
h.logger.Info("接收到e签宝回调",
zap.String("certification_id", certificationID),
zap.String("action", callbackData.Action),
zap.String("flow_id", callbackData.FlowID),
zap.String("status", callbackData.Status))
// 根据动作类型分发处理
switch callbackData.Action {
case "auth_result":
return h.handleAuthResult(ctx, certificationID, callbackData)
case "sign_result":
return h.handleSignResult(ctx, certificationID, callbackData)
case "flow_status":
return h.handleFlowStatus(ctx, certificationID, callbackData)
default:
h.logger.Warn("未知的e签宝回调动作",
zap.String("certification_id", certificationID),
zap.String("action", callbackData.Action))
return fmt.Errorf("未知的回调动作: %s", callbackData.Action)
}
}
// handleAuthResult 处理企业认证结果回调
func (h *EsignCallbackHandler) handleAuthResult(
ctx context.Context,
certificationID string,
callbackData *EsignCallbackData,
) error {
h.logger.Info("处理企业认证结果回调",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID),
zap.String("status", callbackData.Status))
switch callbackData.Status {
case "success", "verified", "completed":
// 认证成功
_, err := h.stateMachine.TransitionToEnterpriseVerified(
ctx,
certificationID,
callbackData.FlowID,
)
return err
case "failed", "rejected", "error":
// 认证失败
failureReason := h.parseAuthFailureReason(callbackData)
_, err := h.stateMachine.TransitionToInfoRejected(
ctx,
certificationID,
failureReason,
callbackData.Message,
)
return err
default:
h.logger.Warn("未知的企业认证状态",
zap.String("certification_id", certificationID),
zap.String("status", callbackData.Status))
return fmt.Errorf("未知的认证状态: %s", callbackData.Status)
}
}
// handleSignResult 处理合同签署结果回调
func (h *EsignCallbackHandler) handleSignResult(
ctx context.Context,
certificationID string,
callbackData *EsignCallbackData,
) error {
h.logger.Info("处理合同签署结果回调",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID),
zap.String("status", callbackData.Status))
switch callbackData.Status {
case "signed", "completed", "success":
// 签署成功
contractURL := h.extractContractURL(callbackData)
_, err := h.stateMachine.TransitionToContractSigned(
ctx,
certificationID,
contractURL,
)
return err
case "rejected", "refused":
// 用户拒绝签署
_, err := h.stateMachine.TransitionToContractRejected(
ctx,
certificationID,
enums.FailureReasonContractRejectedByUser,
callbackData.Message,
)
return err
case "expired", "timeout":
// 签署超时
_, err := h.stateMachine.TransitionToContractExpired(
ctx,
certificationID,
fmt.Sprintf("合同签署超时: %s", callbackData.Message),
)
return err
case "failed", "error":
// 签署失败
failureReason := h.parseSignFailureReason(callbackData)
_, err := h.stateMachine.TransitionToContractRejected(
ctx,
certificationID,
failureReason,
callbackData.Message,
)
return err
default:
h.logger.Warn("未知的合同签署状态",
zap.String("certification_id", certificationID),
zap.String("status", callbackData.Status))
return fmt.Errorf("未知的签署状态: %s", callbackData.Status)
}
}
// handleFlowStatus 处理流程状态回调
func (h *EsignCallbackHandler) handleFlowStatus(
ctx context.Context,
certificationID string,
callbackData *EsignCallbackData,
) error {
h.logger.Info("处理流程状态回调",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID),
zap.String("status", callbackData.Status))
// 流程状态回调主要用于监控和日志记录
// 实际的状态转换由具体的auth_result和sign_result处理
switch callbackData.Status {
case "started", "processing", "in_progress":
h.logger.Info("e签宝流程进行中",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID))
case "paused", "suspended":
h.logger.Warn("e签宝流程被暂停",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID),
zap.String("message", callbackData.Message))
case "cancelled", "terminated":
h.logger.Warn("e签宝流程被取消",
zap.String("certification_id", certificationID),
zap.String("flow_id", callbackData.FlowID),
zap.String("message", callbackData.Message))
default:
h.logger.Info("收到其他流程状态",
zap.String("certification_id", certificationID),
zap.String("status", callbackData.Status))
}
return nil
}
// parseAuthFailureReason 解析企业认证失败原因
func (h *EsignCallbackHandler) parseAuthFailureReason(callbackData *EsignCallbackData) enums.FailureReason {
// 根据e签宝返回的错误信息解析失败原因
message := callbackData.Message
// 检查扩展数据中的错误码
if errorCode, exists := callbackData.Data["error_code"]; exists {
switch errorCode {
case "ENTERPRISE_NOT_FOUND", "ORG_NOT_EXISTS":
return enums.FailureReasonEnterpriseNotExists
case "INFO_MISMATCH", "ORG_INFO_ERROR":
return enums.FailureReasonEnterpriseInfoMismatch
case "STATUS_ABNORMAL", "ORG_STATUS_ERROR":
return enums.FailureReasonEnterpriseStatusAbnormal
case "LEGAL_PERSON_ERROR", "LEGAL_REP_ERROR":
return enums.FailureReasonLegalPersonMismatch
case "DOCUMENT_INVALID", "ID_CARD_ERROR":
return enums.FailureReasonInvalidDocument
}
}
// 根据错误消息文本判断
if message != "" {
if h.containsKeywords(message, []string{"企业不存在", "机构不存在", "not found"}) {
return enums.FailureReasonEnterpriseNotExists
}
if h.containsKeywords(message, []string{"信息不匹配", "信息错误", "mismatch"}) {
return enums.FailureReasonEnterpriseInfoMismatch
}
if h.containsKeywords(message, []string{"状态异常", "status abnormal"}) {
return enums.FailureReasonEnterpriseStatusAbnormal
}
if h.containsKeywords(message, []string{"法定代表人", "legal person", "法人"}) {
return enums.FailureReasonLegalPersonMismatch
}
if h.containsKeywords(message, []string{"证件", "身份证", "document", "id card"}) {
return enums.FailureReasonInvalidDocument
}
}
// 默认返回e签宝验证失败
return enums.FailureReasonEsignVerificationFailed
}
// parseSignFailureReason 解析合同签署失败原因
func (h *EsignCallbackHandler) parseSignFailureReason(callbackData *EsignCallbackData) enums.FailureReason {
// 根据e签宝返回的错误信息解析失败原因
message := callbackData.Message
// 检查扩展数据中的错误码
if errorCode, exists := callbackData.Data["error_code"]; exists {
switch errorCode {
case "USER_REJECTED", "SIGN_REJECTED":
return enums.FailureReasonContractRejectedByUser
case "FLOW_EXPIRED", "SIGN_EXPIRED":
return enums.FailureReasonContractExpired
case "FLOW_ERROR", "SIGN_PROCESS_ERROR":
return enums.FailureReasonSignProcessFailed
case "ESIGN_ERROR", "SYSTEM_ERROR":
return enums.FailureReasonEsignFlowError
}
}
// 根据错误消息文本判断
if message != "" {
if h.containsKeywords(message, []string{"拒绝", "rejected", "refused"}) {
return enums.FailureReasonContractRejectedByUser
}
if h.containsKeywords(message, []string{"过期", "超时", "expired", "timeout"}) {
return enums.FailureReasonContractExpired
}
if h.containsKeywords(message, []string{"流程错误", "process error", "flow error"}) {
return enums.FailureReasonSignProcessFailed
}
}
// 默认返回e签宝流程错误
return enums.FailureReasonEsignFlowError
}
// extractContractURL 提取合同URL
func (h *EsignCallbackHandler) extractContractURL(callbackData *EsignCallbackData) string {
// 优先从扩展数据中获取
if contractURL, exists := callbackData.Data["contract_url"]; exists {
if url, ok := contractURL.(string); ok && url != "" {
return url
}
}
if downloadURL, exists := callbackData.Data["download_url"]; exists {
if url, ok := downloadURL.(string); ok && url != "" {
return url
}
}
if fileURL, exists := callbackData.Data["file_url"]; exists {
if url, ok := fileURL.(string); ok && url != "" {
return url
}
}
// 如果没有找到URL返回空字符串
h.logger.Warn("未能从回调数据中提取合同URL",
zap.Any("callback_data", callbackData.Data))
return ""
}
// containsKeywords 检查文本是否包含关键词
func (h *EsignCallbackHandler) containsKeywords(text string, keywords []string) bool {
for _, keyword := range keywords {
if len(text) >= len(keyword) {
for i := 0; i <= len(text)-len(keyword); i++ {
if text[i:i+len(keyword)] == keyword {
return true
}
}
}
}
return false
}
// ValidateCallbackData 验证回调数据
func (h *EsignCallbackHandler) ValidateCallbackData(callbackData *EsignCallbackData) error {
if callbackData == nil {
return fmt.Errorf("回调数据不能为空")
}
if callbackData.Action == "" {
return fmt.Errorf("回调动作不能为空")
}
if callbackData.FlowID == "" {
return fmt.Errorf("流程ID不能为空")
}
if callbackData.Status == "" {
return fmt.Errorf("状态不能为空")
}
return nil
}
// ParseCallbackData 解析原始回调数据
func (h *EsignCallbackHandler) ParseCallbackData(rawData string) (*EsignCallbackData, error) {
var callbackData EsignCallbackData
if err := json.Unmarshal([]byte(rawData), &callbackData); err != nil {
h.logger.Error("解析e签宝回调数据失败", zap.Error(err), zap.String("raw_data", rawData))
return nil, fmt.Errorf("解析回调数据失败: %w", err)
}
// 保存原始数据
callbackData.OriginalData = rawData
// 验证数据完整性
if err := h.ValidateCallbackData(&callbackData); err != nil {
return nil, fmt.Errorf("回调数据验证失败: %w", err)
}
return &callbackData, nil
}
// GetCallbackType 获取回调类型描述
func (h *EsignCallbackHandler) GetCallbackType(action string) string {
types := map[string]string{
"auth_result": "企业认证结果",
"sign_result": "合同签署结果",
"flow_status": "流程状态更新",
}
if typeName, exists := types[action]; exists {
return typeName
}
return "未知类型"
}

View File

@@ -0,0 +1,438 @@
package state_machine
import (
"fmt"
"tyapi-server/internal/domains/certification/enums"
)
// StateConfig 状态配置
type StateConfig struct {
Status enums.CertificationStatus `json:"status"`
Name string `json:"name"`
ProgressPercentage int `json:"progress_percentage"`
IsUserActionRequired bool `json:"is_user_action_required"`
IsSystemAction bool `json:"is_system_action"`
TimestampField string `json:"timestamp_field,omitempty"`
Description string `json:"description"`
NextValidStatuses []enums.CertificationStatus `json:"next_valid_statuses"`
AllowedActors []enums.ActorType `json:"allowed_actors"`
}
// StateTransitionRule 状态转换规则
type StateTransitionRule struct {
FromStatus enums.CertificationStatus `json:"from_status"`
ToStatus enums.CertificationStatus `json:"to_status"`
TransitionName string `json:"transition_name"`
AllowedActors []enums.ActorType `json:"allowed_actors"`
RequiresValidation bool `json:"requires_validation"`
Description string `json:"description"`
BusinessRules []string `json:"business_rules"`
}
// StateConfigManager 状态配置管理器
type StateConfigManager struct {
stateConfigs map[enums.CertificationStatus]*StateConfig
transitionRules map[string]*StateTransitionRule // key: "from_status->to_status"
actorPermissions map[enums.ActorType][]string // actor允许的操作
}
// NewStateConfigManager 创建状态配置管理器
func NewStateConfigManager() *StateConfigManager {
manager := &StateConfigManager{
stateConfigs: make(map[enums.CertificationStatus]*StateConfig),
transitionRules: make(map[string]*StateTransitionRule),
actorPermissions: make(map[enums.ActorType][]string),
}
manager.initializeStateConfigs()
manager.initializeTransitionRules()
manager.initializeActorPermissions()
return manager
}
// initializeStateConfigs 初始化状态配置
func (m *StateConfigManager) initializeStateConfigs() {
configs := []*StateConfig{
{
Status: enums.StatusPending,
Name: "待认证",
ProgressPercentage: 0,
IsUserActionRequired: true,
IsSystemAction: false,
Description: "等待用户提交企业信息",
NextValidStatuses: []enums.CertificationStatus{enums.StatusInfoSubmitted},
AllowedActors: []enums.ActorType{enums.ActorTypeUser},
},
{
Status: enums.StatusInfoSubmitted,
Name: "已提交企业信息",
ProgressPercentage: 25,
IsUserActionRequired: false,
IsSystemAction: true,
TimestampField: "InfoSubmittedAt",
Description: "企业信息已提交等待e签宝验证",
NextValidStatuses: []enums.CertificationStatus{enums.StatusEnterpriseVerified, enums.StatusInfoRejected},
AllowedActors: []enums.ActorType{enums.ActorTypeEsign, enums.ActorTypeSystem},
},
{
Status: enums.StatusEnterpriseVerified,
Name: "已企业认证",
ProgressPercentage: 50,
IsUserActionRequired: true,
IsSystemAction: false,
TimestampField: "EnterpriseVerifiedAt",
Description: "企业认证完成,用户可申请合同",
NextValidStatuses: []enums.CertificationStatus{enums.StatusContractApplied},
AllowedActors: []enums.ActorType{enums.ActorTypeUser},
},
{
Status: enums.StatusContractApplied,
Name: "已申请签署合同",
ProgressPercentage: 75,
IsUserActionRequired: true,
IsSystemAction: true,
TimestampField: "ContractAppliedAt",
Description: "合同已生成,等待用户签署",
NextValidStatuses: []enums.CertificationStatus{enums.StatusContractSigned, enums.StatusContractRejected, enums.StatusContractExpired},
AllowedActors: []enums.ActorType{enums.ActorTypeEsign, enums.ActorTypeSystem},
},
{
Status: enums.StatusContractSigned,
Name: "认证完成",
ProgressPercentage: 100,
IsUserActionRequired: false,
IsSystemAction: false,
TimestampField: "ContractSignedAt",
Description: "认证流程已完成",
NextValidStatuses: []enums.CertificationStatus{},
AllowedActors: []enums.ActorType{},
},
// 失败状态
{
Status: enums.StatusInfoRejected,
Name: "企业信息被拒绝",
ProgressPercentage: 25,
IsUserActionRequired: true,
IsSystemAction: false,
Description: "企业信息验证失败,需要重新提交",
NextValidStatuses: []enums.CertificationStatus{enums.StatusInfoSubmitted},
AllowedActors: []enums.ActorType{enums.ActorTypeUser},
},
{
Status: enums.StatusContractRejected,
Name: "合同被拒签",
ProgressPercentage: 75,
IsUserActionRequired: true,
IsSystemAction: false,
Description: "用户拒绝签署合同",
NextValidStatuses: []enums.CertificationStatus{enums.StatusEnterpriseVerified},
AllowedActors: []enums.ActorType{enums.ActorTypeUser, enums.ActorTypeSystem},
},
{
Status: enums.StatusContractExpired,
Name: "合同签署超时",
ProgressPercentage: 75,
IsUserActionRequired: true,
IsSystemAction: false,
Description: "合同签署链接已过期",
NextValidStatuses: []enums.CertificationStatus{enums.StatusEnterpriseVerified},
AllowedActors: []enums.ActorType{enums.ActorTypeUser, enums.ActorTypeSystem},
},
}
for _, config := range configs {
m.stateConfigs[config.Status] = config
}
}
// initializeTransitionRules 初始化状态转换规则
func (m *StateConfigManager) initializeTransitionRules() {
rules := []*StateTransitionRule{
// 用户提交企业信息
{
FromStatus: enums.StatusPending,
ToStatus: enums.StatusInfoSubmitted,
TransitionName: "submit_enterprise_info",
AllowedActors: []enums.ActorType{enums.ActorTypeUser},
RequiresValidation: true,
Description: "用户提交企业信息",
BusinessRules: []string{"enterprise_info_complete", "enterprise_info_valid"},
},
// e签宝企业认证成功
{
FromStatus: enums.StatusInfoSubmitted,
ToStatus: enums.StatusEnterpriseVerified,
TransitionName: "enterprise_verification_success",
AllowedActors: []enums.ActorType{enums.ActorTypeEsign, enums.ActorTypeSystem},
RequiresValidation: false,
Description: "e签宝企业认证成功",
BusinessRules: []string{"auth_flow_id_exists"},
},
// e签宝企业认证失败
{
FromStatus: enums.StatusInfoSubmitted,
ToStatus: enums.StatusInfoRejected,
TransitionName: "enterprise_verification_failed",
AllowedActors: []enums.ActorType{enums.ActorTypeEsign, enums.ActorTypeSystem},
RequiresValidation: false,
Description: "e签宝企业认证失败",
BusinessRules: []string{"failure_reason_provided"},
},
// 用户申请合同
{
FromStatus: enums.StatusEnterpriseVerified,
ToStatus: enums.StatusContractApplied,
TransitionName: "apply_contract",
AllowedActors: []enums.ActorType{enums.ActorTypeUser},
RequiresValidation: true,
Description: "用户申请合同签署",
BusinessRules: []string{"enterprise_verified", "auth_flow_id_exists"},
},
// e签宝合同签署成功
{
FromStatus: enums.StatusContractApplied,
ToStatus: enums.StatusContractSigned,
TransitionName: "contract_sign_success",
AllowedActors: []enums.ActorType{enums.ActorTypeEsign, enums.ActorTypeSystem},
RequiresValidation: false,
Description: "e签宝合同签署成功",
BusinessRules: []string{"contract_info_complete"},
},
// 合同签署失败
{
FromStatus: enums.StatusContractApplied,
ToStatus: enums.StatusContractRejected,
TransitionName: "contract_sign_rejected",
AllowedActors: []enums.ActorType{enums.ActorTypeEsign, enums.ActorTypeSystem},
RequiresValidation: false,
Description: "用户拒绝签署合同",
BusinessRules: []string{"failure_reason_provided"},
},
// 合同签署超时
{
FromStatus: enums.StatusContractApplied,
ToStatus: enums.StatusContractExpired,
TransitionName: "contract_sign_expired",
AllowedActors: []enums.ActorType{enums.ActorTypeEsign, enums.ActorTypeSystem},
RequiresValidation: false,
Description: "合同签署超时",
BusinessRules: []string{"failure_reason_provided"},
},
// 重新提交企业信息
{
FromStatus: enums.StatusInfoRejected,
ToStatus: enums.StatusInfoSubmitted,
TransitionName: "resubmit_enterprise_info",
AllowedActors: []enums.ActorType{enums.ActorTypeUser},
RequiresValidation: true,
Description: "用户重新提交企业信息",
BusinessRules: []string{"enterprise_info_complete", "enterprise_info_valid", "retry_limit_check"},
},
// 从合同失败状态恢复
{
FromStatus: enums.StatusContractRejected,
ToStatus: enums.StatusEnterpriseVerified,
TransitionName: "reset_from_contract_rejected",
AllowedActors: []enums.ActorType{enums.ActorTypeSystem, enums.ActorTypeUser},
RequiresValidation: false,
Description: "从合同拒签状态恢复",
BusinessRules: []string{"retry_limit_check"},
},
{
FromStatus: enums.StatusContractExpired,
ToStatus: enums.StatusEnterpriseVerified,
TransitionName: "reset_from_contract_expired",
AllowedActors: []enums.ActorType{enums.ActorTypeSystem, enums.ActorTypeUser},
RequiresValidation: false,
Description: "从合同超时状态恢复",
BusinessRules: []string{"retry_limit_check"},
},
}
for _, rule := range rules {
key := string(rule.FromStatus) + "->" + string(rule.ToStatus)
m.transitionRules[key] = rule
}
}
// initializeActorPermissions 初始化操作者权限
func (m *StateConfigManager) initializeActorPermissions() {
m.actorPermissions = map[enums.ActorType][]string{
enums.ActorTypeUser: {
"submit_enterprise_info",
"apply_contract",
"view_certification",
"retry_from_failure",
},
enums.ActorTypeSystem: {
"auto_transition",
"system_recovery",
"timeout_handling",
"data_cleanup",
},
enums.ActorTypeEsign: {
"verification_callback",
"sign_callback",
"status_notification",
},
enums.ActorTypeAdmin: {
"manual_intervention",
"force_transition",
"view_all_certifications",
"system_configuration",
},
}
}
// GetStateConfig 获取状态配置
func (m *StateConfigManager) GetStateConfig(status enums.CertificationStatus) *StateConfig {
return m.stateConfigs[status]
}
// GetTransitionRule 获取状态转换规则
func (m *StateConfigManager) GetTransitionRule(fromStatus, toStatus enums.CertificationStatus) *StateTransitionRule {
key := string(fromStatus) + "->" + string(toStatus)
return m.transitionRules[key]
}
// CanTransition 检查是否可以执行状态转换
func (m *StateConfigManager) CanTransition(fromStatus, toStatus enums.CertificationStatus, actor enums.ActorType) (bool, string) {
// 获取转换规则
rule := m.GetTransitionRule(fromStatus, toStatus)
if rule == nil {
return false, "不支持的状态转换"
}
// 检查操作者权限
allowed := false
for _, allowedActor := range rule.AllowedActors {
if actor == allowedActor {
allowed = true
break
}
}
if !allowed {
return false, "操作者无权限执行此转换"
}
return true, ""
}
// GetAllowedTransitions 获取指定状态下允许的转换
func (m *StateConfigManager) GetAllowedTransitions(fromStatus enums.CertificationStatus, actor enums.ActorType) []*StateTransitionRule {
var allowedTransitions []*StateTransitionRule
config := m.GetStateConfig(fromStatus)
if config == nil {
return allowedTransitions
}
for _, toStatus := range config.NextValidStatuses {
if canTransition, _ := m.CanTransition(fromStatus, toStatus, actor); canTransition {
if rule := m.GetTransitionRule(fromStatus, toStatus); rule != nil {
allowedTransitions = append(allowedTransitions, rule)
}
}
}
return allowedTransitions
}
// GetActorPermissions 获取操作者权限
func (m *StateConfigManager) GetActorPermissions(actor enums.ActorType) []string {
if permissions, exists := m.actorPermissions[actor]; exists {
return permissions
}
return []string{}
}
// HasPermission 检查操作者是否有指定权限
func (m *StateConfigManager) HasPermission(actor enums.ActorType, permission string) bool {
permissions := m.GetActorPermissions(actor)
for _, p := range permissions {
if p == permission {
return true
}
}
return false
}
// ValidateBusinessRules 验证业务规则
func (m *StateConfigManager) ValidateBusinessRules(rule *StateTransitionRule, context map[string]interface{}) error {
for _, businessRule := range rule.BusinessRules {
if err := m.validateSingleBusinessRule(businessRule, context); err != nil {
return err
}
}
return nil
}
// validateSingleBusinessRule 验证单个业务规则
func (m *StateConfigManager) validateSingleBusinessRule(ruleName string, context map[string]interface{}) error {
switch ruleName {
case "enterprise_info_complete":
if enterpriseInfo, exists := context["enterprise_info"]; !exists || enterpriseInfo == nil {
return fmt.Errorf("企业信息不能为空")
}
case "enterprise_info_valid":
// 这里可以添加更复杂的企业信息验证逻辑
return nil
case "auth_flow_id_exists":
if authFlowID, exists := context["auth_flow_id"]; !exists || authFlowID == "" {
return fmt.Errorf("认证流程ID不能为空")
}
case "failure_reason_provided":
if reason, exists := context["failure_reason"]; !exists || reason == "" {
return fmt.Errorf("失败原因不能为空")
}
case "enterprise_verified":
if status, exists := context["current_status"]; !exists || status != string(enums.StatusEnterpriseVerified) {
return fmt.Errorf("企业必须先完成认证")
}
case "contract_info_complete":
if contractInfo, exists := context["contract_info"]; !exists || contractInfo == nil {
return fmt.Errorf("合同信息不能为空")
}
case "retry_limit_check":
if retryCount, exists := context["retry_count"]; exists {
if count, ok := retryCount.(int); ok && count >= 3 {
return fmt.Errorf("已达到最大重试次数限制")
}
}
}
return nil
}
// GetStateProgress 获取状态进度信息
func (m *StateConfigManager) GetStateProgress(status enums.CertificationStatus) int {
if config := m.GetStateConfig(status); config != nil {
return config.ProgressPercentage
}
return 0
}
// IsUserActionRequired 检查是否需要用户操作
func (m *StateConfigManager) IsUserActionRequired(status enums.CertificationStatus) bool {
if config := m.GetStateConfig(status); config != nil {
return config.IsUserActionRequired
}
return false
}
// IsSystemAction 检查是否为系统操作状态
func (m *StateConfigManager) IsSystemAction(status enums.CertificationStatus) bool {
if config := m.GetStateConfig(status); config != nil {
return config.IsSystemAction
}
return false
}
// GetTimestampField 获取状态对应的时间戳字段
func (m *StateConfigManager) GetTimestampField(status enums.CertificationStatus) string {
if config := m.GetStateConfig(status); config != nil {
return config.TimestampField
}
return ""
}