From f8e4d2880ffaec6b38a59712dd6eed829d7eda05 Mon Sep 17 00:00:00 2001 From: GeekMaster Date: Tue, 2 Sep 2025 18:55:45 +0800 Subject: [PATCH] =?UTF-8?q?3D=E7=94=9F=E6=88=90=E6=9C=8D=E5=8A=A1=E5=B7=B2?= =?UTF-8?q?=E7=BB=8F=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/core/config.go | 20 + api/core/types/ai3d.go | 58 ++ api/core/types/config.go | 3 + api/go.mod | 4 +- api/go.sum | 4 +- api/handler/admin/ai3d_handler.go | 216 +++++ api/handler/admin/config_handler.go | 136 +--- api/handler/admin/jimeng_handler.go | 34 +- api/handler/admin/moderation_handler.go | 97 ++- api/handler/ai3d_handler.go | 236 ++++++ api/handler/jimeng_handler.go | 4 +- api/main.go | 20 + api/service/ai3d/gitee_client.go | 150 ++++ api/service/ai3d/service.go | 327 ++++++++ api/service/ai3d/tencent_client.go | 158 ++++ api/service/jimeng/client.go | 43 +- api/service/jimeng/service.go | 88 +- api/service/migration_service.go | 20 + api/store/model/ai3d_job.go | 23 + api/store/vo/ai3d_job.go | 32 + docs.md | 122 --- docs/ai3d.md | 510 ++++++++++++ web/pnpm-lock.yaml | 11 + web/src/assets/iconfont/iconfont.css | 10 +- web/src/assets/iconfont/iconfont.js | 2 +- web/src/assets/iconfont/iconfont.json | 7 + web/src/assets/iconfont/iconfont.ttf | Bin 58732 -> 59020 bytes web/src/assets/iconfont/iconfont.woff | Bin 38272 -> 38484 bytes web/src/assets/iconfont/iconfont.woff2 | Bin 33184 -> 33340 bytes web/src/components/ThreeDPreview.vue | 547 +++++++++++++ web/src/components/admin/AdminSidebar.vue | 27 +- web/src/router.js | 35 +- web/src/views/AIThreeDCreate.vue | 619 ++++++++++++++ web/src/views/PayReturn.vue | 18 - web/src/views/admin/ai3d/AIThreeDConfig.vue | 405 ++++++++++ web/src/views/admin/ai3d/AIThreeDJobs.vue | 551 +++++++++++++ .../{JimengSetting.vue => JimengConfig.vue} | 2 +- .../admin/moderation/ModerationConfig.vue | 4 +- web/src/views/mobile/Index.vue | 7 + web/src/views/mobile/ThreeDCreate.vue | 765 ++++++++++++++++++ 40 files changed, 4920 insertions(+), 395 deletions(-) create mode 100644 api/core/types/ai3d.go create mode 100644 api/handler/admin/ai3d_handler.go create mode 100644 api/handler/ai3d_handler.go create mode 100644 api/service/ai3d/gitee_client.go create mode 100644 api/service/ai3d/service.go create mode 100644 api/service/ai3d/tencent_client.go create mode 100644 api/store/model/ai3d_job.go create mode 100644 api/store/vo/ai3d_job.go delete mode 100644 docs.md create mode 100644 docs/ai3d.md create mode 100644 web/src/components/ThreeDPreview.vue create mode 100644 web/src/views/AIThreeDCreate.vue delete mode 100644 web/src/views/PayReturn.vue create mode 100644 web/src/views/admin/ai3d/AIThreeDConfig.vue create mode 100644 web/src/views/admin/ai3d/AIThreeDJobs.vue rename web/src/views/admin/jimeng/{JimengSetting.vue => JimengConfig.vue} (99%) create mode 100644 web/src/views/mobile/ThreeDCreate.vue diff --git a/api/core/config.go b/api/core/config.go index a9032d9c..7f2fc971 100644 --- a/api/core/config.go +++ b/api/core/config.go @@ -157,6 +157,24 @@ func LoadSystemConfig(db *gorm.DB) *types.SystemConfig { logger.Error("load moderation config error: ", err) } + // 加载即梦AI配置 + var jimengConfig types.JimengConfig + sysConfig.Id = 0 + db.Where("name", types.ConfigKeyJimeng).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &jimengConfig) + if err != nil { + logger.Error("load jimeng config error: ", err) + } + + // 加载3D生成配置 + var ai3dConfig types.AI3DConfig + sysConfig.Id = 0 + db.Where("name", types.ConfigKeyAI3D).First(&sysConfig) + err = utils.JsonDecode(sysConfig.Value, &ai3dConfig) + if err != nil { + logger.Error("load ai3d config error: ", err) + } + return &types.SystemConfig{ Base: baseConfig, License: license, @@ -167,5 +185,7 @@ func LoadSystemConfig(db *gorm.DB) *types.SystemConfig { Captcha: captchaConfig, WxLogin: wxLoginConfig, Moderation: moderationConfig, + Jimeng: jimengConfig, + AI3D: ai3dConfig, } } diff --git a/api/core/types/ai3d.go b/api/core/types/ai3d.go new file mode 100644 index 00000000..a32fee1c --- /dev/null +++ b/api/core/types/ai3d.go @@ -0,0 +1,58 @@ +package types + +// AI3DConfig 3D生成配置 +type AI3DConfig struct { + Tencent Tencent3DConfig `json:"tencent,omitempty"` + Gitee Gitee3DConfig `json:"gitee,omitempty"` +} + +// Tencent3DConfig 腾讯云3D配置 +type Tencent3DConfig struct { + SecretId string `json:"secret_id,omitempty"` + SecretKey string `json:"secret_key,omitempty"` + Region string `json:"region,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Models []AI3DModel `json:"models,omitempty"` +} + +// Gitee3DConfig Gitee 3D配置 +type Gitee3DConfig struct { + APIKey string `json:"api_key,omitempty"` + Enabled bool `json:"enabled,omitempty"` + Models []AI3DModel `json:"models,omitempty"` +} + +// AI3DJobResult 3D任务结果 +type AI3DJobResult struct { + JobId string `json:"job_id"` // 任务ID + Status string `json:"status"` // 任务状态 + Progress int `json:"progress"` // 任务进度 (0-100) + FileURL string `json:"file_url"` // 3D模型文件URL + PreviewURL string `json:"preview_url"` // 预览图片URL + ErrorMsg string `json:"error_msg"` // 错误信息 +} + +// AI3DModel 3D模型配置 +type AI3DModel struct { + Name string `json:"name"` // 模型名称 + Desc string `json:"desc"` // 模型描述 + Power int `json:"power"` // 算力消耗 + Formats []string `json:"formats"` // 支持输出的文件格式 +} + +// AI3DJobRequest 3D任务请求 +type AI3DJobRequest struct { + Type string `json:"type"` // API类型 (tencent/gitee) + Model string `json:"model"` // 3D模型类型 + Prompt string `json:"prompt"` // 文本提示词 + ImageURL string `json:"image_url"` // 输入图片URL + Power int `json:"power"` // 消耗算力 +} + +// AI3DJobStatus 3D任务状态 +const ( + AI3DJobStatusPending = "pending" // 等待中 + AI3DJobStatusProcessing = "processing" // 处理中 + AI3DJobStatusCompleted = "completed" // 已完成 + AI3DJobStatusFailed = "failed" // 失败 +) diff --git a/api/core/types/config.go b/api/core/types/config.go index 9c903b8c..70f9bbf2 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -108,6 +108,7 @@ type SystemConfig struct { Captcha CaptchaConfig WxLogin WxLoginConfig Jimeng JimengConfig + AI3D AI3DConfig License License Moderation ModerationConfig } @@ -127,4 +128,6 @@ const ( ConfigKeyOss = "oss" ConfigKeyPayment = "payment" ConfigKeyModeration = "moderation" + ConfigKeyAI3D = "ai3d" + ConfigKeyJimeng = "jimeng" ) diff --git a/api/go.mod b/api/go.mod index 37a6180d..d5ac26e0 100644 --- a/api/go.mod +++ b/api/go.mod @@ -33,6 +33,8 @@ require ( github.com/shirou/gopsutil v3.21.11+incompatible github.com/shopspring/decimal v1.3.1 github.com/syndtr/goleveldb v1.0.0 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ai3d v1.1.0 + github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.21 golang.org/x/image v0.15.0 ) @@ -46,8 +48,6 @@ require ( github.com/go-pay/xtime v0.0.2 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/gorilla/css v1.0.0 // indirect - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ai3d v1.1.0 // indirect - github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.20 // indirect github.com/tklauser/go-sysconf v0.3.13 // indirect github.com/tklauser/numcpus v0.7.0 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect diff --git a/api/go.sum b/api/go.sum index b33b4082..b1388b00 100644 --- a/api/go.sum +++ b/api/go.sum @@ -248,8 +248,8 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ai3d v1.1.0 h1:hOyYsl35o74hOhnnPVQIK/bdSIPNp3TKJlCEOXGO7ms= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ai3d v1.1.0/go.mod h1:3689peGF1zp+P9c+GnUcAzkMp+kXi0Tr44zeQ57Z+7Y= github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.0/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.20 h1:8B80/p+WvzBVz+jM6dosTcfhRe7Jotpyqj0NoGW1wfE= -github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.20/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.21 h1:ikHhyiq1PiPytUMtEblKPkbf0zzTEi3CpE9z0MARlqY= +github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.1.21/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0= github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= diff --git a/api/handler/admin/ai3d_handler.go b/api/handler/admin/ai3d_handler.go new file mode 100644 index 00000000..0c335748 --- /dev/null +++ b/api/handler/admin/ai3d_handler.go @@ -0,0 +1,216 @@ +package admin + +import ( + "strconv" + + "geekai/core" + "geekai/core/types" + "geekai/service/ai3d" + "geekai/store/model" + "geekai/store/vo" + "geekai/utils" + "geekai/utils/resp" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +// AI3DHandler 3D管理处理器 +type AI3DHandler struct { + app *core.AppServer + db *gorm.DB + service *ai3d.Service +} + +// NewAI3DHandler 创建3D管理处理器 +func NewAI3DHandler(app *core.AppServer, db *gorm.DB, service *ai3d.Service) *AI3DHandler { + return &AI3DHandler{ + app: app, + db: db, + service: service, + } +} + +// RegisterRoutes 注册路由 +func (h *AI3DHandler) RegisterRoutes() { + admin := h.app.Engine.Group("/api/admin/ai3d") + { + admin.GET("/jobs", h.GetJobList) + admin.GET("/jobs/:id", h.GetJobDetail) + admin.DELETE("/jobs/:id", h.DeleteJob) + admin.GET("/stats", h.GetStats) + admin.GET("/models", h.GetModels) + admin.POST("/config", h.SaveConfig) + } +} + +// GetJobList 获取任务列表 +func (h *AI3DHandler) GetJobList(c *gin.Context) { + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "20")) + status := c.Query("status") + jobType := c.Query("type") + userIdStr := c.Query("user_id") + + var userId uint + if userIdStr != "" { + if id, err := strconv.ParseUint(userIdStr, 10, 32); err == nil { + userId = uint(id) + } + } + + // 构建查询条件 + query := h.db.Model(&model.AI3DJob{}) + + if status != "" { + query = query.Where("status = ?", status) + } + + if jobType != "" { + query = query.Where("type = ?", jobType) + } + + if userId > 0 { + query = query.Where("user_id = ?", userId) + } + + // 获取总数 + var total int64 + query.Count(&total) + + // 获取分页数据 + var jobs []model.AI3DJob + offset := (page - 1) * pageSize + err := query.Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&jobs).Error + + if err != nil { + resp.ERROR(c, "获取任务列表失败") + return + } + + // 转换为VO + var jobList []vo.AI3DJob + for _, job := range jobs { + var jobVo vo.AI3DJob + err = utils.CopyObject(job, &jobVo) + if err != nil { + continue + } + jobList = append(jobList, jobVo) + } + + resp.SUCCESS(c, vo.NewPage(total, page, pageSize, jobList)) +} + +// GetJobDetail 获取任务详情 +func (h *AI3DHandler) GetJobDetail(c *gin.Context) { + idStr := c.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + resp.ERROR(c, "无效的任务ID") + return + } + + var job model.AI3DJob + err = h.db.First(&job, uint(id)).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + resp.ERROR(c, "任务不存在") + } else { + resp.ERROR(c, "获取任务详情失败") + } + return + } + + var jobVo vo.AI3DJob + err = utils.CopyObject(job, &jobVo) + if err != nil { + resp.ERROR(c, "获取任务详情失败") + return + } + resp.SUCCESS(c, jobVo) +} + +// DeleteJob 删除任务 +func (h *AI3DHandler) DeleteJob(c *gin.Context) { + idStr := c.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + resp.ERROR(c, "无效的任务ID") + return + } + + // 检查任务是否存在 + var job model.AI3DJob + err = h.db.First(&job, uint(id)).Error + if err != nil { + if err == gorm.ErrRecordNotFound { + resp.ERROR(c, "任务不存在") + } else { + resp.ERROR(c, "获取任务失败") + } + return + } + + // 删除任务 + err = h.db.Delete(&job).Error + if err != nil { + resp.ERROR(c, "删除任务失败") + return + } + + resp.SUCCESS(c, "删除成功") +} + +// GetStats 获取统计数据 +func (h *AI3DHandler) GetStats(c *gin.Context) { + var stats struct { + Pending int64 `json:"pending"` + Processing int64 `json:"processing"` + Completed int64 `json:"completed"` + Failed int64 `json:"failed"` + } + + // 统计各状态的任务数量 + h.db.Model(&model.AI3DJob{}).Where("status = ?", "pending").Count(&stats.Pending) + h.db.Model(&model.AI3DJob{}).Where("status = ?", "processing").Count(&stats.Processing) + h.db.Model(&model.AI3DJob{}).Where("status = ?", "completed").Count(&stats.Completed) + h.db.Model(&model.AI3DJob{}).Where("status = ?", "failed").Count(&stats.Failed) + + resp.SUCCESS(c, stats) +} + +// GetModels 获取配置 +func (h *AI3DHandler) GetModels(c *gin.Context) { + models := h.service.GetSupportedModels() + resp.SUCCESS(c, models) +} + +// SaveGlobalSettings 保存全局配置 +func (h *AI3DHandler) SaveConfig(c *gin.Context) { + var config types.AI3DConfig + err := c.ShouldBindJSON(&config) + if err != nil { + resp.ERROR(c, "参数错误") + return + } + var exist model.Config + err = h.db.Where("name", types.ConfigKeyAI3D).First(&exist).Error + if err != nil { + exist.Name = types.ConfigKeyAI3D + exist.Value = utils.JsonEncode(config) + err = h.db.Create(&exist).Error + } else { + exist.Value = utils.JsonEncode(config) + err = h.db.Updates(&exist).Error + } + if err != nil { + resp.ERROR(c, "保存配置失败") + return + } + + h.service.UpdateConfig(config) + h.app.SysConfig.AI3D = config + + resp.SUCCESS(c, "保存成功") +} diff --git a/api/handler/admin/config_handler.go b/api/handler/admin/config_handler.go index 48cfd34c..c9210149 100644 --- a/api/handler/admin/config_handler.go +++ b/api/handler/admin/config_handler.go @@ -8,13 +8,11 @@ package admin // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( - "fmt" "geekai/core" "geekai/core/middleware" "geekai/core/types" "geekai/handler" "geekai/service" - "geekai/service/moderation" "geekai/service/oss" "geekai/service/payment" "geekai/service/sms" @@ -28,17 +26,16 @@ import ( type ConfigHandler struct { handler.BaseHandler - licenseService *service.LicenseService - sysConfig *types.SystemConfig - alipayService *payment.AlipayService - wxpayService *payment.WxPayService - epayService *payment.EPayService - smsManager *sms.SmsManager - uploaderManager *oss.UploaderManager - smtpService *service.SmtpService - captchaService *service.CaptchaService - wxLoginService *service.WxLoginService - moderationManager *moderation.ServiceManager + licenseService *service.LicenseService + sysConfig *types.SystemConfig + alipayService *payment.AlipayService + wxpayService *payment.WxPayService + epayService *payment.EPayService + smsManager *sms.SmsManager + uploaderManager *oss.UploaderManager + smtpService *service.SmtpService + captchaService *service.CaptchaService + wxLoginService *service.WxLoginService } func NewConfigHandler( @@ -54,21 +51,19 @@ func NewConfigHandler( smtpService *service.SmtpService, captchaService *service.CaptchaService, wxLoginService *service.WxLoginService, - moderationManager *moderation.ServiceManager, ) *ConfigHandler { return &ConfigHandler{ - BaseHandler: handler.BaseHandler{App: app, DB: db}, - licenseService: licenseService, - sysConfig: sysConfig, - alipayService: alipayService, - wxpayService: wxpayService, - epayService: epayService, - smsManager: smsManager, - uploaderManager: uploaderManager, - moderationManager: moderationManager, - smtpService: smtpService, - captchaService: captchaService, - wxLoginService: wxLoginService, + BaseHandler: handler.BaseHandler{App: app, DB: db}, + licenseService: licenseService, + sysConfig: sysConfig, + alipayService: alipayService, + wxpayService: wxpayService, + epayService: epayService, + smsManager: smsManager, + uploaderManager: uploaderManager, + smtpService: smtpService, + captchaService: captchaService, + wxLoginService: wxLoginService, } } @@ -91,8 +86,6 @@ func (h *ConfigHandler) RegisterRoutes() { rg.POST("update/sms", h.UpdateSms) rg.POST("update/oss", h.UpdateOss) rg.POST("update/smtp", h.UpdateStmp) - rg.POST("update/moderation", h.UpdateModeration) - rg.POST("moderation/test", h.TestModeration) rg.GET("get", h.Get) rg.POST("license/active", h.Active) rg.GET("license/get", h.GetLicense) @@ -450,90 +443,3 @@ func (h *ConfigHandler) GetLicense(c *gin.Context) { license := h.licenseService.GetLicense() resp.SUCCESS(c, license) } - -// UpdateModeration 更新文本审查配置 -func (h *ConfigHandler) UpdateModeration(c *gin.Context) { - var data types.ModerationConfig - if err := c.ShouldBindJSON(&data); err != nil { - resp.ERROR(c, types.InvalidArgs) - return - } - - err := h.Update(types.ConfigKeyModeration, data) - if err != nil { - resp.ERROR(c, err.Error()) - return - } - - h.moderationManager.UpdateConfig(data) - h.sysConfig.Moderation = data - - resp.SUCCESS(c, data) -} - -// 测试结果类型,用于前端显示 -type ModerationTestResult struct { - IsAbnormal bool `json:"isAbnormal"` - Details []ModerationTestDetail `json:"details"` -} - -type ModerationTestDetail struct { - Category string `json:"category"` - Description string `json:"description"` - Confidence string `json:"confidence"` - IsCategory bool `json:"isCategory"` -} - -// TestModeration 测试文本审查服务 -func (h *ConfigHandler) TestModeration(c *gin.Context) { - var data struct { - Text string `json:"text"` - Service string `json:"service"` - } - - if err := c.ShouldBindJSON(&data); err != nil { - resp.ERROR(c, types.InvalidArgs) - return - } - - if data.Text == "" { - resp.ERROR(c, "测试文本不能为空") - return - } - - // 检查是否启用了文本审查 - if !h.sysConfig.Moderation.Enable { - resp.ERROR(c, "文本审查服务未启用") - return - } - - // 获取当前激活的审核服务 - service := h.moderationManager.GetService() - // 执行文本审核 - result, err := service.Moderate(data.Text) - if err != nil { - resp.ERROR(c, "审核服务调用失败: "+err.Error()) - return - } - - // 转换为前端需要的格式 - testResult := ModerationTestResult{ - IsAbnormal: result.Flagged, - Details: make([]ModerationTestDetail, 0), - } - - // 构建详细信息 - for category, description := range types.ModerationCategories { - score := result.CategoryScores[category] - isCategory := result.Categories[category] - - testResult.Details = append(testResult.Details, ModerationTestDetail{ - Category: category, - Description: description, - Confidence: fmt.Sprintf("%.2f", score), - IsCategory: isCategory, - }) - } - - resp.SUCCESS(c, testResult) -} diff --git a/api/handler/admin/jimeng_handler.go b/api/handler/admin/jimeng_handler.go index 0637f69a..174e284a 100644 --- a/api/handler/admin/jimeng_handler.go +++ b/api/handler/admin/jimeng_handler.go @@ -21,18 +21,18 @@ import ( // AdminJimengHandler 管理后台即梦AI处理器 type AdminJimengHandler struct { handler.BaseHandler - jimengService *jimeng.Service - userService *service.UserService - uploader *oss.UploaderManager + jimengClient *jimeng.Client + userService *service.UserService + uploader *oss.UploaderManager } // NewAdminJimengHandler 创建管理后台即梦AI处理器 -func NewAdminJimengHandler(app *core.AppServer, db *gorm.DB, jimengService *jimeng.Service, userService *service.UserService, uploader *oss.UploaderManager) *AdminJimengHandler { +func NewAdminJimengHandler(app *core.AppServer, db *gorm.DB, jimengClient *jimeng.Client, userService *service.UserService, uploader *oss.UploaderManager) *AdminJimengHandler { return &AdminJimengHandler{ - BaseHandler: handler.BaseHandler{App: app, DB: db}, - jimengService: jimengService, - userService: userService, - uploader: uploader, + BaseHandler: handler.BaseHandler{App: app, DB: db}, + jimengClient: jimengClient, + userService: userService, + uploader: uploader, } } @@ -43,7 +43,6 @@ func (h *AdminJimengHandler) RegisterRoutes() { rg.GET("/jobs/:id", h.JobDetail) rg.POST("/jobs/remove", h.BatchRemove) rg.GET("/stats", h.Stats) - rg.GET("/config", h.GetConfig) rg.POST("/config/update", h.UpdateConfig) } @@ -213,12 +212,6 @@ func (h *AdminJimengHandler) Stats(c *gin.Context) { resp.SUCCESS(c, result) } -// GetConfig 获取即梦AI配置 -func (h *AdminJimengHandler) GetConfig(c *gin.Context) { - jimengConfig := h.jimengService.GetConfig() - resp.SUCCESS(c, jimengConfig) -} - // UpdateConfig 更新即梦AI配置 func (h *AdminJimengHandler) UpdateConfig(c *gin.Context) { var req types.JimengConfig @@ -266,9 +259,9 @@ func (h *AdminJimengHandler) UpdateConfig(c *gin.Context) { // 保存配置 tx := h.DB.Begin() value := utils.JsonEncode(&req) - config := model.Config{Name: "jimeng", Value: value} + config := model.Config{Name: types.ConfigKeyJimeng, Value: value} - err := tx.FirstOrCreate(&config, model.Config{Name: "jimeng"}).Error + err := tx.FirstOrCreate(&config).Error if err != nil { resp.ERROR(c, "保存配置失败: "+err.Error()) return @@ -284,13 +277,14 @@ func (h *AdminJimengHandler) UpdateConfig(c *gin.Context) { } // 更新服务中的客户端配置 - updateErr := h.jimengService.UpdateClientConfig(req.AccessKey, req.SecretKey) - if updateErr != nil { - resp.ERROR(c, updateErr.Error()) + err = h.jimengClient.UpdateConfig(req) + if err != nil { + resp.ERROR(c, err.Error()) tx.Rollback() return } tx.Commit() + h.App.SysConfig.Jimeng = req resp.SUCCESS(c, gin.H{"message": "配置更新成功"}) } diff --git a/api/handler/admin/moderation_handler.go b/api/handler/admin/moderation_handler.go index 55cf25ce..c1d4b29c 100644 --- a/api/handler/admin/moderation_handler.go +++ b/api/handler/admin/moderation_handler.go @@ -8,10 +8,12 @@ package admin // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( + "fmt" "geekai/core" "geekai/core/middleware" "geekai/core/types" "geekai/handler" + "geekai/service/moderation" "geekai/store/model" "geekai/utils" "geekai/utils/resp" @@ -22,10 +24,12 @@ import ( type ModerationHandler struct { handler.BaseHandler + sysConfig *types.SystemConfig + moderationManager *moderation.ServiceManager } -func NewModerationHandler(app *core.AppServer, db *gorm.DB) *ModerationHandler { - return &ModerationHandler{BaseHandler: handler.BaseHandler{DB: db, App: app}} +func NewModerationHandler(app *core.AppServer, db *gorm.DB, sysConfig *types.SystemConfig, moderationManager *moderation.ServiceManager) *ModerationHandler { + return &ModerationHandler{BaseHandler: handler.BaseHandler{DB: db, App: app}, sysConfig: sysConfig, moderationManager: moderationManager} } // RegisterRoutes 注册路由 @@ -39,6 +43,8 @@ func (h *ModerationHandler) RegisterRoutes() { group.GET("remove", h.Remove) group.POST("batch-remove", h.BatchRemove) group.GET("source-list", h.GetSourceList) + group.POST("config", h.UpdateModeration) + group.POST("test", h.TestModeration) } } @@ -229,3 +235,90 @@ func (h *ModerationHandler) GetSourceList(c *gin.Context) { resp.SUCCESS(c, sources) } + +// UpdateModeration 更新文本审查配置 +func (h *ModerationHandler) UpdateModeration(c *gin.Context) { + var data types.ModerationConfig + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + err := h.DB.Where("name", types.ConfigKeyModeration).FirstOrCreate(&model.Config{Name: types.ConfigKeyModeration, Value: utils.JsonEncode(data)}).Error + if err != nil { + resp.ERROR(c, err.Error()) + return + } + + h.moderationManager.UpdateConfig(data) + h.sysConfig.Moderation = data + + resp.SUCCESS(c, data) +} + +// 测试结果类型,用于前端显示 +type ModerationTestResult struct { + IsAbnormal bool `json:"isAbnormal"` + Details []ModerationTestDetail `json:"details"` +} + +type ModerationTestDetail struct { + Category string `json:"category"` + Description string `json:"description"` + Confidence string `json:"confidence"` + IsCategory bool `json:"isCategory"` +} + +// TestModeration 测试文本审查服务 +func (h *ModerationHandler) TestModeration(c *gin.Context) { + var data struct { + Text string `json:"text"` + Service string `json:"service"` + } + + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + if data.Text == "" { + resp.ERROR(c, "测试文本不能为空") + return + } + + // 检查是否启用了文本审查 + if !h.sysConfig.Moderation.Enable { + resp.ERROR(c, "文本审查服务未启用") + return + } + + // 获取当前激活的审核服务 + service := h.moderationManager.GetService() + // 执行文本审核 + result, err := service.Moderate(data.Text) + if err != nil { + resp.ERROR(c, "审核服务调用失败: "+err.Error()) + return + } + + // 转换为前端需要的格式 + testResult := ModerationTestResult{ + IsAbnormal: result.Flagged, + Details: make([]ModerationTestDetail, 0), + } + + // 构建详细信息 + for category, description := range types.ModerationCategories { + score := result.CategoryScores[category] + isCategory := result.Categories[category] + + testResult.Details = append(testResult.Details, ModerationTestDetail{ + Category: category, + Description: description, + Confidence: fmt.Sprintf("%.2f", score), + IsCategory: isCategory, + }) + } + + resp.SUCCESS(c, testResult) +} diff --git a/api/handler/ai3d_handler.go b/api/handler/ai3d_handler.go new file mode 100644 index 00000000..28ad5ff2 --- /dev/null +++ b/api/handler/ai3d_handler.go @@ -0,0 +1,236 @@ +package handler + +import ( + "fmt" + "geekai/core" + "geekai/core/middleware" + "geekai/core/types" + "geekai/service" + "geekai/service/ai3d" + "geekai/store/vo" + "geekai/utils/resp" + "strconv" + + "github.com/gin-gonic/gin" + "gorm.io/gorm" +) + +type AI3DHandler struct { + BaseHandler + service *ai3d.Service + userService *service.UserService +} + +func NewAI3DHandler(app *core.AppServer, db *gorm.DB, service *ai3d.Service, userService *service.UserService) *AI3DHandler { + return &AI3DHandler{ + service: service, + userService: userService, + BaseHandler: BaseHandler{ + App: app, + DB: db, + }, + } +} + +// RegisterRoutes 注册路由 +func (h *AI3DHandler) RegisterRoutes() { + group := h.App.Engine.Group("/api/3d/") + + // 公开接口,不需要授权 + group.GET("models/:type", h.GetModels) + + // 需要用户授权的接口 + group.Use(middleware.UserAuthMiddleware(h.App.Config.Session.SecretKey, h.App.Redis)) + { + group.POST("generate", h.Generate) + group.GET("jobs", h.JobList) + group.GET("job/:id", h.JobDetail) + group.DELETE("job/:id", h.DeleteJob) + group.GET("download/:id", h.Download) + } +} + +// Generate 创建3D生成任务 +func (h *AI3DHandler) Generate(c *gin.Context) { + var request vo.AI3DJobCreate + if err := c.ShouldBindJSON(&request); err != nil { + resp.ERROR(c, "参数错误") + return + } + + // 验证必填参数 + if request.Type == "" || request.Model == "" || request.Power <= 0 { + resp.ERROR(c, "缺少必要参数") + return + } + + // 获取用户ID + userId := h.GetLoginUserId(c) + if userId == 0 { + resp.ERROR(c, "用户未登录") + return + } + + // 创建任务 + job, err := h.service.CreateJob(uint(userId), request) + if err != nil { + resp.ERROR(c, fmt.Sprintf("创建任务失败: %v", err)) + return + } + + resp.SUCCESS(c, gin.H{ + "job_id": job.Id, + "message": "任务创建成功", + }) +} + +// JobList 获取任务列表 +func (h *AI3DHandler) JobList(c *gin.Context) { + userId := h.GetLoginUserId(c) + if userId == 0 { + resp.ERROR(c, "用户未登录") + return + } + + page, _ := strconv.Atoi(c.DefaultQuery("page", "1")) + pageSize, _ := strconv.Atoi(c.DefaultQuery("page_size", "10")) + + if page < 1 { + page = 1 + } + if pageSize < 1 || pageSize > 100 { + pageSize = 10 + } + + jobList, err := h.service.GetJobList(uint(userId), page, pageSize) + if err != nil { + resp.ERROR(c, fmt.Sprintf("获取任务列表失败: %v", err)) + return + } + + resp.SUCCESS(c, jobList) +} + +// JobDetail 获取任务详情 +func (h *AI3DHandler) JobDetail(c *gin.Context) { + userId := h.GetLoginUserId(c) + if userId == 0 { + resp.ERROR(c, "用户未登录") + return + } + + idStr := c.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + resp.ERROR(c, "任务ID格式错误") + return + } + + job, err := h.service.GetJobById(uint(id)) + if err != nil { + resp.ERROR(c, "任务不存在") + return + } + + // 检查权限 + if job.UserId != uint(userId) { + resp.ERROR(c, "无权限访问此任务") + return + } + + // 转换为VO + jobVO := vo.AI3DJob{ + Id: job.Id, + UserId: job.UserId, + Type: job.Type, + Power: job.Power, + TaskId: job.TaskId, + ImgURL: job.FileURL, + PreviewURL: job.PreviewURL, + Model: job.Model, + Status: job.Status, + ErrMsg: job.ErrMsg, + Params: job.Params, + CreatedAt: job.CreatedAt.Unix(), + UpdatedAt: job.UpdatedAt.Unix(), + } + + resp.SUCCESS(c, jobVO) +} + +// DeleteJob 删除任务 +func (h *AI3DHandler) DeleteJob(c *gin.Context) { + userId := h.GetLoginUserId(c) + if userId == 0 { + resp.ERROR(c, "用户未登录") + return + } + + idStr := c.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + resp.ERROR(c, "任务ID格式错误") + return + } + + err = h.service.DeleteJob(uint(id), uint(userId)) + if err != nil { + resp.ERROR(c, fmt.Sprintf("删除任务失败: %v", err)) + return + } + + resp.SUCCESS(c, gin.H{"message": "删除成功"}) +} + +// Download 下载3D模型 +func (h *AI3DHandler) Download(c *gin.Context) { + userId := h.GetLoginUserId(c) + if userId == 0 { + resp.ERROR(c, "用户未登录") + return + } + + idStr := c.Param("id") + id, err := strconv.ParseUint(idStr, 10, 32) + if err != nil { + resp.ERROR(c, "任务ID格式错误") + return + } + + job, err := h.service.GetJobById(uint(id)) + if err != nil { + resp.ERROR(c, "任务不存在") + return + } + + // 检查权限 + if job.UserId != uint(userId) { + resp.ERROR(c, "无权限访问此任务") + return + } + + // 检查任务状态 + if job.Status != types.AI3DJobStatusCompleted { + resp.ERROR(c, "任务尚未完成") + return + } + + if job.FileURL == "" { + resp.ERROR(c, "模型文件不存在") + return + } + + // 重定向到下载链接 + c.Redirect(302, job.FileURL) +} + +// GetModels 获取支持的模型列表 +func (h *AI3DHandler) GetModels(c *gin.Context) { + models := h.service.GetSupportedModels() + if len(models) == 0 { + resp.ERROR(c, "无可用3D模型") + return + } + + resp.SUCCESS(c, models) +} diff --git a/api/handler/jimeng_handler.go b/api/handler/jimeng_handler.go index b2b9670e..439da831 100644 --- a/api/handler/jimeng_handler.go +++ b/api/handler/jimeng_handler.go @@ -435,7 +435,7 @@ func (h *JimengHandler) Retry(c *gin.Context) { // getPowerFromConfig 从配置中获取指定类型的算力消耗 func (h *JimengHandler) getPowerFromConfig(taskType model.JMTaskType) int { - config := h.jimengService.GetConfig() + config := h.App.SysConfig.Jimeng switch taskType { case model.JMTaskTypeTextToImage: @@ -457,7 +457,7 @@ func (h *JimengHandler) getPowerFromConfig(taskType model.JMTaskType) int { // GetPowerConfig 获取即梦各任务类型算力消耗配置 func (h *JimengHandler) GetPowerConfig(c *gin.Context) { - config := h.jimengService.GetConfig() + config := h.App.SysConfig.Jimeng resp.SUCCESS(c, gin.H{ "text_to_image": config.Power.TextToImage, "image_to_image": config.Power.ImageToImage, diff --git a/api/main.go b/api/main.go index 33466274..d6092dd7 100644 --- a/api/main.go +++ b/api/main.go @@ -16,6 +16,7 @@ import ( "geekai/handler/admin" logger2 "geekai/logger" "geekai/service" + "geekai/service/ai3d" "geekai/service/dalle" "geekai/service/jimeng" "geekai/service/mj" @@ -210,10 +211,19 @@ func main() { }), // 即梦AI 服务 + fx.Provide(jimeng.NewClient), fx.Provide(jimeng.NewService), fx.Invoke(func(service *jimeng.Service) { service.Start() }), + + // 3D生成服务 + fx.Provide(ai3d.NewTencent3DClient), + fx.Provide(ai3d.NewGitee3DClient), + fx.Provide(ai3d.NewService), + fx.Invoke(func(s *ai3d.Service) { + s.Run() + }), fx.Provide(service.NewSnowflake), // 创建短信服务 @@ -383,6 +393,16 @@ func main() { h.RegisterRoutes() }), + // 3D生成处理器 + fx.Provide(handler.NewAI3DHandler), + fx.Invoke(func(s *core.AppServer, h *handler.AI3DHandler) { + h.RegisterRoutes() + }), + fx.Provide(admin.NewAI3DHandler), + fx.Invoke(func(s *core.AppServer, h *admin.AI3DHandler) { + h.RegisterRoutes() + }), + // 即梦AI 路由 fx.Invoke(func(s *core.AppServer, h *handler.JimengHandler) { h.RegisterRoutes() diff --git a/api/service/ai3d/gitee_client.go b/api/service/ai3d/gitee_client.go new file mode 100644 index 00000000..f83650b8 --- /dev/null +++ b/api/service/ai3d/gitee_client.go @@ -0,0 +1,150 @@ +package ai3d + +import ( + "encoding/json" + "fmt" + "geekai/core/types" + "time" + + "github.com/imroc/req/v3" +) + +type Gitee3DClient struct { + httpClient *req.Client + config types.Gitee3DConfig + apiURL string +} + +type Gitee3DParams struct { + Prompt string `json:"prompt"` // 文本提示词 + ImageURL string `json:"image_url"` // 输入图片URL + ResultFormat string `json:"result_format"` // 输出格式 +} + +type Gitee3DResponse struct { + Code int `json:"code"` + Message string `json:"message"` + Data struct { + TaskID string `json:"task_id"` + } `json:"data"` +} + +type Gitee3DQueryResponse struct { + Code int `json:"code"` + Message string `json:"message"` + Data struct { + Status string `json:"status"` + Progress int `json:"progress"` + ResultURL string `json:"result_url"` + PreviewURL string `json:"preview_url"` + ErrorMsg string `json:"error_msg"` + } `json:"data"` +} + +func NewGitee3DClient(sysConfig *types.SystemConfig) *Gitee3DClient { + return &Gitee3DClient{ + httpClient: req.C().SetTimeout(time.Minute * 3), + config: sysConfig.AI3D.Gitee, + apiURL: "https://ai.gitee.com/v1/async/image-to-3d", + } +} + +func (c *Gitee3DClient) UpdateConfig(config types.Gitee3DConfig) { + c.config = config +} + +// SubmitJob 提交3D生成任务 +func (c *Gitee3DClient) SubmitJob(params Gitee3DParams) (string, error) { + requestBody := map[string]any{ + "prompt": params.Prompt, + "image_url": params.ImageURL, + "result_format": params.ResultFormat, + } + + response, err := c.httpClient.R(). + SetHeader("Authorization", "Bearer "+c.config.APIKey). + SetHeader("Content-Type", "application/json"). + SetBody(requestBody). + Post(c.apiURL + "/async/image-to-3d") + + if err != nil { + return "", fmt.Errorf("failed to submit gitee 3D job: %v", err) + } + + var giteeResp Gitee3DResponse + if err := json.Unmarshal(response.Bytes(), &giteeResp); err != nil { + return "", fmt.Errorf("failed to parse gitee response: %v", err) + } + + if giteeResp.Code != 0 { + return "", fmt.Errorf("gitee API error: %s", giteeResp.Message) + } + + if giteeResp.Data.TaskID == "" { + return "", fmt.Errorf("no task ID returned from gitee 3D API") + } + + return giteeResp.Data.TaskID, nil +} + +// QueryJob 查询任务状态 +func (c *Gitee3DClient) QueryJob(taskId string) (*types.AI3DJobResult, error) { + response, err := c.httpClient.R(). + SetHeader("Authorization", "Bearer "+c.config.APIKey). + Get(fmt.Sprintf("%s/task/%s/get", c.apiURL, taskId)) + + if err != nil { + return nil, fmt.Errorf("failed to query gitee 3D job: %v", err) + } + + var giteeResp Gitee3DQueryResponse + if err := json.Unmarshal(response.Bytes(), &giteeResp); err != nil { + return nil, fmt.Errorf("failed to parse gitee query response: %v", err) + } + + if giteeResp.Code != 0 { + return nil, fmt.Errorf("gitee API error: %s", giteeResp.Message) + } + + result := &types.AI3DJobResult{ + JobId: taskId, + Status: c.convertStatus(giteeResp.Data.Status), + Progress: giteeResp.Data.Progress, + } + + // 根据状态设置结果 + switch giteeResp.Data.Status { + case "completed": + result.FileURL = giteeResp.Data.ResultURL + result.PreviewURL = giteeResp.Data.PreviewURL + case "failed": + result.ErrorMsg = giteeResp.Data.ErrorMsg + } + + return result, nil +} + +// convertStatus 转换Gitee状态到系统状态 +func (c *Gitee3DClient) convertStatus(giteeStatus string) string { + switch giteeStatus { + case "pending": + return types.AI3DJobStatusPending + case "processing": + return types.AI3DJobStatusProcessing + case "completed": + return types.AI3DJobStatusCompleted + case "failed": + return types.AI3DJobStatusFailed + default: + return types.AI3DJobStatusPending + } +} + +// GetSupportedModels 获取支持的模型列表 +func (c *Gitee3DClient) GetSupportedModels() []types.AI3DModel { + return []types.AI3DModel{ + {Name: "Hunyuan3D-2", Power: 100, Formats: []string{"GLB"}, Desc: "Hunyuan3D-2 是腾讯混元团队推出的高质量 3D 生成模型,具备高保真度、细节丰富和高效生成的特点,可快速将文本或图像转换为逼真的 3D 物体。"}, + {Name: "Step1X-3D", Power: 55, Formats: []string{"GLB", "STL"}, Desc: "Step1X-3D 是一款由阶跃星辰(StepFun)与光影焕像(LightIllusions)联合研发并开源的高保真 3D 生成模型,专为高质量、可控的 3D 内容创作而设计。"}, + {Name: "Hi3DGen", Power: 35, Formats: []string{"GLB", "STL"}, Desc: "Hi3DGen 是一个 AI 工具,它可以把你上传的普通图片,智能转换成有“立体感”的图片(法线图),常用于制作 3D 效果,比如游戏建模、虚拟现实、动画制作等。"}, + } +} diff --git a/api/service/ai3d/service.go b/api/service/ai3d/service.go new file mode 100644 index 00000000..f0238902 --- /dev/null +++ b/api/service/ai3d/service.go @@ -0,0 +1,327 @@ +package ai3d + +import ( + "encoding/json" + "fmt" + "geekai/core/types" + logger2 "geekai/logger" + "geekai/store" + "geekai/store/model" + "geekai/store/vo" + "time" + + "github.com/go-redis/redis/v8" + "gorm.io/gorm" +) + +var logger = logger2.GetLogger() + +// Service 3D生成服务 +type Service struct { + db *gorm.DB + taskQueue *store.RedisQueue + tencentClient *Tencent3DClient + giteeClient *Gitee3DClient +} + +// NewService 创建3D生成服务 +func NewService(db *gorm.DB, redisCli *redis.Client, tencentClient *Tencent3DClient, giteeClient *Gitee3DClient) *Service { + return &Service{ + db: db, + taskQueue: store.NewRedisQueue("3D_Task_Queue", redisCli), + tencentClient: tencentClient, + giteeClient: giteeClient, + } +} + +// CreateJob 创建3D生成任务 +func (s *Service) CreateJob(userId uint, request vo.AI3DJobCreate) (*model.AI3DJob, error) { + // 创建任务记录 + job := &model.AI3DJob{ + UserId: userId, + Type: request.Type, + Power: request.Power, + Model: request.Model, + Status: types.AI3DJobStatusPending, + } + + // 序列化参数 + params := map[string]any{ + "prompt": request.Prompt, + "image_url": request.ImageURL, + "model": request.Model, + "power": request.Power, + } + paramsJSON, _ := json.Marshal(params) + job.Params = string(paramsJSON) + + // 保存到数据库 + if err := s.db.Create(job).Error; err != nil { + return nil, fmt.Errorf("failed to create 3D job: %v", err) + } + + // 将任务添加到队列 + s.PushTask(job) + + return job, nil +} + +// PushTask 将任务添加到队列 +func (s *Service) PushTask(job *model.AI3DJob) { + logger.Infof("add a new 3D task to the queue: %+v", job) + if err := s.taskQueue.RPush(job); err != nil { + logger.Errorf("push 3D task to queue failed: %v", err) + } +} + +// Run 启动任务处理器 +func (s *Service) Run() { + // 将数据库中未完成的任务加载到队列 + var jobs []model.AI3DJob + s.db.Where("status IN ?", []string{types.AI3DJobStatusPending, types.AI3DJobStatusProcessing}).Find(&jobs) + for _, job := range jobs { + s.PushTask(&job) + } + + logger.Info("Starting 3D job consumer...") + go func() { + for { + var job model.AI3DJob + err := s.taskQueue.LPop(&job) + if err != nil { + logger.Errorf("taking 3D task with error: %v", err) + continue + } + logger.Infof("handle a new 3D task: %+v", job) + go func() { + if err := s.processJob(&job); err != nil { + logger.Errorf("error processing 3D job: %v", err) + s.updateJobStatus(&job, types.AI3DJobStatusFailed, 0, err.Error()) + } + }() + } + }() +} + +// processJob 处理3D任务 +func (s *Service) processJob(job *model.AI3DJob) error { + // 更新状态为处理中 + s.updateJobStatus(job, types.AI3DJobStatusProcessing, 10, "") + + // 解析参数 + var params map[string]any + if err := json.Unmarshal([]byte(job.Params), ¶ms); err != nil { + return fmt.Errorf("failed to parse job params: %v", err) + } + + var taskId string + var err error + + // 根据类型选择客户端 + switch job.Type { + case "tencent": + if s.tencentClient == nil { + return fmt.Errorf("tencent 3D client not initialized") + } + tencentParams := Tencent3DParams{ + Prompt: s.getString(params, "prompt"), + ImageURL: s.getString(params, "image_url"), + ResultFormat: job.Model, + EnablePBR: false, + } + taskId, err = s.tencentClient.SubmitJob(tencentParams) + case "gitee": + if s.giteeClient == nil { + return fmt.Errorf("gitee 3D client not initialized") + } + giteeParams := Gitee3DParams{ + Prompt: s.getString(params, "prompt"), + ImageURL: s.getString(params, "image_url"), + ResultFormat: job.Model, + } + taskId, err = s.giteeClient.SubmitJob(giteeParams) + default: + return fmt.Errorf("unsupported 3D API type: %s", job.Type) + } + + if err != nil { + return fmt.Errorf("failed to submit 3D job: %v", err) + } + + // 更新任务ID + job.TaskId = taskId + s.db.Model(job).Update("task_id", taskId) + + // 开始轮询任务状态 + go s.pollJobStatus(job) + + return nil +} + +// pollJobStatus 轮询任务状态 +func (s *Service) pollJobStatus(job *model.AI3DJob) { + ticker := time.NewTicker(10 * time.Second) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + result, err := s.queryJobStatus(job) + if err != nil { + logger.Errorf("failed to query job status: %v", err) + continue + } + + // 更新进度 + s.updateJobStatus(job, result.Status, result.Progress, result.ErrorMsg) + + // 如果任务完成或失败,停止轮询 + if result.Status == types.AI3DJobStatusCompleted || result.Status == types.AI3DJobStatusFailed { + if result.Status == types.AI3DJobStatusCompleted { + // 更新结果文件URL + s.db.Model(job).Updates(map[string]interface{}{ + "img_url": result.FileURL, + "preview_url": result.PreviewURL, + }) + } + return + } + } + } +} + +// queryJobStatus 查询任务状态 +func (s *Service) queryJobStatus(job *model.AI3DJob) (*types.AI3DJobResult, error) { + switch job.Type { + case "tencent": + if s.tencentClient == nil { + return nil, fmt.Errorf("tencent 3D client not initialized") + } + return s.tencentClient.QueryJob(job.TaskId) + case "gitee": + if s.giteeClient == nil { + return nil, fmt.Errorf("gitee 3D client not initialized") + } + return s.giteeClient.QueryJob(job.TaskId) + default: + return nil, fmt.Errorf("unsupported 3D API type: %s", job.Type) + } +} + +// updateJobStatus 更新任务状态 +func (s *Service) updateJobStatus(job *model.AI3DJob, status string, progress int, errMsg string) { + updates := map[string]interface{}{ + "status": status, + "progress": progress, + "updated_at": time.Now(), + } + if errMsg != "" { + updates["err_msg"] = errMsg + } + + if err := s.db.Model(job).Updates(updates).Error; err != nil { + logger.Errorf("failed to update job status: %v", err) + } +} + +// GetJobList 获取任务列表 +func (s *Service) GetJobList(userId uint, page, pageSize int) (*vo.Page, error) { + var total int64 + var jobs []model.AI3DJob + + // 查询总数 + if err := s.db.Model(&model.AI3DJob{}).Where("user_id = ?", userId).Count(&total).Error; err != nil { + return nil, err + } + + // 查询任务列表 + offset := (page - 1) * pageSize + if err := s.db.Where("user_id = ?", userId).Order("created_at DESC").Offset(offset).Limit(pageSize).Find(&jobs).Error; err != nil { + return nil, err + } + + // 转换为VO + var jobList []vo.AI3DJob + for _, job := range jobs { + jobVO := vo.AI3DJob{ + Id: job.Id, + UserId: job.UserId, + Type: job.Type, + Power: job.Power, + TaskId: job.TaskId, + ImgURL: job.FileURL, + PreviewURL: job.PreviewURL, + Model: job.Model, + Status: job.Status, + ErrMsg: job.ErrMsg, + Params: job.Params, + CreatedAt: job.CreatedAt.Unix(), + UpdatedAt: job.UpdatedAt.Unix(), + } + jobList = append(jobList, jobVO) + } + + return &vo.Page{ + Page: page, + PageSize: pageSize, + Total: total, + Items: jobList, + }, nil +} + +// GetJobById 根据ID获取任务 +func (s *Service) GetJobById(id uint) (*model.AI3DJob, error) { + var job model.AI3DJob + if err := s.db.Where("id = ?", id).First(&job).Error; err != nil { + return nil, err + } + return &job, nil +} + +// DeleteJob 删除任务 +func (s *Service) DeleteJob(id uint, userId uint) error { + var job model.AI3DJob + if err := s.db.Where("id = ? AND user_id = ?", id, userId).First(&job).Error; err != nil { + return err + } + + // 如果任务已完成,退还算力 + if job.Status == types.AI3DJobStatusCompleted { + // TODO: 实现算力退还逻辑 + logger2.GetLogger().Infof("should refund power %d for user %d", job.Power, userId) + } + + return s.db.Delete(&job).Error +} + +// GetSupportedModels 获取支持的模型列表 +func (s *Service) GetSupportedModels() map[string][]types.AI3DModel { + + models := make(map[string][]types.AI3DModel) + if s.tencentClient != nil { + models["tencent"] = s.tencentClient.GetSupportedModels() + } + if s.giteeClient != nil { + models["gitee"] = s.giteeClient.GetSupportedModels() + } + return models +} + +func (s *Service) UpdateConfig(config types.AI3DConfig) { + if s.tencentClient != nil { + s.tencentClient.UpdateConfig(config.Tencent) + } + if s.giteeClient != nil { + s.giteeClient.UpdateConfig(config.Gitee) + } +} + +// getString 从map中获取字符串值 +func (s *Service) getString(params map[string]interface{}, key string) string { + if val, ok := params[key]; ok { + if str, ok := val.(string); ok { + return str + } + } + return "" +} diff --git a/api/service/ai3d/tencent_client.go b/api/service/ai3d/tencent_client.go new file mode 100644 index 00000000..f309d69e --- /dev/null +++ b/api/service/ai3d/tencent_client.go @@ -0,0 +1,158 @@ +package ai3d + +import ( + "fmt" + "geekai/core/types" + + tencent3d "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/ai3d/v20250513" + tencentcloud "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" +) + +type Tencent3DClient struct { + client *tencent3d.Client + config types.Tencent3DConfig +} + +type Tencent3DParams struct { + Prompt string `json:"prompt"` // 文本提示词 + ImageURL string `json:"image_url"` // 输入图片URL + ResultFormat string `json:"result_format"` // 输出格式 + EnablePBR bool `json:"enable_pbr"` // 是否开启PBR材质 + MultiViewImages []ViewImage `json:"multi_view_images,omitempty"` // 多视角图片 +} + +type ViewImage struct { + ViewType string `json:"view_type"` // 视角类型 (left/right/back) + ViewImageURL string `json:"view_image_url"` // 图片URL +} + +func NewTencent3DClient(sysConfig *types.SystemConfig) (*Tencent3DClient, error) { + config := sysConfig.AI3D.Tencent + credential := tencentcloud.NewCredential(config.SecretId, config.SecretKey) + cpf := profile.NewClientProfile() + cpf.HttpProfile.Endpoint = "ai3d.tencentcloudapi.com" + + client, err := tencent3d.NewClient(credential, config.Region, cpf) + if err != nil { + return nil, fmt.Errorf("failed to create tencent 3D client: %v", err) + } + + return &Tencent3DClient{ + client: client, + config: config, + }, nil +} + +func (c *Tencent3DClient) UpdateConfig(config types.Tencent3DConfig) error { + c.config = config + credential := tencentcloud.NewCredential(config.SecretId, config.SecretKey) + cpf := profile.NewClientProfile() + cpf.HttpProfile.Endpoint = "ai3d.tencentcloudapi.com" + + client, err := tencent3d.NewClient(credential, config.Region, cpf) + if err != nil { + return fmt.Errorf("failed to create tencent 3D client: %v", err) + } + c.client = client + return nil +} + +// SubmitJob 提交3D生成任务 +func (c *Tencent3DClient) SubmitJob(params Tencent3DParams) (string, error) { + request := tencent3d.NewSubmitHunyuanTo3DJobRequest() + + if params.Prompt != "" { + request.Prompt = tencentcloud.StringPtr(params.Prompt) + } + + if params.ImageURL != "" { + request.ImageUrl = tencentcloud.StringPtr(params.ImageURL) + } + + if params.ResultFormat != "" { + request.ResultFormat = tencentcloud.StringPtr(params.ResultFormat) + } + + request.EnablePBR = tencentcloud.BoolPtr(params.EnablePBR) + + if len(params.MultiViewImages) > 0 { + var viewImages []*tencent3d.ViewImage + for _, img := range params.MultiViewImages { + viewImage := &tencent3d.ViewImage{ + ViewType: tencentcloud.StringPtr(img.ViewType), + ViewImageUrl: tencentcloud.StringPtr(img.ViewImageURL), + } + viewImages = append(viewImages, viewImage) + } + request.MultiViewImages = viewImages + } + + response, err := c.client.SubmitHunyuanTo3DJob(request) + if err != nil { + return "", fmt.Errorf("failed to submit tencent 3D job: %v", err) + } + + if response.Response.JobId == nil { + return "", fmt.Errorf("no job ID returned from tencent 3D API") + } + + return *response.Response.JobId, nil +} + +// QueryJob 查询任务状态 +func (c *Tencent3DClient) QueryJob(jobId string) (*types.AI3DJobResult, error) { + request := tencent3d.NewQueryHunyuanTo3DJobRequest() + request.JobId = tencentcloud.StringPtr(jobId) + + response, err := c.client.QueryHunyuanTo3DJob(request) + if err != nil { + return nil, fmt.Errorf("failed to query tencent 3D job: %v", err) + } + + result := &types.AI3DJobResult{ + JobId: jobId, + Status: *response.Response.Status, + Progress: 0, + } + + // 根据状态设置进度 + switch *response.Response.Status { + case "WAIT": + result.Status = "pending" + result.Progress = 10 + case "RUN": + result.Status = "processing" + result.Progress = 50 + case "DONE": + result.Status = "completed" + result.Progress = 100 + // 处理结果文件 + if len(response.Response.ResultFile3Ds) > 0 { + for _, file := range response.Response.ResultFile3Ds { + if file.Url != nil { + result.FileURL = *file.Url + } + if file.PreviewImageUrl != nil { + result.PreviewURL = *file.PreviewImageUrl + } + break // 取第一个文件 + } + } + case "FAIL": + result.Status = "failed" + result.Progress = 0 + if response.Response.ErrorMessage != nil { + result.ErrorMsg = *response.Response.ErrorMessage + } + } + + return result, nil +} + +// GetSupportedModels 获取支持的模型列表 +func (c *Tencent3DClient) GetSupportedModels() []types.AI3DModel { + return []types.AI3DModel{ + {Name: "Hunyuan3D-3", Power: 500, Formats: []string{"OBJ", "GLB", "STL", "USDZ", "FBX", "MP4"}, Desc: "Hunyuan3D 是腾讯混元团队推出的高质量 3D 生成模型,具备高保真度、细节丰富和高效生成的特点,可快速将文本或图像转换为逼真的 3D 物体。"}, + } +} diff --git a/api/service/jimeng/client.go b/api/service/jimeng/client.go index 24fa0126..87864488 100644 --- a/api/service/jimeng/client.go +++ b/api/service/jimeng/client.go @@ -3,8 +3,10 @@ package jimeng import ( "encoding/json" "fmt" + "geekai/core/types" "net/http" "net/url" + "strings" "github.com/volcengine/volc-sdk-golang/base" "github.com/volcengine/volc-sdk-golang/service/visual" @@ -13,14 +15,22 @@ import ( // Client 即梦API客户端 type Client struct { visual *visual.Visual + config types.JimengConfig } // NewClient 创建即梦API客户端 -func NewClient(accessKey, secretKey string) *Client { +func NewClient(sysConfig *types.SystemConfig) *Client { + + client := &Client{} + client.UpdateConfig(sysConfig.Jimeng) + return client +} + +func (c *Client) UpdateConfig(config types.JimengConfig) error { // 使用官方SDK的visual实例 visualInstance := visual.NewInstance() - visualInstance.Client.SetAccessKey(accessKey) - visualInstance.Client.SetSecretKey(secretKey) + visualInstance.Client.SetAccessKey(config.AccessKey) + visualInstance.Client.SetSecretKey(config.SecretKey) // 添加即梦AI专有的API配置 jimengApis := map[string]*base.ApiInfo{ @@ -55,9 +65,32 @@ func NewClient(accessKey, secretKey string) *Client { visualInstance.Client.ApiInfoList[name] = info } - return &Client{ - visual: visualInstance, + c.config = config + c.visual = visualInstance + + return c.testConnection() +} + +// testConnection 测试即梦AI连接 +func (c *Client) testConnection() error { + + // 使用一个简单的查询任务来测试连接 + testReq := &QueryTaskRequest{ + ReqKey: "test_connection", + TaskId: "test_task_id_12345", } + + _, err := c.QueryTask(testReq) + // 即使任务不存在,只要不是认证错误就说明连接正常 + if err != nil { + // 检查是否是认证错误 + if strings.Contains(err.Error(), "InvalidAccessKey") { + return fmt.Errorf("认证失败,请检查AccessKey和SecretKey是否正确") + } + // 其他错误(如任务不存在)说明连接正常 + return nil + } + return nil } // SubmitTask 提交异步任务 diff --git a/api/service/jimeng/service.go b/api/service/jimeng/service.go index f857aba4..ca80c712 100644 --- a/api/service/jimeng/service.go +++ b/api/service/jimeng/service.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "strconv" - "strings" "time" "gorm.io/gorm" @@ -16,8 +15,6 @@ import ( "geekai/store/model" "geekai/utils" - "geekai/core/types" - "github.com/go-redis/redis/v8" ) @@ -36,17 +33,8 @@ type Service struct { } // NewService 创建即梦服务 -func NewService(db *gorm.DB, redisCli *redis.Client, uploader *oss.UploaderManager) *Service { +func NewService(db *gorm.DB, redisCli *redis.Client, uploader *oss.UploaderManager, client *Client) *Service { taskQueue := store.NewRedisQueue("JimengTaskQueue", redisCli) - // 从数据库加载配置 - var config model.Config - db.Where("name = ?", "Jimeng").First(&config) - var jimengConfig types.JimengConfig - if config.Id > 0 { - _ = utils.JsonDecode(config.Value, &jimengConfig) - } - client := NewClient(jimengConfig.AccessKey, jimengConfig.SecretKey) - ctx, cancel := context.WithCancel(context.Background()) return &Service{ db: db, @@ -522,77 +510,3 @@ func (s *Service) GetJob(jobId uint) (*model.JimengJob, error) { } return &job, nil } - -// testConnection 测试即梦AI连接 -func (s *Service) testConnection(accessKey, secretKey string) error { - testClient := NewClient(accessKey, secretKey) - - // 使用一个简单的查询任务来测试连接 - testReq := &QueryTaskRequest{ - ReqKey: "test_connection", - TaskId: "test_task_id_12345", - } - - _, err := testClient.QueryTask(testReq) - // 即使任务不存在,只要不是认证错误就说明连接正常 - if err != nil { - // 检查是否是认证错误 - if strings.Contains(err.Error(), "InvalidAccessKey") { - return fmt.Errorf("认证失败,请检查AccessKey和SecretKey是否正确") - } - // 其他错误(如任务不存在)说明连接正常 - return nil - } - return nil -} - -// UpdateClientConfig 更新客户端配置 -func (s *Service) UpdateClientConfig(accessKey, secretKey string) error { - // 创建新的客户端 - newClient := NewClient(accessKey, secretKey) - - // 测试新客户端是否可用 - err := s.testConnection(accessKey, secretKey) - if err != nil { - return err - } - - // 更新客户端 - s.client = newClient - return nil -} - -var defaultPower = types.JimengPower{ - TextToImage: 20, - ImageToImage: 20, - ImageEdit: 20, - ImageEffects: 20, - TextToVideo: 300, - ImageToVideo: 300, -} - -// GetConfig 获取即梦AI配置 -func (s *Service) GetConfig() *types.JimengConfig { - var config model.Config - err := s.db.Where("name", "jimeng").First(&config).Error - if err != nil { - // 如果配置不存在,返回默认配置 - return &types.JimengConfig{ - AccessKey: "", - SecretKey: "", - Power: defaultPower, - } - } - - var jimengConfig types.JimengConfig - err = utils.JsonDecode(config.Value, &jimengConfig) - if err != nil { - return &types.JimengConfig{ - AccessKey: "", - SecretKey: "", - Power: defaultPower, - } - } - - return &jimengConfig -} diff --git a/api/service/migration_service.go b/api/service/migration_service.go index 26292b27..47027f1c 100644 --- a/api/service/migration_service.go +++ b/api/service/migration_service.go @@ -154,6 +154,24 @@ func (s *MigrationService) MigrateConfigContent() error { return fmt.Errorf("迁移配置内容失败: %v", err) } + // 3D生成配置 + if err := s.saveConfig(types.ConfigKeyAI3D, map[string]any{ + "tencent": map[string]any{ + "access_key": "", + "secret_key": "", + "region": "", + "enabled": false, + "models": make([]types.AI3DModel, 0), + }, + "gitee": map[string]any{ + "api_key": "", + "enabled": false, + "models": make([]types.AI3DModel, 0), + }, + }); err != nil { + return fmt.Errorf("迁移配置内容失败: %v", err) + } + return nil } @@ -161,6 +179,8 @@ func (s *MigrationService) MigrateConfigContent() error { func (s *MigrationService) TableMigration() { // 新数据表 s.db.AutoMigrate(&model.Moderation{}) + s.db.AutoMigrate(&model.AI3DJob{}) + // 订单字段整理 if s.db.Migrator().HasColumn(&model.Order{}, "pay_type") { s.db.Migrator().RenameColumn(&model.Order{}, "pay_type", "channel") diff --git a/api/store/model/ai3d_job.go b/api/store/model/ai3d_job.go new file mode 100644 index 00000000..429f459d --- /dev/null +++ b/api/store/model/ai3d_job.go @@ -0,0 +1,23 @@ +package model + +import "time" + +type AI3DJob struct { + Id uint `gorm:"column:id;primaryKey;autoIncrement" json:"id"` + UserId uint `gorm:"column:user_id;type:int(11);not null;comment:用户ID" json:"user_id"` + Type string `gorm:"column:type;type:varchar(20);not null;comment:API类型 (tencent/gitee)" json:"type"` + Power int `gorm:"column:power;type:int(11);not null;comment:消耗算力" json:"power"` + TaskId string `gorm:"column:task_id;type:varchar(100);comment:第三方任务ID" json:"task_id"` + FileURL string `gorm:"column:file_url;type:varchar(1024);comment:生成的3D模型文件地址" json:"file_url"` + PreviewURL string `gorm:"column:preview_url;type:varchar(1024);comment:预览图片地址" json:"preview_url"` + Model string `gorm:"column:model;type:varchar(50);comment:使用的3D模型类型" json:"model"` + Status string `gorm:"column:status;type:varchar(20);not null;default:pending;comment:任务状态" json:"status"` + ErrMsg string `gorm:"column:err_msg;type:varchar(1024);comment:错误信息" json:"err_msg"` + Params string `gorm:"column:params;type:text;comment:任务参数(JSON格式)" json:"params"` + CreatedAt time.Time `gorm:"column:created_at;type:datetime;not null" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at;type:datetime;not null" json:"updated_at"` +} + +func (m *AI3DJob) TableName() string { + return "geekai_3d_jobs" +} diff --git a/api/store/vo/ai3d_job.go b/api/store/vo/ai3d_job.go new file mode 100644 index 00000000..e4ecebf1 --- /dev/null +++ b/api/store/vo/ai3d_job.go @@ -0,0 +1,32 @@ +package vo + +type AI3DJob struct { + Id uint `json:"id"` + UserId uint `json:"user_id"` + Type string `json:"type"` + Power int `json:"power"` + TaskId string `json:"task_id"` + ImgURL string `json:"img_url"` + PreviewURL string `json:"preview_url"` + Model string `json:"model"` + Status string `json:"status"` + ErrMsg string `json:"err_msg"` + Params string `json:"params"` + CreatedAt int64 `json:"created_at"` + UpdatedAt int64 `json:"updated_at"` +} + +type AI3DJobCreate struct { + Type string `json:"type" binding:"required"` // API类型 (tencent/gitee) + Model string `json:"model" binding:"required"` // 3D模型类型 + Prompt string `json:"prompt"` // 文本提示词 + ImageURL string `json:"image_url"` // 输入图片URL + Power int `json:"power" binding:"required"` // 消耗算力 +} + +type ThreeDJobList struct { + Page int `json:"page"` + PageSize int `json:"page_size"` + Total int `json:"total"` + List []AI3DJob `json:"list"` +} diff --git a/docs.md b/docs.md deleted file mode 100644 index ee474d67..00000000 --- a/docs.md +++ /dev/null @@ -1,122 +0,0 @@ -## 开发 3D 图片生成功能 - -对接 3D 图片生成接口,为当前系统添加 3D 模型生成功能,默认支持腾讯云和 Gitee 的图生 3D API 接口。 - -## 要求 - -1. 完成数据库设计,后端 API 设计,前端页面设计。 -2. 完成前端功能页面以及后台管理页面,具体设计结构可以参考即梦 AI,在对应的模块建立独立的模块 。 -3. 页面设计要精美,但是整体风格要跟整站风格一致。 -4. 支持前端 3D 模型预览,支持 3D 模型下载。 - -## 腾讯云图生 3D API 接口文档 - -1. 提交任务: https://cloud.tencent.com/document/product/1804/120826 -2. 查询任务: https://cloud.tencent.com/document/product/1804/120827 -3. Golang SDK: https://gitee.com/TencentCloud/tencentcloud-sdk-go/blob/master/tencentcloud/ai3d/v20250513/client.go 依赖已经安装到本地了 - -## Gitee 图生 3D API 接口文档 - -1. 提交任务: https://ai.gitee.com/docs/openapi/v1#tag/3d-%E7%94%9F%E6%88%90/post/async/image-to-3d -2. 查询任务:https://ai.gitee.com/docs/openapi/v1#tag/%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1/get/task/{task_id}/get - -首先,你需要认真阅读上述接口文档,然后按照接口文档的示例代码实现腾讯云和 Gitee 的图生 3D API 接口,并且将接口集成到现有的系统中。 - -📋 功能概述 - - 为现有的GeekAI-Plus系统添加3D图片生成功能,集成腾讯云和Gitee的图生3D API接口,包含完整的前后端功能和管理界面。 - - 🗄️ 数据库设计 - - 新增数据表:geekai_3d_jobs - - id (uint): 主键 - - type (string): API类型 (tencent/gitee) - - user_id (uint): 用户ID - - power (int): 消耗算力 - - task_id (string): 第三方任务ID - - img_url (string): 生成的3D模型文件地址 - - model (string): 使用的3D模型类型 - - status (string): 任务状态 - - err_msg (string): 错误信息 - - params (JSON): 任务参数(包含输入图片、提示词等所有参数) - - created_at (int64): 创建时间 - - 🔧 后端API实现 - - 路由结构:/api/3d/* - - POST /api/3d/generate - 创建3D生成任务 - - GET /api/3d/jobs - 获取任务列表 - - GET /api/3d/job/{id} - 获取任务详情 - - GET /api/3d/download/{id} - 下载3D模型 - - DELETE /api/3d/job/{id} - 删除任务 - - 核心服务: - - service/3d/tencent_client.go - 腾讯云3D API客户端 - - service/3d/gitee_client.go - Gitee 3D API客户端 - - service/3d/service.go - 3D生成服务统一接口 - - handler/3d_handler.go - HTTP处理器 - - store/vo/3d_job.go - 数据模型 - - 🎨 前端界面设计 - - 用户端页面:/3d - 3D生成主页面 - - 参考JiMeng.vue的设计风格和布局 - - 使用CustomTab组件分离平台参数: - - Tab 1: "魔力方舟" (Gitee平台参数) - - Tab 2: "腾讯混元" (腾讯云平台参数) - - 每个Tab内包含: - - 图片上传区域 - - 模型选择下拉框 - - 算力消耗实时显示 - - 平台特定的参数配置 - - 生成按钮 - - 任务列表和状态显示 - - 集成3D模型预览器 (three.js) - - 模型下载功能 - - 移动端适配: - - mobile/3dCreate.vue - 移动端3D生成页面 - - 保持Tab切换功能 - - 响应式设计,触控优化 - - 🛠️ 管理后台 - - 管理功能: - - admin/3d/3dJobs.vue - 任务管理列表 - - admin/3d/3dSetting.vue - API配置页面 - - 模型配置管理: - - 分平台配置模型列表 - - 设置每个模型的算力消耗值 - - API密钥和端点配置 - - 🔌 API集成方案 - - 腾讯云集成: - - 使用官方Golang SDK - - 支持异步任务提交和状态查询 - - Gitee集成: - - HTTP客户端实现 - - 标准化响应处理 - - 🎯 核心功能特性 - - - 平台切换:通过CustomTab在魔力方舟和腾讯混元间切换 - - 模型选择:每个平台支持不同的3D模型 - - 动态算力:切换模型时实时更新算力消耗显示 - - 参数隔离:不同平台的参数配置完全分离 - - 3D预览:集成Three.js实现模型预览 - - 统一体验:保持与JiMeng.vue相似的交互风格 - - 📱 用户体验 - - - JiMeng.vue风格的简洁界面 - - Tab切换流畅的平台选择 - - 模型选择时算力消耗实时更新 - - 支持拖拽上传图片 - - 实时任务状态显示 - - 3D模型交互式预览 - - 这个设计将创建一个与现有JiMeng功能风格一致的3D生成模块,通过Tab分离实现平台参数的清晰管理。 - -整个实现严格按照现有系统的代码规范和架构模式,与 JiMeng 等模块保持一致的用户体验! diff --git a/docs/ai3d.md b/docs/ai3d.md new file mode 100644 index 00000000..ed3fb44a --- /dev/null +++ b/docs/ai3d.md @@ -0,0 +1,510 @@ +## 开发 3D 图片生成功能 + +对接 3D 图片生成接口,为当前系统添加 3D 模型生成功能,默认支持腾讯云和 Gitee 的图生 3D API 接口。 + +## 要求 + +1. 完成数据库设计,后端 API 设计,前端页面设计。 +2. 完成前端功能页面以及后台管理页面,具体设计结构可以参考即梦 AI,在对应的模块建立独立的模块 。 +3. 页面设计要精美,但是整体风格要跟整站风格一致。 +4. 支持前端 3D 模型预览,支持 3D 模型下载。 + +## 腾讯云图生 3D API 接口文档 + +1. 提交任务: https://cloud.tencent.com/document/product/1804/120826 +2. 查询任务: https://cloud.tencent.com/document/product/1804/120827 +3. Golang SDK: https://gitee.com/TencentCloud/tencentcloud-sdk-go/blob/master/tencentcloud/ai3d/v20250513/client.go 依赖已经安装到本地了,如果你无法读取远程文件,下面是文件主要内容: + +client.go + +```go +// Copyright (c) 2017-2025 Tencent. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v20250513 + +import ( + "context" + "errors" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common" + tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile" +) + +const APIVersion = "2025-05-13" + +type Client struct { + common.Client +} + +// Deprecated +func NewClientWithSecretId(secretId, secretKey, region string) (client *Client, err error) { + cpf := profile.NewClientProfile() + client = &Client{} + client.Init(region).WithSecretId(secretId, secretKey).WithProfile(cpf) + return +} + +func NewClient(credential common.CredentialIface, region string, clientProfile *profile.ClientProfile) (client *Client, err error) { + client = &Client{} + client.Init(region). + WithCredential(credential). + WithProfile(clientProfile) + return +} + + +func NewQueryHunyuanTo3DJobRequest() (request *QueryHunyuanTo3DJobRequest) { + request = &QueryHunyuanTo3DJobRequest{ + BaseRequest: &tchttp.BaseRequest{}, + } + + request.Init().WithApiInfo("ai3d", APIVersion, "QueryHunyuanTo3DJob") + + + return +} + +func NewQueryHunyuanTo3DJobResponse() (response *QueryHunyuanTo3DJobResponse) { + response = &QueryHunyuanTo3DJobResponse{ + BaseResponse: &tchttp.BaseResponse{}, + } + return + +} + +// QueryHunyuanTo3DJob +// 混元生3D接口,基于混元大模型,根据输入的文本描述/图片智能生成3D。 +// +// 默认提供1个并发,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后,才能开始处理下一个任务。 +func (c *Client) QueryHunyuanTo3DJob(request *QueryHunyuanTo3DJobRequest) (response *QueryHunyuanTo3DJobResponse, err error) { + return c.QueryHunyuanTo3DJobWithContext(context.Background(), request) +} + +// QueryHunyuanTo3DJob +// 混元生3D接口,基于混元大模型,根据输入的文本描述/图片智能生成3D。 +// +// 默认提供1个并发,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后,才能开始处理下一个任务。 +func (c *Client) QueryHunyuanTo3DJobWithContext(ctx context.Context, request *QueryHunyuanTo3DJobRequest) (response *QueryHunyuanTo3DJobResponse, err error) { + if request == nil { + request = NewQueryHunyuanTo3DJobRequest() + } + c.InitBaseRequest(&request.BaseRequest, "ai3d", APIVersion, "QueryHunyuanTo3DJob") + + if c.GetCredential() == nil { + return nil, errors.New("QueryHunyuanTo3DJob require credential") + } + + request.SetContext(ctx) + + response = NewQueryHunyuanTo3DJobResponse() + err = c.Send(request, response) + return +} + +func NewSubmitHunyuanTo3DJobRequest() (request *SubmitHunyuanTo3DJobRequest) { + request = &SubmitHunyuanTo3DJobRequest{ + BaseRequest: &tchttp.BaseRequest{}, + } + + request.Init().WithApiInfo("ai3d", APIVersion, "SubmitHunyuanTo3DJob") + + + return +} + +func NewSubmitHunyuanTo3DJobResponse() (response *SubmitHunyuanTo3DJobResponse) { + response = &SubmitHunyuanTo3DJobResponse{ + BaseResponse: &tchttp.BaseResponse{}, + } + return + +} + +// SubmitHunyuanTo3DJob +// 混元生3D接口,基于混元大模型,根据输入的文本描述/图片智能生成3D。 +// +// 默认提供1个并发,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后,才能开始处理下一个任务。 +func (c *Client) SubmitHunyuanTo3DJob(request *SubmitHunyuanTo3DJobRequest) (response *SubmitHunyuanTo3DJobResponse, err error) { + return c.SubmitHunyuanTo3DJobWithContext(context.Background(), request) +} + +// SubmitHunyuanTo3DJob +// 混元生3D接口,基于混元大模型,根据输入的文本描述/图片智能生成3D。 +// +// 默认提供1个并发,代表最多能同时处理1个已提交的任务,上一个任务处理完毕后,才能开始处理下一个任务。 +func (c *Client) SubmitHunyuanTo3DJobWithContext(ctx context.Context, request *SubmitHunyuanTo3DJobRequest) (response *SubmitHunyuanTo3DJobResponse, err error) { + if request == nil { + request = NewSubmitHunyuanTo3DJobRequest() + } + c.InitBaseRequest(&request.BaseRequest, "ai3d", APIVersion, "SubmitHunyuanTo3DJob") + + if c.GetCredential() == nil { + return nil, errors.New("SubmitHunyuanTo3DJob require credential") + } + + request.SetContext(ctx) + + response = NewSubmitHunyuanTo3DJobResponse() + err = c.Send(request, response) + return +} + +``` + +model.go + +```go +// Copyright (c) 2017-2025 Tencent. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v20250513 + +import ( + tcerr "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors" + tchttp "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/http" + "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/json" +) + +type File3D struct { + // 文件格式 + Type *string `json:"Type,omitnil,omitempty" name:"Type"` + + // 文件的Url(有效期24小时) + Url *string `json:"Url,omitnil,omitempty" name:"Url"` + + // 预览图片Url + PreviewImageUrl *string `json:"PreviewImageUrl,omitnil,omitempty" name:"PreviewImageUrl"` +} + +// Predefined struct for user +type QueryHunyuanTo3DJobRequestParams struct { + // 任务ID。 + JobId *string `json:"JobId,omitnil,omitempty" name:"JobId"` +} + +type QueryHunyuanTo3DJobRequest struct { + *tchttp.BaseRequest + + // 任务ID。 + JobId *string `json:"JobId,omitnil,omitempty" name:"JobId"` +} + +func (r *QueryHunyuanTo3DJobRequest) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *QueryHunyuanTo3DJobRequest) FromJsonString(s string) error { + f := make(map[string]interface{}) + if err := json.Unmarshal([]byte(s), &f); err != nil { + return err + } + delete(f, "JobId") + if len(f) > 0 { + return tcerr.NewTencentCloudSDKError("ClientError.BuildRequestError", "QueryHunyuanTo3DJobRequest has unknown keys!", "") + } + return json.Unmarshal([]byte(s), &r) +} + +// Predefined struct for user +type QueryHunyuanTo3DJobResponseParams struct { + // 任务状态。WAIT:等待中,RUN:执行中,FAIL:任务失败,DONE:任务成功 + Status *string `json:"Status,omitnil,omitempty" name:"Status"` + + // 错误码 + ErrorCode *string `json:"ErrorCode,omitnil,omitempty" name:"ErrorCode"` + + // 错误信息 + ErrorMessage *string `json:"ErrorMessage,omitnil,omitempty" name:"ErrorMessage"` + + // 生成的3D文件数组。 + ResultFile3Ds []*File3D `json:"ResultFile3Ds,omitnil,omitempty" name:"ResultFile3Ds"` + + // 唯一请求 ID,由服务端生成,每次请求都会返回(若请求因其他原因未能抵达服务端,则该次请求不会获得 RequestId)。定位问题时需要提供该次请求的 RequestId。 + RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId"` +} + +type QueryHunyuanTo3DJobResponse struct { + *tchttp.BaseResponse + Response *QueryHunyuanTo3DJobResponseParams `json:"Response"` +} + +func (r *QueryHunyuanTo3DJobResponse) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *QueryHunyuanTo3DJobResponse) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) +} + +// Predefined struct for user +type SubmitHunyuanTo3DJobRequestParams struct { + // 文生3D,3D内容的描述,中文正向提示词。 + // 最多支持1024个 utf-8 字符。 + // 文生3D, image、image_url和 prompt必填其一,且prompt和image/image_url不能同时存在。 + Prompt *string `json:"Prompt,omitnil,omitempty" name:"Prompt"` + + // 输入图 Base64 数据。 + // 大小:单边分辨率要求不小于128,不大于5000。大小不超过8m(base64编码后会大30%左右,建议实际输入图片不超过6m) + // 格式:jpg,png,jpeg,webp。 + // ImageBase64、ImageUrl和 Prompt必填其一,且Prompt和ImageBase64/ImageUrl不能同时存在。 + ImageBase64 *string `json:"ImageBase64,omitnil,omitempty" name:"ImageBase64"` + + // 输入图Url。 + // 大小:单边分辨率要求不小于128,不大于5000。大小不超过8m(base64编码后会大30%左右,建议实际输入图片不超过6m) + // 格式:jpg,png,jpeg,webp。 + // ImageBase64/ImageUrl和 Prompt必填其一,且Prompt和ImageBase64/ImageUrl不能同时存在。 + ImageUrl *string `json:"ImageUrl,omitnil,omitempty" name:"ImageUrl"` + + // 多视角的模型图片,视角参考值: + // left:左视图; + // right:右视图; + // back:后视图; + // + // 每个视角仅限制一张图片。 + // ●图片大小限制:编码后大小不可超过8M。 + // ●图片分辨率限制:单边分辨率小于5000且大于128。 + // ●支持图片格式:支持jpg或png + MultiViewImages []*ViewImage `json:"MultiViewImages,omitnil,omitempty" name:"MultiViewImages"` + + // 生成模型的格式,仅限制生成一种格式。 + // 生成模型文件组默认返回obj格式。 + // 可选值:OBJ,GLB,STL,USDZ,FBX,MP4。 + ResultFormat *string `json:"ResultFormat,omitnil,omitempty" name:"ResultFormat"` + + // 是否开启 PBR材质生成,默认 false。 + EnablePBR *bool `json:"EnablePBR,omitnil,omitempty" name:"EnablePBR"` +} + +type SubmitHunyuanTo3DJobRequest struct { + *tchttp.BaseRequest + + // 文生3D,3D内容的描述,中文正向提示词。 + // 最多支持1024个 utf-8 字符。 + // 文生3D, image、image_url和 prompt必填其一,且prompt和image/image_url不能同时存在。 + Prompt *string `json:"Prompt,omitnil,omitempty" name:"Prompt"` + + // 输入图 Base64 数据。 + // 大小:单边分辨率要求不小于128,不大于5000。大小不超过8m(base64编码后会大30%左右,建议实际输入图片不超过6m) + // 格式:jpg,png,jpeg,webp。 + // ImageBase64、ImageUrl和 Prompt必填其一,且Prompt和ImageBase64/ImageUrl不能同时存在。 + ImageBase64 *string `json:"ImageBase64,omitnil,omitempty" name:"ImageBase64"` + + // 输入图Url。 + // 大小:单边分辨率要求不小于128,不大于5000。大小不超过8m(base64编码后会大30%左右,建议实际输入图片不超过6m) + // 格式:jpg,png,jpeg,webp。 + // ImageBase64/ImageUrl和 Prompt必填其一,且Prompt和ImageBase64/ImageUrl不能同时存在。 + ImageUrl *string `json:"ImageUrl,omitnil,omitempty" name:"ImageUrl"` + + // 多视角的模型图片,视角参考值: + // left:左视图; + // right:右视图; + // back:后视图; + // + // 每个视角仅限制一张图片。 + // ●图片大小限制:编码后大小不可超过8M。 + // ●图片分辨率限制:单边分辨率小于5000且大于128。 + // ●支持图片格式:支持jpg或png + MultiViewImages []*ViewImage `json:"MultiViewImages,omitnil,omitempty" name:"MultiViewImages"` + + // 生成模型的格式,仅限制生成一种格式。 + // 生成模型文件组默认返回obj格式。 + // 可选值:OBJ,GLB,STL,USDZ,FBX,MP4。 + ResultFormat *string `json:"ResultFormat,omitnil,omitempty" name:"ResultFormat"` + + // 是否开启 PBR材质生成,默认 false。 + EnablePBR *bool `json:"EnablePBR,omitnil,omitempty" name:"EnablePBR"` +} + +func (r *SubmitHunyuanTo3DJobRequest) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *SubmitHunyuanTo3DJobRequest) FromJsonString(s string) error { + f := make(map[string]interface{}) + if err := json.Unmarshal([]byte(s), &f); err != nil { + return err + } + delete(f, "Prompt") + delete(f, "ImageBase64") + delete(f, "ImageUrl") + delete(f, "MultiViewImages") + delete(f, "ResultFormat") + delete(f, "EnablePBR") + if len(f) > 0 { + return tcerr.NewTencentCloudSDKError("ClientError.BuildRequestError", "SubmitHunyuanTo3DJobRequest has unknown keys!", "") + } + return json.Unmarshal([]byte(s), &r) +} + +// Predefined struct for user +type SubmitHunyuanTo3DJobResponseParams struct { + // 任务ID(有效期24小时) + JobId *string `json:"JobId,omitnil,omitempty" name:"JobId"` + + // 唯一请求 ID,由服务端生成,每次请求都会返回(若请求因其他原因未能抵达服务端,则该次请求不会获得 RequestId)。定位问题时需要提供该次请求的 RequestId。 + RequestId *string `json:"RequestId,omitnil,omitempty" name:"RequestId"` +} + +type SubmitHunyuanTo3DJobResponse struct { + *tchttp.BaseResponse + Response *SubmitHunyuanTo3DJobResponseParams `json:"Response"` +} + +func (r *SubmitHunyuanTo3DJobResponse) ToJsonString() string { + b, _ := json.Marshal(r) + return string(b) +} + +// FromJsonString It is highly **NOT** recommended to use this function +// because it has no param check, nor strict type check +func (r *SubmitHunyuanTo3DJobResponse) FromJsonString(s string) error { + return json.Unmarshal([]byte(s), &r) +} + +type ViewImage struct { + // 视角类型。 + // 取值:back、left、right + ViewType *string `json:"ViewType,omitnil,omitempty" name:"ViewType"` + + // 图片Url地址 + ViewImageUrl *string `json:"ViewImageUrl,omitnil,omitempty" name:"ViewImageUrl"` +} +``` + +## Gitee 图生 3D API 接口文档 + +1. 提交任务: https://ai.gitee.com/docs/openapi/v1#tag/3d-%E7%94%9F%E6%88%90/post/async/image-to-3d +2. 查询任务:https://ai.gitee.com/docs/openapi/v1#tag/%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1/get/task/{task_id}/get + +首先,你需要认真阅读上述接口文档,然后按照接口文档的示例代码实现腾讯云和 Gitee 的图生 3D API 接口,并且将接口集成到现有的系统中。 + +📋 功能概述 + + 为现有的GeekAI-Plus系统添加3D图片生成功能,集成腾讯云和Gitee的图生3D API接口,包含完整的前后端功能和管理界面。 + + 🗄️ 数据库设计 + + 新增数据表:geekai_3d_jobs + - id (uint): 主键 + - type (string): API类型 (tencent/gitee) + - user_id (uint): 用户ID + - power (int): 消耗算力 + - task_id (string): 第三方任务ID + - img_url (string): 生成的3D模型文件地址 + - model (string): 使用的3D模型类型 + - status (string): 任务状态 + - err_msg (string): 错误信息 + - params (JSON): 任务参数(包含输入图片、提示词等所有参数) + - created_at (int64): 创建时间 + + 🔧 后端API实现 + + 路由结构:/api/3d/* + - POST /api/3d/generate - 创建3D生成任务 + - GET /api/3d/jobs - 获取任务列表 + - GET /api/3d/job/{id} - 获取任务详情 + - GET /api/3d/download/{id} - 下载3D模型 + - DELETE /api/3d/job/{id} - 删除任务 + + 核心服务: + - service/3d/tencent_client.go - 腾讯云3D API客户端 + - service/3d/gitee_client.go - Gitee 3D API客户端 + - service/3d/service.go - 3D生成服务统一接口 + - handler/3d_handler.go - HTTP处理器 + - store/vo/3d_job.go - 数据模型 + + 🎨 前端界面设计 + + 用户端页面:/3d - 3D生成主页面 + - 参考JiMeng.vue的设计风格和布局 + - 使用CustomTab组件分离平台参数: + - Tab 1: "魔力方舟" (Gitee平台参数) + - Tab 2: "腾讯混元" (腾讯云平台参数) + - 每个Tab内包含: + - 图片上传区域 + - 模型选择下拉框 + - 算力消耗实时显示 + - 平台特定的参数配置 + - 生成按钮 + - 任务列表和状态显示 + - 集成3D模型预览器 (three.js) + - 模型下载功能 + + 移动端适配: + - mobile/3dCreate.vue - 移动端3D生成页面 + - 保持Tab切换功能 + - 响应式设计,触控优化 + + 🛠️ 管理后台 + + 管理功能: + - admin/3d/3dJobs.vue - 任务管理列表 + - admin/3d/3dSetting.vue - API配置页面 + - 模型配置管理: + - 分平台配置API秘钥和模型列表 + - 设置每个模型的算力消耗值 + - API密钥和端点配置 + + 🔌 API集成方案 + + 腾讯云集成: + - 使用官方Golang SDK + - 支持异步任务提交和状态查询 + + Gitee集成: + - HTTP客户端实现 + - 标准化响应处理 + + 🎯 核心功能特性 + + - 平台切换:通过CustomTab在魔力方舟和腾讯混元间切换 + - 模型选择:每个平台支持不同的3D模型 + - 动态算力:切换模型时实时更新算力消耗显示 + - 参数隔离:不同平台的参数配置完全分离 + - 3D预览:集成Three.js实现模型预览 + - 统一体验:保持与JiMeng.vue相似的交互风格 + + 📱 用户体验 + + - JiMeng.vue风格的简洁界面 + - Tab切换流畅的平台选择 + - 模型选择时算力消耗实时更新 + - 支持拖拽上传图片 + - 实时任务状态显示 + - 3D模型交互式预览 + + 这个设计将创建一个与现有JiMeng功能风格一致的3D生成模块,通过Tab分离实现平台参数的清晰管理。 + +整个实现严格按照现有系统的代码规范和架构模式,与 JiMeng 等模块保持一致的用户体验! diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 443ab01f..84f88a05 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -458,56 +458,67 @@ packages: resolution: {integrity: sha512-K4ncpWl7sQuyp6rWiGUvb6Q18ba8mzM0rjWJ5JgYKlIXAau1db7hZnR0ldJvqKWWJDxqzSLwGUhA4jp+KqgDtQ==} cpu: [arm] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.46.1': resolution: {integrity: sha512-YykPnXsjUjmXE6j6k2QBBGAn1YsJUix7pYaPLK3RVE0bQL2jfdbfykPxfF8AgBlqtYbfEnYHmLXNa6QETjdOjQ==} cpu: [arm] os: [linux] + libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.46.1': resolution: {integrity: sha512-kKvqBGbZ8i9pCGW3a1FH3HNIVg49dXXTsChGFsHGXQaVJPLA4f/O+XmTxfklhccxdF5FefUn2hvkoGJH0ScWOA==} cpu: [arm64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.46.1': resolution: {integrity: sha512-zzX5nTw1N1plmqC9RGC9vZHFuiM7ZP7oSWQGqpbmfjK7p947D518cVK1/MQudsBdcD84t6k70WNczJOct6+hdg==} cpu: [arm64] os: [linux] + libc: [musl] '@rollup/rollup-linux-loongarch64-gnu@4.46.1': resolution: {integrity: sha512-O8CwgSBo6ewPpktFfSDgB6SJN9XDcPSvuwxfejiddbIC/hn9Tg6Ai0f0eYDf3XvB/+PIWzOQL+7+TZoB8p9Yuw==} cpu: [loong64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.46.1': resolution: {integrity: sha512-JnCfFVEKeq6G3h3z8e60kAp8Rd7QVnWCtPm7cxx+5OtP80g/3nmPtfdCXbVl063e3KsRnGSKDHUQMydmzc/wBA==} cpu: [ppc64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.46.1': resolution: {integrity: sha512-dVxuDqS237eQXkbYzQQfdf/njgeNw6LZuVyEdUaWwRpKHhsLI+y4H/NJV8xJGU19vnOJCVwaBFgr936FHOnJsQ==} cpu: [riscv64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.46.1': resolution: {integrity: sha512-CvvgNl2hrZrTR9jXK1ye0Go0HQRT6ohQdDfWR47/KFKiLd5oN5T14jRdUVGF4tnsN8y9oSfMOqH6RuHh+ck8+w==} cpu: [riscv64] os: [linux] + libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.46.1': resolution: {integrity: sha512-x7ANt2VOg2565oGHJ6rIuuAon+A8sfe1IeUx25IKqi49OjSr/K3awoNqr9gCwGEJo9OuXlOn+H2p1VJKx1psxA==} cpu: [s390x] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.46.1': resolution: {integrity: sha512-9OADZYryz/7E8/qt0vnaHQgmia2Y0wrjSSn1V/uL+zw/i7NUhxbX4cHXdEQ7dnJgzYDS81d8+tf6nbIdRFZQoQ==} cpu: [x64] os: [linux] + libc: [glibc] '@rollup/rollup-linux-x64-musl@4.46.1': resolution: {integrity: sha512-NuvSCbXEKY+NGWHyivzbjSVJi68Xfq1VnIvGmsuXs6TCtveeoDRKutI5vf2ntmNnVq64Q4zInet0UDQ+yMB6tA==} cpu: [x64] os: [linux] + libc: [musl] '@rollup/rollup-win32-arm64-msvc@4.46.1': resolution: {integrity: sha512-mWz+6FSRb82xuUMMV1X3NGiaPFqbLN9aIueHleTZCc46cJvwTlvIh7reQLk4p97dv0nddyewBhwzryBHH7wtPw==} diff --git a/web/src/assets/iconfont/iconfont.css b/web/src/assets/iconfont/iconfont.css index f8aff2db..494e152f 100644 --- a/web/src/assets/iconfont/iconfont.css +++ b/web/src/assets/iconfont/iconfont.css @@ -1,8 +1,8 @@ @font-face { font-family: "iconfont"; /* Project id 4125778 */ - src: url('iconfont.woff2?t=1756631578371') format('woff2'), - url('iconfont.woff?t=1756631578371') format('woff'), - url('iconfont.ttf?t=1756631578371') format('truetype'); + src: url('iconfont.woff2?t=1756786244728') format('woff2'), + url('iconfont.woff?t=1756786244728') format('woff'), + url('iconfont.ttf?t=1756786244728') format('truetype'); } .iconfont { @@ -13,6 +13,10 @@ -moz-osx-font-smoothing: grayscale; } +.icon-cube:before { + content: "\e876"; +} + .icon-tencent:before { content: "\e655"; } diff --git a/web/src/assets/iconfont/iconfont.js b/web/src/assets/iconfont/iconfont.js index 0f79bc47..644ac30a 100644 --- a/web/src/assets/iconfont/iconfont.js +++ b/web/src/assets/iconfont/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_4125778='',(a=>{var l=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,z,m=function(l,c){c.parentNode.insertBefore(l,c)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}h=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_4125778,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?m(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=a.document,z=!1,v(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){z||(z=!0,i())}function v(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(v,50)}p()}})(window); \ No newline at end of file +window._iconfont_svg_string_4125778='',(a=>{var l=(c=(c=document.getElementsByTagName("script"))[c.length-1]).getAttribute("data-injectcss"),c=c.getAttribute("data-disable-injectsvg");if(!c){var h,t,i,o,z,m=function(l,c){c.parentNode.insertBefore(l,c)};if(l&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}h=function(){var l,c=document.createElement("div");c.innerHTML=a._iconfont_svg_string_4125778,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(l=document.body).firstChild?m(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),h()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=h,o=a.document,z=!1,v(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){z||(z=!0,i())}function v(){try{o.documentElement.doScroll("left")}catch(l){return void setTimeout(v,50)}p()}})(window); \ No newline at end of file diff --git a/web/src/assets/iconfont/iconfont.json b/web/src/assets/iconfont/iconfont.json index d1f01f31..c9bc9e62 100644 --- a/web/src/assets/iconfont/iconfont.json +++ b/web/src/assets/iconfont/iconfont.json @@ -5,6 +5,13 @@ "css_prefix_text": "icon-", "description": "", "glyphs": [ + { + "icon_id": "34453337", + "name": "3D会场", + "font_class": "cube", + "unicode": "e876", + "unicode_decimal": 59510 + }, { "icon_id": "3547761", "name": "tencent", diff --git a/web/src/assets/iconfont/iconfont.ttf b/web/src/assets/iconfont/iconfont.ttf index 7834ecc825716727aac7da52786876a4e72fab5f..a875fdff55a761ba986f5013e115b63c7450c80b 100644 GIT binary patch delta 3737 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 diff --git a/web/src/assets/iconfont/iconfont.woff b/web/src/assets/iconfont/iconfont.woff index 8ed592bec51cb01bff91bf7f178f2f3fa37338bc..6d0a267bd1a9c60bc726b1e8010ba40d0b08ecae 100644 GIT binary patch delta 38060 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

yi zAR(YcIk8@K;*J_#h1&Qr3Vhu*C|i!R0fsAa+<$W&8etRvMc>dpX;mG zC4&4NG2`DP?3h#QF-du5(oYCc6+Pi3O)s^yw0_ETu=Ka0_*}dvf#N-JCgAz+U?<6) zAVu~;EqRZUE~+%Plvt4Jpo*L@B(N>bKm30XAd|#Btg{{Wef;rl)Bc`E_pMqxI$fH` z>z;a#f5QVe`THw~(mP2*4A z2J>G@_GgBhE7u=JoCh-?s2{_yllZeBkt~P$MRg4LnWpzbF5i!#3nws4h75K}Ruq5K z^L||EClD79dDkHfj5&N0rGcnVQvC%xlJ8HUr4$|22`?tb6CS5EM!NeaXFaJyO_}It zT)fcL)0uPFqgHPvVHXBc=(A35DQ4#flA_=16;#XQ)aLBkiA{6UCLhVVp$z^y@>jCR zWSIif%M3wnCzv^AH**+j?LeQF$}oTaLLn3)oG1u4ZnM;=SD>O?lM}c)=K^!GOQ-}- z3gteN$P?^J(+g(eaGB2Cs4Ha91P*vB@RJ}(qd%}JxE}e#>o~=~L=gPXgI3KR<|Sob z64Bqw63VFLVu^iu-6nM3e*Yg)H>hWuS^q-xNq_NWw59X_yM~L1Z^FM{*zjkI3;a&S zG+opD0;?G+kJ%~Sjj#fGT#E9`d15&nTt##Tx?dt&5PluP+>^3H+}{ZrUL@T6dHr7} zcxUYO=ic?{?`*&ve~o|F)#$vv3#*)^Z=qw?f?b%~;F~!<4AW1}0Ayp#~LV5RU*G=!Ak{rzXG95STAR4CocPJnyuKW*t(22%;nO zgsI)F#`5YVD6SMd)qg=lZ(h2rGE;#servK8bz|{dENaDc6Ow*Fn|h!$*+O8u3}{m;p`!R?>22#zzkx`NNTeW8>#;&klcg_}|FZ zv)lKhrw?r3Iyf{x7m28-s#*5je06Z^_UjBGt7AhH4KCtHSeIsxrn}PUXhd-;k)ubg z&Zz9)C5k$hoCpVCI+3Cg?wy`VOxJ~rhdzJ9g@?u_CdLk3c*D`Br}ky*9Ub-TzNu%> z5357l=Fi{rH#Kd)7Ky;6_BVff&Y#~lRP|p@>Y|9m4kIBc92Pr%IB~C&PCNHbD4j)F zo}85BV&^k@hln{vN*Em?0>$T`<#|>n@K1MV1PaJ}K+>2vvYgU^NZVJKE9&s15~C zgm2c|Xb(s%0g48_4V{-S2WSgf*N5S$-g;_;X46pChdB^fBQk%eWp;Izoi9nn!ka#O&;s$B2o@JFMTDFV`n`-cc0~*%>wWzfY+PR`TE@ct zJ082?p_H{t$EmWSyw$hY&rFue<GbbQD35w`uEn=y2A}_6t z8}b}Wpf-`bZgGD^mkezjvm`cX7@8yyK3a}S!n~&G+BON)`kT+)+tm}dCzd~T{^0}r z-@JyyD##^8ndK1?NhGOraVu&Hswkk*grYb#g-2M7teD=naKVnd=SAbvyI#0yUN6O2 z)A$H_Z@D}<{UB7fQVA+MSuXnz2nY>}iXv!GgCxsqo`ru&CdMaJ6S0ylM9PM3Ng^iN z6hXx6(+TS;tPt&tN)AB=$ak_C;>6F%`$?9GGF{9R)hACXyQdUj?`)%+MFNPN4*eDC zg?f$tfYV{-iiQ1DUERO%VmolfzD*nY;cvsH&$R<&c;WW@@4tT?{BB5(I&@ zXCw#$*AIU$tQ}u_-~e185zyi+)c^D3%VZT$WtmYK9e7I|`y&!%cr7O5fFi-Ph{*3N_P#9Mg@g z5WR({;6Wy!D?3$46r>Mw3i7j^TAjeJMCBwW==VCJ;!+_Ph#Crp267G+@D*hrAGhV9n}{ z0Tq9PH@Z9y;h4N5V$~oRS&(%Ao5akf*>9}e*xtP;S2PA#bAYqtCV}b(sN~ZclX7L!2u2T!l>$!45c-OEn3Bj86b zk`IzCfOl19oOv77@tO@PE&}Z;lv}G(RD8*xkU0&POO(!Wl+zaigF{I;8IeOZ5ZQm- zfGQU%c>(Z(vVXvjC}0dQF%Xh~loog@8&Iq&3qw5QF%0OCMb&1lNf*9`8ZMO^ykKb* zzU07^Drd6(-{+QtqTG?vZ8kBO=_yZ_@wFh|_!`SXe=b*K){3!)m@xc596%&0qZvg7 zt>j8nLxK@YAS8))LRNWhwPX-rVn}}yL?Y^Fm1H6<>h!RNo-;&25MYF47U`QsP7_Uj z-8w_(WkT4Jp2T=(E<2_ik<+=T6oH~H>B;y9D%0gs9Q`#>e7_2nI6|UIH5J#QWM&LO z4K_hlSpT=6X($mv#X#-*kZwm%E(zopsI{mibI6Don95p+C7Pyz0F98gSxkR{QqHem zYjH?cRkp09a43tupZtuR$8ZdY6cVZko`mv&=_j;Kh1R8LFbepe{C|E!<4b70|G&;V zXU84Ia=CcNj&q(n6~yQGAA3XnOX$9(8(#S;3~Xc(rd8q{+!s3!xo zG|voB`Rdix46Dykrc61y4MJ2~TyJ2;XU<+QW|E~+qf|;p;_+fUjuy|}o~3fVRH~QJ z6LISV1E3MaSn*Ky2Bvi>{vGhE98+gznO&frT^sb~Sp*OezCoL)W0UGU@pfR> zs%uxRy7q2*DMeBd{xB&~IcImN+N7GADmY`1l!R1c9aFtzzJUw7=6M5&bx^*+VSGtQ?oo+h{?tJ}6XJ zz6bZE)9{?>?i+zZ&7s=TPvdez;xOjov>+ILC=8yYgdE7&a7o}3jLTG*CNm1!4KVjc zt-}TmCsc$=Gx#|b1acfOMPPe1=w~2NC6A|erdqY>@s$qJ;3|I^^q1NG9p_(zR()mn zHRlgpf?kJa(VNkl+tHWZ`Z*W&ZIt9>Ujq4C9?u zqsM>pUE9y^Kba8S=D(}A+T7nrY{JQblqmlD1H}qFTGtj4+IHWc_gCsnAluM};Aa`0 zv6&9gI6FwB!^VH5(-+E`r#}GNKJ(0T_@?Kc@t>K!X4R@|-b636h0%O|be3MG5PbaO z{_&~SJGd;i-ob?l?CIgU0|8uB7y~qvTG)Re1Qe{+&(76zptIikgfs;iiIWSmCcczC?4y8R%Hu0A4a z=y6F^rLVWal!)3&JnsKI03JU)@kqSbrHYT#XaSJ0s~CTzUG(^=lJRLJkGJzvrIrnh zEa0Cr9;K&Lj}KTqrJh1-!~-ofB>Fi@QERzIo1}lf@OuGNcj5QQlQd28KP~H8M8ijD zEdP7|UuYy+7epTY1FuE+KWG3BaqELO_U{EPBK#iSB2D%`NgMl0ezfnu;zxQ|E$Su| zXoPf;7viXmgYUSNb0bid{vjr?!T9(BMvH0Z|I2S z1r~|Ap^He)Mx4TsCYQZGmZl?aWqLjB6FPt2$!5tSXb)SM-OK`WJ#!25M&_;1Kf~dY zw-bdaN`vFA^>bL9$^Ift4egi0OK zgVb`$T-3IJIW?fDcD>Prt_~(3pPWX|W4r9`5X{AQw@fG>Vvu)my3n(>);WWCNrdM0jVa+Th` z@@)D@Wu^;{)h5{Hz6KP|PSnP5*G%O|diCua&e^rujV0aEcnQ9-M9lx*I}PIvd4*k1 zI{a^irLu6dbZmCHNLajWYa7V7$vBx7k{w#>~~{pEoi(I&$9mzitQ6 z1JwJ(*>(M@uFfKs#VL^!2^Ym6WQwXJU>g^c7bT0xUB1IX2y;k8@mNn)L`X2?Rn{D; zUbwTrSL`_>&me&3F^dTv+gp`xsdBMco?87>JMgWp?VI+_ zOim3HoOH5T?pC>huDPkPtw!+!7j*Zx!l*M$NTV{#f6*5*iRBv`16|crqc*yJtQa*r zmv3CvsM@c5&eUtmH;m@p;}p2sHE?lw!#sPvsV%)%w8b5|Y^mn8x_sLes(G=tDIGEK zQ>Ao5hrdL+wDguBY+14#aT4f30j)PQCt8j=)cVs3+?$P9k{r>@>sj8gJWd#56*=#C z*)n&Wf1@G)w-nZXG+Soxj7P;flpB%6xU$1mWK-51Q?)eBzSdS0D|m|Nntd@yhNi3u z0O!Ud{Eli&92c~(&{|CzS$#zLmmn~_(e|o*Bu>@W0Kjxszmc9wW|3+Ar_-hC| zf_D0kUv5zjX94Sb!zvnD1tI*z9#N5&3{h1@e6Of3-G|@uS(%`f+5hn$G%>yOKYB!s zXuvtcD17+1~llE?^l>_X z_?^GAG}pQ>KYi`kyv#14tOW)vvZb5EP3TdO%WvNA$)iPni_A6zxSfTOWkRy zIjALi-J6}H=4wtd8H(l1iQkZS5*H-oBGbu?hc^7vvF4;CS+9Y9M)kHVZqk)Dx9hU$*POY2xFN%^UhEjq#Z2bmgOo zWzGEyKR<6@bJ<99-vZVNFKpQ&@SGukh%Tq>=#%BX?KJeLY-~Xs;RKc{xvb(itbqAU zw&-dShIkdC1wv%kWhH{rf60=o$pFvNc#gyOR}QDN@r`uRamF$Fz8wC$qza1#5Ti?$EuHPPrJvKFsC zbpt;3R5hW)xa`<|`?(11fBp7(|IZ)WEh@T+r7J{*sN!=1iN^4Ev1BF7O^$0+l9q&# zrSwkSeEIt?y{C#$^`1-LfBDT*y^7VqP;86lP;W%2Kxc%7MIXtwzd35 zhmg)xx(L>2)`Dr-a?&l>!8E69VZq*zQ3M0Rd(cOtp_?U5;L_E(@Ifd*Mie3F28$Z) z!Ahaw(LC}(e;XcBgzj=d8m}E4_={A^9`zsza}a7(gBr9wx|BDGY8BpV0R>5GjbMQ; zNZU$5+eIsp$IQBWuj}lNILWet8Kcq-|-nP5ax$wj_ zZhj8&2&-7)Vn#|;_N-mMZ>Z+5Skw%O6NGqz*SJK6f1{fLMl@C}h=ZCWYaLEaWu=@c z@%jcs&WwS?y-8(}x(xIENnRyxKfaS7&MzKngkQr2o@O=`1HBVdw}b~91oQ(;Ku=K zoz0R(C|fKCTZQQQM&Pdih#d$)6O?J<*4uI*JkbdOdBTK`a^=^0^Qs&xC$j`uF<2+I zf2lWTdmn6h5U0d5xt?sDdyWw8j@$OwWi9k$LqTg!(K1&~C zIZ1&E>e=xg5$U%0Awjixh`Dq>OUBZ3o7N>~WO;LOjpMIoMN^PD4suWL&)Rl1<^875jEC%dd&{Mt`Ill2Ov%WayCNM_HDKn)YcKf2b+V z@=?s9K}2YUGTC+w1HAZm@+P7(3dsLukl(JL79^_qxeZVwfqqa`JD~Y4rFDG1NiAH| znnfAdNo=9sU;Vhi4Fz%>mF9Tr{GwwRS>9`&s&G+?HRQU4#8o!hKB2|i-asBS_nBBCVY zlkucNP)SL++H}<76Vd9(x#(t9?9|||Q&g`MfeK+x*Ta$$7l0OFsw#ShX1VlnD1ipT zQf?kOcVuV0(80SU&C?MdK?vty(nWGWwjw;zJ+0*O9fkN#01Wx(N68gre>)>GF4Ntz zvr_Ah%@-`{EkvD!tUPJ0v-byW4-D-*dZMwR0pA^;{@m4v-ucdh*L?16`}U1DX1mwS zujyWcUaG*$W)n@_?ElZ3{&Duuq1hkbfXe>O<5TDVSe;+iy`~$!&Fu`s2kSiy%<;C( zI!kSoR?fLRZ7Rf9cyeyWzceJv{sEd!alou!sMM9Au)P zrSvnasnrQp_)vV|D+NlP0%nTBjWr&%8#RNK*nT7oFGJ2?bD;Gum=Pee0Z(rPOH2K9 zbemKR3am~v9d&~zfA?KIymHU^=WW^6i~6rwdE}BS7WR$#Z@%zidjDd*^$Pc0ho2O1 z@3t-Hoxf*d-P${QD^Xh;F0HKh^pd5Ibw;DXLw6reD5roBxq9m?+P8c}mV1UKwP-xK zde!;(!Du1@_h+`w?pwZ0lDkQD`i}@*FOqIqTDE-OWG*Xrf0wVG?%P$K-MZ@5imnNsY|88Y3!5@&xo1&3T-ygL-Lim|Pr3k7uc1QAwF}#) zTaXv_guy)vfBH?P*N~r-bzMd&+34HQ=f7RntJ`a6w`>@)|DkM;{+g?x=k#P%+0f+) z3VLtfhF-{Ie|K$uD2VCF{zld*4p2Ker$U=@p!XeMu4JwXB=d690{TU%msJiLAXwD1 z71b=4CqVwFp*-hM5jPLwZGko;gQ;5}2(@)8pHYez7()6H7r=y(9?b1Z>p67qbNK@M z+5dbnKZs(z`A8(+8$*M6_6X>59nEaE*?|Y%ho(n7e{-n^DPmbv(C;m`F^#61uJwDQ z2o+Jm-#5L+^wMGavIV0^;RAh*RB>@Ik7{`yYZ3 zLA(9;#S4WvI)J8RQtYscx+SXp>4K&e(q2RmB3d!pl_YYe-f^955y88zV>qi#CS#!F ze~OCXxN%8dq017-39;lPk>y^mrL!F;$3aI?V3y{n?-K%6CBVF#Kwh5r>|k}(XerusvEZn+%#}`ip_wU@i;o6RTzT?^ro1Z)t+zWlE#*A!Uk4Y-W?OZp!-6p!u z6YtREd}BFC+?7{RSX3r0#n&oEG|OUJGX^dZ`%o_EEbF8CYs@ZKB02-J*zB=HreDs)!tkrFw?y2#@p$Jg*BQt3Yxnu4tCCu4Ypg04H~)sO+S& zhoS_!_es7TWBQoc|BBk4Z7-W0mcPiP+(v|AY9cN?rW{p9l7?W0of5_Rn;cMsxNe_b#VuSIZQ-f%|pAb@8Co#w*x>?1!hp6?W1B}srV4pY@14?PqTz~b4^O;Tk1!iLP)$$CPUzH7!|+zscXMnsSGP6!f`e z%ibvUBgQ}BIrn^mTCqm_udDV&&t^3Ne_2m$v{lW>zh#ZA^U0B@7BNZ+meDQ#tuj_S zm57Y)tX7W+3?l~yT7-WCXd#EmgH+nXG?@`*B~aOo3}ZueLq1(Wq_t#8TiA*{{lO}< z*7qqD;a>63l5@xKzeply$x9Dk?zST@wRuGEEi$%z#y%zjM@;q4y zVF#qYQE2^n%r(p{p+D3ao<@PT$pHdF4+I-I5WEZyBH;@|bfd!r3T62WstqgcYmD|z}{(P(VFPH<+$oy6UzVpM!pQVX@l0eoY}yfAFRlP zJ&s_!Dme-AioP$Ot?wy&X(ed&KHuto^65w9l<7|!Dft5ive0eO(I~prB0u{o zl_Q^%v6GUX_?VoEqR1(reOG_7I?Q&+DdAO6{_@~q$DZY*hJSA>!GFVRXT~0+VVUQS zJ&*qSJx$luuiMp#zsGgZdm`1%KL#Q0A7$Xf^6_UCWGB(u7QpSDAG-XiTMC6PP4_?g zN+>l5A6Rqn@UWrgB6uVC2*b2Bj2x2jvqeyA4d<5 zPo50m%@=>^)^c|4_*Xu1Njg3G{sRYf&;NVH;rB^cIiYv!FDvMsw}`H=2uK|jF1bXA zn&@Z<6czT$0g&;ZDthOl5C9ob;ZguHaKoOUF=Qa|8GslQzJGk68k;JpXz`|_N5&^6 z#*ZBRYddh$fv3KIWaaY2#gG5}fsyeSZ?_d`QO~LVFAM|4wfwTTNaDRB=s4oBn*^*! z@BFU-M5ISQ*i~8uF|613f?=$PKY#J^c#>)P=8<~|W=hOjW*+nrCR{%XP41P}KI%Z9 z90=|4q1oMQoqzp6w^9dA$u-v-qI=YWGXd%is@Z2DnyWM`lh_M4nx1VJ%y1d^AKJF1 zyH`?IjP9G?TbP?k&2H6@?dmpGOkO4|D~>KZXYc-vt5^2+351tB=wUA+huA)SVkXTu zFX=g;^V2u1&;EXm_<8@wH;A3a4)>Wz;&Qr6^YTQo9e==ol}4BC-h1e(t!v8#gn5OF zxC-);(X^7OM6;PfcTkb@gj{Z7a$~WU-YT0B*^!ZoWga1v%g)}yZ@6=pSUC4@zWS(@ z*zo-MZ!1ilQA7}Eondf->;P7tpnei`=Qpt^i*hUyY}IQWj6v2*$WBPUov?i=VFP38 zlMS9XWPh|yPDGI8{|R5C*|xTH59nAZl92r`$#jpTd|DZZmSNM)Gfmr~om;m+>YhLq z6sjnnd_@udcUnb!Iw%4uOZU(s@HPIQf+EoP$sz(>>#O85qzQ9(HMJ-S)Gw<{XVXc~ z!l)6f4+dyKWf9v7Jr9Q`5nXwqSQ4t{!4^!9dVe32$5H8D-n0EZ~T=ZG682{|nOvvdDJ8c*b@osh5vO1t;jlW@#eWKSv2l zC4YbVF(+;E!;{X0ZL}x4ghktzEdBCpgc7D2ilV!3o8O(0oin`HbEZC*092_ zlpTq!;+XofEQ|6g2_adD5)zFqmt@27pMS@g0uqIzfTV_F%91Pz@~jAea+C$YQRrv~ zwPemG$PdU4(5TDIrf{|J^xd?Uj?tNG>IGCd9R!`)meuyzpvVyA$)&y&t)xveU5TQ< zI1obVfu)yNL158eIG*R0w)LNSpB_vlJCi);OBA3#nn=zEFaF#H@uk#23STI&zJEj$ zqrb3%f8!~T)L^O_O(vsNnw)|pJL9eP{W$RTqO$)Mx;Lq{1lkkod*iesZ@J1+zV zQz}>oNZn{ciz^&`4z`ZIhBtB0a(dXmEb^x^c(SN)@50?ieWM!-akRE*mE;|5zGbBTC>(K3*Yod?C z!#qt=i0`oCacc(zAd@z3-->?@y12&_m^#BWDaow`hm#c0YlOML?}@U^mb+l} zv(~W`Y{d(`RMBgMIe{KekOgTh@P_P1d9wLbmBQ+URc%!-tm^y!e^rt_Y15$l*!rlR za#m-x=2^E16za965S+3w+}`y_$$p`2Y7cxPm?P)xtPKy>cAoRicHqbjbgS+7*uf+J z-VU5IHKlO;l&N!rMzp$iXn(46)z(#=hMKSlb4J=AFm*=SAh0wwbq@#Jb2O4uxv~{c z6{|y~u2r3#tGWv5Si%VHUdPF^z(X?3GUh7g9_HTQ92m!H;kc0J3skKS_P5r86}d|L zwLmrgN;r2yZs0)^gI0R36L_d%x+#-7hd>U2Ct#2SvZ^*F@oma*GZu4{xr=#-d7Swi^K<5R zFfSS|FK}M4KRZ2o*37($7m|uo(92yt~ z!CZ@qcQl3@DNpFA=zLT!q@o!!$@#zFZ0Y$=PjitZH}R1#Nc{RM?~)|ml5hRajcN?5 zcib&T+4J}Fw#9Ng_fdR_NS}XmU%8l!2y{0Y;SGhQ_E#lB)nacB~<_g;et&I9`Qsw9nY0KE1}5`i;|?uiXbSX=PX~o zXL;M_kkWt6>3~Pnp=B+f{#WpX(kl$5L3n+%w4;jTxUKH z?@<#fn$(j6jcC@?{a=1DsdqK}KeFXQT)M;klB*8iC5hK})DHGoUH#WiAZ%Ojb{w%Ru*Uo_avxB3fvt=1zZq z2;6Y=@SNpFHFyfQEH$befAT~ZH;PVwuutEIiJ4fTFjy$W!W)j|<7;F2d~9tz|MJtp zEr`G$wX9(!lkr4Rtyx1$Uy4NFmbZqjL42+?WYr>tL3mjDpha(a8u9J13r0sTxSn3f z;@NXWGiMZzw$hwbQS>qy<;=_;v}k`(1zIQ`KUKCr*eU@oOkgWN@f^sV6Mz>bW|~>Y zY=hoC$XvO&#m~Z(e`o;OKvNCf%5= zbOrGTcFj+W&22cO7z%v%g*P-B-u=H*Oa;D|)5BR6B{+-%?wL7S3ccWEEx zI+wj^+x)(u=u+|a?dhM4k6v)(*!B(U#wO->ofpJ+R%Ywz488!eGG+O>Fy|kHx4#YZ ze~%(wMoYL%R9-k0+(+TfaGig-tp_AtV^=WnMdS&Xc{Rx`_WHj=X5TN!+2r#5{m4XS z|BL@eWspj<(nMz~}pD*mAM{Dd`xME^-SyfQJnOIj_dU$`!$`zD^tXTQM zRWp^YuFA|R?^}vcy=MIKh5e0XG~Lh6gLIKd&Q!jY7+(81fL-;0HtYvht)Pov;dwlZ zWIt#oNaqqp2unp`(oEW)LP-+HTm zVsQ~fv5mj-tH1s=`o@2+N|PmcKT#@81ZCVq{*_zPv_jEsL&tEe$ zbIpA8quuxH+H=AFCubgBjqXtV0*c6u&WG1_cB93C{#!5n&4qur_Mu&c>|4h=^2Pk? zw%t0qcX;XJ!+S?>-S)bCaccagUewuDeRgPl{H7Iq4=~Ul%!y~n10Zv#Obj^3AT!47 zVa^2#aVv8-^UmP(_tOprqa#V>eQ1|(+BO*O^er*1MbBU}`bk`9oqiN#Rym32g}Ucd z>NO`?Db!mFZ^3_N#X{glh65&F326w>r;~;BBhY*dBWf6b5*%H~i%~6ae7_7F1^*<>k`}R}ovZL7twev+P{SLN3hnx!N@aTG`&Ld@ zbbCKNKWe|Nw<0P_ANA0=$aT^C9u)gPd#)8yed2?uV{4`_n?lo2CkUh{X{xf+|-E1zWVGH=@Fke>1LF@PB{( z(Z4PUYu9GfR9=hEt&6E01ucnxwq^~)6x77JbulFeG3YCNJd+R~^8Ynr@>sM`a#4i5 zZSfA2vm-1KBL2VKgQ`~uFaG(;i~snmJN(@pIW2!XyDF|0JJtB?>ZE#2M~9xBosFx7 zPAxXOI-#}w`7a}X205n;y|5K1PoT>N+8;$J=!5q8W6>-M^jR9^*=}&ybR`hBsu?Ws z^l-%RF#{-@!BjRXlkTXLxL|O8>;3~D85%%n@ch_>IinK2Y#7eqL^4uOu8LP})#(;= zsegZUmz&GEddwuOX#KMB@E)(D!&`CvdMn0pb~5d8`Q%J&!dg>Er?+hT?R8h}++A2> zE6H2DVRzgci50fYb~vUG5p)}AqV1NptSF;gUx((URHqB&=eDDc{th&C`JIz`+EqAP z-}5XJ%EEvUpT}ETr*zP{)jsSt?1OO6S4V$ffKDT~{UY4X1yc`Sdfm#MnAOdW!syy! zM&c0KHn;WgRR~>mWXs%k%t@Kjn$cp1sk3=X;q9ao4Tb1vd53LzmNn_+&<9K_Asww-o$*2d4~BB^Dlpa z?7=%EoYD30q__q>m8QS7ZMg@9ku4?d{|cHCBu8lPU7+0%@S-81m4Q864i+6wYKx`+ z+XP{fU~3gUYQVE0RZu6?D3{2oRP?3H{XuaLsKV=HEoEzS*WP1^X3RM!CwP%uw6~N} z1-;A8ni|G`Ex)8VhZXT^#ce?fM$CWRore5#(VkKgAiM3XLDy0~akZdwQu?B)%hpJO zlS<9@Pc;%cs|ZIGcUpa1m>a4m5~8q<-h?e};7iv^f}2dO?we{PbxskF$^r1Yp+*~6 z;klRBUoBeX{j1oBa=EOsw;dB?g~p)ULFNC}0YyA7XwK!LOcD`!=^e7J3ZH*atgA1t zZb_y)e%b9*LJ@L$|LD<~>b`t7lQXTH ztXT@WN7sKkG?`1(2PZocPyFv;4BP$hg7DHW=?nkP4=Jw5;{nG-IuOQ9Zl z3;77>8`PIMU#?j0nFd%GTc>|qICb)aRihr04tad?x*F=~nqBkZHM3nkG_XA$kHt$< zrP9>gWa-I?qm$@|on`+6yVtMng}=4yKe2vYPcQp{PGI|Y#y9>eujrM;b(R4wz68`6s zQ~RaquB;ab!e!Z%OoUv`lyn}mh!CTMI3cAeMlw4zH;}NfBNG8h9f^T~RoOVk65=h% z6k@6yKB{&ZS-G!Z~<>zI`Qu;Au6+vxsF+6z5u-%ZLc@?hyS2t0tbdebn0-~ zb}zR9TmLjAJVSvRDRtzuR^KRNJxN3RQK_|eEKFPtc7f1-9x8uxZA+4!2T~F4;6LgA zSIGm7I=Snt(hSGR%b@mI;BGA_>+k5lj2Nu+SoAc0E$gz1qZ+2_>ar?;F4}qBf$GTY z^8R8^!PAW3U!g$VSv%E#@kQyRm%tpRhvr~m#lGt<0{MXF;=A6sW#PS-_S%YopJN46 z$iQ^?|0;VEI7xqsDt|m9BJZlq%B-xctoy$DsIIQ7tM7BV=b(G;dzfKvW(I~C5f~1E z9vDyoIXplSgcbO$fa}Go=&B6x_u54T6joW((M5FGRnfot!QH9k7ZF+2(xd*@&-haW5u9-hoXA}y8b@M$vl5Rfae8~(|E-rL5@eb8$m$2 zON$G#dq|huoFF1DtjT;JfjBOv1x;2GpdhGGbtoN{StY7v%=&uOl37;b715>g0n_7c zm@gzY;m$;#bgz5VaTqI$;Q)VP6Z5xP{MpZ#1%Br_Ja1%}Wz>>{ z3i{GX>Z-s|!EvZwg4<7RjBNq+Q*=8yJ9?d^gga>C`e$3ptF7*TRGFQTK&o=L>t}9uP^K16&v3g?G z6|;bR%s+zLC0y9(V5GpiPE2*8OwsFvSS&0+c5q&@QAmZ z2Ko!$iuVPVBwC5M8&LIW`xi?uo0++6DF{3Ie)cbNV>x`lCx@F44G+U@!xufOgH(S7 zH^nXE2}_h*ZCdyV^d#oV;Q{BnD?Y!BI4B>8GD<*R@YlFsX?B2YAZ0#iSb}QxZ|x>^Bgss;0S*%0idQ799iE8t~R=$*%|BZj=>jk*mT1eS>C?c zzL|$FH9J`z>}3^61bcb-PsNzc#EQij#Njy4g7ALB8&!o?TGqcZ7=u!FE{K{@1{%<{OaQ4ZTTz?EpAsCtk;ilx3l=(~p0kjT?gA z(;JC!9X;X=o_^txPJ$7a2~xd3JW0F=Uu^Ar6MXJE(QP@i1?Fseqjth*U)FldDewa3 zM)9#BO+unGN}?a1m1{*5Pyol@by~d1I5F7h#2e2pcGk+P@mhI36J{FBI_3z&aQ>Eo zV#)(rd(w{jGV~kNqSxW)TN{7WRF}B~bHs@8%w$HlupP!9_GKL`fXado6FN)c?(v-a zN0*CZ@5AJ;cti_mN5p_8-iO%0+m?dgu`FcolPq1j$o{Nb*WF;%MUoy6?}O+xcjg>e zMb6ouZJ)d5ZBJPeK8614uXove<9{(WNB6t==8g1Trm03Xm0J_Wnt*>BIDY*jvM11(&>F?RsDav;KX=vX=_fIMBkpO?nXVOT;>E>@}K?ZuA z_^do#|DQm2plQMn7*ylv3~?9-XP9dI-&}Ob*VDtPV{aIm6%QtKmc##`I}u7oH2H5H zET@NO$XtrX(nIOz_Iyvlr@TZ3g0c_}<>s;(;9eHXVqkt342f#{+ra_4%^~(&A74 z0WHO2iJ!qspezJ$35#53;nz2xLtfXNO+yc*BWaBza%y)bl8x)~Cx2n447Uq>UA+1S zD!p#E`|mcb|He0^E_`8D5pO%`iI+@o3)62N;s4ZPGKPOwvI^5jJuy(~B1sIRtRw-Q zqYAlXB_V`$+DJOKKoq>p=*xjLsv4L!A%7^*JXz>RO#id0{Rj1pHyK|7c~O9<^#pJt z;rmSU{!kj#P)7)y3U%N_+V0z#m_Io`fB(*T+^&#F6#B>a?HiByZXkL;IIvGg!x0nR&J7bBtpj>AObwP!mHhWhE6gKiCRY4tBjBH=F zYej!o^CS-N0e7A{RoefR51093a>cUUdylx#IIdwR&HG7%`$^OL+f{Lpc=p-L09t;- zR{4%YYu6Nup!JQsfL^4!fA~q)7BEHdBTxDk&_wW4od+9Ag|6DbWrNl3Ldkw~Q3lM9 zerS9;-`$;`9{AS?~xkZ0_64}K$<83fl(1aZgkJRGv+Gs5fE{+V> zW3l>ZEjD-hl{hJlqX>=RcP_TCcUk$Kyk+GraL*?D+ay=8tb%1Mjeqwu z`1#bREq0J%Yh|fM8$s9_AUH|*rAjw2t;JDSGx(NpMB$sf^jUm{!+-y;_`ds|hdY1l zy2OL3MBE+P-;qp?RiSjxJ*V%v?>@Bmug9o*%{8a*M*E*+U*av9XMe1ibPQ*Ip`t zZqBz)>Q#LdknM$*m?C@D3Eaw|`%Hh_40dm9OiwpRushiedM0}Ta0im$w_v(&Lt|#9 zv7vAJp>Vff%+z{grRD~w3jlkv!pHQWR~PMu2^0y z3CD3?_&|4)X7+bFXFBl#s-%CnYGX&g{T

Kd9okHOHJp<7fiYI5~#NQrH6gbE~ai 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 Q%cegw^AE2|F4q760F!$}ivR!s delta 37929 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 diff --git a/web/src/assets/iconfont/iconfont.woff2 b/web/src/assets/iconfont/iconfont.woff2 index 9d77c06de20db7ba11d998fa177077d56623c46b..9da21dc724a757be688c1291cffdc30c93570d35 100644 GIT binary patch literal 33340 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 literal 33184 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 +

+
+ + +
+
+ + +
+ +
+ + +
+ +
+ 重置视角 + + {{ autoRotate ? '停止旋转' : '自动旋转' }} + +
+
+ + +
+
+ +

加载3D模型中...

+
+
+ + +
+
+ +

{{ error }}

+ 重试 +
+
+
+ + + + + diff --git a/web/src/components/admin/AdminSidebar.vue b/web/src/components/admin/AdminSidebar.vue index 172327f9..0b48288a 100644 --- a/web/src/components/admin/AdminSidebar.vue +++ b/web/src/components/admin/AdminSidebar.vue @@ -179,22 +179,39 @@ const items = [ }, ], }, + { + icon: 'cube', + index: '/admin/ai3d', + title: '3D生成', + subs: [ + { + icon: 'list', + index: '/admin/ai3d/jobs', + title: '任务管理', + }, + { + icon: 'config', + index: '/admin/ai3d/config', + title: '配置管理', + }, + ], + }, { icon: 'moderation', index: '/admin/config/moderation', title: '文本审查', subs: [ - { - icon: 'config', - index: '/admin/config/moderation', - title: '审查配置', - }, { icon: 'list', index: '/admin/moderation/list', title: '审核记录', }, + { + icon: 'config', + index: '/admin/moderation/config', + title: '审查配置', + }, ], }, { diff --git a/web/src/router.js b/web/src/router.js index 9d3148c3..c83c2f43 100644 --- a/web/src/router.js +++ b/web/src/router.js @@ -92,6 +92,12 @@ const routes = [ meta: { title: 'Suno音乐创作' }, component: () => import('@/views/Suno.vue'), }, + { + name: 'ai3d', + path: '/ai3d', + meta: { title: 'AI3D模型生成' }, + component: () => import('@/views/AIThreeDCreate.vue'), + }, { name: 'ExternalLink', path: '/external', @@ -148,12 +154,7 @@ const routes = [ meta: { title: '控制台登录' }, component: () => import('@/views/admin/Login.vue'), }, - { - path: '/payReturn', - name: 'pay-return', - meta: { title: '支付回调' }, - component: () => import('@/views/PayReturn.vue'), - }, + { name: 'admin', path: '/admin', @@ -210,7 +211,7 @@ const routes = [ component: () => import('@/views/admin/settings/PluginConfig.vue'), }, { - path: '/admin/config/moderation', + path: '/admin/moderation/config', name: 'admin-config-moderation', meta: { title: '文本审查配置' }, component: () => import('@/views/admin/moderation/ModerationConfig.vue'), @@ -345,7 +346,19 @@ const routes = [ path: '/admin/jimeng/config', name: 'admin-jimeng-config', meta: { title: '即梦设置' }, - component: () => import('@/views/admin/jimeng/JimengSetting.vue'), + component: () => import('@/views/admin/jimeng/JimengConfig.vue'), + }, + { + path: '/admin/ai3d/jobs', + name: 'admin-ai3d-jobs', + meta: { title: '3D任务管理' }, + component: () => import('@/views/admin/ai3d/AIThreeDJobs.vue'), + }, + { + path: '/admin/ai3d/config', + name: 'admin-ai3d-config', + meta: { title: '3D配置管理' }, + component: () => import('@/views/admin/ai3d/AIThreeDConfig.vue'), }, { path: '/admin/powerLog', @@ -459,6 +472,12 @@ const routes = [ name: 'mobile-jimeng', component: () => import('@/views/mobile/JimengCreate.vue'), }, + { + path: '/mobile/3d', + name: 'mobile-3d', + meta: { title: '3D模型生成' }, + component: () => import('@/views/mobile/ThreeDCreate.vue'), + }, ], }, diff --git a/web/src/views/AIThreeDCreate.vue b/web/src/views/AIThreeDCreate.vue new file mode 100644 index 00000000..bedf46fa --- /dev/null +++ b/web/src/views/AIThreeDCreate.vue @@ -0,0 +1,619 @@ + + + + + diff --git a/web/src/views/PayReturn.vue b/web/src/views/PayReturn.vue deleted file mode 100644 index 458d1669..00000000 --- a/web/src/views/PayReturn.vue +++ /dev/null @@ -1,18 +0,0 @@ - - - diff --git a/web/src/views/admin/ai3d/AIThreeDConfig.vue b/web/src/views/admin/ai3d/AIThreeDConfig.vue new file mode 100644 index 00000000..1cda48d5 --- /dev/null +++ b/web/src/views/admin/ai3d/AIThreeDConfig.vue @@ -0,0 +1,405 @@ + + + + + diff --git a/web/src/views/admin/ai3d/AIThreeDJobs.vue b/web/src/views/admin/ai3d/AIThreeDJobs.vue new file mode 100644 index 00000000..4241fc48 --- /dev/null +++ b/web/src/views/admin/ai3d/AIThreeDJobs.vue @@ -0,0 +1,551 @@ + + + + + diff --git a/web/src/views/admin/jimeng/JimengSetting.vue b/web/src/views/admin/jimeng/JimengConfig.vue similarity index 99% rename from web/src/views/admin/jimeng/JimengSetting.vue rename to web/src/views/admin/jimeng/JimengConfig.vue index 61203a35..fe8c7e0b 100644 --- a/web/src/views/admin/jimeng/JimengSetting.vue +++ b/web/src/views/admin/jimeng/JimengConfig.vue @@ -241,7 +241,7 @@ onMounted(() => { // 加载配置 const loadConfig = async () => { try { - const res = await httpGet('/api/admin/jimeng/config') + const res = await httpGet('/api/admin/config/get?key=jimeng') jimengConfig.value = res.data } catch (e) { ElMessage.error('加载配置失败: ' + e.message) diff --git a/web/src/views/admin/moderation/ModerationConfig.vue b/web/src/views/admin/moderation/ModerationConfig.vue index a18eda50..cd954766 100644 --- a/web/src/views/admin/moderation/ModerationConfig.vue +++ b/web/src/views/admin/moderation/ModerationConfig.vue @@ -265,7 +265,7 @@ watch(activeTab, (newTab) => { const saveModerationConfig = async () => { loading.value = true try { - await httpPost('/api/admin/config/update/moderation', configs.value) + await httpPost('/api/admin/moderation/config', configs.value) ElMessage.success('保存成功') } catch (e) { ElMessage.error('保存失败:' + (e.message || '未知错误')) @@ -289,7 +289,7 @@ const testModeration = async () => { testLoading.value = true try { - const res = await httpPost('/api/admin/config/moderation/test', { + const res = await httpPost('/api/admin/moderation/test', { text: testForm.value.text.trim(), service: configs.value.active, }) diff --git a/web/src/views/mobile/Index.vue b/web/src/views/mobile/Index.vue index b020c93c..921291bb 100644 --- a/web/src/views/mobile/Index.vue +++ b/web/src/views/mobile/Index.vue @@ -167,6 +167,13 @@ const features = ref([ color: '#F97316', url: '/mobile/jimeng', }, + { + key: '3d', + name: '3D生成', + icon: 'icon-3d', + color: '#8B5CF6', + url: '/mobile/3d', + }, { key: 'agent', name: '智能体', icon: 'icon-app', color: '#3B82F6', url: '/mobile/apps' }, { key: 'imgWall', diff --git a/web/src/views/mobile/ThreeDCreate.vue b/web/src/views/mobile/ThreeDCreate.vue new file mode 100644 index 00000000..801c9dbd --- /dev/null +++ b/web/src/views/mobile/ThreeDCreate.vue @@ -0,0 +1,765 @@ + + + + +