Files
new-api/model/model_meta.go
t0ng7u af59b61f8a 🚀 feat: Introduce full Model & Vendor Management suite (backend + frontend) and UI refinements
Backend
• Add `model/model_meta.go` and `model/vendor_meta.go` defining Model & Vendor entities with CRUD helpers, soft-delete and time stamps
• Create corresponding controllers `controller/model_meta.go`, `controller/vendor_meta.go` and register routes in `router/api-router.go`
• Auto-migrate new tables in DB startup logic

Frontend
• Build complete “Model Management” module under `/console/models`
  - New pages, tables, filters, actions, hooks (`useModelsData`) and dynamic vendor tabs
  - Modals `EditModelModal.jsx` & unified `EditVendorModal.jsx`; latter now uses default confirm/cancel footer and mobile-friendly modal sizing (`full-width` / `small`) via `useIsMobile`
• Update sidebar (`SiderBar.js`) and routing (`App.js`) to surface the feature
• Add helper updates (`render.js`) incl. `stringToColor`, dynamic LobeHub icon retrieval, and tag color palettes

Table UX improvements
• Replace separate status column with inline Enable / Disable buttons in operation column (matching channel table style)
• Limit visible tags to max 3; overflow represented as “+x” tag with padded `Popover` showing remaining tags
• Color all tags deterministically using `stringToColor` for consistent theming
• Change vendor column tag color to white for better contrast

Misc
• Minor layout tweaks, compact-mode toggle relocation, lint fixes and TypeScript/ESLint clean-up

These changes collectively deliver end-to-end model & vendor administration while unifying visual language across management tables.
2025-07-31 22:28:09 +08:00

109 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package model
import (
"one-api/common"
"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 != "" {
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("id DESC").Find(&models).Error
return models, total, err
}