285 lines
9.9 KiB
Go
285 lines
9.9 KiB
Go
|
|
package handlers
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"context"
|
|||
|
|
"encoding/json"
|
|||
|
|
"time"
|
|||
|
|
|
|||
|
|
"github.com/hibiken/asynq"
|
|||
|
|
"github.com/shopspring/decimal"
|
|||
|
|
"go.uber.org/zap"
|
|||
|
|
|
|||
|
|
"tyapi-server/internal/application/api"
|
|||
|
|
finance_services "tyapi-server/internal/domains/finance/services"
|
|||
|
|
product_services "tyapi-server/internal/domains/product/services"
|
|||
|
|
"tyapi-server/internal/infrastructure/task/entities"
|
|||
|
|
"tyapi-server/internal/infrastructure/task/repositories"
|
|||
|
|
"tyapi-server/internal/infrastructure/task/types"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
// ApiTaskHandler API任务处理器
|
|||
|
|
type ApiTaskHandler struct {
|
|||
|
|
logger *zap.Logger
|
|||
|
|
apiApplicationService api.ApiApplicationService
|
|||
|
|
walletService finance_services.WalletAggregateService
|
|||
|
|
subscriptionService *product_services.ProductSubscriptionService
|
|||
|
|
asyncTaskRepo repositories.AsyncTaskRepository
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewApiTaskHandler 创建API任务处理器
|
|||
|
|
func NewApiTaskHandler(
|
|||
|
|
logger *zap.Logger,
|
|||
|
|
apiApplicationService api.ApiApplicationService,
|
|||
|
|
walletService finance_services.WalletAggregateService,
|
|||
|
|
subscriptionService *product_services.ProductSubscriptionService,
|
|||
|
|
asyncTaskRepo repositories.AsyncTaskRepository,
|
|||
|
|
) *ApiTaskHandler {
|
|||
|
|
return &ApiTaskHandler{
|
|||
|
|
logger: logger,
|
|||
|
|
apiApplicationService: apiApplicationService,
|
|||
|
|
walletService: walletService,
|
|||
|
|
subscriptionService: subscriptionService,
|
|||
|
|
asyncTaskRepo: asyncTaskRepo,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// HandleApiCall 处理API调用任务
|
|||
|
|
func (h *ApiTaskHandler) HandleApiCall(ctx context.Context, t *asynq.Task) error {
|
|||
|
|
h.logger.Info("开始处理API调用任务")
|
|||
|
|
|
|||
|
|
var payload types.ApiCallPayload
|
|||
|
|
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
|||
|
|
h.logger.Error("解析API调用任务载荷失败", zap.Error(err))
|
|||
|
|
h.updateTaskStatus(ctx, t, "failed", "解析任务载荷失败")
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h.logger.Info("处理API调用任务",
|
|||
|
|
zap.String("api_call_id", payload.ApiCallID),
|
|||
|
|
zap.String("user_id", payload.UserID),
|
|||
|
|
zap.String("product_id", payload.ProductID))
|
|||
|
|
|
|||
|
|
// 这里实现API调用的具体逻辑
|
|||
|
|
// 例如:记录API调用、更新使用统计等
|
|||
|
|
|
|||
|
|
// 更新任务状态为成功
|
|||
|
|
h.updateTaskStatus(ctx, t, "completed", "")
|
|||
|
|
h.logger.Info("API调用任务处理完成", zap.String("api_call_id", payload.ApiCallID))
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// HandleDeduction 处理扣款任务
|
|||
|
|
func (h *ApiTaskHandler) HandleDeduction(ctx context.Context, t *asynq.Task) error {
|
|||
|
|
h.logger.Info("开始处理扣款任务")
|
|||
|
|
|
|||
|
|
var payload types.DeductionPayload
|
|||
|
|
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
|||
|
|
h.logger.Error("解析扣款任务载荷失败", zap.Error(err))
|
|||
|
|
h.updateTaskStatus(ctx, t, "failed", "解析任务载荷失败")
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h.logger.Info("处理扣款任务",
|
|||
|
|
zap.String("user_id", payload.UserID),
|
|||
|
|
zap.String("amount", payload.Amount),
|
|||
|
|
zap.String("transaction_id", payload.TransactionID))
|
|||
|
|
|
|||
|
|
// 调用钱包服务进行扣款
|
|||
|
|
if h.walletService != nil {
|
|||
|
|
amount, err := decimal.NewFromString(payload.Amount)
|
|||
|
|
if err != nil {
|
|||
|
|
h.logger.Error("金额格式错误", zap.Error(err))
|
|||
|
|
h.updateTaskStatus(ctx, t, "failed", "金额格式错误")
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if err := h.walletService.Deduct(ctx, payload.UserID, amount, payload.ApiCallID, payload.TransactionID, payload.ProductID); err != nil {
|
|||
|
|
h.logger.Error("扣款处理失败", zap.Error(err))
|
|||
|
|
h.updateTaskStatus(ctx, t, "failed", "扣款处理失败: "+err.Error())
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
h.logger.Warn("钱包服务未初始化,跳过扣款", zap.String("user_id", payload.UserID))
|
|||
|
|
h.updateTaskStatus(ctx, t, "failed", "钱包服务未初始化")
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新任务状态为成功
|
|||
|
|
h.updateTaskStatus(ctx, t, "completed", "")
|
|||
|
|
h.logger.Info("扣款任务处理完成", zap.String("transaction_id", payload.TransactionID))
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// HandleCompensation 处理补偿任务
|
|||
|
|
func (h *ApiTaskHandler) HandleCompensation(ctx context.Context, t *asynq.Task) error {
|
|||
|
|
h.logger.Info("开始处理补偿任务")
|
|||
|
|
|
|||
|
|
var payload types.CompensationPayload
|
|||
|
|
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
|||
|
|
h.logger.Error("解析补偿任务载荷失败", zap.Error(err))
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h.logger.Info("处理补偿任务",
|
|||
|
|
zap.String("transaction_id", payload.TransactionID),
|
|||
|
|
zap.String("type", payload.Type))
|
|||
|
|
|
|||
|
|
// 这里实现补偿的具体逻辑
|
|||
|
|
// 例如:调用钱包服务进行退款等
|
|||
|
|
|
|||
|
|
h.logger.Info("补偿任务处理完成", zap.String("transaction_id", payload.TransactionID))
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// HandleUsageStats 处理使用统计任务
|
|||
|
|
func (h *ApiTaskHandler) HandleUsageStats(ctx context.Context, t *asynq.Task) error {
|
|||
|
|
h.logger.Info("开始处理使用统计任务")
|
|||
|
|
|
|||
|
|
var payload types.UsageStatsPayload
|
|||
|
|
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
|||
|
|
h.logger.Error("解析使用统计任务载荷失败", zap.Error(err))
|
|||
|
|
h.updateTaskStatus(ctx, t, "failed", "解析任务载荷失败")
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h.logger.Info("处理使用统计任务",
|
|||
|
|
zap.String("subscription_id", payload.SubscriptionID),
|
|||
|
|
zap.String("user_id", payload.UserID),
|
|||
|
|
zap.Int("increment", payload.Increment))
|
|||
|
|
|
|||
|
|
// 调用订阅服务更新使用统计
|
|||
|
|
if h.subscriptionService != nil {
|
|||
|
|
if err := h.subscriptionService.IncrementSubscriptionAPIUsage(ctx, payload.SubscriptionID, int64(payload.Increment)); err != nil {
|
|||
|
|
h.logger.Error("更新使用统计失败", zap.Error(err))
|
|||
|
|
h.updateTaskStatus(ctx, t, "failed", "更新使用统计失败: "+err.Error())
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
h.logger.Warn("订阅服务未初始化,跳过使用统计更新", zap.String("subscription_id", payload.SubscriptionID))
|
|||
|
|
h.updateTaskStatus(ctx, t, "failed", "订阅服务未初始化")
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 更新任务状态为成功
|
|||
|
|
h.updateTaskStatus(ctx, t, "completed", "")
|
|||
|
|
h.logger.Info("使用统计任务处理完成", zap.String("subscription_id", payload.SubscriptionID))
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// HandleApiLog 处理API日志任务
|
|||
|
|
func (h *ApiTaskHandler) HandleApiLog(ctx context.Context, t *asynq.Task) error {
|
|||
|
|
h.logger.Info("开始处理API日志任务")
|
|||
|
|
|
|||
|
|
var payload types.ApiLogPayload
|
|||
|
|
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
|||
|
|
h.logger.Error("解析API日志任务载荷失败", zap.Error(err))
|
|||
|
|
h.updateTaskStatus(ctx, t, "failed", "解析任务载荷失败")
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h.logger.Info("处理API日志任务",
|
|||
|
|
zap.String("transaction_id", payload.TransactionID),
|
|||
|
|
zap.String("user_id", payload.UserID),
|
|||
|
|
zap.String("api_name", payload.ApiName),
|
|||
|
|
zap.String("product_id", payload.ProductID))
|
|||
|
|
|
|||
|
|
// 记录结构化日志
|
|||
|
|
h.logger.Info("API调用日志",
|
|||
|
|
zap.String("transaction_id", payload.TransactionID),
|
|||
|
|
zap.String("user_id", payload.UserID),
|
|||
|
|
zap.String("api_name", payload.ApiName),
|
|||
|
|
zap.String("product_id", payload.ProductID),
|
|||
|
|
zap.Time("timestamp", time.Now()))
|
|||
|
|
|
|||
|
|
// 这里可以添加其他日志记录逻辑
|
|||
|
|
// 例如:写入专门的日志文件、发送到日志系统、写入数据库等
|
|||
|
|
|
|||
|
|
// 更新任务状态为成功
|
|||
|
|
h.updateTaskStatus(ctx, t, "completed", "")
|
|||
|
|
h.logger.Info("API日志任务处理完成", zap.String("transaction_id", payload.TransactionID))
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// updateTaskStatus 更新任务状态
|
|||
|
|
func (h *ApiTaskHandler) updateTaskStatus(ctx context.Context, t *asynq.Task, status string, errorMsg string) {
|
|||
|
|
// 从任务载荷中提取任务ID
|
|||
|
|
var payload map[string]interface{}
|
|||
|
|
if err := json.Unmarshal(t.Payload(), &payload); err != nil {
|
|||
|
|
h.logger.Error("解析任务载荷失败,无法更新状态", zap.Error(err))
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 尝试从payload中获取任务ID
|
|||
|
|
taskID, ok := payload["task_id"].(string)
|
|||
|
|
if !ok {
|
|||
|
|
h.logger.Error("无法从任务载荷中获取任务ID")
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 根据状态决定更新方式
|
|||
|
|
if status == "failed" {
|
|||
|
|
// 失败时:需要检查是否达到最大重试次数
|
|||
|
|
h.handleTaskFailure(ctx, taskID, errorMsg)
|
|||
|
|
} else if status == "completed" {
|
|||
|
|
// 成功时:清除错误信息并更新状态
|
|||
|
|
if err := h.asyncTaskRepo.UpdateStatusWithSuccess(ctx, taskID, entities.TaskStatus(status)); err != nil {
|
|||
|
|
h.logger.Error("更新任务状态失败",
|
|||
|
|
zap.String("task_id", taskID),
|
|||
|
|
zap.String("status", status),
|
|||
|
|
zap.Error(err))
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 其他状态:只更新状态
|
|||
|
|
if err := h.asyncTaskRepo.UpdateStatus(ctx, taskID, entities.TaskStatus(status)); err != nil {
|
|||
|
|
h.logger.Error("更新任务状态失败",
|
|||
|
|
zap.String("task_id", taskID),
|
|||
|
|
zap.String("status", status),
|
|||
|
|
zap.Error(err))
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
h.logger.Info("任务状态已更新",
|
|||
|
|
zap.String("task_id", taskID),
|
|||
|
|
zap.String("status", status),
|
|||
|
|
zap.String("error_msg", errorMsg))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleTaskFailure 处理任务失败
|
|||
|
|
func (h *ApiTaskHandler) handleTaskFailure(ctx context.Context, taskID string, errorMsg string) {
|
|||
|
|
// 获取当前任务信息
|
|||
|
|
task, err := h.asyncTaskRepo.GetByID(ctx, taskID)
|
|||
|
|
if err != nil {
|
|||
|
|
h.logger.Error("获取任务信息失败", zap.String("task_id", taskID), zap.Error(err))
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 增加重试次数
|
|||
|
|
newRetryCount := task.RetryCount + 1
|
|||
|
|
|
|||
|
|
// 检查是否达到最大重试次数
|
|||
|
|
if newRetryCount >= task.MaxRetries {
|
|||
|
|
// 达到最大重试次数,标记为最终失败
|
|||
|
|
if err := h.asyncTaskRepo.UpdateStatusWithRetryAndError(ctx, taskID, entities.TaskStatusFailed, errorMsg); err != nil {
|
|||
|
|
h.logger.Error("更新任务状态失败",
|
|||
|
|
zap.String("task_id", taskID),
|
|||
|
|
zap.String("status", "failed"),
|
|||
|
|
zap.Error(err))
|
|||
|
|
}
|
|||
|
|
h.logger.Info("任务最终失败,已达到最大重试次数",
|
|||
|
|
zap.String("task_id", taskID),
|
|||
|
|
zap.Int("retry_count", newRetryCount),
|
|||
|
|
zap.Int("max_retries", task.MaxRetries))
|
|||
|
|
} else {
|
|||
|
|
// 未达到最大重试次数,保持pending状态,记录错误信息
|
|||
|
|
if err := h.asyncTaskRepo.UpdateRetryCountAndError(ctx, taskID, newRetryCount, errorMsg); err != nil {
|
|||
|
|
h.logger.Error("更新任务重试次数失败",
|
|||
|
|
zap.String("task_id", taskID),
|
|||
|
|
zap.Int("retry_count", newRetryCount),
|
|||
|
|
zap.Error(err))
|
|||
|
|
}
|
|||
|
|
h.logger.Info("任务失败,准备重试",
|
|||
|
|
zap.String("task_id", taskID),
|
|||
|
|
zap.Int("retry_count", newRetryCount),
|
|||
|
|
zap.Int("max_retries", task.MaxRetries))
|
|||
|
|
}
|
|||
|
|
}
|