Browse Source

feat: seed AI providers, models, Casbin policies, menu

- 5 providers: OpenAI, Claude, Qwen, Zhipu, DeepSeek
- 9 models with pricing and capabilities
- Casbin policies for AI routes (user role)
- AI 对话 menu item
master
dark 1 month ago
parent
commit
9f7466064c
  1. 71
      backend/internal/svc/servicecontext.go

71
backend/internal/svc/servicecontext.go

@ -91,6 +91,10 @@ func NewServiceContext(c config.Config) *ServiceContext {
seedMenus(db)
seedRoleMenus(db)
// 种子 AI 供应商和模型
seedAIProviders(db)
seedAIModels(db)
// 初始化存储
store, err := storage.NewStorage(c.Storage)
if err != nil {
@ -261,6 +265,16 @@ func seedCasbinPolicies(enforcer *casbin.Enforcer) {
// super_admin: 机构删除
{"super_admin", "/api/v1/organization/:id", "DELETE"},
// AI: all authenticated users
{"user", "/api/v1/ai/chat/completions", "POST"},
{"user", "/api/v1/ai/conversations", "GET"},
{"user", "/api/v1/ai/conversation", "POST"},
{"user", "/api/v1/ai/conversation/:id", "GET"},
{"user", "/api/v1/ai/conversation/:id", "PUT"},
{"user", "/api/v1/ai/conversation/:id", "DELETE"},
{"user", "/api/v1/ai/models", "GET"},
{"user", "/api/v1/ai/quota/me", "GET"},
}
for _, p := range policies {
@ -298,10 +312,11 @@ func seedMenus(db *gorm.DB) {
{Name: "仪表盘", Path: "/dashboard", Icon: "LayoutDashboard", Type: "config", SortOrder: 2, Visible: true, Status: 1},
{Name: "用户管理", Path: "/users", Icon: "Users", Type: "config", SortOrder: 3, Visible: true, Status: 1},
{Name: "文件管理", Path: "/files", Icon: "FolderOpen", Type: "config", SortOrder: 4, Visible: true, Status: 1},
{Name: "角色管理", Path: "/roles", Icon: "Shield", Type: "config", SortOrder: 5, Visible: true, Status: 1},
{Name: "菜单管理", Path: "/menus", Icon: "Menu", Type: "config", SortOrder: 6, Visible: true, Status: 1},
{Name: "机构管理", Path: "/organizations", Icon: "Building2", Type: "config", SortOrder: 7, Visible: true, Status: 1},
{Name: "设置", Path: "/settings", Icon: "Settings", Type: "default", SortOrder: 8, Visible: true, Status: 1},
{Name: "AI 对话", Path: "/ai/chat", Icon: "Bot", Type: "config", SortOrder: 5, Visible: true, Status: 1},
{Name: "角色管理", Path: "/roles", Icon: "Shield", Type: "config", SortOrder: 6, Visible: true, Status: 1},
{Name: "菜单管理", Path: "/menus", Icon: "Menu", Type: "config", SortOrder: 7, Visible: true, Status: 1},
{Name: "机构管理", Path: "/organizations", Icon: "Building2", Type: "config", SortOrder: 8, Visible: true, Status: 1},
{Name: "设置", Path: "/settings", Icon: "Settings", Type: "default", SortOrder: 9, Visible: true, Status: 1},
}
for _, m := range menus {
@ -363,3 +378,51 @@ func seedRoleMenus(db *gorm.DB) {
}
log.Println("[Seed] RoleMenus seeded successfully")
}
// seedAIProviders 种子 AI 供应商(幂等)
func seedAIProviders(db *gorm.DB) {
providers := []model.AIProvider{
{Name: "openai", DisplayName: "OpenAI", BaseUrl: "https://api.openai.com/v1", SdkType: "openai_compat", Protocol: "openai", IsActive: true, SortOrder: 1},
{Name: "claude", DisplayName: "Anthropic Claude", BaseUrl: "https://api.anthropic.com", SdkType: "anthropic", Protocol: "anthropic", IsActive: true, SortOrder: 2},
{Name: "qwen", DisplayName: "阿里千问", BaseUrl: "https://dashscope.aliyuncs.com/compatible-mode/v1", SdkType: "openai_compat", Protocol: "openai", IsActive: true, SortOrder: 3},
{Name: "zhipu", DisplayName: "智谱 GLM", BaseUrl: "https://open.bigmodel.cn/api/paas/v4", SdkType: "openai_compat", Protocol: "openai", IsActive: true, SortOrder: 4},
{Name: "deepseek", DisplayName: "DeepSeek", BaseUrl: "https://api.deepseek.com/v1", SdkType: "openai_compat", Protocol: "openai", IsActive: true, SortOrder: 5},
}
for _, p := range providers {
var existing model.AIProvider
if err := db.Where("name = ?", p.Name).First(&existing).Error; err != nil {
db.Create(&p)
}
}
log.Println("[Seed] AI providers seeded successfully")
}
// seedAIModels 种子 AI 模型(幂等)
func seedAIModels(db *gorm.DB) {
// First, fetch providers by name to get their IDs
providerIds := map[string]int64{}
var providers []model.AIProvider
db.Find(&providers)
for _, p := range providers {
providerIds[p.Name] = p.Id
}
models := []model.AIModel{
{ProviderId: providerIds["openai"], ModelId: "gpt-4o", DisplayName: "GPT-4o", InputPrice: 0.0025, OutputPrice: 0.01, MaxTokens: 16384, ContextWindow: 128000, SupportsStream: true, SupportsVision: true, IsActive: true, SortOrder: 1},
{ProviderId: providerIds["openai"], ModelId: "gpt-4o-mini", DisplayName: "GPT-4o Mini", InputPrice: 0.00015, OutputPrice: 0.0006, MaxTokens: 16384, ContextWindow: 128000, SupportsStream: true, SupportsVision: true, IsActive: true, SortOrder: 2},
{ProviderId: providerIds["claude"], ModelId: "claude-sonnet-4-5-20250514", DisplayName: "Claude Sonnet 4.5", InputPrice: 0.003, OutputPrice: 0.015, MaxTokens: 8192, ContextWindow: 200000, SupportsStream: true, SupportsVision: true, IsActive: true, SortOrder: 3},
{ProviderId: providerIds["claude"], ModelId: "claude-haiku-3-5-20241022", DisplayName: "Claude Haiku 3.5", InputPrice: 0.0008, OutputPrice: 0.004, MaxTokens: 8192, ContextWindow: 200000, SupportsStream: true, SupportsVision: true, IsActive: true, SortOrder: 4},
{ProviderId: providerIds["qwen"], ModelId: "qwen-plus", DisplayName: "通义千问 Plus", InputPrice: 0.0008, OutputPrice: 0.002, MaxTokens: 8192, ContextWindow: 131072, SupportsStream: true, SupportsVision: false, IsActive: true, SortOrder: 5},
{ProviderId: providerIds["qwen"], ModelId: "qwen-turbo", DisplayName: "通义千问 Turbo", InputPrice: 0.0003, OutputPrice: 0.0006, MaxTokens: 8192, ContextWindow: 131072, SupportsStream: true, SupportsVision: false, IsActive: true, SortOrder: 6},
{ProviderId: providerIds["zhipu"], ModelId: "glm-4-flash", DisplayName: "GLM-4 Flash", InputPrice: 0.0001, OutputPrice: 0.0001, MaxTokens: 4096, ContextWindow: 128000, SupportsStream: true, SupportsVision: false, IsActive: true, SortOrder: 7},
{ProviderId: providerIds["deepseek"], ModelId: "deepseek-chat", DisplayName: "DeepSeek Chat", InputPrice: 0.00014, OutputPrice: 0.00028, MaxTokens: 8192, ContextWindow: 64000, SupportsStream: true, SupportsVision: false, IsActive: true, SortOrder: 8},
{ProviderId: providerIds["deepseek"], ModelId: "deepseek-reasoner", DisplayName: "DeepSeek Reasoner", InputPrice: 0.00055, OutputPrice: 0.00219, MaxTokens: 8192, ContextWindow: 64000, SupportsStream: true, SupportsVision: false, IsActive: true, SortOrder: 9},
}
for _, m := range models {
var existing model.AIModel
if err := db.Where("model_id = ?", m.ModelId).First(&existing).Error; err != nil {
db.Create(&m)
}
}
log.Println("[Seed] AI models seeded successfully")
}

Loading…
Cancel
Save