Files
tyapi-server/internal/application/certification/certification_application_service_impl.go
2025-07-21 15:13:26 +08:00

732 lines
23 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"
"tyapi-server/internal/application/certification/dto/commands"
"tyapi-server/internal/application/certification/dto/queries"
"tyapi-server/internal/application/certification/dto/responses"
"tyapi-server/internal/domains/certification/entities"
"tyapi-server/internal/domains/certification/enums"
"tyapi-server/internal/domains/certification/repositories"
"tyapi-server/internal/domains/certification/services"
"tyapi-server/internal/domains/certification/services/state_machine"
"go.uber.org/zap"
)
// CertificationApplicationServiceImpl 认证应用服务实现
// 负责用例协调DTO转换是应用层的核心组件
type CertificationApplicationServiceImpl struct {
// 领域服务依赖
aggregateService services.CertificationAggregateService
workflowOrchestrator services.CertificationWorkflowOrchestrator
// 仓储依赖
queryRepository repositories.CertificationQueryRepository
// 基础设施依赖
callbackHandler *state_machine.EsignCallbackHandler
logger *zap.Logger
}
// NewCertificationApplicationService 创建认证应用服务
func NewCertificationApplicationService(
aggregateService services.CertificationAggregateService,
workflowOrchestrator services.CertificationWorkflowOrchestrator,
queryRepository repositories.CertificationQueryRepository,
callbackHandler *state_machine.EsignCallbackHandler,
logger *zap.Logger,
) CertificationApplicationService {
return &CertificationApplicationServiceImpl{
aggregateService: aggregateService,
workflowOrchestrator: workflowOrchestrator,
queryRepository: queryRepository,
callbackHandler: callbackHandler,
logger: logger,
}
}
// ================ 用户操作用例 ================
// CreateCertification 创建认证申请
func (s *CertificationApplicationServiceImpl) CreateCertification(
ctx context.Context,
cmd *commands.CreateCertificationCommand,
) (*responses.CertificationResponse, error) {
s.logger.Info("开始创建认证申请", zap.String("user_id", cmd.UserID))
// 1. 调用聚合服务创建认证
cert, err := s.aggregateService.CreateCertification(ctx, cmd.UserID)
if err != nil {
s.logger.Error("创建认证申请失败", zap.Error(err), zap.String("user_id", cmd.UserID))
return nil, fmt.Errorf("创建认证申请失败: %w", err)
}
// 2. 转换为响应DTO
response := s.convertToResponse(cert)
s.logger.Info("认证申请创建成功",
zap.String("user_id", cmd.UserID),
zap.String("certification_id", cert.ID))
return response, nil
}
// SubmitEnterpriseInfo 提交企业信息
func (s *CertificationApplicationServiceImpl) SubmitEnterpriseInfo(
ctx context.Context,
cmd *commands.SubmitEnterpriseInfoCommand,
) (*responses.CertificationResponse, error) {
s.logger.Info("开始提交企业信息",
zap.String("certification_id", cmd.CertificationID),
zap.String("user_id", cmd.UserID))
// 1. 构建工作流命令
workflowCmd := &services.SubmitEnterpriseInfoCommand{
CertificationID: cmd.CertificationID,
UserID: cmd.UserID,
EnterpriseInfo: cmd.EnterpriseInfo,
}
// 2. 执行工作流
workflowResult, err := s.workflowOrchestrator.SubmitEnterpriseInfo(ctx, workflowCmd)
if err != nil {
s.logger.Error("提交企业信息失败", zap.Error(err))
return nil, fmt.Errorf("提交企业信息失败: %w", err)
}
// 3. 加载最新的认证信息
cert, err := s.aggregateService.LoadCertification(ctx, cmd.CertificationID)
if err != nil {
s.logger.Error("加载认证信息失败", zap.Error(err))
return nil, fmt.Errorf("加载认证信息失败: %w", err)
}
// 4. 转换为响应DTO
response := s.convertToResponse(cert)
// 5. 添加工作流结果信息
if workflowResult.Data != nil {
response.Metadata = workflowResult.Data
}
s.logger.Info("企业信息提交成功", zap.String("certification_id", cmd.CertificationID))
return response, nil
}
// ApplyContract 申请合同签署
func (s *CertificationApplicationServiceImpl) ApplyContract(
ctx context.Context,
cmd *commands.ApplyContractCommand,
) (*responses.ContractSignUrlResponse, error) {
s.logger.Info("开始申请合同签署",
zap.String("certification_id", cmd.CertificationID),
zap.String("user_id", cmd.UserID))
// 1. 构建工作流命令
workflowCmd := &services.ApplyContractCommand{
CertificationID: cmd.CertificationID,
UserID: cmd.UserID,
}
// 2. 执行工作流
workflowResult, err := s.workflowOrchestrator.ApplyContract(ctx, workflowCmd)
if err != nil {
s.logger.Error("申请合同签署失败", zap.Error(err))
return nil, fmt.Errorf("申请合同签署失败: %w", err)
}
// 3. 从工作流结果提取签署URL信息
signURL, _ := workflowResult.Data["contract_sign_url"].(string)
contractURL, _ := workflowResult.Data["contract_url"].(string)
nextAction, _ := workflowResult.Data["next_action"].(string)
// 4. 构建响应
response := responses.NewContractSignUrlResponse(
cmd.CertificationID,
signURL,
contractURL,
nextAction,
workflowResult.Message,
)
s.logger.Info("合同申请成功", zap.String("certification_id", cmd.CertificationID))
return response, nil
}
// RetryOperation 重试失败操作
func (s *CertificationApplicationServiceImpl) RetryOperation(
ctx context.Context,
cmd *commands.RetryOperationCommand,
) (*responses.CertificationResponse, error) {
s.logger.Info("开始重试操作",
zap.String("certification_id", cmd.CertificationID),
zap.String("operation", cmd.Operation))
// 1. 执行重试工作流
workflowResult, err := s.workflowOrchestrator.RetryOperation(ctx, cmd.CertificationID, cmd.Operation)
if err != nil {
s.logger.Error("重试操作失败", zap.Error(err))
return nil, fmt.Errorf("重试操作失败: %w", err)
}
// 2. 加载最新的认证信息
cert, err := s.aggregateService.LoadCertification(ctx, cmd.CertificationID)
if err != nil {
s.logger.Error("加载认证信息失败", zap.Error(err))
return nil, fmt.Errorf("加载认证信息失败: %w", err)
}
// 3. 转换为响应DTO
response := s.convertToResponse(cert)
// 4. 添加重试结果信息
if workflowResult.Data != nil {
response.Metadata = workflowResult.Data
}
s.logger.Info("重试操作成功", zap.String("certification_id", cmd.CertificationID))
return response, nil
}
// ================ 查询用例 ================
// GetCertification 获取认证详情
func (s *CertificationApplicationServiceImpl) GetCertification(
ctx context.Context,
query *queries.GetCertificationQuery,
) (*responses.CertificationResponse, error) {
s.logger.Debug("获取认证详情", zap.String("certification_id", query.CertificationID))
// 1. 从查询仓储获取认证信息
cert, err := s.queryRepository.GetByID(ctx, query.CertificationID)
if err != nil {
s.logger.Error("获取认证信息失败", zap.Error(err))
return nil, fmt.Errorf("认证信息不存在: %w", err)
}
// 2. 权限验证如果提供了用户ID
if query.UserID != "" && cert.UserID != query.UserID {
return nil, fmt.Errorf("无权限访问此认证信息")
}
// 3. 转换为响应DTO
response := s.convertToResponse(cert)
return response, nil
}
// GetUserCertifications 获取用户认证列表
func (s *CertificationApplicationServiceImpl) GetUserCertifications(
ctx context.Context,
query *queries.GetUserCertificationsQuery,
) (*responses.CertificationListResponse, error) {
s.logger.Debug("获取用户认证列表", zap.String("user_id", query.UserID))
// 1. 转换为通用列表查询对象
domainQuery := &queries.ListCertificationsQuery{
Page: query.Page,
PageSize: query.PageSize,
UserID: query.UserID,
Status: query.Status,
}
// 根据包含选项设置状态过滤
if !query.IncludeCompleted && !query.IncludeFailed {
// 只显示进行中的认证
domainQuery.Statuses = []enums.CertificationStatus{
enums.StatusPending,
enums.StatusInfoSubmitted,
enums.StatusEnterpriseVerified,
enums.StatusContractApplied,
}
}
// 转换为领域查询对象
listQuery := domainQuery.ToDomainQuery()
// 2. 执行查询
certs, total, err := s.queryRepository.List(ctx, listQuery)
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
}
// 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
}
// SearchCertifications 搜索认证
func (s *CertificationApplicationServiceImpl) SearchCertifications(
ctx context.Context,
query *queries.SearchCertificationsQuery,
) (*responses.CertificationListResponse, error) {
s.logger.Debug("搜索认证", zap.String("keyword", query.Keyword))
// 1. 转换为领域查询对象
domainQuery := query.ToDomainQuery()
// 2. 根据搜索字段选择不同的搜索方法
var certs []*entities.Certification
var total int64
var err error
if len(domainQuery.SearchFields) == 1 {
switch domainQuery.SearchFields[0] {
case "company_name":
certs, err = s.queryRepository.SearchByCompanyName(ctx, domainQuery.Keyword, domainQuery.GetLimit())
total = int64(len(certs)) // 简化实现,实际应该有单独的计数方法
case "legal_person_name":
certs, err = s.queryRepository.SearchByLegalPerson(ctx, domainQuery.Keyword, domainQuery.GetLimit())
total = int64(len(certs))
default:
// 通用搜索,这里简化为按公司名搜索
certs, err = s.queryRepository.SearchByCompanyName(ctx, domainQuery.Keyword, domainQuery.GetLimit())
total = int64(len(certs))
}
} else {
// 多字段搜索,这里简化为按公司名搜索
certs, err = s.queryRepository.SearchByCompanyName(ctx, domainQuery.Keyword, domainQuery.GetLimit())
total = int64(len(certs))
}
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
}
// GetCertificationStatistics 获取认证统计
func (s *CertificationApplicationServiceImpl) GetCertificationStatistics(
ctx context.Context,
query *queries.GetCertificationStatisticsQuery,
) (*responses.CertificationStatisticsResponse, error) {
s.logger.Debug("获取认证统计", zap.String("period", query.Period))
// 1. 转换为领域查询对象
domainQuery := query.ToDomainQuery()
// 2. 验证查询参数
if err := domainQuery.Validate(); err != nil {
return nil, fmt.Errorf("统计查询参数无效: %w", err)
}
// 3. 确定时间周期
var period repositories.CertificationTimePeriod
switch query.Period {
case "daily":
period = repositories.PeriodDaily
case "weekly":
period = repositories.PeriodWeekly
case "monthly":
period = repositories.PeriodMonthly
case "yearly":
period = repositories.PeriodYearly
default:
period = repositories.PeriodDaily
}
// 4. 获取统计数据
stats, err := s.queryRepository.GetStatistics(ctx, period)
if err != nil {
s.logger.Error("获取认证统计失败", zap.Error(err))
return nil, fmt.Errorf("获取认证统计失败: %w", err)
}
// 5. 获取进度统计(如果需要)
var progressStats *repositories.CertificationProgressStats
if query.IncludeProgressStats {
progressStats, err = s.queryRepository.GetProgressStatistics(ctx)
if err != nil {
s.logger.Warn("获取进度统计失败", zap.Error(err))
}
}
// 6. 构建响应
response := &responses.CertificationStatisticsResponse{
Period: query.Period,
TimeRange: domainQuery.GetTimeRange(),
Statistics: stats,
ProgressStats: progressStats,
GeneratedAt: domainQuery.StartDate,
Charts: s.generateChartsData(stats, progressStats),
}
return response, nil
}
// ================ e签宝回调处理 ================
// HandleEsignCallback 处理e签宝回调
func (s *CertificationApplicationServiceImpl) HandleEsignCallback(
ctx context.Context,
cmd *commands.EsignCallbackCommand,
) (*responses.CallbackResponse, error) {
s.logger.Info("开始处理e签宝回调",
zap.String("certification_id", cmd.CertificationID),
zap.String("callback_type", cmd.CallbackType))
// 1. 解析回调数据
callbackData, err := s.callbackHandler.ParseCallbackData(cmd.RawData)
if err != nil {
s.logger.Error("解析回调数据失败", zap.Error(err))
return responses.NewCallbackResponse(false, cmd.CertificationID, cmd.CallbackType,
fmt.Sprintf("解析回调数据失败: %s", err.Error())), err
}
// 2. 构建工作流回调命令
workflowCmd := &services.EsignCallbackCommand{
CertificationID: cmd.CertificationID,
CallbackType: cmd.CallbackType,
CallbackData: callbackData,
}
// 3. 根据回调类型分发处理
var workflowResult *services.WorkflowResult
switch cmd.CallbackType {
case "auth_result":
workflowResult, err = s.workflowOrchestrator.HandleEnterpriseVerificationCallback(ctx, workflowCmd)
case "sign_result":
workflowResult, err = s.workflowOrchestrator.HandleContractSignCallback(ctx, workflowCmd)
default:
err = fmt.Errorf("不支持的回调类型: %s", cmd.CallbackType)
}
if err != nil {
s.logger.Error("处理回调失败", zap.Error(err))
return responses.NewCallbackResponse(false, cmd.CertificationID, cmd.CallbackType,
fmt.Sprintf("处理回调失败: %s", err.Error())), err
}
// 4. 构建成功响应
response := responses.NewCallbackResponse(true, cmd.CertificationID, cmd.CallbackType, workflowResult.Message)
// 5. 设置状态转换信息
if workflowResult.StateTransition != nil {
response.StateTransition = workflowResult.StateTransition
response.OldStatus = workflowResult.StateTransition.OldStatus
response.NewStatus = workflowResult.StateTransition.NewStatus
}
s.logger.Info("e签宝回调处理成功", zap.String("certification_id", cmd.CertificationID))
return response, nil
}
// ================ 管理员操作 ================
// ForceTransitionStatus 强制状态转换(管理员)
func (s *CertificationApplicationServiceImpl) ForceTransitionStatus(
ctx context.Context,
cmd *commands.ForceTransitionStatusCommand,
) (*responses.CertificationResponse, error) {
s.logger.Info("开始强制状态转换",
zap.String("certification_id", cmd.CertificationID),
zap.String("admin_id", cmd.AdminID),
zap.String("target_status", string(cmd.TargetStatus)))
// 1. 权限验证(这里简化,实际应该有更复杂的权限验证)
// TODO: 实现管理员权限验证逻辑
// 2. 执行状态转换
result, err := s.aggregateService.TransitionState(
ctx,
cmd.CertificationID,
cmd.TargetStatus,
enums.ActorTypeAdmin,
cmd.AdminID,
cmd.Reason,
map[string]interface{}{
"force": cmd.Force,
},
)
if err != nil {
s.logger.Error("强制状态转换失败", zap.Error(err))
return nil, fmt.Errorf("强制状态转换失败: %w", err)
}
// 3. 加载最新的认证信息
cert, err := s.aggregateService.LoadCertification(ctx, cmd.CertificationID)
if err != nil {
s.logger.Error("加载认证信息失败", zap.Error(err))
return nil, fmt.Errorf("加载认证信息失败: %w", err)
}
// 4. 转换为响应DTO
response := s.convertToResponse(cert)
// 5. 添加状态转换信息
if result != nil {
response.Metadata = map[string]interface{}{
"state_transition": result,
"admin_operation": true,
}
}
s.logger.Info("强制状态转换成功", zap.String("certification_id", cmd.CertificationID))
return response, nil
}
// GetSystemMonitoring 获取系统监控数据
func (s *CertificationApplicationServiceImpl) GetSystemMonitoring(
ctx context.Context,
query *queries.GetSystemMonitoringQuery,
) (*responses.SystemMonitoringResponse, error) {
s.logger.Debug("获取系统监控数据", zap.String("time_range", query.TimeRange))
// 1. 获取基础统计数据
stats, err := s.queryRepository.GetStatistics(ctx, repositories.PeriodDaily)
if err != nil {
s.logger.Error("获取基础统计失败", zap.Error(err))
return nil, fmt.Errorf("获取系统监控数据失败: %w", err)
}
// 2. 构建监控指标
metrics := make(map[string]interface{})
if query.ShouldIncludeMetric("certification_count") {
metrics["certification_count"] = stats.TotalCertifications
}
if query.ShouldIncludeMetric("success_rate") {
metrics["success_rate"] = stats.SuccessRate
}
if query.ShouldIncludeMetric("failure_rate") {
metrics["failure_rate"] = 1.0 - stats.SuccessRate
}
if query.ShouldIncludeMetric("avg_processing_time") {
metrics["avg_processing_time"] = stats.AvgProcessingTime.String()
}
if query.ShouldIncludeMetric("status_distribution") {
metrics["status_distribution"] = stats.StatusDistribution
}
// 3. 生成系统警告
alerts := s.generateSystemAlerts(stats)
// 4. 评估系统健康状态
systemHealth := s.evaluateSystemHealth(stats, alerts)
// 5. 构建响应
response := &responses.SystemMonitoringResponse{
TimeRange: query.TimeRange,
Metrics: metrics,
Alerts: alerts,
SystemHealth: systemHealth,
LastUpdatedAt: stats.StartDate,
}
return response, 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,
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
}
// generateChartsData 生成图表数据
func (s *CertificationApplicationServiceImpl) generateChartsData(
stats *repositories.CertificationStatistics,
progressStats *repositories.CertificationProgressStats,
) map[string]interface{} {
charts := make(map[string]interface{})
// 状态分布饼图
if stats.StatusDistribution != nil {
statusChart := make(map[string]interface{})
statusChart["type"] = "pie"
statusChart["data"] = stats.StatusDistribution
charts["status_distribution"] = statusChart
}
// 失败原因分布
if stats.FailureDistribution != nil {
failureChart := make(map[string]interface{})
failureChart["type"] = "bar"
failureChart["data"] = stats.FailureDistribution
charts["failure_distribution"] = failureChart
}
// 进度分布
if progressStats != nil && progressStats.ProgressDistribution != nil {
progressChart := make(map[string]interface{})
progressChart["type"] = "histogram"
progressChart["data"] = progressStats.ProgressDistribution
charts["progress_distribution"] = progressChart
}
return charts
}
// generateSystemAlerts 生成系统警告
func (s *CertificationApplicationServiceImpl) generateSystemAlerts(stats *repositories.CertificationStatistics) []responses.SystemAlert {
var alerts []responses.SystemAlert
// 成功率警告
if stats.SuccessRate < 0.8 {
level := "warning"
if stats.SuccessRate < 0.6 {
level = "critical"
}
alert := responses.SystemAlert{
Level: level,
Type: "success_rate_low",
Message: fmt.Sprintf("认证成功率过低:%.1f%%", stats.SuccessRate*100),
Metric: "success_rate",
Value: stats.SuccessRate,
Threshold: 0.8,
CreatedAt: stats.StartDate,
}
alerts = append(alerts, alert)
}
// 处理时间警告
if stats.AvgProcessingTime.Hours() > 24 {
alert := responses.SystemAlert{
Level: "warning",
Type: "processing_time_high",
Message: fmt.Sprintf("平均处理时间过长:%.1f小时", stats.AvgProcessingTime.Hours()),
Metric: "avg_processing_time",
Value: stats.AvgProcessingTime.String(),
Threshold: "24h",
CreatedAt: stats.StartDate,
}
alerts = append(alerts, alert)
}
return alerts
}
// evaluateSystemHealth 评估系统健康状态
func (s *CertificationApplicationServiceImpl) evaluateSystemHealth(
stats *repositories.CertificationStatistics,
alerts []responses.SystemAlert,
) responses.SystemHealthStatus {
overall := "healthy"
components := map[string]string{
"certification_service": "healthy",
"esign_integration": "healthy",
"database": "healthy",
"state_machine": "healthy",
}
// 根据警告判断健康状态
for _, alert := range alerts {
if alert.Level == "critical" {
overall = "critical"
break
} else if alert.Level == "warning" && overall == "healthy" {
overall = "warning"
}
}
// 根据统计数据评估组件状态
if stats.SuccessRate < 0.6 {
components["certification_service"] = "critical"
components["esign_integration"] = "warning"
} else if stats.SuccessRate < 0.8 {
components["certification_service"] = "warning"
}
return responses.SystemHealthStatus{
Overall: overall,
Components: components,
LastCheck: stats.StartDate,
Details: map[string]interface{}{
"total_certifications": stats.TotalCertifications,
"success_rate": stats.SuccessRate,
"avg_processing_time": stats.AvgProcessingTime.String(),
},
}
}