package ai import ( "time" "gorm.io/gorm" "eaisalestrain/backend/internal/model" "eaisalestrain/backend/internal/store" ) // ────────────────────────────────────────────── // 按用户算力点计费(对齐 pj034 router.py 的 compute_credits / log_ai_call) // ────────────────────────────────────────────── // AI 能力常量(pj034 AiCapability 的精简子集) const ( CapabilityAIChat = "ai_chat" // PathCoach 对话(每轮扣 1 点) CapabilityTextGen = "text_gen" // 快捷动作(情景演练/查佣金/产品对比,扣 1 点) CapabilityEmbed = "embed" // 知识检索内部 embedding(不扣点,仅审计) CapabilityEssayGrade = "essay_grade" // 简答题 LLM 评分(系统自动,不扣点,仅审计) ) // CapabilityCredits 各能力扣点成本 var CapabilityCredits = map[string]int{ CapabilityAIChat: 1, CapabilityTextGen: 1, CapabilityEmbed: 0, CapabilityEssayGrade: 0, } // ComputeCredits 扣点决策:仅「成功」才扣点;失败不扣。 func ComputeCredits(capability string, success bool) int { if !success { return 0 } return CapabilityCredits[capability] } // LogEntry 一次 AI 调用的审计入参 type LogEntry struct { UserID uint Capability string Provider string RouteID string Model string TokensInput int TokensOutput int Success bool ErrorMessage string LatencyMs int } // LogCall 写 ai_call_log;成功且需扣点时从用户余额扣点。审计写入失败不阻断主流程。 func LogCall(e LogEntry) { credits := ComputeCredits(e.Capability, e.Success) status := "success" if !e.Success { status = "failed" } rec := model.AiCallLog{ UserID: e.UserID, Capability: e.Capability, Provider: e.Provider, RouteID: e.RouteID, Model: e.Model, TokensInput: e.TokensInput, TokensOutput: e.TokensOutput, CreditsCharged: credits, Status: status, ErrorMessage: e.ErrorMessage, LatencyMs: e.LatencyMs, CreatedAt: time.Now(), } if err := store.DB.Create(&rec).Error; err != nil { return } if credits > 0 { store.DB.Model(&model.User{}).Where("id = ?", e.UserID). UpdateColumn("ai_points", gorm.Expr("ai_points - ?", credits)) } }