Backend • model/model_meta.go – Import strconv – SearchModels: support numeric vendor ID filter vs. fuzzy name search – Explicitly order by `models.id` to avoid “ambiguous column name: id” error Frontend • hooks/useModelsData.js – Change vendor-filter API to pass vendor ID – Automatically reload models when `activeVendorKey` changes – Update vendor counts only when viewing “All” to preserve other tab totals • Add missing effect in EditModelModal to refresh vendor list only when modal visible • Other minor updates to keep lints clean Result Tabs now: 1. Trigger API requests on click 2. Show accurate per-vendor totals 3. Filter models without resetting other counts Backend search handles both vendor IDs and names without SQL errors.
115 lines
4.1 KiB
Go
115 lines
4.1 KiB
Go
package model
|
||
|
||
import (
|
||
"one-api/common"
|
||
"strconv"
|
||
|
||
"gorm.io/gorm"
|
||
)
|
||
|
||
// Model 用于存储模型的元数据,例如描述、标签等
|
||
// ModelName 字段具有唯一性约束,确保每个模型只会出现一次
|
||
// Tags 字段使用逗号分隔的字符串保存标签集合,后期可根据需要扩展为 JSON 类型
|
||
// Status: 1 表示启用,0 表示禁用,保留以便后续功能扩展
|
||
// CreatedTime 和 UpdatedTime 使用 Unix 时间戳(秒)保存方便跨数据库移植
|
||
// DeletedAt 采用 GORM 的软删除特性,便于后续数据恢复
|
||
//
|
||
// 该表设计遵循第三范式(3NF):
|
||
// 1. 每一列都与主键(Id 或 ModelName)直接相关
|
||
// 2. 不存在部分依赖(ModelName 是唯一键)
|
||
// 3. 不存在传递依赖(描述、标签等都依赖于 ModelName,而非依赖于其他非主键列)
|
||
// 这样既保证了数据一致性,也方便后期扩展
|
||
|
||
type BoundChannel struct {
|
||
Name string `json:"name"`
|
||
Type int `json:"type"`
|
||
}
|
||
|
||
type Model struct {
|
||
Id int `json:"id"`
|
||
ModelName string `json:"model_name" gorm:"uniqueIndex;size:128;not null"`
|
||
Description string `json:"description,omitempty" gorm:"type:text"`
|
||
Tags string `json:"tags,omitempty" gorm:"type:varchar(255)"`
|
||
VendorID int `json:"vendor_id,omitempty" gorm:"index"`
|
||
Endpoints string `json:"endpoints,omitempty" gorm:"type:text"`
|
||
Status int `json:"status" gorm:"default:1"`
|
||
CreatedTime int64 `json:"created_time" gorm:"bigint"`
|
||
UpdatedTime int64 `json:"updated_time" gorm:"bigint"`
|
||
DeletedAt gorm.DeletedAt `json:"-" gorm:"index"`
|
||
|
||
BoundChannels []BoundChannel `json:"bound_channels,omitempty" gorm:"-"`
|
||
}
|
||
|
||
// Insert 创建新的模型元数据记录
|
||
func (mi *Model) Insert() error {
|
||
now := common.GetTimestamp()
|
||
mi.CreatedTime = now
|
||
mi.UpdatedTime = now
|
||
return DB.Create(mi).Error
|
||
}
|
||
|
||
// Update 更新现有模型记录
|
||
func (mi *Model) Update() error {
|
||
mi.UpdatedTime = common.GetTimestamp()
|
||
return DB.Save(mi).Error
|
||
}
|
||
|
||
// Delete 软删除模型记录
|
||
func (mi *Model) Delete() error {
|
||
return DB.Delete(mi).Error
|
||
}
|
||
|
||
// GetModelByName 根据模型名称查询元数据
|
||
func GetModelByName(name string) (*Model, error) {
|
||
var mi Model
|
||
err := DB.Where("model_name = ?", name).First(&mi).Error
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return &mi, nil
|
||
}
|
||
|
||
// GetAllModels 分页获取所有模型元数据
|
||
func GetAllModels(offset int, limit int) ([]*Model, error) {
|
||
var models []*Model
|
||
err := DB.Offset(offset).Limit(limit).Find(&models).Error
|
||
return models, err
|
||
}
|
||
|
||
// GetBoundChannels 查询支持该模型的渠道(名称+类型)
|
||
func GetBoundChannels(modelName string) ([]BoundChannel, error) {
|
||
var channels []BoundChannel
|
||
err := DB.Table("channels").
|
||
Select("channels.name, channels.type").
|
||
Joins("join abilities on abilities.channel_id = channels.id").
|
||
Where("abilities.model = ? AND abilities.enabled = ?", modelName, true).
|
||
Group("channels.id").
|
||
Scan(&channels).Error
|
||
return channels, err
|
||
}
|
||
|
||
// SearchModels 根据关键词和供应商搜索模型,支持分页
|
||
func SearchModels(keyword string, vendor string, offset int, limit int) ([]*Model, int64, error) {
|
||
var models []*Model
|
||
db := DB.Model(&Model{})
|
||
if keyword != "" {
|
||
like := "%" + keyword + "%"
|
||
db = db.Where("model_name LIKE ? OR description LIKE ? OR tags LIKE ?", like, like, like)
|
||
}
|
||
if vendor != "" {
|
||
// 如果是数字,按供应商 ID 精确匹配;否则按名称模糊匹配
|
||
if vid, err := strconv.Atoi(vendor); err == nil {
|
||
db = db.Where("models.vendor_id = ?", vid)
|
||
} else {
|
||
db = db.Joins("JOIN vendors ON vendors.id = models.vendor_id").Where("vendors.name LIKE ?", "%"+vendor+"%")
|
||
}
|
||
}
|
||
var total int64
|
||
err := db.Count(&total).Error
|
||
if err != nil {
|
||
return nil, 0, err
|
||
}
|
||
err = db.Offset(offset).Limit(limit).Order("models.id DESC").Find(&models).Error
|
||
return models, total, err
|
||
}
|