diff --git a/api/handler/admin/api_key_handler.go b/api/handler/admin/api_key_handler.go index c6d977ef..b4614f6c 100644 --- a/api/handler/admin/api_key_handler.go +++ b/api/handler/admin/api_key_handler.go @@ -8,8 +8,6 @@ import ( "chatplus/store/vo" "chatplus/utils" "chatplus/utils/resp" - "time" - "github.com/gin-gonic/gin" "gorm.io/gorm" ) @@ -27,21 +25,21 @@ func NewApiKeyHandler(app *core.AppServer, db *gorm.DB) *ApiKeyHandler { func (h *ApiKeyHandler) Save(c *gin.Context) { var data struct { - Id uint `json:"id"` - Value string `json:"value"` - LastUsedAt string `json:"last_used_at"` - CreatedAt int64 `json:"created_at"` + Id uint `json:"id"` + Platform string `json:"platform"` + Value string `json:"value"` } if err := c.ShouldBindJSON(&data); err != nil { resp.ERROR(c, types.InvalidArgs) return } - apiKey := model.ApiKey{Value: data.Value, LastUsedAt: utils.Str2stamp(data.LastUsedAt)} - apiKey.Id = data.Id - if apiKey.Id > 0 { - apiKey.CreatedAt = time.Unix(data.CreatedAt, 0) + apiKey := model.ApiKey{} + if data.Id > 0 { + h.db.Find(&apiKey) } + apiKey.Platform = data.Platform + apiKey.Value = data.Value res := h.db.Save(&apiKey) if res.Error != nil { resp.ERROR(c, "更新数据库失败!") @@ -60,14 +58,9 @@ func (h *ApiKeyHandler) Save(c *gin.Context) { } func (h *ApiKeyHandler) List(c *gin.Context) { - userId := h.GetInt(c, "user_id", -1) - query := h.db.Session(&gorm.Session{}) - if userId >= 0 { - query = query.Where("user_id", userId) - } var items []model.ApiKey var keys = make([]vo.ApiKey, 0) - res := query.Find(&items) + res := h.db.Find(&items) if res.Error == nil { for _, item := range items { var key vo.ApiKey diff --git a/api/handler/admin/chat_model_handler.go b/api/handler/admin/chat_model_handler.go new file mode 100644 index 00000000..1d40f6e2 --- /dev/null +++ b/api/handler/admin/chat_model_handler.go @@ -0,0 +1,143 @@ +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" + "time" +) + +type ChatModelHandler struct { + handler.BaseHandler + db *gorm.DB +} + +func NewChatModelHandler(app *core.AppServer, db *gorm.DB) *ChatModelHandler { + h := ChatModelHandler{db: db} + h.App = app + return &h +} + +func (h *ChatModelHandler) Save(c *gin.Context) { + var data struct { + Id uint `json:"id"` + Name string `json:"name"` + Value string `json:"value"` + Enabled bool `json:"enabled"` + SortNum int `json:"sort_num"` + Platform string `json:"platform"` + CreatedAt int64 `json:"created_at"` + } + if err := c.ShouldBindJSON(&data); err != nil { + resp.ERROR(c, types.InvalidArgs) + return + } + + item := model.ChatModel{Platform: data.Platform, Name: data.Name, Value: data.Value, Enabled: data.Enabled} + item.Id = data.Id + if item.Id > 0 { + item.CreatedAt = time.Unix(data.CreatedAt, 0) + } + res := h.db.Save(&item) + if res.Error != nil { + resp.ERROR(c, "更新数据库失败!") + return + } + + var itemVo vo.ChatModel + err := utils.CopyObject(item, &itemVo) + if err != nil { + resp.ERROR(c, "数据拷贝失败!") + return + } + itemVo.Id = item.Id + itemVo.CreatedAt = item.CreatedAt.Unix() + resp.SUCCESS(c, itemVo) +} + +// List 模型列表 +func (h *ChatModelHandler) List(c *gin.Context) { + session := h.db.Session(&gorm.Session{}) + enable := h.GetBool(c, "enable") + if enable { + session = session.Where("enabled", enable) + } + var items []model.ChatModel + var cms = make([]vo.ChatModel, 0) + res := session.Order("sort_num ASC").Find(&items) + if res.Error == nil { + for _, item := range items { + var cm vo.ChatModel + err := utils.CopyObject(item, &cm) + if err == nil { + cm.Id = item.Id + cm.CreatedAt = item.CreatedAt.Unix() + cm.UpdatedAt = item.UpdatedAt.Unix() + cms = append(cms, cm) + } else { + logger.Error(err) + } + } + } + resp.SUCCESS(c, cms) +} + +func (h *ChatModelHandler) 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.ChatModel{}).Where("id = ?", data.Id).Update("enabled", data.Enabled) + if res.Error != nil { + resp.ERROR(c, "更新数据库失败!") + return + } + resp.SUCCESS(c) +} + +func (h *ChatModelHandler) 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.ChatModel{}).Where("id = ?", id).Update("sort_num", data.Sorts[index]) + if res.Error != nil { + resp.ERROR(c, "更新数据库失败!") + return + } + } + + resp.SUCCESS(c) +} + +func (h *ChatModelHandler) Remove(c *gin.Context) { + id := h.GetInt(c, "id", 0) + + if id > 0 { + res := h.db.Where("id = ?", id).Delete(&model.ChatModel{}) + if res.Error != nil { + resp.ERROR(c, "更新数据库失败!") + return + } + } + resp.SUCCESS(c) +} diff --git a/api/handler/admin/chat_role_handler.go b/api/handler/admin/chat_role_handler.go index ca439cdc..163f04a2 100644 --- a/api/handler/admin/chat_role_handler.go +++ b/api/handler/admin/chat_role_handler.go @@ -55,7 +55,7 @@ func (h *ChatRoleHandler) Save(c *gin.Context) { func (h *ChatRoleHandler) List(c *gin.Context) { var items []model.ChatRole var roles = make([]vo.ChatRole, 0) - res := h.db.Order("sort ASC").Find(&items) + res := h.db.Order("sort_num ASC").Find(&items) if res.Error != nil { resp.ERROR(c, "No data found") return @@ -75,24 +75,24 @@ func (h *ChatRoleHandler) List(c *gin.Context) { resp.SUCCESS(c, roles) } -// SetSort 更新角色排序 -func (h *ChatRoleHandler) SetSort(c *gin.Context) { +// Sort 更新角色排序 +func (h *ChatRoleHandler) Sort(c *gin.Context) { var data struct { - Id uint `json:"id"` - Sort int `json:"sort"` + Ids []uint `json:"ids"` + Sorts []int `json:"sorts"` } + if err := c.ShouldBindJSON(&data); err != nil { resp.ERROR(c, types.InvalidArgs) return } - if data.Id <= 0 { - resp.HACKER(c) - return - } - res := h.db.Model(&model.ChatRole{}).Where("id = ?", data.Id).Update("sort", data.Sort) - if res.Error != nil { - resp.ERROR(c, "更新数据库失败!") - return + + for index, id := range data.Ids { + res := h.db.Model(&model.ChatRole{}).Where("id = ?", id).Update("sort_num", data.Sorts[index]) + if res.Error != nil { + resp.ERROR(c, "更新数据库失败!") + return + } } resp.SUCCESS(c) diff --git a/api/handler/chat_model_handler.go b/api/handler/chat_model_handler.go new file mode 100644 index 00000000..59ea2d76 --- /dev/null +++ b/api/handler/chat_model_handler.go @@ -0,0 +1,44 @@ +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 ChatModelHandler struct { + BaseHandler + db *gorm.DB +} + +func NewChatModelHandler(app *core.AppServer, db *gorm.DB) *ChatModelHandler { + h := ChatModelHandler{db: db} + h.App = app + return &h +} + +// List 模型列表 +func (h *ChatModelHandler) List(c *gin.Context) { + var items []model.ChatModel + var cms = make([]vo.ChatModel, 0) + res := h.db.Where("enabled = ?", true).Order("sort_num ASC").Find(&items) + if res.Error == nil { + for _, item := range items { + var cm vo.ChatModel + err := utils.CopyObject(item, &cm) + if err == nil { + cm.Id = item.Id + cm.CreatedAt = item.CreatedAt.Unix() + cm.UpdatedAt = item.UpdatedAt.Unix() + cms = append(cms, cm) + } else { + logger.Error(err) + } + } + } + resp.SUCCESS(c, cms) +} diff --git a/api/handler/chat_role_handler.go b/api/handler/chat_role_handler.go index 01ded3f3..0abad4c5 100644 --- a/api/handler/chat_role_handler.go +++ b/api/handler/chat_role_handler.go @@ -25,7 +25,7 @@ func NewChatRoleHandler(app *core.AppServer, db *gorm.DB) *ChatRoleHandler { // List get user list func (h *ChatRoleHandler) List(c *gin.Context) { var roles []model.ChatRole - res := h.db.Where("enable", true).Order("sort ASC").Find(&roles) + 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()) return diff --git a/api/main.go b/api/main.go index 477e5253..c992b77b 100644 --- a/api/main.go +++ b/api/main.go @@ -117,6 +117,7 @@ func main() { fx.Provide(handler.NewRewardHandler), fx.Provide(handler.NewCaptchaHandler), fx.Provide(handler.NewMidJourneyHandler), + fx.Provide(handler.NewChatModelHandler), fx.Provide(admin.NewConfigHandler), fx.Provide(admin.NewAdminHandler), @@ -125,6 +126,7 @@ func main() { fx.Provide(admin.NewChatRoleHandler), fx.Provide(admin.NewRewardHandler), fx.Provide(admin.NewDashboardHandler), + fx.Provide(admin.NewChatModelHandler), // 创建服务 fx.Provide(service.NewAliYunSmsService), @@ -216,7 +218,7 @@ func main() { group := s.Engine.Group("/api/admin/role/") group.GET("list", h.List) group.POST("save", h.Save) - group.POST("sort", h.SetSort) + group.POST("sort", h.Sort) group.GET("remove", h.Remove) }), fx.Invoke(func(s *core.AppServer, h *admin.RewardHandler) { @@ -227,6 +229,18 @@ func main() { group := s.Engine.Group("/api/admin/dashboard/") group.GET("stats", h.Stats) }), + fx.Invoke(func(s *core.AppServer, h *handler.ChatModelHandler) { + group := s.Engine.Group("/api/model/") + group.GET("list", h.List) + }), + fx.Invoke(func(s *core.AppServer, h *admin.ChatModelHandler) { + group := s.Engine.Group("/api/admin/model/") + 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.Invoke(func(s *core.AppServer, db *gorm.DB) { err := s.Run(db) diff --git a/api/store/model/chat_role.go b/api/store/model/chat_role.go index e2499c17..cc05cf7d 100644 --- a/api/store/model/chat_role.go +++ b/api/store/model/chat_role.go @@ -8,5 +8,5 @@ type ChatRole struct { HelloMsg string // 打招呼的消息 Icon string // 角色聊天图标 Enable bool // 是否启用被启用 - Sort int //排序数字 + SortNum int //排序数字 } diff --git a/api/store/vo/chat_role.go b/api/store/vo/chat_role.go index 8feed9ac..52f696e5 100644 --- a/api/store/vo/chat_role.go +++ b/api/store/vo/chat_role.go @@ -10,5 +10,5 @@ type ChatRole struct { HelloMsg string `json:"hello_msg"` // 打招呼的消息 Icon string `json:"icon"` // 角色聊天图标 Enable bool `json:"enable"` // 是否启用被启用 - Sort int `json:"sort"` // 排序 + SortNum int `json:"sort"` // 排序 } diff --git a/database/update-v3.1.0.sql b/database/update-v3.1.0.sql index 70aef44b..9748b34d 100644 --- a/database/update-v3.1.0.sql +++ b/database/update-v3.1.0.sql @@ -1,3 +1,4 @@ +TRUNCATE `chatgpt_plus`.`chatgpt_chat_items`; ALTER TABLE `chatgpt_chat_items` CHANGE `model` `model_id` INT(11) NOT NULL DEFAULT '0' COMMENT '模型 ID'; ALTER TABLE `chatgpt_api_keys` ADD `platform` CHAR(20) DEFAULT NULL COMMENT '平台' AFTER id; ALTER TABLE `chatgpt_users` CHANGE `tokens` `total_tokens` BIGINT NOT NULL DEFAULT '0' COMMENT '累计消耗 tokens'; @@ -28,4 +29,8 @@ INSERT INTO `chatgpt_chat_models` (`id`, `platform`, `name`, `value`, `sort_num` ALTER TABLE `chatgpt_users` DROP `username`, -DROP `nickname`; \ No newline at end of file +DROP `nickname`; + +-- 2023/09/04 +ALTER TABLE `chatgpt_chat_roles` CHANGE `sort` `sort_num` SMALLINT NOT NULL DEFAULT '0' COMMENT '角色排序'; +ALTER TABLE `chatgpt_api_keys` DROP `user_id`; \ No newline at end of file diff --git a/web/src/assets/iconfont/iconfont.css b/web/src/assets/iconfont/iconfont.css index 84ffdb27..46707785 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=1691463643989') format('woff2'), - url('iconfont.woff?t=1691463643989') format('woff'), - url('iconfont.ttf?t=1691463643989') format('truetype'); + src: url('iconfont.woff2?t=1693316408040') format('woff2'), + url('iconfont.woff?t=1693316408040') format('woff'), + url('iconfont.ttf?t=1693316408040') format('truetype'); } .iconfont { @@ -13,6 +13,30 @@ -moz-osx-font-smoothing: grayscale; } +.icon-image:before { + content: "\e7de"; +} + +.icon-order:before { + content: "\e600"; +} + +.icon-service:before { + content: "\e62d"; +} + +.icon-like:before { + content: "\e640"; +} + +.icon-recharge:before { + content: "\e637"; +} + +.icon-model:before { + content: "\e867"; +} + .icon-plugin:before { content: "\e69d"; } diff --git a/web/src/assets/iconfont/iconfont.js b/web/src/assets/iconfont/iconfont.js index a8ffef0a..650dac66 100644 --- a/web/src/assets/iconfont/iconfont.js +++ b/web/src/assets/iconfont/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_4125778='',function(a){var l=(l=document.getElementsByTagName("script"))[l.length-1],c=l.getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var t,o,i,e,h,s=function(l,c){c.parentNode.insertBefore(l,c)};if(c&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}t=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?s(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(t,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),t()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(i=t,e=a.document,h=!1,d(),e.onreadystatechange=function(){"complete"==e.readyState&&(e.onreadystatechange=null,n())})}function n(){h||(h=!0,i())}function d(){try{e.documentElement.doScroll("left")}catch(l){return void setTimeout(d,50)}n()}}(window); \ No newline at end of file +window._iconfont_svg_string_4125778='',function(a){var l=(l=document.getElementsByTagName("script"))[l.length-1],c=l.getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var t,o,i,e,h,s=function(l,c){c.parentNode.insertBefore(l,c)};if(c&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(l){console&&console.log(l)}}t=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?s(c,l.firstChild):l.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(t,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),t()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(i=t,e=a.document,h=!1,v(),e.onreadystatechange=function(){"complete"==e.readyState&&(e.onreadystatechange=null,m())})}function m(){h||(h=!0,i())}function v(){try{e.documentElement.doScroll("left")}catch(l){return void setTimeout(v,50)}m()}}(window); \ No newline at end of file diff --git a/web/src/assets/iconfont/iconfont.json b/web/src/assets/iconfont/iconfont.json index 5ef0933e..cef04dc9 100644 --- a/web/src/assets/iconfont/iconfont.json +++ b/web/src/assets/iconfont/iconfont.json @@ -5,6 +5,48 @@ "css_prefix_text": "icon-", "description": "", "glyphs": [ + { + "icon_id": "4766917", + "name": "image", + "font_class": "image", + "unicode": "e7de", + "unicode_decimal": 59358 + }, + { + "icon_id": "1375", + "name": "订单", + "font_class": "order", + "unicode": "e600", + "unicode_decimal": 58880 + }, + { + "icon_id": "6562297", + "name": "客服", + "font_class": "service", + "unicode": "e62d", + "unicode_decimal": 58925 + }, + { + "icon_id": "21598358", + "name": "喜欢", + "font_class": "like", + "unicode": "e640", + "unicode_decimal": 58944 + }, + { + "icon_id": "936873", + "name": "充值1", + "font_class": "recharge", + "unicode": "e637", + "unicode_decimal": 58935 + }, + { + "icon_id": "18991679", + "name": "model", + "font_class": "model", + "unicode": "e867", + "unicode_decimal": 59495 + }, { "icon_id": "5244045", "name": "插件", diff --git a/web/src/assets/iconfont/iconfont.ttf b/web/src/assets/iconfont/iconfont.ttf index 911cfdf5..0c775bcb 100644 Binary files a/web/src/assets/iconfont/iconfont.ttf and b/web/src/assets/iconfont/iconfont.ttf differ diff --git a/web/src/assets/iconfont/iconfont.woff b/web/src/assets/iconfont/iconfont.woff index 089502a7..3d2fb95c 100644 Binary files a/web/src/assets/iconfont/iconfont.woff and b/web/src/assets/iconfont/iconfont.woff differ diff --git a/web/src/assets/iconfont/iconfont.woff2 b/web/src/assets/iconfont/iconfont.woff2 index f4cf4d31..2d0a0ca9 100644 Binary files a/web/src/assets/iconfont/iconfont.woff2 and b/web/src/assets/iconfont/iconfont.woff2 differ diff --git a/web/src/components/BindMobile.vue b/web/src/components/BindMobile.vue index 26a6eb4c..1f04fecf 100644 --- a/web/src/components/BindMobile.vue +++ b/web/src/components/BindMobile.vue @@ -1,8 +1,8 @@