Browse Source
- Provider/Model/ApiKey: standard CRUD + special lookups - Conversation: user-scoped pagination - ChatMessage: conversation-ordered retrieval - UsageRecord: insert + user-scoped pagination - UserQuota: freeze/settle/unfreeze atomic operationsmaster
7 changed files with 495 additions and 0 deletions
@ -0,0 +1,87 @@ |
|||
package model |
|||
|
|||
import ( |
|||
"context" |
|||
"errors" |
|||
|
|||
"gorm.io/gorm" |
|||
) |
|||
|
|||
// AIApiKeyInsert 插入AI API密钥
|
|||
func AIApiKeyInsert(ctx context.Context, db *gorm.DB, apiKey *AIApiKey) (int64, error) { |
|||
result := db.WithContext(ctx).Create(apiKey) |
|||
if result.Error != nil { |
|||
return 0, result.Error |
|||
} |
|||
return apiKey.Id, nil |
|||
} |
|||
|
|||
// AIApiKeyFindOne 根据ID查询AI API密钥
|
|||
func AIApiKeyFindOne(ctx context.Context, db *gorm.DB, id int64) (*AIApiKey, error) { |
|||
var apiKey AIApiKey |
|||
result := db.WithContext(ctx).First(&apiKey, id) |
|||
if result.Error != nil { |
|||
if errors.Is(result.Error, gorm.ErrRecordNotFound) { |
|||
return nil, ErrNotFound |
|||
} |
|||
return nil, result.Error |
|||
} |
|||
return &apiKey, nil |
|||
} |
|||
|
|||
// AIApiKeyFindList 查询AI API密钥列表(分页)
|
|||
func AIApiKeyFindList(ctx context.Context, db *gorm.DB, page, pageSize int64) ([]AIApiKey, int64, error) { |
|||
var apiKeys []AIApiKey |
|||
var total int64 |
|||
|
|||
query := db.WithContext(ctx).Model(&AIApiKey{}) |
|||
|
|||
if err := query.Count(&total).Error; err != nil { |
|||
return nil, 0, err |
|||
} |
|||
|
|||
offset := (page - 1) * pageSize |
|||
if offset < 0 { |
|||
offset = 0 |
|||
} |
|||
err := query.Order("created_at DESC").Offset(int(offset)).Limit(int(pageSize)).Find(&apiKeys).Error |
|||
if err != nil { |
|||
return nil, 0, err |
|||
} |
|||
|
|||
return apiKeys, total, nil |
|||
} |
|||
|
|||
// AIApiKeyUpdate 更新AI API密钥
|
|||
func AIApiKeyUpdate(ctx context.Context, db *gorm.DB, apiKey *AIApiKey) error { |
|||
result := db.WithContext(ctx).Save(apiKey) |
|||
return result.Error |
|||
} |
|||
|
|||
// AIApiKeyDelete 删除AI API密钥
|
|||
func AIApiKeyDelete(ctx context.Context, db *gorm.DB, id int64) error { |
|||
result := db.WithContext(ctx).Delete(&AIApiKey{}, id) |
|||
return result.Error |
|||
} |
|||
|
|||
// AIApiKeyFindByProviderAndUser 根据供应商ID和用户ID查询API密钥
|
|||
func AIApiKeyFindByProviderAndUser(ctx context.Context, db *gorm.DB, providerId, userId int64) ([]AIApiKey, error) { |
|||
var apiKeys []AIApiKey |
|||
err := db.WithContext(ctx).Where("provider_id = ? AND user_id = ?", providerId, userId). |
|||
Order("created_at DESC").Find(&apiKeys).Error |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return apiKeys, nil |
|||
} |
|||
|
|||
// AIApiKeyFindSystemKeys 查询系统级API密钥(userId=0为系统密钥)
|
|||
func AIApiKeyFindSystemKeys(ctx context.Context, db *gorm.DB, providerId int64) ([]AIApiKey, error) { |
|||
var apiKeys []AIApiKey |
|||
err := db.WithContext(ctx).Where("provider_id = ? AND user_id = 0", providerId). |
|||
Order("created_at DESC").Find(&apiKeys).Error |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return apiKeys, nil |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
package model |
|||
|
|||
import ( |
|||
"context" |
|||
|
|||
"gorm.io/gorm" |
|||
) |
|||
|
|||
// AIChatMessageInsert 插入AI聊天消息
|
|||
func AIChatMessageInsert(ctx context.Context, db *gorm.DB, message *AIChatMessage) (int64, error) { |
|||
result := db.WithContext(ctx).Create(message) |
|||
if result.Error != nil { |
|||
return 0, result.Error |
|||
} |
|||
return message.Id, nil |
|||
} |
|||
|
|||
// AIChatMessageFindByConversation 根据对话ID查询所有消息(按创建时间升序,不分页)
|
|||
func AIChatMessageFindByConversation(ctx context.Context, db *gorm.DB, conversationId int64) ([]AIChatMessage, error) { |
|||
var messages []AIChatMessage |
|||
err := db.WithContext(ctx).Where("conversation_id = ?", conversationId). |
|||
Order("created_at ASC").Find(&messages).Error |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return messages, nil |
|||
} |
|||
@ -0,0 +1,65 @@ |
|||
package model |
|||
|
|||
import ( |
|||
"context" |
|||
"errors" |
|||
|
|||
"gorm.io/gorm" |
|||
) |
|||
|
|||
// AIConversationInsert 插入AI对话
|
|||
func AIConversationInsert(ctx context.Context, db *gorm.DB, conversation *AIConversation) (int64, error) { |
|||
result := db.WithContext(ctx).Create(conversation) |
|||
if result.Error != nil { |
|||
return 0, result.Error |
|||
} |
|||
return conversation.Id, nil |
|||
} |
|||
|
|||
// AIConversationFindOne 根据ID查询AI对话
|
|||
func AIConversationFindOne(ctx context.Context, db *gorm.DB, id int64) (*AIConversation, error) { |
|||
var conversation AIConversation |
|||
result := db.WithContext(ctx).First(&conversation, id) |
|||
if result.Error != nil { |
|||
if errors.Is(result.Error, gorm.ErrRecordNotFound) { |
|||
return nil, ErrNotFound |
|||
} |
|||
return nil, result.Error |
|||
} |
|||
return &conversation, nil |
|||
} |
|||
|
|||
// AIConversationUpdate 更新AI对话
|
|||
func AIConversationUpdate(ctx context.Context, db *gorm.DB, conversation *AIConversation) error { |
|||
result := db.WithContext(ctx).Save(conversation) |
|||
return result.Error |
|||
} |
|||
|
|||
// AIConversationDelete 删除AI对话
|
|||
func AIConversationDelete(ctx context.Context, db *gorm.DB, id int64) error { |
|||
result := db.WithContext(ctx).Delete(&AIConversation{}, id) |
|||
return result.Error |
|||
} |
|||
|
|||
// AIConversationFindByUser 根据用户ID查询对话列表(分页,按更新时间倒序)
|
|||
func AIConversationFindByUser(ctx context.Context, db *gorm.DB, userId int64, page, pageSize int64) ([]AIConversation, int64, error) { |
|||
var conversations []AIConversation |
|||
var total int64 |
|||
|
|||
query := db.WithContext(ctx).Model(&AIConversation{}).Where("user_id = ?", userId) |
|||
|
|||
if err := query.Count(&total).Error; err != nil { |
|||
return nil, 0, err |
|||
} |
|||
|
|||
offset := (page - 1) * pageSize |
|||
if offset < 0 { |
|||
offset = 0 |
|||
} |
|||
err := query.Order("updated_at DESC").Offset(int(offset)).Limit(int(pageSize)).Find(&conversations).Error |
|||
if err != nil { |
|||
return nil, 0, err |
|||
} |
|||
|
|||
return conversations, total, nil |
|||
} |
|||
@ -0,0 +1,98 @@ |
|||
package model |
|||
|
|||
import ( |
|||
"context" |
|||
"errors" |
|||
|
|||
"gorm.io/gorm" |
|||
) |
|||
|
|||
// AIModelInsert 插入AI模型
|
|||
func AIModelInsert(ctx context.Context, db *gorm.DB, aiModel *AIModel) (int64, error) { |
|||
result := db.WithContext(ctx).Create(aiModel) |
|||
if result.Error != nil { |
|||
return 0, result.Error |
|||
} |
|||
return aiModel.Id, nil |
|||
} |
|||
|
|||
// AIModelFindOne 根据ID查询AI模型
|
|||
func AIModelFindOne(ctx context.Context, db *gorm.DB, id int64) (*AIModel, error) { |
|||
var aiModel AIModel |
|||
result := db.WithContext(ctx).First(&aiModel, id) |
|||
if result.Error != nil { |
|||
if errors.Is(result.Error, gorm.ErrRecordNotFound) { |
|||
return nil, ErrNotFound |
|||
} |
|||
return nil, result.Error |
|||
} |
|||
return &aiModel, nil |
|||
} |
|||
|
|||
// AIModelFindList 查询AI模型列表(分页)
|
|||
func AIModelFindList(ctx context.Context, db *gorm.DB, page, pageSize int64) ([]AIModel, int64, error) { |
|||
var models []AIModel |
|||
var total int64 |
|||
|
|||
query := db.WithContext(ctx).Model(&AIModel{}) |
|||
|
|||
if err := query.Count(&total).Error; err != nil { |
|||
return nil, 0, err |
|||
} |
|||
|
|||
offset := (page - 1) * pageSize |
|||
if offset < 0 { |
|||
offset = 0 |
|||
} |
|||
err := query.Order("sort_order ASC, id ASC").Offset(int(offset)).Limit(int(pageSize)).Find(&models).Error |
|||
if err != nil { |
|||
return nil, 0, err |
|||
} |
|||
|
|||
return models, total, nil |
|||
} |
|||
|
|||
// AIModelUpdate 更新AI模型
|
|||
func AIModelUpdate(ctx context.Context, db *gorm.DB, aiModel *AIModel) error { |
|||
result := db.WithContext(ctx).Save(aiModel) |
|||
return result.Error |
|||
} |
|||
|
|||
// AIModelDelete 删除AI模型
|
|||
func AIModelDelete(ctx context.Context, db *gorm.DB, id int64) error { |
|||
result := db.WithContext(ctx).Delete(&AIModel{}, id) |
|||
return result.Error |
|||
} |
|||
|
|||
// AIModelFindByModelId 根据模型标识查询AI模型
|
|||
func AIModelFindByModelId(ctx context.Context, db *gorm.DB, modelId string) (*AIModel, error) { |
|||
var aiModel AIModel |
|||
result := db.WithContext(ctx).Where("model_id = ?", modelId).First(&aiModel) |
|||
if result.Error != nil { |
|||
if errors.Is(result.Error, gorm.ErrRecordNotFound) { |
|||
return nil, ErrNotFound |
|||
} |
|||
return nil, result.Error |
|||
} |
|||
return &aiModel, nil |
|||
} |
|||
|
|||
// AIModelFindByProvider 根据供应商ID查询AI模型列表
|
|||
func AIModelFindByProvider(ctx context.Context, db *gorm.DB, providerId int64) ([]AIModel, error) { |
|||
var models []AIModel |
|||
err := db.WithContext(ctx).Where("provider_id = ?", providerId).Order("sort_order ASC, id ASC").Find(&models).Error |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return models, nil |
|||
} |
|||
|
|||
// AIModelFindAllActive 查询所有启用的AI模型
|
|||
func AIModelFindAllActive(ctx context.Context, db *gorm.DB) ([]AIModel, error) { |
|||
var models []AIModel |
|||
err := db.WithContext(ctx).Where("is_active = ?", true).Order("sort_order ASC, id ASC").Find(&models).Error |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return models, nil |
|||
} |
|||
@ -0,0 +1,88 @@ |
|||
package model |
|||
|
|||
import ( |
|||
"context" |
|||
"errors" |
|||
|
|||
"gorm.io/gorm" |
|||
) |
|||
|
|||
// AIProviderInsert 插入AI供应商
|
|||
func AIProviderInsert(ctx context.Context, db *gorm.DB, provider *AIProvider) (int64, error) { |
|||
result := db.WithContext(ctx).Create(provider) |
|||
if result.Error != nil { |
|||
return 0, result.Error |
|||
} |
|||
return provider.Id, nil |
|||
} |
|||
|
|||
// AIProviderFindOne 根据ID查询AI供应商
|
|||
func AIProviderFindOne(ctx context.Context, db *gorm.DB, id int64) (*AIProvider, error) { |
|||
var provider AIProvider |
|||
result := db.WithContext(ctx).First(&provider, id) |
|||
if result.Error != nil { |
|||
if errors.Is(result.Error, gorm.ErrRecordNotFound) { |
|||
return nil, ErrNotFound |
|||
} |
|||
return nil, result.Error |
|||
} |
|||
return &provider, nil |
|||
} |
|||
|
|||
// AIProviderFindList 查询AI供应商列表(分页)
|
|||
func AIProviderFindList(ctx context.Context, db *gorm.DB, page, pageSize int64) ([]AIProvider, int64, error) { |
|||
var providers []AIProvider |
|||
var total int64 |
|||
|
|||
query := db.WithContext(ctx).Model(&AIProvider{}) |
|||
|
|||
if err := query.Count(&total).Error; err != nil { |
|||
return nil, 0, err |
|||
} |
|||
|
|||
offset := (page - 1) * pageSize |
|||
if offset < 0 { |
|||
offset = 0 |
|||
} |
|||
err := query.Order("sort_order ASC, id ASC").Offset(int(offset)).Limit(int(pageSize)).Find(&providers).Error |
|||
if err != nil { |
|||
return nil, 0, err |
|||
} |
|||
|
|||
return providers, total, nil |
|||
} |
|||
|
|||
// AIProviderUpdate 更新AI供应商
|
|||
func AIProviderUpdate(ctx context.Context, db *gorm.DB, provider *AIProvider) error { |
|||
result := db.WithContext(ctx).Save(provider) |
|||
return result.Error |
|||
} |
|||
|
|||
// AIProviderDelete 删除AI供应商
|
|||
func AIProviderDelete(ctx context.Context, db *gorm.DB, id int64) error { |
|||
result := db.WithContext(ctx).Delete(&AIProvider{}, id) |
|||
return result.Error |
|||
} |
|||
|
|||
// AIProviderFindByName 根据名称查询AI供应商
|
|||
func AIProviderFindByName(ctx context.Context, db *gorm.DB, name string) (*AIProvider, error) { |
|||
var provider AIProvider |
|||
result := db.WithContext(ctx).Where("name = ?", name).First(&provider) |
|||
if result.Error != nil { |
|||
if errors.Is(result.Error, gorm.ErrRecordNotFound) { |
|||
return nil, ErrNotFound |
|||
} |
|||
return nil, result.Error |
|||
} |
|||
return &provider, nil |
|||
} |
|||
|
|||
// AIProviderFindAllActive 查询所有启用的AI供应商
|
|||
func AIProviderFindAllActive(ctx context.Context, db *gorm.DB) ([]AIProvider, error) { |
|||
var providers []AIProvider |
|||
err := db.WithContext(ctx).Where("is_active = ?", true).Order("sort_order ASC, id ASC").Find(&providers).Error |
|||
if err != nil { |
|||
return nil, err |
|||
} |
|||
return providers, nil |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
package model |
|||
|
|||
import ( |
|||
"context" |
|||
|
|||
"gorm.io/gorm" |
|||
) |
|||
|
|||
// AIUsageRecordInsert 插入AI使用记录
|
|||
func AIUsageRecordInsert(ctx context.Context, db *gorm.DB, record *AIUsageRecord) (int64, error) { |
|||
result := db.WithContext(ctx).Create(record) |
|||
if result.Error != nil { |
|||
return 0, result.Error |
|||
} |
|||
return record.Id, nil |
|||
} |
|||
|
|||
// AIUsageRecordFindByUser 根据用户ID查询使用记录(分页,按创建时间倒序)
|
|||
func AIUsageRecordFindByUser(ctx context.Context, db *gorm.DB, userId int64, page, pageSize int64) ([]AIUsageRecord, int64, error) { |
|||
var records []AIUsageRecord |
|||
var total int64 |
|||
|
|||
query := db.WithContext(ctx).Model(&AIUsageRecord{}).Where("user_id = ?", userId) |
|||
|
|||
if err := query.Count(&total).Error; err != nil { |
|||
return nil, 0, err |
|||
} |
|||
|
|||
offset := (page - 1) * pageSize |
|||
if offset < 0 { |
|||
offset = 0 |
|||
} |
|||
err := query.Order("created_at DESC").Offset(int(offset)).Limit(int(pageSize)).Find(&records).Error |
|||
if err != nil { |
|||
return nil, 0, err |
|||
} |
|||
|
|||
return records, total, nil |
|||
} |
|||
@ -0,0 +1,91 @@ |
|||
package model |
|||
|
|||
import ( |
|||
"context" |
|||
"errors" |
|||
|
|||
"gorm.io/gorm" |
|||
) |
|||
|
|||
// AIUserQuotaFindByUser 根据用户ID查询额度
|
|||
func AIUserQuotaFindByUser(ctx context.Context, db *gorm.DB, userId int64) (*AIUserQuota, error) { |
|||
var quota AIUserQuota |
|||
result := db.WithContext(ctx).Where("user_id = ?", userId).First("a) |
|||
if result.Error != nil { |
|||
if errors.Is(result.Error, gorm.ErrRecordNotFound) { |
|||
return nil, ErrNotFound |
|||
} |
|||
return nil, result.Error |
|||
} |
|||
return "a, nil |
|||
} |
|||
|
|||
// AIUserQuotaEnsure 查找或创建用户额度记录
|
|||
func AIUserQuotaEnsure(ctx context.Context, db *gorm.DB, userId int64) (*AIUserQuota, error) { |
|||
var quota AIUserQuota |
|||
result := db.WithContext(ctx).Where("user_id = ?", userId).First("a) |
|||
if result.Error != nil { |
|||
if errors.Is(result.Error, gorm.ErrRecordNotFound) { |
|||
quota = AIUserQuota{UserId: userId} |
|||
if err := db.WithContext(ctx).Create("a).Error; err != nil { |
|||
return nil, err |
|||
} |
|||
return "a, nil |
|||
} |
|||
return nil, result.Error |
|||
} |
|||
return "a, nil |
|||
} |
|||
|
|||
// AIUserQuotaFreeze 冻结额度(原子操作:balance -= amount, frozen_amount += amount)
|
|||
func AIUserQuotaFreeze(ctx context.Context, db *gorm.DB, userId int64, amount float64) error { |
|||
result := db.WithContext(ctx).Model(&AIUserQuota{}). |
|||
Where("user_id = ? AND balance >= ?", userId, amount). |
|||
Updates(map[string]interface{}{ |
|||
"balance": gorm.Expr("balance - ?", amount), |
|||
"frozen_amount": gorm.Expr("frozen_amount + ?", amount), |
|||
}) |
|||
if result.Error != nil { |
|||
return result.Error |
|||
} |
|||
if result.RowsAffected == 0 { |
|||
return errors.New("insufficient balance") |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
// AIUserQuotaSettle 结算额度(原子操作:frozen_amount -= frozenAmount, total_consumed += actualCost, balance += refund)
|
|||
func AIUserQuotaSettle(ctx context.Context, db *gorm.DB, userId int64, frozenAmount, actualCost float64) error { |
|||
refund := frozenAmount - actualCost |
|||
result := db.WithContext(ctx).Model(&AIUserQuota{}). |
|||
Where("user_id = ?", userId). |
|||
Updates(map[string]interface{}{ |
|||
"frozen_amount": gorm.Expr("frozen_amount - ?", frozenAmount), |
|||
"total_consumed": gorm.Expr("total_consumed + ?", actualCost), |
|||
"balance": gorm.Expr("balance + ?", refund), |
|||
}) |
|||
if result.Error != nil { |
|||
return result.Error |
|||
} |
|||
if result.RowsAffected == 0 { |
|||
return ErrNotFound |
|||
} |
|||
return nil |
|||
} |
|||
|
|||
// AIUserQuotaUnfreeze 解冻额度(原子操作:frozen_amount -= amount, balance += amount)
|
|||
func AIUserQuotaUnfreeze(ctx context.Context, db *gorm.DB, userId int64, amount float64) error { |
|||
result := db.WithContext(ctx).Model(&AIUserQuota{}). |
|||
Where("user_id = ?", userId). |
|||
Updates(map[string]interface{}{ |
|||
"frozen_amount": gorm.Expr("frozen_amount - ?", amount), |
|||
"balance": gorm.Expr("balance + ?", amount), |
|||
}) |
|||
if result.Error != nil { |
|||
return result.Error |
|||
} |
|||
if result.RowsAffected == 0 { |
|||
return ErrNotFound |
|||
} |
|||
return nil |
|||
} |
|||
Loading…
Reference in new issue