2025-07-21 15:13:26 +08:00
|
|
|
package services
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
|
|
"tyapi-server/internal/domains/certification/entities"
|
|
|
|
|
"tyapi-server/internal/domains/certification/enums"
|
|
|
|
|
"tyapi-server/internal/domains/certification/repositories"
|
|
|
|
|
|
|
|
|
|
"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
|
2025-07-28 01:46:39 +08:00
|
|
|
LoadCertificationByUserID(ctx context.Context, userID string) (*entities.Certification, error)
|
|
|
|
|
LoadCertificationByAuthFlowId(ctx context.Context, authFlowId string) (*entities.Certification, error)
|
|
|
|
|
LoadCertificationByEsignFlowId(ctx context.Context, esignFlowId string) (*entities.Certification, error)
|
2025-07-21 15:13:26 +08:00
|
|
|
// 业务规则验证
|
|
|
|
|
ValidateBusinessRules(ctx context.Context, cert *entities.Certification) error
|
|
|
|
|
CheckInvariance(ctx context.Context, cert *entities.Certification) error
|
|
|
|
|
|
|
|
|
|
// 查询方法
|
2025-07-28 01:46:39 +08:00
|
|
|
ExistsByUserID(ctx context.Context, userID string) (bool, error)
|
2025-07-21 15:13:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// CertificationAggregateServiceImpl 认证聚合服务实现
|
|
|
|
|
type CertificationAggregateServiceImpl struct {
|
2025-07-28 01:46:39 +08:00
|
|
|
commandRepo repositories.CertificationCommandRepository
|
|
|
|
|
queryRepo repositories.CertificationQueryRepository
|
|
|
|
|
logger *zap.Logger
|
2025-07-21 15:13:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewCertificationAggregateService 创建认证聚合服务
|
|
|
|
|
func NewCertificationAggregateService(
|
|
|
|
|
commandRepo repositories.CertificationCommandRepository,
|
|
|
|
|
queryRepo repositories.CertificationQueryRepository,
|
|
|
|
|
logger *zap.Logger,
|
|
|
|
|
) CertificationAggregateService {
|
|
|
|
|
return &CertificationAggregateServiceImpl{
|
2025-07-28 01:46:39 +08:00
|
|
|
commandRepo: commandRepo,
|
|
|
|
|
queryRepo: queryRepo,
|
|
|
|
|
logger: logger,
|
2025-07-21 15:13:26 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ================ 聚合根管理 ================
|
|
|
|
|
|
|
|
|
|
// CreateCertification 创建认证申请
|
|
|
|
|
func (s *CertificationAggregateServiceImpl) CreateCertification(ctx context.Context, userID string) (*entities.Certification, error) {
|
|
|
|
|
s.logger.Info("创建认证申请", zap.String("user_id", userID))
|
|
|
|
|
|
|
|
|
|
// 1. 检查用户是否已有认证申请
|
2025-07-28 01:46:39 +08:00
|
|
|
exists, err := s.ExistsByUserID(ctx, userID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
s.logger.Error("检查用户认证是否存在失败", zap.Error(err), zap.String("user_id", userID))
|
|
|
|
|
return nil, fmt.Errorf("检查用户认证是否存在失败: %w", err)
|
|
|
|
|
}
|
|
|
|
|
if exists {
|
|
|
|
|
s.logger.Info("用户已有认证申请,不允许创建新申请",
|
|
|
|
|
zap.String("user_id", userID))
|
|
|
|
|
return nil, fmt.Errorf("用户已有认证申请")
|
2025-07-21 15:13:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
// LoadCertificationByUserID 加载用户认证聚合根
|
|
|
|
|
func (s *CertificationAggregateServiceImpl) LoadCertificationByUserID(ctx context.Context, userID string) (*entities.Certification, error) {
|
|
|
|
|
s.logger.Debug("加载用户认证聚合根", zap.String("user_id", userID))
|
|
|
|
|
|
|
|
|
|
// 从查询仓储加载
|
|
|
|
|
cert, err := s.queryRepo.GetByUserID(ctx, userID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
s.logger.Error("加载用户认证聚合根失败", zap.Error(err), zap.String("user_id", userID))
|
|
|
|
|
return nil, fmt.Errorf("认证申请不存在: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cert, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LoadCertificationByAuthFlowId 加载认证聚合根
|
|
|
|
|
func (s *CertificationAggregateServiceImpl) LoadCertificationByAuthFlowId(ctx context.Context, authFlowId string) (*entities.Certification, error) {
|
|
|
|
|
s.logger.Debug("加载认证聚合根", zap.String("auth_flow_id", authFlowId))
|
|
|
|
|
|
|
|
|
|
// 从查询仓储加载
|
|
|
|
|
cert, err := s.queryRepo.FindByAuthFlowID(ctx, authFlowId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
s.logger.Error("加载认证聚合根失败", zap.Error(err), zap.String("auth_flow_id", authFlowId))
|
|
|
|
|
return nil, fmt.Errorf("认证申请不存在: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cert, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// LoadCertificationByEsignFlowId 加载认证聚合根
|
|
|
|
|
func (s *CertificationAggregateServiceImpl) LoadCertificationByEsignFlowId(ctx context.Context, esignFlowId string) (*entities.Certification, error) {
|
|
|
|
|
s.logger.Debug("加载认证聚合根", zap.String("esign_flow_id", esignFlowId))
|
|
|
|
|
|
|
|
|
|
// 从查询仓储加载
|
|
|
|
|
cert, err := s.queryRepo.FindByEsignFlowID(ctx, esignFlowId)
|
|
|
|
|
if err != nil {
|
|
|
|
|
s.logger.Error("加载认证聚合根失败", zap.Error(err), zap.String("esign_flow_id", esignFlowId))
|
|
|
|
|
return nil, fmt.Errorf("认证申请不存在: %w", err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return cert, nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-21 15:13:26 +08:00
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ================ 业务规则验证 ================
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ================ 查询方法 ================
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
// Exists 判断认证是否存在
|
|
|
|
|
func (s *CertificationAggregateServiceImpl) ExistsByUserID(ctx context.Context, userID string) (bool, error) {
|
|
|
|
|
return s.queryRepo.ExistsByUserID(ctx, userID)
|
2025-07-21 15:13:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ================ 私有方法 ================
|
|
|
|
|
|
|
|
|
|
// 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("合同签署状态下必须有签署完成时间")
|
|
|
|
|
}
|
2025-07-28 01:46:39 +08:00
|
|
|
|
|
|
|
|
case enums.StatusCompleted:
|
|
|
|
|
if cert.ContractFileID == "" || cert.EsignFlowID == "" || cert.ContractURL == "" {
|
|
|
|
|
return fmt.Errorf("认证完成状态下必须有完整的合同信息")
|
|
|
|
|
}
|
|
|
|
|
if cert.ContractSignedAt == nil {
|
|
|
|
|
return fmt.Errorf("认证完成状态下必须有合同签署时间")
|
|
|
|
|
}
|
|
|
|
|
if cert.CompletedAt == nil {
|
|
|
|
|
return fmt.Errorf("认证完成状态下必须有完成时间")
|
|
|
|
|
}
|
2025-07-21 15:13:26 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 失败状态检查
|
|
|
|
|
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("合同申请时间不能晚于合同签署时间")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-28 01:46:39 +08:00
|
|
|
if cert.ContractSignedAt != nil && cert.CompletedAt != nil {
|
|
|
|
|
if cert.ContractSignedAt.After(*cert.CompletedAt) {
|
|
|
|
|
return fmt.Errorf("合同签署时间不能晚于认证完成时间")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-21 15:13:26 +08:00
|
|
|
return nil
|
|
|
|
|
}
|