Files
tyapi-server/internal/infrastructure/task/handlers/api_task_handler.go

285 lines
9.9 KiB
Go
Raw Normal View History

2025-09-12 01:15:09 +08:00
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))
}
}