diff --git a/api/core/app_server.go b/api/core/app_server.go
index d633fc18..2fca4ea7 100644
--- a/api/core/app_server.go
+++ b/api/core/app_server.go
@@ -217,6 +217,7 @@ func needLogin(c *gin.Context) bool {
c.Request.URL.Path == "/api/sd/client" ||
c.Request.URL.Path == "/api/config/get" ||
c.Request.URL.Path == "/api/product/list" ||
+ c.Request.URL.Path == "/api/menu/list" ||
strings.HasPrefix(c.Request.URL.Path, "/api/test") ||
strings.HasPrefix(c.Request.URL.Path, "/api/function/") ||
strings.HasPrefix(c.Request.URL.Path, "/api/sms/") ||
diff --git a/api/core/types/config.go b/api/core/types/config.go
index 324c3cad..24a94e78 100644
--- a/api/core/types/config.go
+++ b/api/core/types/config.go
@@ -144,13 +144,13 @@ type SystemConfig struct {
PowerPrice float64 `json:"power_price,omitempty"` // 算力单价
OrderPayTimeout int `json:"order_pay_timeout,omitempty"` //订单支付超时时间
- VipInfoText string `json:"vip_info_text"` // 会员页面充值说明
+ VipInfoText string `json:"vip_info_text,omitempty"` // 会员页面充值说明
DefaultModels []int `json:"default_models,omitempty"` // 默认开通的 AI 模型
- MjPower int `json:"mj_power,omitempty"` // MJ 绘画消耗算力
- MjActionPower int `json:"mj_action_power"` // MJ 操作(放大,变换)消耗算力
- SdPower int `json:"sd_power,omitempty"` // SD 绘画消耗算力
- DallPower int `json:"dall_power,omitempty"` // DALLE3 绘图消耗算力
+ MjPower int `json:"mj_power,omitempty"` // MJ 绘画消耗算力
+ MjActionPower int `json:"mj_action_power,omitempty"` // MJ 操作(放大,变换)消耗算力
+ SdPower int `json:"sd_power,omitempty"` // SD 绘画消耗算力
+ DallPower int `json:"dall_power,omitempty"` // DALLE3 绘图消耗算力
WechatCardURL string `json:"wechat_card_url,omitempty"` // 微信客服地址
diff --git a/api/handler/admin/menu_handler.go b/api/handler/admin/menu_handler.go
new file mode 100644
index 00000000..7358ffa9
--- /dev/null
+++ b/api/handler/admin/menu_handler.go
@@ -0,0 +1,121 @@
+package admin
+
+import (
+ "chatplus/core"
+ "chatplus/core/types"
+ "chatplus/handler"
+ "chatplus/store/model"
+ "chatplus/store/vo"
+ "chatplus/utils"
+ "chatplus/utils/resp"
+ "github.com/gin-gonic/gin"
+ "gorm.io/gorm"
+)
+
+type MenuHandler struct {
+ handler.BaseHandler
+}
+
+func NewMenuHandler(app *core.AppServer, db *gorm.DB) *MenuHandler {
+ return &MenuHandler{BaseHandler: handler.BaseHandler{App: app, DB: db}}
+}
+
+func (h *MenuHandler) Save(c *gin.Context) {
+ var data struct {
+ Id uint `json:"id"`
+ Name string `json:"name"`
+ Icon string `json:"icon"`
+ URL string `json:"url"`
+ SortNum int `json:"sort_num"`
+ Enabled bool `json:"enabled"`
+ }
+ if err := c.ShouldBindJSON(&data); err != nil {
+ resp.ERROR(c, types.InvalidArgs)
+ return
+ }
+
+ res := h.DB.Save(&model.Menu{
+ Id: data.Id,
+ Name: data.Name,
+ Icon: data.Icon,
+ URL: data.URL,
+ SortNum: data.SortNum,
+ Enabled: data.Enabled,
+ })
+ if res.Error != nil {
+ resp.ERROR(c, "更新数据库失败!")
+ return
+ }
+ resp.SUCCESS(c)
+}
+
+// List 数据列表
+func (h *MenuHandler) List(c *gin.Context) {
+ var items []model.Menu
+ var list = make([]vo.Menu, 0)
+ res := h.DB.Order("sort_num ASC").Find(&items)
+ if res.Error == nil {
+ for _, item := range items {
+ var product vo.Menu
+ err := utils.CopyObject(item, &product)
+ if err == nil {
+ list = append(list, product)
+ }
+ }
+ }
+ resp.SUCCESS(c, list)
+}
+
+func (h *MenuHandler) Enable(c *gin.Context) {
+ var data struct {
+ Id uint `json:"id"`
+ Enabled bool `json:"enabled"`
+ }
+
+ if err := c.ShouldBindJSON(&data); err != nil {
+ resp.ERROR(c, types.InvalidArgs)
+ return
+ }
+
+ res := h.DB.Model(&model.Menu{}).Where("id", data.Id).UpdateColumn("enabled", data.Enabled)
+ if res.Error != nil {
+ resp.ERROR(c, "更新数据库失败!")
+ return
+ }
+ resp.SUCCESS(c)
+}
+
+func (h *MenuHandler) Sort(c *gin.Context) {
+ var data struct {
+ Ids []uint `json:"ids"`
+ Sorts []int `json:"sorts"`
+ }
+
+ if err := c.ShouldBindJSON(&data); err != nil {
+ resp.ERROR(c, types.InvalidArgs)
+ return
+ }
+
+ for index, id := range data.Ids {
+ res := h.DB.Model(&model.Menu{}).Where("id", id).Update("sort_num", data.Sorts[index])
+ if res.Error != nil {
+ resp.ERROR(c, "更新数据库失败!")
+ return
+ }
+ }
+
+ resp.SUCCESS(c)
+}
+
+func (h *MenuHandler) Remove(c *gin.Context) {
+ id := h.GetInt(c, "id", 0)
+
+ if id > 0 {
+ res := h.DB.Where("id", id).Delete(&model.Menu{})
+ if res.Error != nil {
+ resp.ERROR(c, "更新数据库失败!")
+ return
+ }
+ }
+ resp.SUCCESS(c)
+}
diff --git a/api/handler/admin/product_handler.go b/api/handler/admin/product_handler.go
index 8e960c24..42eff03f 100644
--- a/api/handler/admin/product_handler.go
+++ b/api/handler/admin/product_handler.go
@@ -65,21 +65,11 @@ func (h *ProductHandler) Save(c *gin.Context) {
resp.SUCCESS(c, itemVo)
}
-// List 模型列表
+// List 数据列表
func (h *ProductHandler) List(c *gin.Context) {
- if err := utils.CheckPermission(c, h.DB); err != nil {
- resp.NotPermission(c)
- return
- }
-
- session := h.DB.Session(&gorm.Session{})
- enable := h.GetBool(c, "enable")
- if enable {
- session = session.Where("enabled", enable)
- }
var items []model.Product
var list = make([]vo.Product, 0)
- res := session.Order("sort_num ASC").Find(&items)
+ res := h.DB.Order("sort_num ASC").Find(&items)
if res.Error == nil {
for _, item := range items {
var product vo.Product
@@ -128,7 +118,7 @@ func (h *ProductHandler) Sort(c *gin.Context) {
}
for index, id := range data.Ids {
- res := h.DB.Model(&model.Product{}).Where("id = ?", id).Update("sort_num", data.Sorts[index])
+ res := h.DB.Model(&model.Product{}).Where("id", id).Update("sort_num", data.Sorts[index])
if res.Error != nil {
resp.ERROR(c, "更新数据库失败!")
return
@@ -142,7 +132,7 @@ func (h *ProductHandler) Remove(c *gin.Context) {
id := h.GetInt(c, "id", 0)
if id > 0 {
- res := h.DB.Where("id = ?", id).Delete(&model.Product{})
+ res := h.DB.Where("id", id).Delete(&model.Product{})
if res.Error != nil {
resp.ERROR(c, "更新数据库失败!")
return
diff --git a/api/handler/chat_role_handler.go b/api/handler/chat_role_handler.go
index 4e48a60b..a555256d 100644
--- a/api/handler/chat_role_handler.go
+++ b/api/handler/chat_role_handler.go
@@ -25,9 +25,10 @@ func (h *ChatRoleHandler) List(c *gin.Context) {
all := h.GetBool(c, "all")
userId := h.GetLoginUserId(c)
var roles []model.ChatRole
+ var roleVos = make([]vo.ChatRole, 0)
res := h.DB.Where("enable", true).Order("sort_num ASC").Find(&roles)
if res.Error != nil {
- resp.ERROR(c, "No roles found,"+res.Error.Error())
+ resp.SUCCESS(c, roleVos)
return
}
@@ -55,8 +56,7 @@ func (h *ChatRoleHandler) List(c *gin.Context) {
resp.ERROR(c, "角色解析失败!")
return
}
- // 转成 vo
- var roleVos = make([]vo.ChatRole, 0)
+
for _, r := range roles {
if !utils.ContainsStr(roleKeys, r.Key) {
continue
diff --git a/api/handler/menu_handler.go b/api/handler/menu_handler.go
new file mode 100644
index 00000000..b922f54c
--- /dev/null
+++ b/api/handler/menu_handler.go
@@ -0,0 +1,36 @@
+package handler
+
+import (
+ "chatplus/core"
+ "chatplus/store/model"
+ "chatplus/store/vo"
+ "chatplus/utils"
+ "chatplus/utils/resp"
+ "github.com/gin-gonic/gin"
+ "gorm.io/gorm"
+)
+
+type MenuHandler struct {
+ BaseHandler
+}
+
+func NewMenuHandler(app *core.AppServer, db *gorm.DB) *MenuHandler {
+ return &MenuHandler{BaseHandler: BaseHandler{App: app, DB: db}}
+}
+
+// List 数据列表
+func (h *MenuHandler) List(c *gin.Context) {
+ var items []model.Menu
+ var list = make([]vo.Menu, 0)
+ res := h.DB.Where("enabled", true).Order("sort_num ASC").Find(&items)
+ if res.Error == nil {
+ for _, item := range items {
+ var product vo.Menu
+ err := utils.CopyObject(item, &product)
+ if err == nil {
+ list = append(list, product)
+ }
+ }
+ }
+ resp.SUCCESS(c, list)
+}
diff --git a/api/handler/sd_handler.go b/api/handler/sd_handler.go
index 9881a35d..09b35d49 100644
--- a/api/handler/sd_handler.go
+++ b/api/handler/sd_handler.go
@@ -3,6 +3,7 @@ package handler
import (
"chatplus/core"
"chatplus/core/types"
+ "chatplus/service"
"chatplus/service/oss"
"chatplus/service/sd"
"chatplus/store/model"
@@ -23,15 +24,17 @@ import (
type SdJobHandler struct {
BaseHandler
- redis *redis.Client
- pool *sd.ServicePool
- uploader *oss.UploaderManager
+ redis *redis.Client
+ pool *sd.ServicePool
+ uploader *oss.UploaderManager
+ snowflake *service.Snowflake
}
-func NewSdJobHandler(app *core.AppServer, db *gorm.DB, pool *sd.ServicePool, manager *oss.UploaderManager) *SdJobHandler {
+func NewSdJobHandler(app *core.AppServer, db *gorm.DB, pool *sd.ServicePool, manager *oss.UploaderManager, snowflake *service.Snowflake) *SdJobHandler {
return &SdJobHandler{
- pool: pool,
- uploader: manager,
+ pool: pool,
+ uploader: manager,
+ snowflake: snowflake,
BaseHandler: BaseHandler{
App: app,
DB: db,
@@ -116,8 +119,13 @@ func (h *SdJobHandler) Image(c *gin.Context) {
}
idValue, _ := c.Get(types.LoginUserID)
userId := utils.IntValue(utils.InterfaceToString(idValue), 0)
+ taskId, err := h.snowflake.Next(true)
+ if err != nil {
+ resp.ERROR(c, "error with generate task id: "+err.Error())
+ return
+ }
params := types.SdTaskParams{
- TaskId: fmt.Sprintf("task(%s)", utils.RandString(15)),
+ TaskId: taskId,
Prompt: data.Prompt,
NegativePrompt: data.NegativePrompt,
Steps: data.Steps,
diff --git a/api/main.go b/api/main.go
index c5ce4baa..6d82fccb 100644
--- a/api/main.go
+++ b/api/main.go
@@ -417,6 +417,20 @@ func main() {
group := s.Engine.Group("/api/admin/powerLog/")
group.POST("list", h.List)
}),
+ fx.Provide(admin.NewMenuHandler),
+ fx.Invoke(func(s *core.AppServer, h *admin.MenuHandler) {
+ group := s.Engine.Group("/api/admin/menu/")
+ group.POST("save", h.Save)
+ group.GET("list", h.List)
+ group.POST("enable", h.Enable)
+ group.POST("sort", h.Sort)
+ group.GET("remove", h.Remove)
+ }),
+ fx.Provide(handler.NewMenuHandler),
+ fx.Invoke(func(s *core.AppServer, h *handler.MenuHandler) {
+ group := s.Engine.Group("/api/menu/")
+ group.GET("list", h.List)
+ }),
fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
err := s.Run(db)
if err != nil {
diff --git a/api/service/mj/pool.go b/api/service/mj/pool.go
index 14ea1da4..0143467f 100644
--- a/api/service/mj/pool.go
+++ b/api/service/mj/pool.go
@@ -181,6 +181,7 @@ func (p *ServicePool) SyncTaskProgress() {
CreatedAt: time.Now(),
})
}
+ continue
}
if servicePlus := p.getService(job.ChannelId); servicePlus != nil {
diff --git a/api/service/mj/service.go b/api/service/mj/service.go
index 861095d4..f73a9c63 100644
--- a/api/service/mj/service.go
+++ b/api/service/mj/service.go
@@ -77,6 +77,13 @@ func (s *Service) Run() {
}
}
+ var job model.MidJourneyJob
+ tx := s.db.Where("id = ?", task.Id).First(&job)
+ if tx.Error != nil {
+ logger.Error("任务不存在,任务ID:", task.TaskId)
+ continue
+ }
+
logger.Infof("%s handle a new MidJourney task: %+v", s.Name, task)
var res ImageRes
switch task.Type {
@@ -97,8 +104,6 @@ func (s *Service) Run() {
break
}
- var job model.MidJourneyJob
- s.db.Where("id = ?", task.Id).First(&job)
if err != nil || (res.Code != 1 && res.Code != 22) {
errMsg := fmt.Sprintf("%v,%s", err, res.Description)
logger.Error("绘画任务执行失败:", errMsg)
@@ -127,14 +132,17 @@ func (s *Service) canHandleTask() bool {
return handledNum < s.maxHandleTaskNum
}
-// remove the expired tasks
+// remove the timeout tasks
func (s *Service) checkTasks() {
for k, t := range s.taskStartTimes {
if time.Now().Unix()-t.Unix() > s.taskTimeout {
delete(s.taskStartTimes, k)
atomic.AddInt32(&s.HandledTaskNum, -1)
- // delete task from database
- s.db.Delete(&model.MidJourneyJob{Id: uint(k)}, "progress < 100")
+
+ s.db.Model(&model.MidJourneyJob{Id: uint(k)}).UpdateColumns(map[string]interface{}{
+ "progress": -1,
+ "err_msg": "任务超时",
+ })
}
}
}
diff --git a/api/service/sd/service.go b/api/service/sd/service.go
index ccb56974..f87067a3 100644
--- a/api/service/sd/service.go
+++ b/api/service/sd/service.go
@@ -114,6 +114,7 @@ func (s *Service) Txt2Img(task types.SdTask) error {
Width: task.Params.Width,
Height: task.Params.Height,
SamplerName: task.Params.Sampler,
+ ForceTaskId: task.Params.TaskId,
}
if task.Params.Seed > 0 {
body.Seed = task.Params.Seed
diff --git a/api/store/model/menu.go b/api/store/model/menu.go
new file mode 100644
index 00000000..e215e204
--- /dev/null
+++ b/api/store/model/menu.go
@@ -0,0 +1,11 @@
+package model
+
+// Menu 系统菜单
+type Menu struct {
+ Id uint `gorm:"primarykey;column:id"`
+ Name string // 菜单名称
+ Icon string // 菜单图标
+ URL string // 菜单跳转地址
+ SortNum int // 排序
+ Enabled bool // 启用状态
+}
diff --git a/api/store/vo/menu.go b/api/store/vo/menu.go
new file mode 100644
index 00000000..c9975a45
--- /dev/null
+++ b/api/store/vo/menu.go
@@ -0,0 +1,11 @@
+package vo
+
+// Menu 系统菜单
+type Menu struct {
+ Id uint `json:"id"`
+ Name string `json:"name"`
+ Icon string `json:"icon"`
+ URL string `json:"url"`
+ SortNum int `json:"sort_num"`
+ Enabled bool `json:"enabled"`
+}
diff --git a/database/update-v4.0.2.sql b/database/update-v4.0.2.sql
new file mode 100644
index 00000000..00d7708f
--- /dev/null
+++ b/database/update-v4.0.2.sql
@@ -0,0 +1,19 @@
+-- 菜单表
+CREATE TABLE `chatgpt_plus`.`chatgpt_menus` (
+ `id` INT(11) NOT NULL AUTO_INCREMENT ,
+ `name` VARCHAR(30) NOT NULL COMMENT '菜单名称' ,
+ `icon` VARCHAR(150) NOT NULL COMMENT '菜单图标' ,
+ `url` VARCHAR(100) NOT NULL COMMENT '地址' ,
+ `sort_num` SMALLINT(3) NOT NULL COMMENT '排序' ,
+ `enabled` TINYINT(1) NOT NULL COMMENT '是否启用' ,
+ PRIMARY KEY (`id`)) ENGINE = InnoDB COMMENT = '前端菜单表';
+
+INSERT INTO `chatgpt_menus` (`id`, `name`, `icon`, `url`, `sort_num`, `enabled`) VALUES
+ (1, '对话聊天', '/images/menu/chat.png', '/chat', 0, 1),
+ (5, 'MJ 绘画', '/images/menu/mj.png', '/mj', 1, 1),
+ (6, 'SD 绘画', '/images/menu/sd.png', '/sd', 2, 1),
+ (7, '算力日志', '/images/menu/log.png', '/powerLog', 5, 1),
+ (8, '应用中心', '/images/menu/app.png', '/apps', 3, 1),
+ (9, '作品展示', '/images/menu/img-wall.png', '/images-wall', 4, 1),
+ (10, '会员计划', '/images/menu/member.png', '/member', 6, 1),
+ (11, '分享计划', '/images/menu/share.png', '/invite', 7, 1);
\ No newline at end of file
diff --git a/web/public/images/menu/app.png b/web/public/images/menu/app.png
new file mode 100644
index 00000000..2785231c
Binary files /dev/null and b/web/public/images/menu/app.png differ
diff --git a/web/public/images/chat.png b/web/public/images/menu/chat.png
similarity index 100%
rename from web/public/images/chat.png
rename to web/public/images/menu/chat.png
diff --git a/web/public/images/menu/img-wall.png b/web/public/images/menu/img-wall.png
new file mode 100644
index 00000000..61f9fdb8
Binary files /dev/null and b/web/public/images/menu/img-wall.png differ
diff --git a/web/public/images/menu/log.png b/web/public/images/menu/log.png
new file mode 100644
index 00000000..03870d66
Binary files /dev/null and b/web/public/images/menu/log.png differ
diff --git a/web/public/images/menu/member.png b/web/public/images/menu/member.png
new file mode 100644
index 00000000..cceac4ca
Binary files /dev/null and b/web/public/images/menu/member.png differ
diff --git a/web/public/images/mj.png b/web/public/images/menu/mj.png
similarity index 100%
rename from web/public/images/mj.png
rename to web/public/images/menu/mj.png
diff --git a/web/public/images/sd.png b/web/public/images/menu/sd.png
similarity index 100%
rename from web/public/images/sd.png
rename to web/public/images/menu/sd.png
diff --git a/web/public/images/menu/share.png b/web/public/images/menu/share.png
new file mode 100644
index 00000000..8d84b074
Binary files /dev/null and b/web/public/images/menu/share.png differ
diff --git a/web/src/router.js b/web/src/router.js
index f4c81e61..968b0690 100644
--- a/web/src/router.js
+++ b/web/src/router.js
@@ -173,7 +173,7 @@ const routes = [
name: 'admin-manger',
meta: {title: '管理员'},
component: () => import('@/views/admin/Manager.vue'),
- },
+ }
]
},
diff --git a/web/src/views/ChatPlus.vue b/web/src/views/ChatPlus.vue
index 19a729a6..0914358f 100644
--- a/web/src/views/ChatPlus.vue
+++ b/web/src/views/ChatPlus.vue
@@ -313,12 +313,16 @@ httpGet("/api/config/get?key=system").then(res => {
// 获取系统公告
httpGet("/api/config/get?key=notice").then(res => {
- notice.value = md.render(res.data['content'])
- const oldNotice = localStorage.getItem(noticeKey.value);
- // 如果公告有更新,则显示公告
- if (oldNotice !== notice.value && notice.value.length > 10) {
- showNotice.value = true
+ try {
+ notice.value = md.render(res.data['content'])
+ const oldNotice = localStorage.getItem(noticeKey.value);
+ // 如果公告有更新,则显示公告
+ if (oldNotice !== notice.value && notice.value.length > 10) {
+ showNotice.value = true
+ }
+ } catch (e) {
}
+
}).catch(e => {
ElMessage.error("获取系统配置失败:" + e.message)
})
diff --git a/web/src/views/Home.vue b/web/src/views/Home.vue
index db214038..43687e56 100644
--- a/web/src/views/Home.vue
+++ b/web/src/views/Home.vue
@@ -11,10 +11,9 @@
-