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))
|
||
}
|
||
} |