package app import ( "encoding/json" "errors" "path" "strings" "time" "git.echol.cn/loser/st/server/global" "git.echol.cn/loser/st/server/model/app" "git.echol.cn/loser/st/server/model/app/request" "git.echol.cn/loser/st/server/model/app/response" "go.uber.org/zap" "gorm.io/datatypes" "gorm.io/gorm" ) type ExtensionService struct{} // CreateExtension 创建扩展 func (s *ExtensionService) CreateExtension(userID uint, req *request.CreateExtensionRequest) (*app.AIExtension, error) { // 检查扩展名是否重复 var count int64 global.GVA_DB.Model(&app.AIExtension{}).Where("user_id = ? AND name = ?", userID, req.Name).Count(&count) if count > 0 { return nil, errors.New("同名扩展已存在") } now := time.Now().Unix() ext := app.AIExtension{ UserID: userID, Name: req.Name, DisplayName: req.DisplayName, Version: req.Version, Author: req.Author, Description: req.Description, Homepage: req.Homepage, Repository: req.Repository, License: req.License, ExtensionType: req.ExtensionType, Category: req.Category, ScriptPath: req.ScriptPath, StylePath: req.StylePath, InstallSource: req.InstallSource, SourceURL: req.SourceURL, Branch: req.Branch, IsInstalled: true, IsEnabled: false, InstallDate: &now, } // 设置默认值 if ext.Version == "" { ext.Version = "1.0.0" } if ext.Branch == "" { ext.Branch = "main" } // 序列化 JSON 字段 if req.Tags != nil { ext.Tags = mustMarshal(req.Tags) } if req.Dependencies != nil { ext.Dependencies = mustMarshal(req.Dependencies) } if req.Conflicts != nil { ext.Conflicts = mustMarshal(req.Conflicts) } if req.AssetPaths != nil { ext.AssetPaths = mustMarshal(req.AssetPaths) } if req.ManifestData != nil { ext.ManifestData = mustMarshal(req.ManifestData) } if req.Settings != nil { ext.Settings = mustMarshal(req.Settings) } if req.Options != nil { ext.Options = mustMarshal(req.Options) } if req.Metadata != nil { ext.Metadata = mustMarshal(req.Metadata) } if err := global.GVA_DB.Create(&ext).Error; err != nil { global.GVA_LOG.Error("创建扩展失败", zap.Error(err)) return nil, err } return &ext, nil } // UpdateExtension 更新扩展 func (s *ExtensionService) UpdateExtension(userID, extID uint, req *request.UpdateExtensionRequest) error { var ext app.AIExtension if err := global.GVA_DB.Where("id = ? AND user_id = ?", extID, userID).First(&ext).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return errors.New("扩展不存在") } return err } updates := make(map[string]interface{}) if req.DisplayName != "" { updates["display_name"] = req.DisplayName } if req.Description != "" { updates["description"] = req.Description } if req.Version != "" { updates["version"] = req.Version } if req.Author != "" { updates["author"] = req.Author } if req.Homepage != "" { updates["homepage"] = req.Homepage } if req.Repository != "" { updates["repository"] = req.Repository } if req.License != "" { updates["license"] = req.License } if req.ExtensionType != "" { updates["extension_type"] = req.ExtensionType } if req.Category != "" { updates["category"] = req.Category } if req.ScriptPath != "" { updates["script_path"] = req.ScriptPath } if req.StylePath != "" { updates["style_path"] = req.StylePath } if req.Tags != nil { updates["tags"] = datatypes.JSON(mustMarshal(req.Tags)) } if req.Dependencies != nil { updates["dependencies"] = datatypes.JSON(mustMarshal(req.Dependencies)) } if req.Conflicts != nil { updates["conflicts"] = datatypes.JSON(mustMarshal(req.Conflicts)) } if req.AssetPaths != nil { updates["asset_paths"] = datatypes.JSON(mustMarshal(req.AssetPaths)) } if req.ManifestData != nil { updates["manifest_data"] = datatypes.JSON(mustMarshal(req.ManifestData)) } if req.Settings != nil { updates["settings"] = datatypes.JSON(mustMarshal(req.Settings)) } if req.Options != nil { updates["options"] = datatypes.JSON(mustMarshal(req.Options)) } if req.Metadata != nil { updates["metadata"] = datatypes.JSON(mustMarshal(req.Metadata)) } if len(updates) == 0 { return nil } return global.GVA_DB.Model(&app.AIExtension{}).Where("id = ? AND user_id = ?", extID, userID).Updates(updates).Error } // DeleteExtension 删除扩展 func (s *ExtensionService) DeleteExtension(userID, extID uint) error { result := global.GVA_DB.Where("id = ? AND user_id = ?", extID, userID).Delete(&app.AIExtension{}) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return errors.New("扩展不存在或无权删除") } return nil } // GetExtension 获取扩展详情 func (s *ExtensionService) GetExtension(userID, extID uint) (*app.AIExtension, error) { var ext app.AIExtension if err := global.GVA_DB.Where("id = ? AND user_id = ?", extID, userID).First(&ext).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("扩展不存在") } return nil, err } return &ext, nil } // GetExtensionList 获取扩展列表 func (s *ExtensionService) GetExtensionList(userID uint, req *request.ExtensionListRequest) ([]app.AIExtension, int64, error) { var extensions []app.AIExtension var total int64 db := global.GVA_DB.Model(&app.AIExtension{}).Where("user_id = ?", userID) // 关键词搜索 if req.Keyword != "" { keyword := "%" + req.Keyword + "%" db = db.Where("(name ILIKE ? OR display_name ILIKE ? OR description ILIKE ?)", keyword, keyword, keyword) } // 名称过滤 if req.Name != "" { db = db.Where("name = ?", req.Name) } // 类型过滤 if req.ExtensionType != "" { db = db.Where("extension_type = ?", req.ExtensionType) } // 分类过滤 if req.Category != "" { db = db.Where("category = ?", req.Category) } // 启用状态过滤 if req.IsEnabled != nil { db = db.Where("is_enabled = ?", *req.IsEnabled) } // 安装状态过滤 if req.IsInstalled != nil { db = db.Where("is_installed = ?", *req.IsInstalled) } // 标签过滤 if req.Tag != "" { db = db.Where("tags @> ?", datatypes.JSON(mustMarshal([]string{req.Tag}))) } // 获取总数 if err := db.Count(&total).Error; err != nil { return nil, 0, err } // 分页查询 offset := (req.Page - 1) * req.PageSize if err := db.Order("created_at DESC").Offset(offset).Limit(req.PageSize).Find(&extensions).Error; err != nil { return nil, 0, err } return extensions, total, nil } // GetEnabledExtensions 获取启用的扩展列表 func (s *ExtensionService) GetEnabledExtensions(userID uint) ([]app.AIExtension, error) { var extensions []app.AIExtension if err := global.GVA_DB.Where("user_id = ? AND is_enabled = ? AND is_installed = ?", userID, true, true). Order("created_at ASC").Find(&extensions).Error; err != nil { return nil, err } return extensions, nil } // ToggleExtension 启用/禁用扩展 func (s *ExtensionService) ToggleExtension(userID, extID uint, isEnabled bool) error { updates := map[string]interface{}{ "is_enabled": isEnabled, } if isEnabled { now := time.Now().Unix() updates["last_enabled"] = &now } result := global.GVA_DB.Model(&app.AIExtension{}).Where("id = ? AND user_id = ?", extID, userID).Updates(updates) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return errors.New("扩展不存在") } return nil } // GetExtensionSettings 获取扩展设置 func (s *ExtensionService) GetExtensionSettings(userID, extID uint) (map[string]interface{}, error) { var ext app.AIExtension if err := global.GVA_DB.Select("settings").Where("id = ? AND user_id = ?", extID, userID).First(&ext).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("扩展不存在") } return nil, err } var settings map[string]interface{} if len(ext.Settings) > 0 { _ = json.Unmarshal(ext.Settings, &settings) } if settings == nil { settings = make(map[string]interface{}) } return settings, nil } // UpdateExtensionSettings 更新扩展设置 func (s *ExtensionService) UpdateExtensionSettings(userID, extID uint, settings map[string]interface{}) error { result := global.GVA_DB.Model(&app.AIExtension{}). Where("id = ? AND user_id = ?", extID, userID). Update("settings", datatypes.JSON(mustMarshal(settings))) if result.Error != nil { return result.Error } if result.RowsAffected == 0 { return errors.New("扩展不存在") } return nil } // GetExtensionManifest 获取扩展 manifest func (s *ExtensionService) GetExtensionManifest(userID, extID uint) (map[string]interface{}, error) { var ext app.AIExtension if err := global.GVA_DB.Select("manifest_data").Where("id = ? AND user_id = ?", extID, userID).First(&ext).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, errors.New("扩展不存在") } return nil, err } var manifest map[string]interface{} if len(ext.ManifestData) > 0 { _ = json.Unmarshal(ext.ManifestData, &manifest) } if manifest == nil { manifest = make(map[string]interface{}) } return manifest, nil } // UpdateExtensionStats 更新扩展统计信息 func (s *ExtensionService) UpdateExtensionStats(userID, extID uint, action string, value int) error { var updateExpr string switch action { case "usage": updateExpr = "usage_count" case "error": updateExpr = "error_count" case "load": // load 直接设置加载时间(ms),不累加 return global.GVA_DB.Model(&app.AIExtension{}). Where("id = ? AND user_id = ?", extID, userID). Update("load_time", value).Error default: return errors.New("无效的统计类型") } return global.GVA_DB.Model(&app.AIExtension{}). Where("id = ? AND user_id = ?", extID, userID). Update(updateExpr, gorm.Expr(updateExpr+" + ?", value)).Error } // ExportExtension 导出扩展数据 func (s *ExtensionService) ExportExtension(userID, extID uint) (*response.ExtensionResponse, error) { ext, err := s.GetExtension(userID, extID) if err != nil { return nil, err } resp := response.ToExtensionResponse(ext) return &resp, nil } // ImportExtension 从文件导入扩展(支持 zip 和 json) func (s *ExtensionService) ImportExtension(userID uint, filename string, fileData []byte) (*app.AIExtension, error) { ext := strings.ToLower(path.Ext(filename)) switch ext { case ".json": // JSON 文件:直接解析为 CreateExtensionRequest var req request.CreateExtensionRequest if err := json.Unmarshal(fileData, &req); err != nil { return nil, errors.New("JSON 文件格式错误: " + err.Error()) } if req.Name == "" { req.Name = strings.TrimSuffix(filename, path.Ext(filename)) } if req.ExtensionType == "" { req.ExtensionType = "ui" } req.InstallSource = "file" return s.CreateExtension(userID, &req) case ".zip": // ZIP 文件:解压到扩展目录,解析 manifest.json,创建数据库记录 return s.ImportExtensionFromZip(userID, filename, fileData) default: return nil, errors.New("不支持的文件格式,请上传 .zip 或 .json 文件") } } // UpgradeExtension 升级扩展(从源地址重新安装) func (s *ExtensionService) UpgradeExtension(userID, extID uint) (*app.AIExtension, error) { return s.UpgradeExtensionFromSource(userID, extID) } // mustMarshal JSON 序列化辅助函数 func mustMarshal(v interface{}) []byte { data, err := json.Marshal(v) if err != nil { return []byte("{}") } return data }