Files
tyapi-server/internal/application/certification/certification_application_service_impl.go
2025-07-28 01:46:39 +08:00

944 lines
33 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package certification
import (
"context"
"fmt"
"io"
"net/http"
"time"
"tyapi-server/internal/application/certification/dto/commands"
"tyapi-server/internal/application/certification/dto/queries"
"tyapi-server/internal/application/certification/dto/responses"
api_service "tyapi-server/internal/domains/api/services"
"tyapi-server/internal/domains/certification/entities"
certification_value_objects "tyapi-server/internal/domains/certification/entities/value_objects"
"tyapi-server/internal/domains/certification/enums"
"tyapi-server/internal/domains/certification/repositories"
"tyapi-server/internal/domains/certification/services"
finance_service "tyapi-server/internal/domains/finance/services"
user_entities "tyapi-server/internal/domains/user/entities"
user_service "tyapi-server/internal/domains/user/services"
"tyapi-server/internal/infrastructure/external/storage"
"tyapi-server/internal/shared/database"
"tyapi-server/internal/shared/esign"
"go.uber.org/zap"
)
// CertificationApplicationServiceImpl 认证应用服务实现
// 负责用例协调DTO转换是应用层的核心组件
type CertificationApplicationServiceImpl struct {
// 领域服务依赖
aggregateService services.CertificationAggregateService
userAggregateService user_service.UserAggregateService
smsCodeService *user_service.SMSCodeService
esignClient *esign.Client
esignConfig *esign.Config
qiniuStorageService *storage.QiNiuStorageService
contractAggregateService user_service.ContractAggregateService
walletAggregateService finance_service.WalletAggregateService
apiUserAggregateService api_service.ApiUserAggregateService
enterpriseInfoSubmitRecordService *services.EnterpriseInfoSubmitRecordService
// 仓储依赖
queryRepository repositories.CertificationQueryRepository
enterpriseInfoSubmitRecordRepo repositories.EnterpriseInfoSubmitRecordRepository
txManager *database.TransactionManager
logger *zap.Logger
}
// NewCertificationApplicationService 创建认证应用服务
func NewCertificationApplicationService(
aggregateService services.CertificationAggregateService,
userAggregateService user_service.UserAggregateService,
queryRepository repositories.CertificationQueryRepository,
enterpriseInfoSubmitRecordRepo repositories.EnterpriseInfoSubmitRecordRepository,
smsCodeService *user_service.SMSCodeService,
esignClient *esign.Client,
esignConfig *esign.Config,
qiniuStorageService *storage.QiNiuStorageService,
contractAggregateService user_service.ContractAggregateService,
walletAggregateService finance_service.WalletAggregateService,
apiUserAggregateService api_service.ApiUserAggregateService,
enterpriseInfoSubmitRecordService *services.EnterpriseInfoSubmitRecordService,
txManager *database.TransactionManager,
logger *zap.Logger,
) CertificationApplicationService {
return &CertificationApplicationServiceImpl{
aggregateService: aggregateService,
userAggregateService: userAggregateService,
queryRepository: queryRepository,
enterpriseInfoSubmitRecordRepo: enterpriseInfoSubmitRecordRepo,
smsCodeService: smsCodeService,
esignClient: esignClient,
esignConfig: esignConfig,
qiniuStorageService: qiniuStorageService,
contractAggregateService: contractAggregateService,
walletAggregateService: walletAggregateService,
apiUserAggregateService: apiUserAggregateService,
enterpriseInfoSubmitRecordService: enterpriseInfoSubmitRecordService,
txManager: txManager,
logger: logger,
}
}
// ================ 用户操作用例 ================
// SubmitEnterpriseInfo 提交企业信息
func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
ctx context.Context,
cmd *commands.SubmitEnterpriseInfoCommand,
) (*responses.CertificationResponse, error) {
s.logger.Info("开始提交企业信息",
zap.String("user_id", cmd.UserID))
// 验证验证码
if err := s.smsCodeService.VerifyCode(ctx, cmd.LegalPersonPhone, cmd.VerificationCode, user_entities.SMSSceneCertification); err != nil {
return nil, fmt.Errorf("验证码错误或已过期")
}
s.logger.Info("开始处理企业信息提交",
zap.String("user_id", cmd.UserID))
// 1. 检查企业信息是否重复(统一社会信用代码,已经认证了的,不能重复提交)
exists, err := s.userAggregateService.CheckUnifiedSocialCodeExists(ctx, cmd.UnifiedSocialCode, cmd.UserID)
if err != nil {
return nil, fmt.Errorf("检查企业信息失败: %s", err.Error())
}
if exists {
return nil, fmt.Errorf("该企业信息已被其他用户使用,请确认企业信息是否正确")
}
// 1.5 插入企业信息提交记录
record := entities.NewEnterpriseInfoSubmitRecord(
cmd.UserID,
cmd.CompanyName,
cmd.UnifiedSocialCode,
cmd.LegalPersonName,
cmd.LegalPersonID,
cmd.LegalPersonPhone,
cmd.EnterpriseAddress,
cmd.EnterpriseEmail,
)
enterpriseInfo := &certification_value_objects.EnterpriseInfo{
CompanyName: cmd.CompanyName,
UnifiedSocialCode: cmd.UnifiedSocialCode,
LegalPersonName: cmd.LegalPersonName,
LegalPersonID: cmd.LegalPersonID,
LegalPersonPhone: cmd.LegalPersonPhone,
EnterpriseAddress: cmd.EnterpriseAddress,
EnterpriseEmail: cmd.EnterpriseEmail,
}
err = enterpriseInfo.Validate()
if err != nil {
s.logger.Error("企业信息验证失败", zap.Error(err))
record.MarkAsFailed(err.Error())
return nil, fmt.Errorf("企业信息验证失败: %s", err.Error())
}
err = s.enterpriseInfoSubmitRecordService.ValidateWithWestdex(ctx, enterpriseInfo)
if err != nil {
s.logger.Error("企业信息验证失败", zap.Error(err))
record.MarkAsFailed(err.Error())
saveErr := s.enterpriseInfoSubmitRecordService.Save(ctx, record)
if saveErr != nil {
s.logger.Error("保存企业信息提交记录失败", zap.Error(saveErr))
}
return nil, fmt.Errorf("企业信息验证失败, %s", err.Error())
}
record.MarkAsVerified()
err = s.enterpriseInfoSubmitRecordService.Save(ctx, record)
if err != nil {
return nil, fmt.Errorf("保存企业信息提交记录失败: %s", err.Error())
}
var response *responses.CertificationResponse
err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
// 2. 检查用户认证是否存在
existsCert, err := s.aggregateService.ExistsByUserID(txCtx, cmd.UserID)
if err != nil {
return fmt.Errorf("检查用户认证是否存在失败: %s", err.Error())
}
if !existsCert {
// 创建
_, err := s.aggregateService.CreateCertification(txCtx, cmd.UserID)
if err != nil {
return fmt.Errorf("创建认证信息失败: %s", err.Error())
}
}
// 3. 加载认证聚合根
cert, err := s.aggregateService.LoadCertificationByUserID(txCtx, cmd.UserID)
if err != nil {
return fmt.Errorf("加载认证信息失败: %s", err.Error())
}
// 3. 调用e签宝看是否进行过认证
respMeta := map[string]interface{}{}
identity, err := s.esignClient.QueryOrgIdentityInfo(&esign.QueryOrgIdentityRequest{
OrgName: cmd.CompanyName,
})
if identity != nil && identity.Data.RealnameStatus == 1 {
// 已提交
err = cert.SubmitEnterpriseInfo(enterpriseInfo, "", "")
if err != nil {
return fmt.Errorf("提交企业认证信息失败: %s", err.Error())
}
s.logger.Info("企业认证成功", zap.Any("identity", identity))
// 完成企业认证流程
err = s.completeEnterpriseVerification(txCtx, cert, cmd.UserID, cmd.CompanyName, cmd.LegalPersonName)
if err != nil {
return err
}
respMeta = map[string]interface{}{
"enterprise_info": enterpriseInfo,
"next_action": "企业已认证,可进行后续操作",
}
} else {
if err != nil {
s.logger.Error("e签宝查询企业认证信息失败或未进行企业认证", zap.Error(err))
}
authURL, err := s.esignClient.GenerateEnterpriseAuth(&esign.EnterpriseAuthRequest{
CompanyName: enterpriseInfo.CompanyName,
UnifiedSocialCode: enterpriseInfo.UnifiedSocialCode,
LegalPersonName: enterpriseInfo.LegalPersonName,
LegalPersonID: enterpriseInfo.LegalPersonID,
TransactorName: enterpriseInfo.LegalPersonName,
TransactorMobile: enterpriseInfo.LegalPersonPhone,
TransactorID: enterpriseInfo.LegalPersonID,
})
if err != nil {
s.logger.Error("生成企业认证链接失败", zap.Error(err))
return fmt.Errorf("生成企业认证链接失败: %s", err.Error())
}
err = cert.SubmitEnterpriseInfo(enterpriseInfo, authURL.AuthShortURL, authURL.AuthFlowID)
if err != nil {
return fmt.Errorf("提交企业认证信息失败: %s", err.Error())
}
respMeta = map[string]interface{}{
"enterprise_info": enterpriseInfo,
"authUrl": authURL.AuthURL,
"next_action": "请完成企业认证",
}
}
err = s.aggregateService.SaveCertification(txCtx, cert)
if err != nil {
return fmt.Errorf("保存认证信息失败: %s", err.Error())
}
// 5. 转换为响应DTO
response = s.convertToResponse(cert)
// 6. 添加工作流结果信息
if respMeta != nil {
response.Metadata = respMeta
}
return nil
})
if err != nil {
return nil, err
}
s.logger.Info("企业信息提交成功", zap.String("user_id", cmd.UserID))
return response, nil
}
// ConfirmAuth 确认认证状态
func (s *CertificationApplicationServiceImpl) ConfirmAuth(
ctx context.Context,
cmd *queries.ConfirmAuthCommand,
) (*responses.ConfirmAuthResponse, error) {
s.logger.Info("开始确认状态", zap.String("user_id", cmd.UserID))
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
if err != nil {
return nil, fmt.Errorf("加载认证信息失败: %s", err.Error())
}
// 企业认证
if cert.Status != enums.StatusInfoSubmitted {
return nil, fmt.Errorf("认证状态不正确,当前状态: %s", enums.GetStatusName(cert.Status))
}
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cert.UserID)
if err != nil {
return nil, fmt.Errorf("查找企业信息失败: %w", err)
}
identity, err := s.esignClient.QueryOrgIdentityInfo(&esign.QueryOrgIdentityRequest{
OrgName: record.CompanyName,
})
if err != nil {
s.logger.Error("查询企业认证信息失败", zap.Error(err))
return nil, fmt.Errorf("查询企业认证信息失败: %w", err)
}
reason := ""
if identity != nil && identity.Data.RealnameStatus == 1 {
err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
err = s.completeEnterpriseVerification(txCtx, cert, cert.UserID, record.CompanyName, record.LegalPersonName)
if err != nil {
return err
}
reason = "企业认证成功"
return nil
})
if err != nil {
return nil, fmt.Errorf("完成企业认证失败: %w", err)
}
} else {
reason = "企业未完成"
}
return &responses.ConfirmAuthResponse{
Status: cert.Status,
Reason: reason,
}, nil
}
// ConfirmSign 确认签署状态
func (s *CertificationApplicationServiceImpl) ConfirmSign(
ctx context.Context,
cmd *queries.ConfirmSignCommand,
) (*responses.ConfirmSignResponse, error) {
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
if err != nil {
return nil, fmt.Errorf("加载认证信息失败: %s", err.Error())
}
reason, err := s.checkAndUpdateSignStatus(ctx, cert)
if err != nil {
return nil, fmt.Errorf("确认签署状态失败: %w", err)
}
return &responses.ConfirmSignResponse{
Status: cert.Status,
Reason: reason,
}, nil
}
// ApplyContract 申请合同签署
func (s *CertificationApplicationServiceImpl) ApplyContract(
ctx context.Context,
cmd *commands.ApplyContractCommand,
) (*responses.ContractSignUrlResponse, error) {
s.logger.Info("开始申请合同签署",
zap.String("user_id", cmd.UserID))
// 1. 验证命令完整性
if err := s.validateApplyContractCommand(cmd); err != nil {
return nil, fmt.Errorf("命令验证失败: %s", err.Error())
}
// 2. 加载认证聚合根
cert, err := s.aggregateService.LoadCertificationByUserID(ctx, cmd.UserID)
if err != nil {
return nil, fmt.Errorf("加载认证信息失败: %s", err.Error())
}
// 3. 验证业务前置条件
if err := s.validateContractApplicationPreconditions(cert, cmd.UserID); err != nil {
return nil, fmt.Errorf("业务前置条件验证失败: %s", err.Error())
}
// 5. 生成合同和签署链接
enterpriseInfo, err := s.userAggregateService.GetUserWithEnterpriseInfo(ctx, cmd.UserID)
if err != nil {
s.logger.Error("获取企业信息失败", zap.Error(err))
return nil, fmt.Errorf("获取企业信息失败: %w", err)
}
contractInfo, err := s.generateContractAndSignURL(ctx, cert, enterpriseInfo.EnterpriseInfo)
if err != nil {
s.logger.Error("生成合同失败", zap.Error(err))
return nil, fmt.Errorf("生成合同失败: %s", err.Error())
}
err = cert.ApplyContract(contractInfo.EsignFlowID, contractInfo.ContractSignURL)
if err != nil {
s.logger.Error("合同申请状态转换失败", zap.Error(err))
return nil, fmt.Errorf("合同申请失败: %s", err.Error())
}
// 7. 保存认证信息
err = s.aggregateService.SaveCertification(ctx, cert)
if err != nil {
s.logger.Error("保存认证信息失败", zap.Error(err))
return nil, fmt.Errorf("保存认证信息失败: %s", err.Error())
}
// 8. 构建响应
response := responses.NewContractSignUrlResponse(
cert.ID,
contractInfo.ContractSignURL,
contractInfo.ContractURL,
"请在规定时间内完成合同签署",
"合同申请成功",
)
s.logger.Info("合同申请成功", zap.String("user_id", cmd.UserID))
return response, nil
}
// ================ 查询用例 ================
// GetCertification 获取认证详情
func (s *CertificationApplicationServiceImpl) GetCertification(
ctx context.Context,
query *queries.GetCertificationQuery,
) (*responses.CertificationResponse, error) {
s.logger.Debug("获取认证详情", zap.String("user_id", query.UserID))
// 1. 检查用户认证是否存在
exists, err := s.aggregateService.ExistsByUserID(ctx, query.UserID)
if err != nil {
s.logger.Error("获取认证信息失败", zap.Error(err))
return nil, fmt.Errorf("获取认证信息失败: %w", err)
}
var cert *entities.Certification
if !exists {
// 创建新的认证记录
cert, err = s.aggregateService.CreateCertification(ctx, query.UserID)
if err != nil {
s.logger.Error("创建认证信息失败", zap.Error(err))
return nil, fmt.Errorf("创建认证信息失败: %w", err)
}
} else {
// 加载现有认证记录
cert, err = s.aggregateService.LoadCertificationByUserID(ctx, query.UserID)
if err != nil {
s.logger.Error("加载认证信息失败", zap.Error(err))
return nil, fmt.Errorf("加载认证信息失败: %w", err)
}
}
// 2. 检查是否需要更新合同文件
if cert.IsContractFileNeedUpdate() {
err = s.updateContractFile(ctx, cert)
if err != nil {
return nil, err
}
}
if cert.Status == enums.StatusInfoSubmitted {
err = s.checkAndCompleteEnterpriseVerification(ctx, cert)
if err != nil {
return nil, err
}
}
if cert.Status == enums.StatusContractApplied {
_, err = s.checkAndUpdateSignStatus(ctx, cert)
if err != nil {
return nil, err
}
}
// 2. 转换为响应DTO
response := s.convertToResponse(cert)
// 3. 添加状态相关的元数据
meta := cert.GetDataByStatus()
if meta != nil {
response.Metadata = meta
}
s.logger.Info("获取认证详情成功", zap.String("user_id", query.UserID))
return response, nil
}
// ListCertifications 获取认证列表(管理员)
func (s *CertificationApplicationServiceImpl) ListCertifications(
ctx context.Context,
query *queries.ListCertificationsQuery,
) (*responses.CertificationListResponse, error) {
s.logger.Debug("获取认证列表(管理员)")
// 1. 转换为领域查询对象
domainQuery := query.ToDomainQuery()
// 2. 执行查询
certs, total, err := s.queryRepository.List(ctx, domainQuery)
if err != nil {
s.logger.Error("查询认证列表失败", zap.Error(err))
return nil, fmt.Errorf("查询认证列表失败: %w", err)
}
// 3. 转换为响应DTO
items := make([]*responses.CertificationResponse, len(certs))
for i, cert := range certs {
items[i] = s.convertToResponse(cert)
}
// 4. 构建列表响应
response := responses.NewCertificationListResponse(items, total, query.Page, query.PageSize)
return response, nil
}
// ================ e签宝回调处理 ================
// HandleEsignCallback 处理e签宝回调
func (s *CertificationApplicationServiceImpl) HandleEsignCallback(
ctx context.Context,
cmd *commands.EsignCallbackCommand,
) error {
// if err := esign.VerifySignature(cmd.Data, cmd.Headers, cmd.QueryParams, s.esignConfig.AppSecret); err != nil {
// return fmt.Errorf("e签宝回调验签失败: %w", err)
// }
// 4. 根据回调类型处理业务逻辑
switch cmd.Data.Action {
case "AUTH_PASS":
// 只处理企业认证通过
if cmd.Data.AuthType == "ORG" {
err := s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
// 1. 根据AuthFlowId加载认证信息
cert, err := s.aggregateService.LoadCertificationByAuthFlowId(txCtx, cmd.Data.AuthFlowId)
if err != nil {
return fmt.Errorf("加载认证信息失败: %s", err.Error())
}
if cmd.Data.Organization == nil || cmd.Data.Organization.OrgName == "" {
return fmt.Errorf("组织信息为空")
}
if cert.Status != enums.StatusInfoSubmitted {
return fmt.Errorf("认证状态不正确")
}
// 2. 完成企业认证
err = cert.CompleteEnterpriseVerification()
if err != nil {
return fmt.Errorf("完成企业认证失败: %s", err.Error())
}
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(txCtx, cert.UserID)
if err != nil {
s.logger.Error("查找已认证企业信息失败", zap.Error(err))
return fmt.Errorf("查找已认证企业信息失败: %w", err)
}
// 5. 写入用户域
err = s.userAggregateService.CreateOrUpdateEnterpriseInfo(
txCtx,
record.UserID,
record.CompanyName,
record.UnifiedSocialCode,
record.LegalPersonName,
record.LegalPersonID,
record.LegalPersonPhone,
record.EnterpriseAddress,
record.EnterpriseEmail,
)
if err != nil {
s.logger.Error("同步企业信息到用户域失败", zap.Error(err))
return fmt.Errorf("同步企业信息到用户域失败: %w", err)
}
// 生成合同
err = s.generateAndAddContractFile(txCtx, cert, record.CompanyName, record.LegalPersonName, record.UnifiedSocialCode, record.EnterpriseAddress, record.LegalPersonPhone, record.LegalPersonID, record.EnterpriseEmail)
if err != nil {
return err
}
// 3. 保存认证信息
err = s.aggregateService.SaveCertification(txCtx, cert)
if err != nil {
return fmt.Errorf("保存认证信息失败: %s", err.Error())
}
s.logger.Info("完成企业认证", zap.String("certification_id", cert.ID))
return nil
})
if err != nil {
s.logger.Error("完成企业认证失败", zap.Error(err))
return fmt.Errorf("完成企业认证失败: %w", err)
}
}
return nil
default:
s.logger.Info("忽略未知的回调动作", zap.String("action", cmd.Data.Action))
return nil
}
}
// ================ 辅助方法 ================
// convertToResponse 转换实体为响应DTO
func (s *CertificationApplicationServiceImpl) convertToResponse(cert *entities.Certification) *responses.CertificationResponse {
response := &responses.CertificationResponse{
ID: cert.ID,
UserID: cert.UserID,
Status: cert.Status,
StatusName: enums.GetStatusName(cert.Status),
Progress: cert.GetProgress(),
CreatedAt: cert.CreatedAt,
UpdatedAt: cert.UpdatedAt,
InfoSubmittedAt: cert.InfoSubmittedAt,
EnterpriseVerifiedAt: cert.EnterpriseVerifiedAt,
ContractAppliedAt: cert.ContractAppliedAt,
ContractSignedAt: cert.ContractSignedAt,
CompletedAt: cert.CompletedAt,
IsCompleted: cert.IsCompleted(),
IsFailed: enums.IsFailureStatus(cert.Status),
IsUserActionRequired: cert.IsUserActionRequired(),
NextAction: enums.GetUserActionHint(cert.Status),
AvailableActions: cert.GetAvailableActions(),
RetryCount: cert.RetryCount,
Metadata: make(map[string]interface{}),
}
// 设置企业信息(从认证实体中构建)
// TODO: 这里需要从企业信息服务或其他地方获取完整的企业信息
// response.EnterpriseInfo = cert.EnterpriseInfo
// 设置合同信息(从认证实体中构建)
if cert.ContractFileID != "" || cert.EsignFlowID != "" {
// TODO: 从认证实体字段构建合同信息值对象
// response.ContractInfo = &value_objects.ContractInfo{...}
}
// 设置失败信息
if enums.IsFailureStatus(cert.Status) {
response.FailureReason = cert.FailureReason
response.FailureReasonName = enums.GetFailureReasonName(cert.FailureReason)
response.FailureMessage = cert.FailureMessage
response.CanRetry = enums.IsRetryable(cert.FailureReason)
}
return response
}
// validateApplyContractCommand 验证申请合同命令
func (s *CertificationApplicationServiceImpl) validateApplyContractCommand(cmd *commands.ApplyContractCommand) error {
if cmd.UserID == "" {
return fmt.Errorf("用户ID不能为空")
}
return nil
}
// validateContractApplicationPreconditions 验证合同申请前置条件
func (s *CertificationApplicationServiceImpl) validateContractApplicationPreconditions(cert *entities.Certification, userID string) error {
if cert.UserID != userID {
return fmt.Errorf("用户无权限操作此认证申请")
}
if cert.Status != enums.StatusEnterpriseVerified {
return fmt.Errorf("必须先完成企业认证才能申请合同")
}
return nil
}
// generateContractAndSignURL 生成合同和签署链接
func (s *CertificationApplicationServiceImpl) generateContractAndSignURL(ctx context.Context, cert *entities.Certification, enterpriseInfo *user_entities.EnterpriseInfo) (*certification_value_objects.ContractInfo, error) {
// 发起签署流程
signFlowID, err := s.esignClient.CreateSignFlow(&esign.CreateSignFlowRequest{
FileID: cert.ContractFileID,
SignerAccount: enterpriseInfo.UnifiedSocialCode,
SignerName: enterpriseInfo.CompanyName,
TransactorPhone: enterpriseInfo.LegalPersonPhone,
TransactorName: enterpriseInfo.LegalPersonName,
TransactorIDCardNum: enterpriseInfo.LegalPersonID,
})
if err != nil {
return nil, fmt.Errorf("生成合同失败: %s", err.Error())
}
_, shortUrl, err := s.esignClient.GetSignURL(signFlowID, enterpriseInfo.LegalPersonPhone, enterpriseInfo.CompanyName)
if err != nil {
return nil, fmt.Errorf("获取签署链接失败: %s", err.Error())
}
return &certification_value_objects.ContractInfo{
ContractFileID: cert.ContractFileID,
EsignFlowID: signFlowID,
ContractSignURL: shortUrl,
}, nil
}
// ================ 重构后的公共方法 ================
// completeEnterpriseVerification 完成企业认证的公共方法
func (s *CertificationApplicationServiceImpl) completeEnterpriseVerification(
ctx context.Context,
cert *entities.Certification,
userID string,
companyName string,
legalPersonName string,
) error {
// 完成企业认证
err := cert.CompleteEnterpriseVerification()
if err != nil {
s.logger.Error("完成企业认证失败", zap.Error(err))
return fmt.Errorf("完成企业认证失败: %w", err)
}
// 保存企业信息到用户域
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, userID)
if err != nil {
s.logger.Error("查找企业信息失败", zap.Error(err))
return fmt.Errorf("查找企业信息失败: %w", err)
}
err = s.userAggregateService.CreateEnterpriseInfo(
ctx,
userID,
record.CompanyName,
record.UnifiedSocialCode,
record.LegalPersonName,
record.LegalPersonID,
record.LegalPersonPhone,
record.EnterpriseAddress,
record.EnterpriseEmail,
)
if err != nil {
s.logger.Error("保存企业信息到用户域失败", zap.Error(err))
return fmt.Errorf("保存企业信息失败: %s", err.Error())
} else {
s.logger.Info("企业信息已保存到用户域", zap.String("user_id", userID))
}
// 生成合同
err = s.generateAndAddContractFile(ctx, cert, record.CompanyName, record.LegalPersonName, record.UnifiedSocialCode, record.EnterpriseAddress, record.LegalPersonPhone, record.LegalPersonID, record.EnterpriseEmail)
if err != nil {
return err
}
// 保存认证信息
err = s.aggregateService.SaveCertification(ctx, cert)
if err != nil {
s.logger.Error("保存认证信息失败", zap.Error(err))
return fmt.Errorf("保存认证信息失败: %w", err)
}
return nil
}
// generateAndAddContractFile 生成并添加合同文件的公共方法
func (s *CertificationApplicationServiceImpl) generateAndAddContractFile(
ctx context.Context,
cert *entities.Certification,
companyName string,
legalPersonName string,
unifiedSocialCode string,
enterpriseAddress string,
legalPersonPhone string,
legalPersonID string,
enterpriseEmail string,
) error {
fileComponent := map[string]string{
"YFCompanyName": companyName,
"YFCompanyName2": companyName,
"YFLegalPersonName": legalPersonName,
"YFLegalPersonName2": legalPersonName,
"YFUnifiedSocialCode": unifiedSocialCode,
"YFEnterpriseAddress": enterpriseAddress,
"YFContactPerson": legalPersonName,
"YFMobile": legalPersonPhone,
"YFEmail": enterpriseEmail,
"SignDate": time.Now().Format("2006年01月02日"),
"SignDate2": time.Now().Format("2006年01月02日"),
"SignDate3": time.Now().Format("2006年01月02日"),
}
fillTemplateResp, err := s.esignClient.FillTemplate(fileComponent)
if err != nil {
s.logger.Error("生成合同失败", zap.Error(err))
return fmt.Errorf("生成合同失败: %s", err.Error())
}
err = cert.AddContractFileID(fillTemplateResp.FileID, fillTemplateResp.FileDownloadUrl)
if err != nil {
s.logger.Error("加入合同文件ID链接失败", zap.Error(err))
return fmt.Errorf("加入合同文件ID链接失败: %s", err.Error())
}
return nil
}
// updateContractFile 更新合同文件的公共方法
func (s *CertificationApplicationServiceImpl) updateContractFile(ctx context.Context, cert *entities.Certification) error {
// 获取企业信息
enterpriseInfo, err := s.userAggregateService.GetUserWithEnterpriseInfo(ctx, cert.UserID)
if err != nil {
s.logger.Error("获取企业信息失败", zap.Error(err))
return fmt.Errorf("获取企业信息失败: %w", err)
}
// 生成合同
err = s.generateAndAddContractFile(ctx, cert, enterpriseInfo.EnterpriseInfo.CompanyName, enterpriseInfo.EnterpriseInfo.LegalPersonName, enterpriseInfo.EnterpriseInfo.UnifiedSocialCode, enterpriseInfo.EnterpriseInfo.EnterpriseAddress, enterpriseInfo.EnterpriseInfo.LegalPersonPhone, enterpriseInfo.EnterpriseInfo.LegalPersonID, enterpriseInfo.EnterpriseInfo.EnterpriseEmail)
if err != nil {
return err
}
// 更新认证信息
err = s.aggregateService.SaveCertification(ctx, cert)
if err != nil {
s.logger.Error("保存认证信息失败", zap.Error(err))
return fmt.Errorf("保存认证信息失败: %w", err)
}
return nil
}
// checkAndCompleteEnterpriseVerification 检查并完成企业认证的公共方法
func (s *CertificationApplicationServiceImpl) checkAndCompleteEnterpriseVerification(ctx context.Context, cert *entities.Certification) error {
record, err := s.enterpriseInfoSubmitRecordRepo.FindLatestByUserID(ctx, cert.UserID)
if err != nil {
return fmt.Errorf("查找企业信息失败: %w", err)
}
identity, err := s.esignClient.QueryOrgIdentityInfo(&esign.QueryOrgIdentityRequest{
OrgName: record.CompanyName,
})
if err != nil {
s.logger.Error("查询企业认证信息失败", zap.Error(err))
}
if identity != nil && identity.Data.RealnameStatus == 1 {
err = s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
return s.completeEnterpriseVerification(txCtx, cert, cert.UserID, record.CompanyName, record.LegalPersonName)
})
if err != nil {
return fmt.Errorf("完成企业认证失败: %w", err)
}
}
return nil
}
// checkAndUpdateSignStatus 检查并更新签署状态的公共方法
func (s *CertificationApplicationServiceImpl) checkAndUpdateSignStatus(ctx context.Context, cert *entities.Certification) (string, error) {
var reason string
err := s.txManager.ExecuteInTx(ctx, func(txCtx context.Context) error {
if cert.Status != enums.StatusContractApplied {
return fmt.Errorf("认证状态不正确")
}
detail, err := s.esignClient.QuerySignFlowDetail(cert.EsignFlowID)
if err != nil {
return fmt.Errorf("查询签署流程详情失败: %s", err.Error())
}
if detail.Data.SignFlowStatus == 2 {
err = cert.SignSuccess()
if err != nil {
return fmt.Errorf("合同签署成功失败: %s", err.Error())
}
err = cert.CompleteCertification()
if err != nil {
return fmt.Errorf("完成认证失败: %s", err.Error())
}
// 同步合同信息到用户域
err = s.handleContractAfterSignComplete(txCtx, cert)
if err != nil {
s.logger.Error("同步合同信息到用户域失败", zap.Error(err))
return fmt.Errorf("同步合同信息失败: %s", err.Error())
}
reason = "合同签署成功"
} else if detail.Data.SignFlowStatus == 7 {
err = cert.ContractRejection(detail.Data.SignFlowDescription)
if err != nil {
return fmt.Errorf("合同签署失败: %s", err.Error())
}
reason = "合同签署拒签"
} else if detail.Data.SignFlowStatus == 5 {
err = cert.ContractExpiration()
if err != nil {
return fmt.Errorf("合同签署过期失败: %s", err.Error())
}
reason = "合同签署过期"
} else {
reason = "合同签署中"
}
err = s.aggregateService.SaveCertification(ctx, cert)
if err != nil {
return fmt.Errorf("保存认证信息失败: %s", err.Error())
}
return nil
})
if err != nil {
return "", err
}
return reason, nil
}
// handleContractAfterSignComplete 处理签署完成后的合同
func (s *CertificationApplicationServiceImpl) handleContractAfterSignComplete(ctx context.Context, cert *entities.Certification) error {
// 获取用户的企业信息
user, err := s.userAggregateService.LoadUser(ctx, cert.UserID)
if err != nil {
return fmt.Errorf("加载用户信息失败: %w", err)
}
if user.EnterpriseInfo == nil {
return fmt.Errorf("用户企业信息不存在")
}
// 1. 获取所有已签署合同文件信息
downloadSignedFileResponse, err := s.esignClient.DownloadSignedFile(cert.EsignFlowID)
if err != nil {
return fmt.Errorf("下载已签署文件失败: %s", err.Error())
}
files := downloadSignedFileResponse.Data.Files
if len(files) == 0 {
return fmt.Errorf("未获取到已签署合同文件")
}
for _, file := range files {
fileUrl := file.DownloadUrl
fileName := file.FileName
fileId := file.FileId
s.logger.Info("下载已签署文件准备", zap.String("file_url", fileUrl), zap.String("file_name", fileName))
// 2. 下载文件内容
fileBytes, err := s.downloadFileContent(ctx, fileUrl)
if err != nil {
s.logger.Error("下载合同文件内容失败", zap.String("file_name", fileName), zap.Error(err))
continue
}
// 3. 上传到七牛云
uploadResult, err := s.qiniuStorageService.UploadFile(ctx, fileBytes, fileName)
if err != nil {
s.logger.Error("上传合同文件到七牛云失败", zap.String("file_name", fileName), zap.Error(err))
continue
}
qiniuURL := uploadResult.URL
s.logger.Info("合同文件已上传七牛云", zap.String("file_name", fileName), zap.String("qiniu_url", qiniuURL))
// 4. 保存到合同聚合根
_, err = s.contractAggregateService.CreateContract(
ctx,
user.EnterpriseInfo.ID,
cert.UserID,
fileName,
user_entities.ContractTypeCooperation,
fileId,
qiniuURL,
)
if err != nil {
s.logger.Error("保存合同信息到聚合根失败", zap.String("file_name", fileName), zap.Error(err))
continue
}
s.logger.Info("合同信息已保存到聚合根", zap.String("file_name", fileName), zap.String("qiniu_url", qiniuURL))
}
_, err = s.walletAggregateService.CreateWallet(ctx, cert.UserID)
if err != nil {
s.logger.Error("创建钱包失败", zap.String("user_id", cert.UserID), zap.Error(err))
}
// 6. 创建API用户
err = s.apiUserAggregateService.CreateApiUser(ctx, cert.UserID)
if err != nil {
s.logger.Error("创建API用户失败", zap.String("user_id", cert.UserID), zap.Error(err))
}
err = s.userAggregateService.CompleteCertification(ctx, cert.UserID)
if err != nil {
s.logger.Error("用户域完成认证失败", zap.String("user_id", cert.UserID), zap.Error(err))
}
return nil
}
// downloadFileContent 通过URL下载文件内容
func (s *CertificationApplicationServiceImpl) downloadFileContent(ctx context.Context, fileUrl string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fileUrl, nil)
if err != nil {
return nil, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("下载失败,状态码: %d", resp.StatusCode)
}
return io.ReadAll(resp.Body)
}