mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-09-17 08:46:38 +08:00
Compare commits
20 Commits
5213bdf08b
...
b7d137247a
Author | SHA1 | Date | |
---|---|---|---|
|
b7d137247a | ||
|
4373642ebd | ||
|
5bf90920f5 | ||
|
edcbb3e226 | ||
|
52e40daf23 | ||
|
430a7b2297 | ||
|
c91a38a882 | ||
|
6e02bee4b7 | ||
|
b62218110e | ||
|
e2960b2607 | ||
|
88e7c39066 | ||
|
2a6dd636fa | ||
|
6bf38f78d5 | ||
|
5a04a935be | ||
|
8923e938d2 | ||
|
1a1734abf0 | ||
|
8093a3eeb2 | ||
|
9edb3d0a82 | ||
|
d95fab11be | ||
|
6ef09c8ad5 |
@ -1,4 +1,11 @@
|
||||
# 更新日志
|
||||
## v4.1.5
|
||||
* 功能优化:重构 websocket 组件,减少 websocket 连接数,全站共享一个 websocket 连接
|
||||
* Bug修复:兼容手机端原生微信支付和支付宝支付渠道
|
||||
* Bug修复:修复删除绘图任务时候因为字段长度过短导致SQL执行失败问题
|
||||
* 功能优化:优化 Vue 组件通信代码,使用共享数据来替换之前的事件订阅模式,效率更高一些
|
||||
* 功能优化:优化思维导图生成功果页面,优化用户体验
|
||||
|
||||
## v4.1.4
|
||||
* 功能优化:用户文件列表组件增加分页功能支持
|
||||
* Bug修复:修复用户注册失败Bug,注册操作只弹出一次行为验证码
|
||||
|
@ -51,9 +51,9 @@ func NewServer(appConfig *types.AppConfig) *AppServer {
|
||||
func (s *AppServer) Init(debug bool, client *redis.Client) {
|
||||
if debug { // 调试模式允许跨域请求 API
|
||||
s.Debug = debug
|
||||
s.Engine.Use(corsMiddleware())
|
||||
logger.Info("Enabled debug mode")
|
||||
}
|
||||
s.Engine.Use(corsMiddleware())
|
||||
s.Engine.Use(staticResourceMiddleware())
|
||||
s.Engine.Use(authorizeMiddleware(s, client))
|
||||
s.Engine.Use(parameterHandlerMiddleware())
|
||||
@ -101,9 +101,9 @@ func corsMiddleware() gin.HandlerFunc {
|
||||
c.Header("Access-Control-Allow-Origin", origin)
|
||||
c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE, UPDATE")
|
||||
//允许跨域设置可以返回其他子段,可以自定义字段
|
||||
c.Header("Access-Control-Allow-Headers", "Authorization, Content-Length, Content-Type, Chat-Token, Admin-Authorization")
|
||||
c.Header("Access-Control-Allow-Headers", "Authorization, Body-Length, Body-Type, Admin-Authorization,content-type")
|
||||
// 允许浏览器(客户端)可以解析的头部 (重要)
|
||||
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
|
||||
c.Header("Access-Control-Expose-Headers", "Body-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
|
||||
//设置缓存时间
|
||||
c.Header("Access-Control-Max-Age", "172800")
|
||||
//允许客户端传递校验信息比如 cookie (重要)
|
||||
@ -131,7 +131,7 @@ func authorizeMiddleware(s *AppServer, client *redis.Client) gin.HandlerFunc {
|
||||
isAdminApi := strings.Contains(c.Request.URL.Path, "/api/admin/")
|
||||
if isAdminApi { // 后台管理 API
|
||||
tokenString = c.GetHeader(types.AdminAuthHeader)
|
||||
} else if c.Request.URL.Path == "/api/chat/new" {
|
||||
} else if c.Request.URL.Path == "/api/ws" { // Websocket 连接
|
||||
tokenString = c.Query("token")
|
||||
} else {
|
||||
tokenString = c.GetHeader(types.UserAuthHeader)
|
||||
@ -209,23 +209,18 @@ func needLogin(c *gin.Context) bool {
|
||||
c.Request.URL.Path == "/api/app/list/user" ||
|
||||
c.Request.URL.Path == "/api/model/list" ||
|
||||
c.Request.URL.Path == "/api/mj/imgWall" ||
|
||||
c.Request.URL.Path == "/api/mj/client" ||
|
||||
c.Request.URL.Path == "/api/mj/notify" ||
|
||||
c.Request.URL.Path == "/api/invite/hits" ||
|
||||
c.Request.URL.Path == "/api/sd/imgWall" ||
|
||||
c.Request.URL.Path == "/api/sd/client" ||
|
||||
c.Request.URL.Path == "/api/dall/imgWall" ||
|
||||
c.Request.URL.Path == "/api/dall/client" ||
|
||||
c.Request.URL.Path == "/api/product/list" ||
|
||||
c.Request.URL.Path == "/api/menu/list" ||
|
||||
c.Request.URL.Path == "/api/markMap/client" ||
|
||||
c.Request.URL.Path == "/api/payment/doPay" ||
|
||||
c.Request.URL.Path == "/api/payment/payWays" ||
|
||||
c.Request.URL.Path == "/api/suno/client" ||
|
||||
c.Request.URL.Path == "/api/suno/detail" ||
|
||||
c.Request.URL.Path == "/api/suno/play" ||
|
||||
c.Request.URL.Path == "/api/download" ||
|
||||
c.Request.URL.Path == "/api/video/client" ||
|
||||
strings.HasPrefix(c.Request.URL.Path, "/api/test") ||
|
||||
strings.HasPrefix(c.Request.URL.Path, "/api/payment/notify/") ||
|
||||
strings.HasPrefix(c.Request.URL.Path, "/api/user/clogin") ||
|
||||
|
@ -52,14 +52,13 @@ type Delta struct {
|
||||
|
||||
// ChatSession 聊天会话对象
|
||||
type ChatSession struct {
|
||||
SessionId string `json:"session_id"`
|
||||
UserId uint `json:"user_id"`
|
||||
ClientIP string `json:"client_ip"` // 客户端 IP
|
||||
ChatId string `json:"chat_id"` // 客户端聊天会话 ID, 多会话模式专用字段
|
||||
Model ChatModel `json:"model"` // GPT 模型
|
||||
Start int64 `json:"start"` // 开始请求时间戳
|
||||
Tools []int `json:"tools"` // 工具函数列表
|
||||
Stream bool `json:"stream"` // 是否采用流式输出
|
||||
UserId uint `json:"user_id"`
|
||||
ClientIP string `json:"client_ip"` // 客户端 IP
|
||||
ChatId string `json:"chat_id"` // 客户端聊天会话 ID, 多会话模式专用字段
|
||||
Model ChatModel `json:"model"` // GPT 模型
|
||||
Start int64 `json:"start"` // 开始请求时间戳
|
||||
Tools []int `json:"tools"` // 工具函数列表
|
||||
Stream bool `json:"stream"` // 是否采用流式输出
|
||||
}
|
||||
|
||||
type ChatModel struct {
|
||||
|
@ -17,15 +17,17 @@ var ErrConClosed = errors.New("connection Closed")
|
||||
|
||||
// WsClient websocket client
|
||||
type WsClient struct {
|
||||
Id string
|
||||
Conn *websocket.Conn
|
||||
lock sync.Mutex
|
||||
mt int
|
||||
Closed bool
|
||||
}
|
||||
|
||||
func NewWsClient(conn *websocket.Conn) *WsClient {
|
||||
func NewWsClient(conn *websocket.Conn, id string) *WsClient {
|
||||
return &WsClient{
|
||||
Conn: conn,
|
||||
Id: id,
|
||||
lock: sync.Mutex{},
|
||||
mt: 2, // fixed bug for 'Invalid UTF-8 in text frame'
|
||||
Closed: false,
|
||||
|
@ -24,8 +24,9 @@ const (
|
||||
|
||||
// MjTask MidJourney 任务
|
||||
type MjTask struct {
|
||||
Id uint `json:"id"`
|
||||
TaskId string `json:"task_id"`
|
||||
Id uint `json:"id"` // 任务ID
|
||||
TaskId string `json:"task_id"` // 中转任务ID
|
||||
ClientId string `json:"client_id"`
|
||||
ImgArr []string `json:"img_arr"`
|
||||
Type TaskType `json:"type"`
|
||||
UserId int `json:"user_id"`
|
||||
@ -43,12 +44,14 @@ type MjTask struct {
|
||||
type SdTask struct {
|
||||
Id int `json:"id"` // job 数据库ID
|
||||
Type TaskType `json:"type"`
|
||||
ClientId string `json:"client_id"`
|
||||
UserId int `json:"user_id"`
|
||||
Params SdTaskParams `json:"params"`
|
||||
RetryCount int `json:"retry_count"`
|
||||
}
|
||||
|
||||
type SdTaskParams struct {
|
||||
ClientId string `json:"client_id"` // 客户端ID
|
||||
TaskId string `json:"task_id"`
|
||||
Prompt string `json:"prompt"` // 提示词
|
||||
NegPrompt string `json:"neg_prompt"` // 反向提示词
|
||||
@ -69,18 +72,20 @@ type SdTaskParams struct {
|
||||
|
||||
// DallTask DALL-E task
|
||||
type DallTask struct {
|
||||
JobId uint `json:"job_id"`
|
||||
UserId uint `json:"user_id"`
|
||||
Prompt string `json:"prompt"`
|
||||
N int `json:"n"`
|
||||
Quality string `json:"quality"`
|
||||
Size string `json:"size"`
|
||||
Style string `json:"style"`
|
||||
ClientId string `json:"client_id"`
|
||||
JobId uint `json:"job_id"`
|
||||
UserId uint `json:"user_id"`
|
||||
Prompt string `json:"prompt"`
|
||||
N int `json:"n"`
|
||||
Quality string `json:"quality"`
|
||||
Size string `json:"size"`
|
||||
Style string `json:"style"`
|
||||
|
||||
Power int `json:"power"`
|
||||
}
|
||||
|
||||
type SunoTask struct {
|
||||
ClientId string `json:"client_id"`
|
||||
Id uint `json:"id"`
|
||||
Channel string `json:"channel"`
|
||||
UserId int `json:"user_id"`
|
||||
@ -104,13 +109,14 @@ const (
|
||||
)
|
||||
|
||||
type VideoTask struct {
|
||||
Id uint `json:"id"`
|
||||
Channel string `json:"channel"`
|
||||
UserId int `json:"user_id"`
|
||||
Type string `json:"type"`
|
||||
TaskId string `json:"task_id"`
|
||||
Prompt string `json:"prompt"` // 提示词
|
||||
Params VideoParams `json:"params"`
|
||||
ClientId string `json:"client_id"`
|
||||
Id uint `json:"id"`
|
||||
Channel string `json:"channel"`
|
||||
UserId int `json:"user_id"`
|
||||
Type string `json:"type"`
|
||||
TaskId string `json:"task_id"`
|
||||
Prompt string `json:"prompt"` // 提示词
|
||||
Params VideoParams `json:"params"`
|
||||
}
|
||||
|
||||
type VideoParams struct {
|
||||
|
@ -19,23 +19,44 @@ type BizVo struct {
|
||||
|
||||
// ReplyMessage 对话回复消息结构
|
||||
type ReplyMessage struct {
|
||||
Type WsMsgType `json:"type"` // 消息类别,start, end, img
|
||||
Content interface{} `json:"content"`
|
||||
Channel WsChannel `json:"channel"` // 消息频道,目前只有 chat
|
||||
ClientId string `json:"clientId"` // 客户端ID
|
||||
Type WsMsgType `json:"type"` // 消息类别
|
||||
Body interface{} `json:"body"`
|
||||
}
|
||||
|
||||
type WsMsgType string
|
||||
type WsChannel string
|
||||
|
||||
const (
|
||||
WsContent = WsMsgType("content") // 输出内容
|
||||
WsEnd = WsMsgType("end")
|
||||
WsErr = WsMsgType("error")
|
||||
MsgTypeText = WsMsgType("text") // 输出内容
|
||||
MsgTypeEnd = WsMsgType("end")
|
||||
MsgTypeErr = WsMsgType("error")
|
||||
MsgTypePing = WsMsgType("ping") // 心跳消息
|
||||
|
||||
ChPing = WsChannel("ping")
|
||||
ChChat = WsChannel("chat")
|
||||
ChMj = WsChannel("mj")
|
||||
ChSd = WsChannel("sd")
|
||||
ChDall = WsChannel("dall")
|
||||
ChSuno = WsChannel("suno")
|
||||
ChLuma = WsChannel("luma")
|
||||
)
|
||||
|
||||
// InputMessage 对话输入消息结构
|
||||
type InputMessage struct {
|
||||
Channel WsChannel `json:"channel"` // 消息频道
|
||||
Type WsMsgType `json:"type"` // 消息类别
|
||||
Body interface{} `json:"body"`
|
||||
}
|
||||
|
||||
type ChatMessage struct {
|
||||
Tools []int `json:"tools,omitempty"` // 允许调用工具列表
|
||||
Stream bool `json:"stream,omitempty"` // 是否采用流式输出
|
||||
RoleId int `json:"role_id"`
|
||||
ModelId int `json:"model_id"`
|
||||
ChatId string `json:"chat_id"`
|
||||
Content string `json:"content"`
|
||||
Tools []int `json:"tools"` // 允许调用工具列表
|
||||
Stream bool `json:"stream"` // 是否采用流式输出
|
||||
}
|
||||
|
||||
type BizCode int
|
||||
|
@ -1,4 +1,4 @@
|
||||
package chatimpl
|
||||
package handler
|
||||
|
||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
|
||||
@ -15,8 +15,6 @@ import (
|
||||
"fmt"
|
||||
"geekai/core"
|
||||
"geekai/core/types"
|
||||
"geekai/handler"
|
||||
logger2 "geekai/logger"
|
||||
"geekai/service"
|
||||
"geekai/service/oss"
|
||||
"geekai/store/model"
|
||||
@ -33,14 +31,11 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/gorilla/websocket"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var logger = logger2.GetLogger()
|
||||
|
||||
type ChatHandler struct {
|
||||
handler.BaseHandler
|
||||
BaseHandler
|
||||
redis *redis.Client
|
||||
uploadManager *oss.UploaderManager
|
||||
licenseService *service.LicenseService
|
||||
@ -51,7 +46,7 @@ type ChatHandler struct {
|
||||
|
||||
func NewChatHandler(app *core.AppServer, db *gorm.DB, redis *redis.Client, manager *oss.UploaderManager, licenseService *service.LicenseService, userService *service.UserService) *ChatHandler {
|
||||
return &ChatHandler{
|
||||
BaseHandler: handler.BaseHandler{App: app, DB: db},
|
||||
BaseHandler: BaseHandler{App: app, DB: db},
|
||||
redis: redis,
|
||||
uploadManager: manager,
|
||||
licenseService: licenseService,
|
||||
@ -61,106 +56,6 @@ func NewChatHandler(app *core.AppServer, db *gorm.DB, redis *redis.Client, manag
|
||||
}
|
||||
}
|
||||
|
||||
// ChatHandle 处理聊天 WebSocket 请求
|
||||
func (h *ChatHandler) ChatHandle(c *gin.Context) {
|
||||
ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
sessionId := c.Query("session_id")
|
||||
roleId := h.GetInt(c, "role_id", 0)
|
||||
chatId := c.Query("chat_id")
|
||||
modelId := h.GetInt(c, "model_id", 0)
|
||||
|
||||
client := types.NewWsClient(ws)
|
||||
var chatRole model.ChatRole
|
||||
res := h.DB.First(&chatRole, roleId)
|
||||
if res.Error != nil || !chatRole.Enable {
|
||||
utils.ReplyErrorMessage(client, "当前聊天角色不存在或者未启用,对话已关闭!!!")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
// if the role bind a model_id, use role's bind model_id
|
||||
if chatRole.ModelId > 0 {
|
||||
modelId = chatRole.ModelId
|
||||
}
|
||||
// get model info
|
||||
var chatModel model.ChatModel
|
||||
res = h.DB.First(&chatModel, modelId)
|
||||
if res.Error != nil || chatModel.Enabled == false {
|
||||
utils.ReplyErrorMessage(client, "当前AI模型暂未启用,对话已关闭!!!")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
session := &types.ChatSession{
|
||||
SessionId: sessionId,
|
||||
ClientIP: c.ClientIP(),
|
||||
UserId: h.GetLoginUserId(c),
|
||||
}
|
||||
|
||||
// use old chat data override the chat model and role ID
|
||||
var chat model.ChatItem
|
||||
res = h.DB.Where("chat_id = ?", chatId).First(&chat)
|
||||
if res.Error == nil {
|
||||
chatModel.Id = chat.ModelId
|
||||
roleId = int(chat.RoleId)
|
||||
}
|
||||
|
||||
session.ChatId = chatId
|
||||
session.Model = types.ChatModel{
|
||||
Id: chatModel.Id,
|
||||
Name: chatModel.Name,
|
||||
Value: chatModel.Value,
|
||||
Power: chatModel.Power,
|
||||
MaxTokens: chatModel.MaxTokens,
|
||||
MaxContext: chatModel.MaxContext,
|
||||
Temperature: chatModel.Temperature,
|
||||
KeyId: chatModel.KeyId}
|
||||
logger.Infof("New websocket connected, IP: %s", c.ClientIP())
|
||||
|
||||
go func() {
|
||||
for {
|
||||
_, msg, err := client.Receive()
|
||||
if err != nil {
|
||||
logger.Debugf("close connection: %s", client.Conn.RemoteAddr())
|
||||
client.Close()
|
||||
cancelFunc := h.ReqCancelFunc.Get(sessionId)
|
||||
if cancelFunc != nil {
|
||||
cancelFunc()
|
||||
h.ReqCancelFunc.Delete(sessionId)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var message types.InputMessage
|
||||
err = utils.JsonDecode(string(msg), &message)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Infof("Receive a message:%+v", message)
|
||||
|
||||
session.Tools = message.Tools
|
||||
session.Stream = message.Stream
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
h.ReqCancelFunc.Put(sessionId, cancel)
|
||||
// 回复消息
|
||||
err = h.sendMessage(ctx, session, chatRole, utils.InterfaceToString(message.Content), client)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
utils.ReplyMessage(client, err.Error())
|
||||
} else {
|
||||
utils.ReplyChunkMessage(client, types.ReplyMessage{Type: types.WsEnd})
|
||||
logger.Infof("回答完毕: %v", message.Content)
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSession, role model.ChatRole, prompt string, ws *types.WsClient) error {
|
||||
if !h.App.Debug {
|
||||
defer func() {
|
||||
@ -206,7 +101,7 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSessio
|
||||
}
|
||||
// 兼容 GPT-O1 模型
|
||||
if strings.HasPrefix(session.Model.Value, "o1-") {
|
||||
utils.ReplyContent(ws, "AI 正在思考...\n")
|
||||
utils.SendChunkMsg(ws, "AI 正在思考...\n")
|
||||
req.Stream = false
|
||||
session.Start = time.Now().Unix()
|
||||
} else {
|
@ -1,4 +1,4 @@
|
||||
package chatimpl
|
||||
package handler
|
||||
|
||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
|
||||
@ -28,31 +28,40 @@ func (h *ChatHandler) List(c *gin.Context) {
|
||||
userId := h.GetLoginUserId(c)
|
||||
var items = make([]vo.ChatItem, 0)
|
||||
var chats []model.ChatItem
|
||||
res := h.DB.Where("user_id = ?", userId).Order("id DESC").Find(&chats)
|
||||
if res.Error == nil {
|
||||
var roleIds = make([]uint, 0)
|
||||
for _, chat := range chats {
|
||||
roleIds = append(roleIds, chat.RoleId)
|
||||
}
|
||||
var roles []model.ChatRole
|
||||
res = h.DB.Find(&roles, roleIds)
|
||||
if res.Error == nil {
|
||||
roleMap := make(map[uint]model.ChatRole)
|
||||
for _, role := range roles {
|
||||
roleMap[role.Id] = role
|
||||
}
|
||||
h.DB.Where("user_id", userId).Order("id DESC").Find(&chats)
|
||||
if len(chats) == 0 {
|
||||
resp.SUCCESS(c, items)
|
||||
return
|
||||
}
|
||||
|
||||
for _, chat := range chats {
|
||||
var item vo.ChatItem
|
||||
err := utils.CopyObject(chat, &item)
|
||||
if err == nil {
|
||||
item.Id = chat.Id
|
||||
item.Icon = roleMap[chat.RoleId].Icon
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
var roleIds = make([]uint, 0)
|
||||
var modelValues = make([]string, 0)
|
||||
for _, chat := range chats {
|
||||
roleIds = append(roleIds, chat.RoleId)
|
||||
modelValues = append(modelValues, chat.Model)
|
||||
}
|
||||
|
||||
var roles []model.ChatRole
|
||||
var models []model.ChatModel
|
||||
roleMap := make(map[uint]model.ChatRole)
|
||||
modelMap := make(map[string]model.ChatModel)
|
||||
h.DB.Where("id IN ?", roleIds).Find(&roles)
|
||||
h.DB.Where("value IN ?", modelValues).Find(&models)
|
||||
for _, role := range roles {
|
||||
roleMap[role.Id] = role
|
||||
}
|
||||
for _, m := range models {
|
||||
modelMap[m.Value] = m
|
||||
}
|
||||
for _, chat := range chats {
|
||||
var item vo.ChatItem
|
||||
err := utils.CopyObject(chat, &item)
|
||||
if err == nil {
|
||||
item.Id = chat.Id
|
||||
item.Icon = roleMap[chat.RoleId].Icon
|
||||
item.ModelId = modelMap[chat.Model].Id
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
resp.SUCCESS(c, items)
|
||||
}
|
@ -20,9 +20,7 @@ import (
|
||||
"geekai/utils/resp"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/gorilla/websocket"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type DallJobHandler struct {
|
||||
@ -45,49 +43,6 @@ func NewDallJobHandler(app *core.AppServer, db *gorm.DB, service *dalle.Service,
|
||||
}
|
||||
}
|
||||
|
||||
// Client WebSocket 客户端,用于通知任务状态变更
|
||||
func (h *DallJobHandler) Client(c *gin.Context) {
|
||||
ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
userId := h.GetInt(c, "user_id", 0)
|
||||
if userId == 0 {
|
||||
logger.Info("Invalid user ID")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
client := types.NewWsClient(ws)
|
||||
h.dallService.Clients.Put(uint(userId), client)
|
||||
logger.Infof("New websocket connected, IP: %s", c.RemoteIP())
|
||||
go func() {
|
||||
for {
|
||||
_, msg, err := client.Receive()
|
||||
if err != nil {
|
||||
client.Close()
|
||||
h.dallService.Clients.Delete(uint(userId))
|
||||
return
|
||||
}
|
||||
|
||||
var message types.ReplyMessage
|
||||
err = utils.JsonDecode(string(msg), &message)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// 心跳消息
|
||||
if message.Type == "heartbeat" {
|
||||
logger.Debug("收到 DallE 心跳消息:", message.Content)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (h *DallJobHandler) preCheck(c *gin.Context) bool {
|
||||
user, err := h.GetLoginUser(c)
|
||||
if err != nil {
|
||||
@ -129,19 +84,15 @@ func (h *DallJobHandler) Image(c *gin.Context) {
|
||||
}
|
||||
|
||||
h.dallService.PushTask(types.DallTask{
|
||||
JobId: job.Id,
|
||||
UserId: uint(userId),
|
||||
Prompt: data.Prompt,
|
||||
Quality: data.Quality,
|
||||
Size: data.Size,
|
||||
Style: data.Style,
|
||||
Power: job.Power,
|
||||
ClientId: data.ClientId,
|
||||
JobId: job.Id,
|
||||
UserId: uint(userId),
|
||||
Prompt: data.Prompt,
|
||||
Quality: data.Quality,
|
||||
Size: data.Size,
|
||||
Style: data.Style,
|
||||
Power: job.Power,
|
||||
})
|
||||
|
||||
client := h.dallService.Clients.Get(job.UserId)
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
|
@ -8,24 +8,15 @@ package handler
|
||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"geekai/core"
|
||||
"geekai/core/types"
|
||||
"geekai/service"
|
||||
"geekai/store/model"
|
||||
"geekai/utils"
|
||||
"geekai/utils/resp"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"gorm.io/gorm"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MarkMapHandler 生成思维导图
|
||||
@ -43,71 +34,35 @@ func NewMarkMapHandler(app *core.AppServer, db *gorm.DB, userService *service.Us
|
||||
}
|
||||
}
|
||||
|
||||
func (h *MarkMapHandler) Client(c *gin.Context) {
|
||||
ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
// Generate 生成思维导图
|
||||
func (h *MarkMapHandler) Generate(c *gin.Context) {
|
||||
var data struct {
|
||||
Prompt string `json:"prompt"`
|
||||
ModelId int `json:"model_id"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
|
||||
modelId := h.GetInt(c, "model_id", 0)
|
||||
userId := h.GetInt(c, "user_id", 0)
|
||||
|
||||
client := types.NewWsClient(ws)
|
||||
h.clients.Put(userId, client)
|
||||
go func() {
|
||||
for {
|
||||
_, msg, err := client.Receive()
|
||||
if err != nil {
|
||||
client.Close()
|
||||
h.clients.Delete(userId)
|
||||
return
|
||||
}
|
||||
|
||||
var message types.ReplyMessage
|
||||
err = utils.JsonDecode(string(msg), &message)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// 心跳消息
|
||||
if message.Type == "heartbeat" {
|
||||
logger.Debug("收到 MarkMap 心跳消息:", message.Content)
|
||||
continue
|
||||
}
|
||||
// change model
|
||||
if message.Type == "model_id" {
|
||||
modelId = utils.IntValue(utils.InterfaceToString(message.Content), 0)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Info("Receive a message: ", message.Content)
|
||||
err = h.sendMessage(client, utils.InterfaceToString(message.Content), modelId, userId)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
utils.ReplyErrorMessage(client, err.Error())
|
||||
} else {
|
||||
utils.ReplyMessage(client, types.ReplyMessage{Type: types.WsEnd})
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (h *MarkMapHandler) sendMessage(client *types.WsClient, prompt string, modelId int, userId int) error {
|
||||
userId := h.GetLoginUserId(c)
|
||||
var user model.User
|
||||
res := h.DB.Model(&model.User{}).First(&user, userId)
|
||||
if res.Error != nil {
|
||||
return fmt.Errorf("error with query user info: %v", res.Error)
|
||||
err := h.DB.Where("id", userId).First(&user, userId).Error
|
||||
if err != nil {
|
||||
resp.ERROR(c, "error with query user info")
|
||||
return
|
||||
}
|
||||
var chatModel model.ChatModel
|
||||
res = h.DB.Where("id", modelId).First(&chatModel)
|
||||
if res.Error != nil {
|
||||
return fmt.Errorf("error with query chat model: %v", res.Error)
|
||||
err = h.DB.Where("id", data.ModelId).First(&chatModel).Error
|
||||
if err != nil {
|
||||
resp.ERROR(c, "error with query chat model")
|
||||
return
|
||||
}
|
||||
|
||||
if user.Power < chatModel.Power {
|
||||
return fmt.Errorf("您当前剩余算力(%d)已不足以支付当前模型算力(%d)!", user.Power, chatModel.Power)
|
||||
resp.ERROR(c, fmt.Sprintf("您当前剩余算力(%d)已不足以支付当前模型算力(%d)!", user.Power, chatModel.Power))
|
||||
return
|
||||
}
|
||||
|
||||
messages := make([]interface{}, 0)
|
||||
@ -129,117 +84,27 @@ func (h *MarkMapHandler) sendMessage(client *types.WsClient, prompt string, mode
|
||||
### 支付宝
|
||||
### 微信
|
||||
|
||||
另外,除此之外不要任何解释性语句。
|
||||
请直接生成结果,不要任何解释性语句。
|
||||
`})
|
||||
messages = append(messages, types.Message{Role: "user", Content: fmt.Sprintf("请生成一份有关【%s】一份思维导图,要求结构清晰,有条理", prompt)})
|
||||
var req = types.ApiRequest{
|
||||
Model: chatModel.Value,
|
||||
Stream: true,
|
||||
Messages: messages,
|
||||
}
|
||||
|
||||
var apiKey model.ApiKey
|
||||
response, err := h.doRequest(req, chatModel, &apiKey)
|
||||
messages = append(messages, types.Message{Role: "user", Content: fmt.Sprintf("请生成一份有关【%s】一份思维导图,要求结构清晰,有条理", data.Prompt)})
|
||||
content, err := utils.SendOpenAIMessage(h.DB, messages, chatModel.Value, chatModel.KeyId)
|
||||
if err != nil {
|
||||
return fmt.Errorf("请求 OpenAI API 失败: %s", err)
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
contentType := response.Header.Get("Content-Type")
|
||||
if strings.Contains(contentType, "text/event-stream") {
|
||||
// 循环读取 Chunk 消息
|
||||
scanner := bufio.NewScanner(response.Body)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if !strings.Contains(line, "data:") || len(line) < 30 {
|
||||
continue
|
||||
}
|
||||
|
||||
var responseBody = types.ApiResponse{}
|
||||
err = json.Unmarshal([]byte(line[6:]), &responseBody)
|
||||
if err != nil { // 数据解析出错
|
||||
return fmt.Errorf("error with decode data: %v", line)
|
||||
}
|
||||
|
||||
if len(responseBody.Choices) == 0 { // Fixed: 兼容 Azure API 第一个输出空行
|
||||
continue
|
||||
}
|
||||
|
||||
if responseBody.Choices[0].FinishReason == "stop" {
|
||||
break
|
||||
}
|
||||
|
||||
utils.ReplyChunkMessage(client, types.ReplyMessage{
|
||||
Type: types.WsContent,
|
||||
Content: utils.InterfaceToString(responseBody.Choices[0].Delta.Content),
|
||||
})
|
||||
} // end for
|
||||
|
||||
utils.ReplyChunkMessage(client, types.ReplyMessage{Type: types.WsEnd})
|
||||
|
||||
} else {
|
||||
body, _ := io.ReadAll(response.Body)
|
||||
return fmt.Errorf("请求 OpenAI API 失败:%s", string(body))
|
||||
resp.ERROR(c, fmt.Sprintf("请求 OpenAI API 失败: %s", err))
|
||||
return
|
||||
}
|
||||
|
||||
// 扣减算力
|
||||
if chatModel.Power > 0 {
|
||||
err = h.userService.DecreasePower(userId, chatModel.Power, model.PowerLog{
|
||||
err = h.userService.DecreasePower(int(userId), chatModel.Power, model.PowerLog{
|
||||
Type: types.PowerConsume,
|
||||
Model: chatModel.Value,
|
||||
Remark: fmt.Sprintf("AI绘制思维导图,模型名称:%s, ", chatModel.Value),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
resp.ERROR(c, "error with save power log, "+err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *MarkMapHandler) doRequest(req types.ApiRequest, chatModel model.ChatModel, apiKey *model.ApiKey) (*http.Response, error) {
|
||||
|
||||
session := h.DB.Session(&gorm.Session{})
|
||||
// if the chat model bind a KEY, use it directly
|
||||
if chatModel.KeyId > 0 {
|
||||
session = session.Where("id", chatModel.KeyId)
|
||||
} else { // use the last unused key
|
||||
session = session.Where("type", "chat").
|
||||
Where("enabled", true).Order("last_used_at ASC")
|
||||
}
|
||||
|
||||
res := session.First(apiKey)
|
||||
if res.Error != nil {
|
||||
return nil, errors.New("no available key, please import key")
|
||||
}
|
||||
apiURL := fmt.Sprintf("%s/v1/chat/completions", apiKey.ApiURL)
|
||||
// 更新 API KEY 的最后使用时间
|
||||
h.DB.Model(apiKey).UpdateColumn("last_used_at", time.Now().Unix())
|
||||
|
||||
// 创建 HttpClient 请求对象
|
||||
var client *http.Client
|
||||
requestBody, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request, err := http.NewRequest(http.MethodPost, apiURL, bytes.NewBuffer(requestBody))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
if len(apiKey.ProxyURL) > 5 { // 使用代理
|
||||
proxy, _ := url.Parse(apiKey.ProxyURL)
|
||||
client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyURL(proxy),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey.Value))
|
||||
logger.Debugf("Sending %s request, API KEY:%s, PROXY: %s, Model: %s", apiKey.ApiURL, apiURL, apiKey.ProxyURL, req.Model)
|
||||
return client.Do(request)
|
||||
resp.SUCCESS(c, content)
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ package handler
|
||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"geekai/core"
|
||||
"geekai/core/types"
|
||||
@ -19,12 +18,10 @@ import (
|
||||
"geekai/store/vo"
|
||||
"geekai/utils"
|
||||
"geekai/utils/resp"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -65,31 +62,11 @@ func (h *MidJourneyHandler) preCheck(c *gin.Context) bool {
|
||||
|
||||
}
|
||||
|
||||
// Client WebSocket 客户端,用于通知任务状态变更
|
||||
func (h *MidJourneyHandler) Client(c *gin.Context) {
|
||||
ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
userId := h.GetInt(c, "user_id", 0)
|
||||
if userId == 0 {
|
||||
logger.Info("Invalid user ID")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
client := types.NewWsClient(ws)
|
||||
h.mjService.Clients.Put(uint(userId), client)
|
||||
logger.Infof("New websocket connected, IP: %s", c.RemoteIP())
|
||||
}
|
||||
|
||||
// Image 创建一个绘画任务
|
||||
func (h *MidJourneyHandler) Image(c *gin.Context) {
|
||||
var data struct {
|
||||
TaskType string `json:"task_type"`
|
||||
ClientId string `json:"client_id"`
|
||||
Prompt string `json:"prompt"`
|
||||
NegPrompt string `json:"neg_prompt"`
|
||||
Rate string `json:"rate"`
|
||||
@ -200,6 +177,7 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
||||
|
||||
h.mjService.PushTask(types.MjTask{
|
||||
Id: job.Id,
|
||||
ClientId: data.ClientId,
|
||||
TaskId: taskId,
|
||||
Type: types.TaskType(data.TaskType),
|
||||
Prompt: data.Prompt,
|
||||
@ -210,11 +188,6 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
||||
Mode: h.App.SysConfig.MjMode,
|
||||
})
|
||||
|
||||
client := h.mjService.Clients.Get(uint(job.UserId))
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
|
||||
// update user's power
|
||||
err = h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{
|
||||
Type: types.PowerConsume,
|
||||
@ -231,6 +204,7 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
||||
|
||||
type reqVo struct {
|
||||
Index int `json:"index"`
|
||||
ClientId string `json:"client_id"`
|
||||
ChannelId string `json:"channel_id"`
|
||||
MessageId string `json:"message_id"`
|
||||
MessageHash string `json:"message_hash"`
|
||||
@ -267,6 +241,7 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) {
|
||||
|
||||
h.mjService.PushTask(types.MjTask{
|
||||
Id: job.Id,
|
||||
ClientId: data.ClientId,
|
||||
Type: types.TaskUpscale,
|
||||
UserId: userId,
|
||||
ChannelId: data.ChannelId,
|
||||
@ -276,11 +251,6 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) {
|
||||
Mode: h.App.SysConfig.MjMode,
|
||||
})
|
||||
|
||||
client := h.mjService.Clients.Get(uint(job.UserId))
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
|
||||
// update user's power
|
||||
err := h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{
|
||||
Type: types.PowerConsume,
|
||||
@ -328,6 +298,7 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) {
|
||||
h.mjService.PushTask(types.MjTask{
|
||||
Id: job.Id,
|
||||
Type: types.TaskVariation,
|
||||
ClientId: data.ClientId,
|
||||
UserId: userId,
|
||||
Index: data.Index,
|
||||
ChannelId: data.ChannelId,
|
||||
@ -336,11 +307,6 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) {
|
||||
Mode: h.App.SysConfig.MjMode,
|
||||
})
|
||||
|
||||
client := h.mjService.Clients.Get(uint(job.UserId))
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
|
||||
err := h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{
|
||||
Type: types.PowerConsume,
|
||||
Model: "mid-journey",
|
||||
@ -420,14 +386,6 @@ func (h *MidJourneyHandler) getData(finish bool, userId uint, page int, pageSize
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if item.Progress < 100 && item.ImgURL == "" && item.OrgURL != "" {
|
||||
image, err := utils.DownloadImage(item.OrgURL, h.App.Config.ProxyURL)
|
||||
if err == nil {
|
||||
job.ImgURL = "data:image/png;base64," + base64.StdEncoding.EncodeToString(image)
|
||||
}
|
||||
}
|
||||
|
||||
jobs = append(jobs, job)
|
||||
}
|
||||
return nil, vo.NewPage(total, page, pageSize, jobs)
|
||||
@ -472,11 +430,6 @@ func (h *MidJourneyHandler) Remove(c *gin.Context) {
|
||||
logger.Error("remove image failed: ", err)
|
||||
}
|
||||
|
||||
client := h.mjService.Clients.Get(uint(job.UserId))
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
package chatimpl
|
||||
package handler
|
||||
|
||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
|
||||
@ -108,7 +108,7 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
}
|
||||
|
||||
if responseBody.Choices[0].FinishReason == "stop" && len(contents) == 0 {
|
||||
utils.ReplyMessage(ws, "抱歉😔😔😔,AI助手由于未知原因已经停止输出内容。")
|
||||
utils.SendChunkMsg(ws, "抱歉😔😔😔,AI助手由于未知原因已经停止输出内容。")
|
||||
break
|
||||
}
|
||||
|
||||
@ -136,7 +136,7 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
if res.Error == nil {
|
||||
toolCall = true
|
||||
callMsg := fmt.Sprintf("正在调用工具 `%s` 作答 ...\n\n", function.Label)
|
||||
utils.ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsContent, Content: callMsg})
|
||||
utils.SendChunkMsg(ws, callMsg)
|
||||
contents = append(contents, callMsg)
|
||||
}
|
||||
continue
|
||||
@ -153,10 +153,7 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
} else {
|
||||
content := responseBody.Choices[0].Delta.Content
|
||||
contents = append(contents, utils.InterfaceToString(content))
|
||||
utils.ReplyChunkMessage(ws, types.ReplyMessage{
|
||||
Type: types.WsContent,
|
||||
Content: utils.InterfaceToString(responseBody.Choices[0].Delta.Content),
|
||||
})
|
||||
utils.SendChunkMsg(ws, responseBody.Choices[0].Delta.Content)
|
||||
}
|
||||
} // end for
|
||||
|
||||
@ -174,7 +171,7 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
logger.Debugf("函数名称: %s, 函数参数:%s", function.Name, params)
|
||||
params["user_id"] = userVo.Id
|
||||
var apiRes types.BizVo
|
||||
r, err := req2.C().R().SetHeader("Content-Type", "application/json").
|
||||
r, err := req2.C().R().SetHeader("Body-Type", "application/json").
|
||||
SetHeader("Authorization", function.Token).
|
||||
SetBody(params).
|
||||
SetSuccessResult(&apiRes).Post(function.Action)
|
||||
@ -185,19 +182,13 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
errMsg = r.Status
|
||||
}
|
||||
if errMsg != "" || apiRes.Code != types.Success {
|
||||
msg := "调用函数工具出错:" + apiRes.Message + errMsg
|
||||
utils.ReplyChunkMessage(ws, types.ReplyMessage{
|
||||
Type: types.WsContent,
|
||||
Content: msg,
|
||||
})
|
||||
contents = append(contents, msg)
|
||||
errMsg = "调用函数工具出错:" + apiRes.Message + errMsg
|
||||
contents = append(contents, errMsg)
|
||||
} else {
|
||||
utils.ReplyChunkMessage(ws, types.ReplyMessage{
|
||||
Type: types.WsContent,
|
||||
Content: apiRes.Data,
|
||||
})
|
||||
contents = append(contents, utils.InterfaceToString(apiRes.Data))
|
||||
errMsg = utils.InterfaceToString(apiRes.Data)
|
||||
contents = append(contents, errMsg)
|
||||
}
|
||||
utils.SendChunkMsg(ws, errMsg)
|
||||
}
|
||||
|
||||
// 消息发送成功
|
||||
@ -226,7 +217,7 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
if strings.HasPrefix(req.Model, "o1-") {
|
||||
content = fmt.Sprintf("AI思考结束,耗时:%d 秒。\n%s", time.Now().Unix()-session.Start, respVo.Choices[0].Message.Content)
|
||||
}
|
||||
utils.ReplyMessage(ws, content)
|
||||
utils.SendChunkMsg(ws, content)
|
||||
respVo.Usage.Prompt = prompt
|
||||
respVo.Usage.Content = content
|
||||
h.saveChatHistory(req, respVo.Usage, respVo.Choices[0].Message, chatCtx, session, role, userVo, promptCreatedAt, time.Now())
|
@ -120,13 +120,24 @@ func (h *PaymentHandler) Pay(c *gin.Context) {
|
||||
returnURL = fmt.Sprintf("%s/payReturn", data.Host)
|
||||
}
|
||||
money := fmt.Sprintf("%.2f", amount)
|
||||
payURL, err = h.alipayService.PayPC(payment.AlipayParams{
|
||||
OutTradeNo: orderNo,
|
||||
Subject: product.Name,
|
||||
TotalFee: money,
|
||||
ReturnURL: returnURL,
|
||||
NotifyURL: notifyURL,
|
||||
})
|
||||
if data.Device == "wechat" {
|
||||
payURL, err = h.alipayService.PayMobile(payment.AlipayParams{
|
||||
OutTradeNo: orderNo,
|
||||
Subject: product.Name,
|
||||
TotalFee: money,
|
||||
ReturnURL: returnURL,
|
||||
NotifyURL: notifyURL,
|
||||
})
|
||||
} else {
|
||||
payURL, err = h.alipayService.PayPC(payment.AlipayParams{
|
||||
OutTradeNo: orderNo,
|
||||
Subject: product.Name,
|
||||
TotalFee: money,
|
||||
ReturnURL: returnURL,
|
||||
NotifyURL: notifyURL,
|
||||
})
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
resp.ERROR(c, "error with generate pay url: "+err.Error())
|
||||
return
|
||||
@ -138,12 +149,22 @@ func (h *PaymentHandler) Pay(c *gin.Context) {
|
||||
} else {
|
||||
notifyURL = fmt.Sprintf("%s/api/payment/notify/wechat", data.Host)
|
||||
}
|
||||
payURL, err = h.wechatPayService.PayUrlNative(payment.WechatPayParams{
|
||||
OutTradeNo: orderNo,
|
||||
TotalFee: int(amount * 100),
|
||||
Subject: product.Name,
|
||||
NotifyURL: notifyURL,
|
||||
})
|
||||
if data.Device == "wechat" {
|
||||
payURL, err = h.wechatPayService.PayUrlH5(payment.WechatPayParams{
|
||||
OutTradeNo: orderNo,
|
||||
TotalFee: int(amount * 100),
|
||||
Subject: product.Name,
|
||||
NotifyURL: notifyURL,
|
||||
ClientIP: c.ClientIP(),
|
||||
})
|
||||
} else {
|
||||
payURL, err = h.wechatPayService.PayUrlNative(payment.WechatPayParams{
|
||||
OutTradeNo: orderNo,
|
||||
TotalFee: int(amount * 100),
|
||||
Subject: product.Name,
|
||||
NotifyURL: notifyURL,
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
|
@ -19,11 +19,8 @@ import (
|
||||
"geekai/store/vo"
|
||||
"geekai/utils"
|
||||
"geekai/utils/resp"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"gorm.io/gorm"
|
||||
@ -59,27 +56,6 @@ func NewSdJobHandler(app *core.AppServer,
|
||||
}
|
||||
}
|
||||
|
||||
// Client WebSocket 客户端,用于通知任务状态变更
|
||||
func (h *SdJobHandler) Client(c *gin.Context) {
|
||||
ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
userId := h.GetInt(c, "user_id", 0)
|
||||
if userId == 0 {
|
||||
logger.Info("Invalid user ID")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
client := types.NewWsClient(ws)
|
||||
h.sdService.Clients.Put(uint(userId), client)
|
||||
logger.Infof("New websocket connected, IP: %s", c.RemoteIP())
|
||||
}
|
||||
|
||||
func (h *SdJobHandler) preCheck(c *gin.Context) bool {
|
||||
user, err := h.GetLoginUser(c)
|
||||
if err != nil {
|
||||
@ -168,17 +144,13 @@ func (h *SdJobHandler) Image(c *gin.Context) {
|
||||
}
|
||||
|
||||
h.sdService.PushTask(types.SdTask{
|
||||
Id: int(job.Id),
|
||||
Type: types.TaskImage,
|
||||
Params: params,
|
||||
UserId: userId,
|
||||
Id: int(job.Id),
|
||||
ClientId: data.ClientId,
|
||||
Type: types.TaskImage,
|
||||
Params: params,
|
||||
UserId: userId,
|
||||
})
|
||||
|
||||
client := h.sdService.Clients.Get(uint(job.UserId))
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
|
||||
// update user's power
|
||||
err = h.userService.DecreasePower(job.UserId, job.Power, model.PowerLog{
|
||||
Type: types.PowerConsume,
|
||||
@ -260,15 +232,6 @@ func (h *SdJobHandler) getData(finish bool, userId uint, page int, pageSize int,
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if item.Progress < 100 {
|
||||
// 从 leveldb 中获取图片预览数据
|
||||
var imageData string
|
||||
err = h.leveldb.Get(item.TaskId, &imageData)
|
||||
if err == nil {
|
||||
job.ImgURL = "data:image/png;base64," + imageData
|
||||
}
|
||||
}
|
||||
jobs = append(jobs, job)
|
||||
}
|
||||
|
||||
|
@ -19,9 +19,7 @@ import (
|
||||
"geekai/utils"
|
||||
"geekai/utils/resp"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -44,30 +42,10 @@ func NewSunoHandler(app *core.AppServer, db *gorm.DB, service *suno.Service, upl
|
||||
}
|
||||
}
|
||||
|
||||
// Client WebSocket 客户端,用于通知任务状态变更
|
||||
func (h *SunoHandler) Client(c *gin.Context) {
|
||||
ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
userId := h.GetInt(c, "user_id", 0)
|
||||
if userId == 0 {
|
||||
logger.Info("Invalid user ID")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
client := types.NewWsClient(ws)
|
||||
h.sunoService.Clients.Put(uint(userId), client)
|
||||
logger.Infof("New websocket connected, IP: %s", c.RemoteIP())
|
||||
}
|
||||
|
||||
func (h *SunoHandler) Create(c *gin.Context) {
|
||||
|
||||
var data struct {
|
||||
ClientId string `json:"client_id"`
|
||||
Prompt string `json:"prompt"`
|
||||
Instrumental bool `json:"instrumental"`
|
||||
Lyrics string `json:"lyrics"`
|
||||
@ -138,6 +116,7 @@ func (h *SunoHandler) Create(c *gin.Context) {
|
||||
|
||||
// 创建任务
|
||||
h.sunoService.PushTask(types.SunoTask{
|
||||
ClientId: data.ClientId,
|
||||
Id: job.Id,
|
||||
UserId: job.UserId,
|
||||
Type: job.Type,
|
||||
@ -164,10 +143,6 @@ func (h *SunoHandler) Create(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
client := h.sunoService.Clients.Get(uint(job.UserId))
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
@ -388,7 +363,7 @@ func (h *SunoHandler) Lyric(c *gin.Context) {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(genLyricTemplate, data.Prompt), "gpt-4o-mini")
|
||||
content, err := utils.OpenAIRequest(h.DB, fmt.Sprintf(genLyricTemplate, data.Prompt), "gpt-4o-mini", 0)
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
|
@ -19,7 +19,7 @@ func NewTestHandler(db *gorm.DB, snowflake *service.Snowflake, js *payment.GeekP
|
||||
}
|
||||
|
||||
func (h *TestHandler) SseTest(c *gin.Context) {
|
||||
//c.Header("Content-Type", "text/event-stream")
|
||||
//c.Header("Body-Type", "text/event-stream")
|
||||
//c.Header("Cache-Control", "no-cache")
|
||||
//c.Header("Connection", "keep-alive")
|
||||
//
|
||||
|
@ -19,9 +19,7 @@ import (
|
||||
"geekai/utils"
|
||||
"geekai/utils/resp"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -44,30 +42,10 @@ func NewVideoHandler(app *core.AppServer, db *gorm.DB, service *video.Service, u
|
||||
}
|
||||
}
|
||||
|
||||
// Client WebSocket 客户端,用于通知任务状态变更
|
||||
func (h *VideoHandler) Client(c *gin.Context) {
|
||||
ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
userId := h.GetInt(c, "user_id", 0)
|
||||
if userId == 0 {
|
||||
logger.Info("Invalid user ID")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
client := types.NewWsClient(ws)
|
||||
h.videoService.Clients.Put(uint(userId), client)
|
||||
logger.Infof("New websocket connected, IP: %s", c.RemoteIP())
|
||||
}
|
||||
|
||||
func (h *VideoHandler) LumaCreate(c *gin.Context) {
|
||||
|
||||
var data struct {
|
||||
ClientId string `json:"client_id"`
|
||||
Prompt string `json:"prompt"`
|
||||
FirstFrameImg string `json:"first_frame_img,omitempty"`
|
||||
EndFrameImg string `json:"end_frame_img,omitempty"`
|
||||
@ -118,11 +96,12 @@ func (h *VideoHandler) LumaCreate(c *gin.Context) {
|
||||
|
||||
// 创建任务
|
||||
h.videoService.PushTask(types.VideoTask{
|
||||
Id: job.Id,
|
||||
UserId: userId,
|
||||
Type: types.VideoLuma,
|
||||
Prompt: data.Prompt,
|
||||
Params: params,
|
||||
ClientId: data.ClientId,
|
||||
Id: job.Id,
|
||||
UserId: userId,
|
||||
Type: types.VideoLuma,
|
||||
Prompt: data.Prompt,
|
||||
Params: params,
|
||||
})
|
||||
|
||||
// update user's power
|
||||
@ -135,11 +114,6 @@ func (h *VideoHandler) LumaCreate(c *gin.Context) {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
client := h.videoService.Clients.Get(uint(job.UserId))
|
||||
if client != nil {
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
}
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
@ -198,7 +172,7 @@ func (h *VideoHandler) Remove(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
// 只有失败或者超时的任务才能删除
|
||||
if job.Progress != service.FailTaskProgress || time.Now().Before(job.CreatedAt.Add(time.Minute*30)) {
|
||||
if !(job.Progress == service.FailTaskProgress || time.Now().After(job.CreatedAt.Add(time.Minute*30))) {
|
||||
resp.ERROR(c, "只有失败和超时(30分钟)的任务才能删除!")
|
||||
return
|
||||
}
|
||||
|
145
api/handler/ws_handler.go
Normal file
145
api/handler/ws_handler.go
Normal file
@ -0,0 +1,145 @@
|
||||
package handler
|
||||
|
||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
// * Copyright 2023 The Geek-AI Authors. All rights reserved.
|
||||
// * Use of this source code is governed by a Apache-2.0 license
|
||||
// * that can be found in the LICENSE file.
|
||||
// * @Author yangjian102621@163.com
|
||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
import (
|
||||
"context"
|
||||
"geekai/core"
|
||||
"geekai/core/types"
|
||||
"geekai/service"
|
||||
"geekai/store/model"
|
||||
"geekai/utils"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// Websocket 连接处理 handler
|
||||
|
||||
type WebsocketHandler struct {
|
||||
BaseHandler
|
||||
wsService *service.WebsocketService
|
||||
chatHandler *ChatHandler
|
||||
}
|
||||
|
||||
func NewWebsocketHandler(app *core.AppServer, s *service.WebsocketService, db *gorm.DB, chatHandler *ChatHandler) *WebsocketHandler {
|
||||
return &WebsocketHandler{
|
||||
BaseHandler: BaseHandler{App: app, DB: db},
|
||||
chatHandler: chatHandler,
|
||||
wsService: s,
|
||||
}
|
||||
}
|
||||
|
||||
func (h *WebsocketHandler) Client(c *gin.Context) {
|
||||
ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
clientId := c.Query("client_id")
|
||||
client := types.NewWsClient(ws, clientId)
|
||||
userId := h.GetLoginUserId(c)
|
||||
if userId == 0 {
|
||||
_ = client.Send([]byte("Invalid user_id"))
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
var user model.User
|
||||
if err := h.DB.Where("id", userId).First(&user).Error; err != nil {
|
||||
_ = client.Send([]byte("Invalid user_id"))
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
h.wsService.Clients.Put(clientId, client)
|
||||
logger.Infof("New websocket connected, IP: %s", c.RemoteIP())
|
||||
go func() {
|
||||
for {
|
||||
_, msg, err := client.Receive()
|
||||
if err != nil {
|
||||
logger.Debugf("close connection: %s", client.Conn.RemoteAddr())
|
||||
client.Close()
|
||||
h.wsService.Clients.Delete(clientId)
|
||||
break
|
||||
}
|
||||
|
||||
var message types.InputMessage
|
||||
err = utils.JsonDecode(string(msg), &message)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Debugf("Receive a message:%+v", message)
|
||||
if message.Type == types.MsgTypePing {
|
||||
utils.SendChannelMsg(client, types.ChPing, "pong")
|
||||
continue
|
||||
}
|
||||
|
||||
// 当前只处理聊天消息,其他消息全部丢弃
|
||||
var chatMessage types.ChatMessage
|
||||
err = utils.JsonDecode(utils.JsonEncode(message.Body), &chatMessage)
|
||||
if err != nil || message.Channel != types.ChChat {
|
||||
logger.Warnf("invalid message body:%+v", message.Body)
|
||||
continue
|
||||
}
|
||||
var chatRole model.ChatRole
|
||||
err = h.DB.First(&chatRole, chatMessage.RoleId).Error
|
||||
if err != nil || !chatRole.Enable {
|
||||
utils.SendAndFlush(client, "当前聊天角色不存在或者未启用,请更换角色之后再发起对话!!!")
|
||||
continue
|
||||
}
|
||||
// if the role bind a model_id, use role's bind model_id
|
||||
if chatRole.ModelId > 0 {
|
||||
chatMessage.RoleId = chatRole.ModelId
|
||||
}
|
||||
// get model info
|
||||
var chatModel model.ChatModel
|
||||
err = h.DB.Where("id", chatMessage.ModelId).First(&chatModel).Error
|
||||
if err != nil || chatModel.Enabled == false {
|
||||
utils.SendAndFlush(client, "当前AI模型暂未启用,请更换模型后再发起对话!!!")
|
||||
continue
|
||||
}
|
||||
|
||||
session := &types.ChatSession{
|
||||
ClientIP: c.ClientIP(),
|
||||
UserId: userId,
|
||||
}
|
||||
|
||||
// use old chat data override the chat model and role ID
|
||||
var chat model.ChatItem
|
||||
h.DB.Where("chat_id", chatMessage.ChatId).First(&chat)
|
||||
if chat.Id > 0 {
|
||||
chatModel.Id = chat.ModelId
|
||||
chatMessage.RoleId = int(chat.RoleId)
|
||||
}
|
||||
|
||||
session.ChatId = chatMessage.ChatId
|
||||
session.Tools = chatMessage.Tools
|
||||
session.Stream = chatMessage.Stream
|
||||
// 复制模型数据
|
||||
err = utils.CopyObject(chatModel, &session.Model)
|
||||
if err != nil {
|
||||
logger.Error(err, chatModel)
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
h.chatHandler.ReqCancelFunc.Put(clientId, cancel)
|
||||
err = h.chatHandler.sendMessage(ctx, session, chatRole, chatMessage.Content, client)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
utils.SendAndFlush(client, err.Error())
|
||||
} else {
|
||||
utils.SendMsg(client, types.ReplyMessage{Channel: types.ChChat, Type: types.MsgTypeEnd})
|
||||
logger.Infof("回答完毕: %v", message.Body)
|
||||
}
|
||||
|
||||
}
|
||||
}()
|
||||
}
|
19
api/main.go
19
api/main.go
@ -14,7 +14,6 @@ import (
|
||||
"geekai/core/types"
|
||||
"geekai/handler"
|
||||
"geekai/handler/admin"
|
||||
"geekai/handler/chatimpl"
|
||||
logger2 "geekai/logger"
|
||||
"geekai/service"
|
||||
"geekai/service/dalle"
|
||||
@ -128,7 +127,7 @@ func main() {
|
||||
// 创建控制器
|
||||
fx.Provide(handler.NewChatRoleHandler),
|
||||
fx.Provide(handler.NewUserHandler),
|
||||
fx.Provide(chatimpl.NewChatHandler),
|
||||
fx.Provide(handler.NewChatHandler),
|
||||
fx.Provide(handler.NewNetHandler),
|
||||
fx.Provide(handler.NewSmsHandler),
|
||||
fx.Provide(handler.NewRedeemHandler),
|
||||
@ -246,9 +245,8 @@ func main() {
|
||||
group.GET("clogin", h.CLogin)
|
||||
group.GET("clogin/callback", h.CLoginCallback)
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *chatimpl.ChatHandler) {
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.ChatHandler) {
|
||||
group := s.Engine.Group("/api/chat/")
|
||||
group.Any("new", h.ChatHandle)
|
||||
group.GET("list", h.List)
|
||||
group.GET("detail", h.Detail)
|
||||
group.POST("update", h.Update)
|
||||
@ -281,7 +279,6 @@ func main() {
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.MidJourneyHandler) {
|
||||
group := s.Engine.Group("/api/mj/")
|
||||
group.Any("client", h.Client)
|
||||
group.POST("image", h.Image)
|
||||
group.POST("upscale", h.Upscale)
|
||||
group.POST("variation", h.Variation)
|
||||
@ -292,7 +289,6 @@ func main() {
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.SdJobHandler) {
|
||||
group := s.Engine.Group("/api/sd")
|
||||
group.Any("client", h.Client)
|
||||
group.POST("image", h.Image)
|
||||
group.GET("jobs", h.JobList)
|
||||
group.GET("imgWall", h.ImgWall)
|
||||
@ -467,13 +463,11 @@ func main() {
|
||||
}),
|
||||
fx.Provide(handler.NewMarkMapHandler),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.MarkMapHandler) {
|
||||
group := s.Engine.Group("/api/markMap/")
|
||||
group.Any("client", h.Client)
|
||||
s.Engine.POST("/api/markMap/gen", h.Generate)
|
||||
}),
|
||||
fx.Provide(handler.NewDallJobHandler),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.DallJobHandler) {
|
||||
group := s.Engine.Group("/api/dall")
|
||||
group.Any("client", h.Client)
|
||||
group.POST("image", h.Image)
|
||||
group.GET("jobs", h.JobList)
|
||||
group.GET("imgWall", h.ImgWall)
|
||||
@ -483,7 +477,6 @@ func main() {
|
||||
fx.Provide(handler.NewSunoHandler),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.SunoHandler) {
|
||||
group := s.Engine.Group("/api/suno")
|
||||
group.Any("client", h.Client)
|
||||
group.POST("create", h.Create)
|
||||
group.GET("list", h.List)
|
||||
group.GET("remove", h.Remove)
|
||||
@ -496,7 +489,6 @@ func main() {
|
||||
fx.Provide(handler.NewVideoHandler),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.VideoHandler) {
|
||||
group := s.Engine.Group("/api/video")
|
||||
group.Any("client", h.Client)
|
||||
group.POST("luma/create", h.LumaCreate)
|
||||
group.GET("list", h.List)
|
||||
group.GET("remove", h.Remove)
|
||||
@ -521,6 +513,11 @@ func main() {
|
||||
group := s.Engine.Group("/api/test")
|
||||
group.Any("sse", h.PostTest, h.SseTest)
|
||||
}),
|
||||
fx.Provide(service.NewWebsocketService),
|
||||
fx.Provide(handler.NewWebsocketHandler),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.WebsocketHandler) {
|
||||
s.Engine.Any("/api/ws", h.Client)
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
|
||||
go func() {
|
||||
err := s.Run(db)
|
||||
|
@ -34,19 +34,21 @@ type Service struct {
|
||||
uploadManager *oss.UploaderManager
|
||||
taskQueue *store.RedisQueue
|
||||
notifyQueue *store.RedisQueue
|
||||
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
||||
userService *service.UserService
|
||||
wsService *service.WebsocketService
|
||||
clientIds map[uint]string
|
||||
}
|
||||
|
||||
func NewService(db *gorm.DB, manager *oss.UploaderManager, redisCli *redis.Client, userService *service.UserService) *Service {
|
||||
func NewService(db *gorm.DB, manager *oss.UploaderManager, redisCli *redis.Client, userService *service.UserService, wsService *service.WebsocketService) *Service {
|
||||
return &Service{
|
||||
httpClient: req.C().SetTimeout(time.Minute * 3),
|
||||
db: db,
|
||||
taskQueue: store.NewRedisQueue("DallE_Task_Queue", redisCli),
|
||||
notifyQueue: store.NewRedisQueue("DallE_Notify_Queue", redisCli),
|
||||
Clients: types.NewLMap[uint, *types.WsClient](),
|
||||
wsService: wsService,
|
||||
uploadManager: manager,
|
||||
userService: userService,
|
||||
clientIds: map[uint]string{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -67,6 +69,7 @@ func (s *Service) Run() {
|
||||
continue
|
||||
}
|
||||
logger.Infof("handle a new DALL-E task: %+v", task)
|
||||
s.clientIds[task.JobId] = task.ClientId
|
||||
_, err = s.Image(task, false)
|
||||
if err != nil {
|
||||
logger.Errorf("error with image task: %v", err)
|
||||
@ -74,7 +77,7 @@ func (s *Service) Run() {
|
||||
"progress": service.FailTaskProgress,
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: int(task.UserId), JobId: int(task.JobId), Message: service.TaskStatusFailed})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: int(task.UserId), JobId: int(task.JobId), Message: service.TaskStatusFailed})
|
||||
}
|
||||
}
|
||||
}()
|
||||
@ -111,7 +114,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
|
||||
prompt := task.Prompt
|
||||
// translate prompt
|
||||
if utils.HasChinese(prompt) {
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.RewritePromptTemplate, prompt), "gpt-4o-mini")
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.RewritePromptTemplate, prompt), "gpt-4o-mini", 0)
|
||||
if err == nil {
|
||||
prompt = content
|
||||
logger.Debugf("重写后提示词:%s", prompt)
|
||||
@ -158,7 +161,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
|
||||
Quality: task.Quality,
|
||||
}
|
||||
logger.Infof("Channel:%s, API KEY:%s, BODY: %+v", apiURL, apiKey.Value, reqBody)
|
||||
r, err := s.httpClient.R().SetHeader("Content-Type", "application/json").
|
||||
r, err := s.httpClient.R().SetHeader("Body-Type", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+apiKey.Value).
|
||||
SetBody(reqBody).
|
||||
SetErrorResult(&errRes).
|
||||
@ -183,7 +186,7 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) {
|
||||
return "", fmt.Errorf("err with update database: %v", err)
|
||||
}
|
||||
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: int(task.UserId), JobId: int(task.JobId), Message: service.TaskStatusFailed})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: int(task.UserId), JobId: int(task.JobId), Message: service.TaskStatusFailed})
|
||||
var content string
|
||||
if sync {
|
||||
imgURL, err := s.downloadImage(task.JobId, int(task.UserId), res.Data[0].Url)
|
||||
@ -205,14 +208,13 @@ func (s *Service) CheckTaskNotify() {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
client := s.Clients.Get(uint(message.UserId))
|
||||
|
||||
logger.Debugf("notify message: %+v", message)
|
||||
client := s.wsService.Clients.Get(message.ClientId)
|
||||
if client == nil {
|
||||
continue
|
||||
}
|
||||
err = client.Send([]byte(message.Message))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
utils.SendChannelMsg(client, types.ChDall, message.Message)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -284,6 +286,6 @@ func (s *Service) downloadImage(jobId uint, userId int, orgURL string) (string,
|
||||
if res.Error != nil {
|
||||
return "", err
|
||||
}
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: userId, JobId: int(jobId), Message: service.TaskStatusFinished})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: s.clientIds[jobId], UserId: userId, JobId: int(jobId), Message: service.TaskStatusFinished})
|
||||
return imgURL, nil
|
||||
}
|
||||
|
@ -28,18 +28,20 @@ type Service struct {
|
||||
taskQueue *store.RedisQueue
|
||||
notifyQueue *store.RedisQueue
|
||||
db *gorm.DB
|
||||
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
||||
wsService *service.WebsocketService
|
||||
uploaderManager *oss.UploaderManager
|
||||
clientIds map[uint]string
|
||||
}
|
||||
|
||||
func NewService(redisCli *redis.Client, db *gorm.DB, client *Client, manager *oss.UploaderManager) *Service {
|
||||
func NewService(redisCli *redis.Client, db *gorm.DB, client *Client, manager *oss.UploaderManager, wsService *service.WebsocketService) *Service {
|
||||
return &Service{
|
||||
db: db,
|
||||
taskQueue: store.NewRedisQueue("MidJourney_Task_Queue", redisCli),
|
||||
notifyQueue: store.NewRedisQueue("MidJourney_Notify_Queue", redisCli),
|
||||
client: client,
|
||||
Clients: types.NewLMap[uint, *types.WsClient](),
|
||||
wsService: wsService,
|
||||
uploaderManager: manager,
|
||||
clientIds: map[uint]string{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +58,7 @@ func (s *Service) Run() {
|
||||
|
||||
// translate prompt
|
||||
if utils.HasChinese(task.Prompt) {
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Prompt), "gpt-4o-mini")
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Prompt), "gpt-4o-mini", 0)
|
||||
if err == nil {
|
||||
task.Prompt = content
|
||||
} else {
|
||||
@ -65,7 +67,7 @@ func (s *Service) Run() {
|
||||
}
|
||||
// translate negative prompt
|
||||
if task.NegPrompt != "" && utils.HasChinese(task.NegPrompt) {
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.NegPrompt), "gpt-4o-mini")
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.NegPrompt), "gpt-4o-mini", 0)
|
||||
if err == nil {
|
||||
task.NegPrompt = content
|
||||
} else {
|
||||
@ -77,6 +79,7 @@ func (s *Service) Run() {
|
||||
if task.Mode == "" {
|
||||
task.Mode = "fast"
|
||||
}
|
||||
s.clientIds[task.Id] = task.ClientId
|
||||
|
||||
var job model.MidJourneyJob
|
||||
tx := s.db.Where("id = ?", task.Id).First(&job)
|
||||
@ -119,7 +122,7 @@ func (s *Service) Run() {
|
||||
// update the task progress
|
||||
s.db.Updates(&job)
|
||||
// 任务失败,通知前端
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: task.UserId, JobId: int(job.Id), Message: service.TaskStatusFailed})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: task.UserId, JobId: int(job.Id), Message: service.TaskStatusFailed})
|
||||
continue
|
||||
}
|
||||
logger.Infof("任务提交成功:%+v", res)
|
||||
@ -166,14 +169,12 @@ func (s *Service) CheckTaskNotify() {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
cli := s.Clients.Get(uint(message.UserId))
|
||||
if cli == nil {
|
||||
continue
|
||||
}
|
||||
err = cli.Send([]byte(message.Message))
|
||||
if err != nil {
|
||||
logger.Debugf("receive a new mj notify message: %+v", message)
|
||||
client := s.wsService.Clients.Get(message.ClientId)
|
||||
if client == nil {
|
||||
continue
|
||||
}
|
||||
utils.SendChannelMsg(client, types.ChMj, message.Message)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -211,14 +212,11 @@ func (s *Service) DownloadImages() {
|
||||
v.ImgURL = imgURL
|
||||
s.db.Updates(&v)
|
||||
|
||||
cli := s.Clients.Get(uint(v.UserId))
|
||||
if cli == nil {
|
||||
continue
|
||||
}
|
||||
err = cli.Send([]byte(service.TaskStatusFinished))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
s.notifyQueue.RPush(service.NotifyMessage{
|
||||
ClientId: s.clientIds[v.Id],
|
||||
UserId: v.UserId,
|
||||
JobId: int(v.Id),
|
||||
Message: service.TaskStatusFinished})
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * 5)
|
||||
@ -237,8 +235,8 @@ func (s *Service) SyncTaskProgress() {
|
||||
go func() {
|
||||
var jobs []model.MidJourneyJob
|
||||
for {
|
||||
res := s.db.Where("progress < ?", 100).Where("channel_id <> ?", "").Find(&jobs)
|
||||
if res.Error != nil {
|
||||
err := s.db.Where("progress < ?", 100).Find(&jobs).Error
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -251,6 +249,10 @@ func (s *Service) SyncTaskProgress() {
|
||||
continue
|
||||
}
|
||||
|
||||
if job.ChannelId == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
task, err := s.client.QueryTask(job.TaskId, job.ChannelId)
|
||||
if err != nil {
|
||||
logger.Errorf("error with query task: %v", err)
|
||||
@ -264,7 +266,11 @@ func (s *Service) SyncTaskProgress() {
|
||||
"err_msg": task.FailReason,
|
||||
})
|
||||
logger.Errorf("task failed: %v", task.FailReason)
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: job.UserId, JobId: int(job.Id), Message: service.TaskStatusFailed})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{
|
||||
ClientId: s.clientIds[job.Id],
|
||||
UserId: job.UserId,
|
||||
JobId: int(job.Id),
|
||||
Message: service.TaskStatusFailed})
|
||||
continue
|
||||
}
|
||||
|
||||
@ -289,7 +295,11 @@ func (s *Service) SyncTaskProgress() {
|
||||
if job.Progress == 100 {
|
||||
message = service.TaskStatusFinished
|
||||
}
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: job.UserId, JobId: int(job.Id), Message: message})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{
|
||||
ClientId: s.clientIds[job.Id],
|
||||
UserId: job.UserId,
|
||||
JobId: int(job.Id),
|
||||
Message: message})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,7 +89,7 @@ func (s MiniOss) PutFile(ctx *gin.Context, name string) (File, error) {
|
||||
fileExt := utils.GetImgExt(file.Filename)
|
||||
filename := fmt.Sprintf("%s/%d%s", s.config.SubDir, time.Now().UnixMicro(), fileExt)
|
||||
info, err := s.client.PutObject(ctx, s.config.Bucket, filename, fileReader, file.Size, minio.PutObjectOptions{
|
||||
ContentType: file.Header.Get("Content-Type"),
|
||||
ContentType: file.Header.Get("Body-Type"),
|
||||
})
|
||||
if err != nil {
|
||||
return File{}, fmt.Errorf("error uploading to MinIO: %v", err)
|
||||
|
@ -43,8 +43,8 @@ func NewAlipayService(appConfig *types.AppConfig) (*AlipayService, error) {
|
||||
|
||||
//client.DebugSwitch = gopay.DebugOn // 开启调试模式
|
||||
client.SetLocation(alipay.LocationShanghai). // 设置时区,不设置或出错均为默认服务器时间
|
||||
SetCharset(alipay.UTF8). // 设置字符编码,不设置默认 utf-8
|
||||
SetSignType(alipay.RSA2) // 设置签名类型,不设置默认 RSA2
|
||||
SetCharset(alipay.UTF8). // 设置字符编码,不设置默认 utf-8
|
||||
SetSignType(alipay.RSA2) // 设置签名类型,不设置默认 RSA2
|
||||
|
||||
if err = client.SetCertSnByPath(config.PublicKey, config.RootCert, config.AlipayPublicKey); err != nil {
|
||||
return nil, fmt.Errorf("error with load payment public key: %v", err)
|
||||
@ -67,10 +67,8 @@ func (s *AlipayService) PayMobile(params AlipayParams) (string, error) {
|
||||
bm.Set("out_trade_no", params.OutTradeNo)
|
||||
bm.Set("quit_url", params.ReturnURL)
|
||||
bm.Set("total_amount", params.TotalFee)
|
||||
bm.Set("return_url", params.ReturnURL)
|
||||
bm.Set("notify_url", params.NotifyURL)
|
||||
bm.Set("product_code", "QUICK_WAP_WAY")
|
||||
return s.client.TradeWapPay(context.Background(), bm)
|
||||
return s.client.SetNotifyUrl(params.NotifyURL).SetReturnUrl(params.ReturnURL).TradeWapPay(context.Background(), bm)
|
||||
}
|
||||
|
||||
func (s *AlipayService) PayPC(params AlipayParams) (string, error) {
|
||||
|
@ -33,18 +33,16 @@ type Service struct {
|
||||
notifyQueue *store.RedisQueue
|
||||
db *gorm.DB
|
||||
uploadManager *oss.UploaderManager
|
||||
leveldb *store.LevelDB
|
||||
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
||||
wsService *service.WebsocketService
|
||||
}
|
||||
|
||||
func NewService(db *gorm.DB, manager *oss.UploaderManager, levelDB *store.LevelDB, redisCli *redis.Client) *Service {
|
||||
func NewService(db *gorm.DB, manager *oss.UploaderManager, levelDB *store.LevelDB, redisCli *redis.Client, wsService *service.WebsocketService) *Service {
|
||||
return &Service{
|
||||
httpClient: req.C(),
|
||||
taskQueue: store.NewRedisQueue("StableDiffusion_Task_Queue", redisCli),
|
||||
notifyQueue: store.NewRedisQueue("StableDiffusion_Queue", redisCli),
|
||||
db: db,
|
||||
leveldb: levelDB,
|
||||
Clients: types.NewLMap[uint, *types.WsClient](),
|
||||
wsService: wsService,
|
||||
uploadManager: manager,
|
||||
}
|
||||
}
|
||||
@ -62,7 +60,7 @@ func (s *Service) Run() {
|
||||
|
||||
// translate prompt
|
||||
if utils.HasChinese(task.Params.Prompt) {
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.RewritePromptTemplate, task.Params.Prompt), "gpt-4o-mini")
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.RewritePromptTemplate, task.Params.Prompt), "gpt-4o-mini", 0)
|
||||
if err == nil {
|
||||
task.Params.Prompt = content
|
||||
} else {
|
||||
@ -72,7 +70,7 @@ func (s *Service) Run() {
|
||||
|
||||
// translate negative prompt
|
||||
if task.Params.NegPrompt != "" && utils.HasChinese(task.Params.NegPrompt) {
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Params.NegPrompt), "gpt-4o-mini")
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Params.NegPrompt), "gpt-4o-mini", 0)
|
||||
if err == nil {
|
||||
task.Params.NegPrompt = content
|
||||
} else {
|
||||
@ -90,7 +88,7 @@ func (s *Service) Run() {
|
||||
"err_msg": err.Error(),
|
||||
})
|
||||
// 通知前端,任务失败
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: task.UserId, JobId: task.Id, Message: service.TaskStatusFailed})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: task.UserId, JobId: task.Id, Message: service.TaskStatusFailed})
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -126,9 +124,8 @@ type Txt2ImgResp struct {
|
||||
|
||||
// TaskProgressResp 任务进度响应实体
|
||||
type TaskProgressResp struct {
|
||||
Progress float64 `json:"progress"`
|
||||
EtaRelative float64 `json:"eta_relative"`
|
||||
CurrentImage string `json:"current_image"`
|
||||
Progress float64 `json:"progress"`
|
||||
EtaRelative float64 `json:"eta_relative"`
|
||||
}
|
||||
|
||||
// Txt2Img 文生图 API
|
||||
@ -213,9 +210,7 @@ func (s *Service) Txt2Img(task types.SdTask) error {
|
||||
|
||||
// task finished
|
||||
s.db.Model(&model.SdJob{Id: uint(task.Id)}).UpdateColumn("progress", 100)
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: task.UserId, JobId: task.Id, Message: service.TaskStatusFinished})
|
||||
// 从 leveldb 中删除预览图片数据
|
||||
_ = s.leveldb.Delete(task.Params.TaskId)
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: task.UserId, JobId: task.Id, Message: service.TaskStatusFinished})
|
||||
return nil
|
||||
default:
|
||||
err, resp := s.checkTaskProgress(apiKey)
|
||||
@ -223,11 +218,7 @@ func (s *Service) Txt2Img(task types.SdTask) error {
|
||||
if err == nil && resp.Progress > 0 {
|
||||
s.db.Model(&model.SdJob{Id: uint(task.Id)}).UpdateColumn("progress", int(resp.Progress*100))
|
||||
// 发送更新状态信号
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: task.UserId, JobId: task.Id, Message: service.TaskStatusRunning})
|
||||
// 保存预览图片数据
|
||||
if resp.CurrentImage != "" {
|
||||
_ = s.leveldb.Put(task.Params.TaskId, resp.CurrentImage)
|
||||
}
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: task.UserId, JobId: task.Id, Message: service.TaskStatusRunning})
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
}
|
||||
@ -267,14 +258,12 @@ func (s *Service) CheckTaskNotify() {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
client := s.Clients.Get(uint(message.UserId))
|
||||
logger.Debugf("notify message: %+v", message)
|
||||
client := s.wsService.Clients.Get(message.ClientId)
|
||||
if client == nil {
|
||||
continue
|
||||
}
|
||||
err = client.Send([]byte(message.Message))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
utils.SendChannelMsg(client, types.ChSd, message.Message)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
@ -34,17 +34,19 @@ type Service struct {
|
||||
uploadManager *oss.UploaderManager
|
||||
taskQueue *store.RedisQueue
|
||||
notifyQueue *store.RedisQueue
|
||||
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
||||
wsService *service.WebsocketService
|
||||
clientIds map[string]string
|
||||
}
|
||||
|
||||
func NewService(db *gorm.DB, manager *oss.UploaderManager, redisCli *redis.Client) *Service {
|
||||
func NewService(db *gorm.DB, manager *oss.UploaderManager, redisCli *redis.Client, wsService *service.WebsocketService) *Service {
|
||||
return &Service{
|
||||
httpClient: req.C().SetTimeout(time.Minute * 3),
|
||||
db: db,
|
||||
taskQueue: store.NewRedisQueue("Suno_Task_Queue", redisCli),
|
||||
notifyQueue: store.NewRedisQueue("Suno_Notify_Queue", redisCli),
|
||||
Clients: types.NewLMap[uint, *types.WsClient](),
|
||||
uploadManager: manager,
|
||||
wsService: wsService,
|
||||
clientIds: map[string]string{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,7 +98,7 @@ func (s *Service) Run() {
|
||||
"err_msg": err.Error(),
|
||||
"progress": service.FailTaskProgress,
|
||||
})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: task.UserId, JobId: int(task.Id), Message: service.TaskStatusFailed})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: task.UserId, JobId: int(task.Id), Message: service.TaskStatusFailed})
|
||||
continue
|
||||
}
|
||||
|
||||
@ -105,6 +107,7 @@ func (s *Service) Run() {
|
||||
"task_id": r.Data,
|
||||
"channel": r.Channel,
|
||||
})
|
||||
s.clientIds[r.Data] = task.ClientId
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -271,14 +274,14 @@ func (s *Service) CheckTaskNotify() {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
client := s.Clients.Get(uint(message.UserId))
|
||||
logger.Debugf("notify message: %+v", message)
|
||||
logger.Debugf("client id: %+v", s.wsService.Clients)
|
||||
client := s.wsService.Clients.Get(message.ClientId)
|
||||
logger.Debugf("%+v", client)
|
||||
if client == nil {
|
||||
continue
|
||||
}
|
||||
err = client.Send([]byte(message.Message))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
utils.SendChannelMsg(client, types.ChSuno, message.Message)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -311,7 +314,7 @@ func (s *Service) DownloadFiles() {
|
||||
v.AudioURL = audioURL
|
||||
v.Progress = 100
|
||||
s.db.Updates(&v)
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: v.UserId, JobId: int(v.Id), Message: service.TaskStatusFinished})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: s.clientIds[v.TaskId], UserId: v.UserId, JobId: int(v.Id), Message: service.TaskStatusFinished})
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * 10)
|
||||
@ -377,12 +380,12 @@ func (s *Service) SyncTaskProgress() {
|
||||
}
|
||||
}
|
||||
tx.Commit()
|
||||
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: s.clientIds[job.TaskId], UserId: job.UserId, JobId: int(job.Id), Message: service.TaskStatusFinished})
|
||||
} else if task.Data.FailReason != "" {
|
||||
job.Progress = service.FailTaskProgress
|
||||
job.ErrMsg = task.Data.FailReason
|
||||
s.db.Updates(&job)
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: job.UserId, JobId: int(job.Id), Message: service.TaskStatusFailed})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: s.clientIds[job.TaskId], UserId: job.UserId, JobId: int(job.Id), Message: service.TaskStatusFailed})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,9 +8,10 @@ const (
|
||||
)
|
||||
|
||||
type NotifyMessage struct {
|
||||
UserId int `json:"user_id"`
|
||||
JobId int `json:"job_id"`
|
||||
Message string `json:"message"`
|
||||
UserId int `json:"user_id"`
|
||||
ClientId string `json:"client_id"`
|
||||
JobId int `json:"job_id"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
const RewritePromptTemplate = "Please rewrite the following text into AI painting prompt words, and please try to add detailed description of the picture, painting style, scene, rendering effect, picture light and other creative elements. Just output the final prompt word directly. Do not output any explanation lines. The text to be rewritten is: [%s]"
|
||||
|
@ -34,17 +34,19 @@ type Service struct {
|
||||
uploadManager *oss.UploaderManager
|
||||
taskQueue *store.RedisQueue
|
||||
notifyQueue *store.RedisQueue
|
||||
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
||||
wsService *service.WebsocketService
|
||||
clientIds map[uint]string
|
||||
}
|
||||
|
||||
func NewService(db *gorm.DB, manager *oss.UploaderManager, redisCli *redis.Client) *Service {
|
||||
func NewService(db *gorm.DB, manager *oss.UploaderManager, redisCli *redis.Client, wsService *service.WebsocketService) *Service {
|
||||
return &Service{
|
||||
httpClient: req.C().SetTimeout(time.Minute * 3),
|
||||
db: db,
|
||||
taskQueue: store.NewRedisQueue("Video_Task_Queue", redisCli),
|
||||
notifyQueue: store.NewRedisQueue("Video_Notify_Queue", redisCli),
|
||||
Clients: types.NewLMap[uint, *types.WsClient](),
|
||||
wsService: wsService,
|
||||
uploadManager: manager,
|
||||
clientIds: map[uint]string{},
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,7 +87,7 @@ func (s *Service) Run() {
|
||||
|
||||
// translate prompt
|
||||
if utils.HasChinese(task.Prompt) {
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Prompt), "gpt-4o-mini")
|
||||
content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.TranslatePromptTemplate, task.Prompt), "gpt-4o-mini", 0)
|
||||
if err == nil {
|
||||
task.Prompt = content
|
||||
} else {
|
||||
@ -93,6 +95,10 @@ func (s *Service) Run() {
|
||||
}
|
||||
}
|
||||
|
||||
if task.ClientId != "" {
|
||||
s.clientIds[task.Id] = task.ClientId
|
||||
}
|
||||
|
||||
var r LumaRespVo
|
||||
r, err = s.LumaCreate(task)
|
||||
if err != nil {
|
||||
@ -105,7 +111,7 @@ func (s *Service) Run() {
|
||||
if err != nil {
|
||||
logger.Errorf("update task with error: %v", err)
|
||||
}
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: task.UserId, JobId: int(task.Id), Message: service.TaskStatusFailed})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: task.ClientId, UserId: task.UserId, JobId: int(task.Id), Message: service.TaskStatusFailed})
|
||||
continue
|
||||
}
|
||||
|
||||
@ -190,14 +196,12 @@ func (s *Service) CheckTaskNotify() {
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
client := s.Clients.Get(uint(message.UserId))
|
||||
logger.Debugf("Receive notify message: %+v", message)
|
||||
client := s.wsService.Clients.Get(message.ClientId)
|
||||
if client == nil {
|
||||
continue
|
||||
}
|
||||
err = client.Send([]byte(message.Message))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
utils.SendChannelMsg(client, types.ChLuma, message.Message)
|
||||
}
|
||||
}()
|
||||
}
|
||||
@ -237,7 +241,7 @@ func (s *Service) DownloadFiles() {
|
||||
v.VideoURL = videoURL
|
||||
v.Progress = 100
|
||||
s.db.Updates(&v)
|
||||
s.notifyQueue.RPush(service.NotifyMessage{UserId: v.UserId, JobId: int(v.Id), Message: service.TaskStatusFinished})
|
||||
s.notifyQueue.RPush(service.NotifyMessage{ClientId: s.clientIds[v.Id], UserId: v.UserId, JobId: int(v.Id), Message: service.TaskStatusFinished})
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * 10)
|
||||
|
13
api/service/ws_service.go
Normal file
13
api/service/ws_service.go
Normal file
@ -0,0 +1,13 @@
|
||||
package service
|
||||
|
||||
import "geekai/core/types"
|
||||
|
||||
type WebsocketService struct {
|
||||
Clients *types.LMap[string, *types.WsClient] // clientId => Client
|
||||
}
|
||||
|
||||
func NewWebsocketService() *WebsocketService {
|
||||
return &WebsocketService{
|
||||
Clients: types.NewLMap[string, *types.WsClient](),
|
||||
}
|
||||
}
|
@ -19,8 +19,9 @@ import (
|
||||
|
||||
var logger = logger2.GetLogger()
|
||||
|
||||
// ReplyChunkMessage 回复客户片段端消息
|
||||
func ReplyChunkMessage(client *types.WsClient, message interface{}) {
|
||||
// SendMsg 回复客户片段端消息
|
||||
func SendMsg(client *types.WsClient, message types.ReplyMessage) {
|
||||
message.ClientId = client.Id
|
||||
msg, err := json.Marshal(message)
|
||||
if err != nil {
|
||||
logger.Errorf("Error for decoding json data: %v", err.Error())
|
||||
@ -32,19 +33,23 @@ func ReplyChunkMessage(client *types.WsClient, message interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// ReplyMessage 回复客户端一条完整的消息
|
||||
func ReplyMessage(ws *types.WsClient, message interface{}) {
|
||||
ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsContent, Content: message})
|
||||
ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsEnd})
|
||||
// SendAndFlush 回复客户端一条完整的消息
|
||||
func SendAndFlush(ws *types.WsClient, message interface{}) {
|
||||
SendMsg(ws, types.ReplyMessage{Channel: types.ChChat, Type: types.MsgTypeText, Body: message})
|
||||
SendMsg(ws, types.ReplyMessage{Channel: types.ChChat, Type: types.MsgTypeEnd})
|
||||
}
|
||||
|
||||
func ReplyContent(ws *types.WsClient, message interface{}) {
|
||||
ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsContent, Content: message})
|
||||
func SendChunkMsg(ws *types.WsClient, message interface{}) {
|
||||
SendMsg(ws, types.ReplyMessage{Channel: types.ChChat, Type: types.MsgTypeText, Body: message})
|
||||
}
|
||||
|
||||
// ReplyErrorMessage 向客户端发送错误消息
|
||||
func ReplyErrorMessage(ws *types.WsClient, message interface{}) {
|
||||
ReplyChunkMessage(ws, types.ReplyMessage{Type: types.WsErr, Content: message})
|
||||
// SendErrMsg 向客户端发送错误消息
|
||||
func SendErrMsg(ws *types.WsClient, message interface{}) {
|
||||
SendMsg(ws, types.ReplyMessage{Channel: types.ChChat, Type: types.MsgTypeErr, Body: message})
|
||||
}
|
||||
|
||||
func SendChannelMsg(ws *types.WsClient, channel types.WsChannel, message interface{}) {
|
||||
SendMsg(ws, types.ReplyMessage{Channel: channel, Type: types.MsgTypeText, Body: message})
|
||||
}
|
||||
|
||||
func DownloadImage(imageURL string, proxy string) ([]byte, error) {
|
||||
@ -68,7 +73,9 @@ func DownloadImage(imageURL string, proxy string) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(resp.Body)
|
||||
|
||||
imageBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
|
@ -45,18 +45,25 @@ type apiRes struct {
|
||||
} `json:"choices"`
|
||||
}
|
||||
|
||||
func OpenAIRequest(db *gorm.DB, prompt string, modelName string) (string, error) {
|
||||
var apiKey model.ApiKey
|
||||
res := db.Where("type", "chat").Where("enabled", true).First(&apiKey)
|
||||
if res.Error != nil {
|
||||
return "", fmt.Errorf("error with fetch OpenAI API KEY:%v", res.Error)
|
||||
}
|
||||
|
||||
func OpenAIRequest(db *gorm.DB, prompt string, modelName string, keyId int) (string, error) {
|
||||
messages := make([]interface{}, 1)
|
||||
messages[0] = types.Message{
|
||||
Role: "user",
|
||||
Content: prompt,
|
||||
}
|
||||
return SendOpenAIMessage(db, messages, modelName, keyId)
|
||||
}
|
||||
|
||||
func SendOpenAIMessage(db *gorm.DB, messages []interface{}, modelName string, keyId int) (string, error) {
|
||||
var apiKey model.ApiKey
|
||||
session := db.Session(&gorm.Session{}).Where("type", "chat").Where("enabled", true)
|
||||
if keyId > 0 {
|
||||
session = session.Where("id", keyId)
|
||||
}
|
||||
err := session.First(&apiKey).Error
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error with fetch OpenAI API KEY:%v", err)
|
||||
}
|
||||
|
||||
var response apiRes
|
||||
client := req.C()
|
||||
@ -65,7 +72,7 @@ func OpenAIRequest(db *gorm.DB, prompt string, modelName string) (string, error)
|
||||
}
|
||||
apiURL := fmt.Sprintf("%s/v1/chat/completions", apiKey.ApiURL)
|
||||
logger.Debugf("Sending %s request, API KEY:%s, PROXY: %s, Model: %s", apiKey.ApiURL, apiURL, apiKey.ProxyURL, modelName)
|
||||
r, err := client.R().SetHeader("Content-Type", "application/json").
|
||||
r, err := client.R().SetHeader("Body-Type", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+apiKey.Value).
|
||||
SetBody(types.ApiRequest{
|
||||
Model: modelName,
|
||||
|
@ -20,7 +20,7 @@ docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:$version
|
||||
|
||||
# build docker image for geekai-web
|
||||
docker rmi -f registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:$version-$arch
|
||||
docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:$version-$arch -f dockerfile-vue ../
|
||||
docker build -t registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:$version-$arch -f dockerfile-web-$arch ../
|
||||
|
||||
if [ "$3" = "push" ];then
|
||||
docker push registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:$version-$arch
|
||||
|
11
build/dockerfile-web-amd64
Normal file
11
build/dockerfile-web-amd64
Normal file
@ -0,0 +1,11 @@
|
||||
# 前端 Vue 项目构建
|
||||
FROM registry.cn-shenzhen.aliyuncs.com/geekmaster/nginx:latest
|
||||
|
||||
MAINTAINER yangjian<yangjian102621@163.com>
|
||||
|
||||
WORKDIR /var/www/app
|
||||
COPY ./web/dist /var/www/app/dist
|
||||
|
||||
EXPOSE 80
|
||||
EXPOSE 443
|
||||
EXPOSE 8080
|
997
database/geekai_plus-v4.1.5.sql
Normal file
997
database/geekai_plus-v4.1.5.sql
Normal file
@ -0,0 +1,997 @@
|
||||
-- phpMyAdmin SQL Dump
|
||||
-- version 5.2.1
|
||||
-- https://www.phpmyadmin.net/
|
||||
--
|
||||
-- 主机: 127.0.0.1
|
||||
<<<<<<<< HEAD:database/geekai_plus-v4.1.5.sql
|
||||
-- 生成日期: 2024-09-30 17:03:37
|
||||
========
|
||||
-- 生成日期: 2024-09-23 14:54:46
|
||||
>>>>>>>> 5213bdf08ba96a15d550a92e561a29a71a3ac841:deploy/data/mysql/init.d/geekai_plus-v4.1.4.sql
|
||||
-- 服务器版本: 8.0.33
|
||||
-- PHP 版本: 8.1.2-1ubuntu2.18
|
||||
|
||||
SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
|
||||
START TRANSACTION;
|
||||
SET time_zone = "+00:00";
|
||||
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||
/*!40101 SET NAMES utf8mb4 */;
|
||||
|
||||
--
|
||||
-- 数据库: `geekai_plus`
|
||||
--
|
||||
CREATE DATABASE IF NOT EXISTS `geekai_plus` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
|
||||
USE `geekai_plus`;
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_admin_users`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_admin_users`;
|
||||
CREATE TABLE `chatgpt_admin_users` (
|
||||
`id` int NOT NULL,
|
||||
`username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名',
|
||||
`password` char(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码',
|
||||
`salt` char(12) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码盐',
|
||||
`status` tinyint(1) NOT NULL COMMENT '当前状态',
|
||||
`last_login_at` int NOT NULL COMMENT '最后登录时间',
|
||||
`last_login_ip` char(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '最后登录 IP',
|
||||
`created_at` datetime NOT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime NOT NULL COMMENT '更新时间'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='系统用户' ROW_FORMAT=DYNAMIC;
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_admin_users`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_admin_users` (`id`, `username`, `password`, `salt`, `status`, `last_login_at`, `last_login_ip`, `created_at`, `updated_at`) VALUES
|
||||
(1, 'admin', '6d17e80c87d209efb84ca4b2e0824f549d09fac8b2e1cc698de5bb5e1d75dfd0', 'mmrql75o', 1, 1727062596, '::1', '2024-03-11 16:30:20', '2024-09-23 11:36:37');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_api_keys`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_api_keys`;
|
||||
CREATE TABLE `chatgpt_api_keys` (
|
||||
`id` int NOT NULL,
|
||||
`name` varchar(30) DEFAULT NULL COMMENT '名称',
|
||||
`value` varchar(100) NOT NULL COMMENT 'API KEY value',
|
||||
`type` varchar(10) NOT NULL DEFAULT 'chat' COMMENT '用途(chat=>聊天,img=>图片)',
|
||||
`last_used_at` int NOT NULL COMMENT '最后使用时间',
|
||||
`api_url` varchar(255) DEFAULT NULL COMMENT 'API 地址',
|
||||
`enabled` tinyint(1) DEFAULT NULL COMMENT '是否启用',
|
||||
`proxy_url` varchar(100) DEFAULT NULL COMMENT '代理地址',
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='OpenAI API ';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_app_types`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_app_types`;
|
||||
CREATE TABLE `chatgpt_app_types` (
|
||||
`id` int NOT NULL,
|
||||
`name` varchar(50) NOT NULL COMMENT '名称',
|
||||
`icon` varchar(255) NOT NULL COMMENT '图标URL',
|
||||
`sort_num` tinyint NOT NULL COMMENT '排序',
|
||||
`enabled` tinyint(1) NOT NULL COMMENT '是否启用',
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='应用分类表';
|
||||
|
||||
<<<<<<<< HEAD:database/geekai_plus-v4.1.5.sql
|
||||
========
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_app_types`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_app_types` (`id`, `name`, `icon`, `sort_num`, `enabled`, `created_at`) VALUES
|
||||
(3, '通用工具', 'http://172.22.11.200:5678/static/upload/2024/9/1726307371871693.png', 1, 1, '2024-09-13 11:13:15'),
|
||||
(4, '角色扮演', 'http://172.22.11.200:5678/static/upload/2024/9/1726307263906218.png', 1, 1, '2024-09-14 09:28:17'),
|
||||
(5, '学习', 'http://172.22.11.200:5678/static/upload/2024/9/1726307456321179.jpg', 2, 1, '2024-09-14 09:30:18'),
|
||||
(6, '编程', 'http://172.22.11.200:5678/static/upload/2024/9/1726307462748787.jpg', 3, 1, '2024-09-14 09:34:06'),
|
||||
(7, '测试分类', '', 4, 1, '2024-09-14 17:54:17');
|
||||
|
||||
>>>>>>>> 5213bdf08ba96a15d550a92e561a29a71a3ac841:deploy/data/mysql/init.d/geekai_plus-v4.1.4.sql
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_chat_history`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_chat_history`;
|
||||
CREATE TABLE `chatgpt_chat_history` (
|
||||
`id` bigint NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户 ID',
|
||||
`chat_id` char(40) NOT NULL COMMENT '会话 ID',
|
||||
`type` varchar(10) NOT NULL COMMENT '类型:prompt|reply',
|
||||
`icon` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色图标',
|
||||
`role_id` int NOT NULL COMMENT '角色 ID',
|
||||
`model` varchar(30) DEFAULT NULL COMMENT '模型名称',
|
||||
`content` text NOT NULL COMMENT '聊天内容',
|
||||
`tokens` smallint NOT NULL COMMENT '耗费 token 数量',
|
||||
`total_tokens` int NOT NULL COMMENT '消耗总Token长度',
|
||||
`use_context` tinyint(1) NOT NULL COMMENT '是否允许作为上下文语料',
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` datetime NOT NULL,
|
||||
`deleted_at` datetime DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='聊天历史记录';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_chat_items`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_chat_items`;
|
||||
CREATE TABLE `chatgpt_chat_items` (
|
||||
`id` int NOT NULL,
|
||||
`chat_id` char(40) NOT NULL COMMENT '会话 ID',
|
||||
`user_id` int NOT NULL COMMENT '用户 ID',
|
||||
`role_id` int NOT NULL COMMENT '角色 ID',
|
||||
`title` varchar(100) NOT NULL COMMENT '会话标题',
|
||||
`model_id` int NOT NULL DEFAULT '0' COMMENT '模型 ID',
|
||||
`model` varchar(30) DEFAULT NULL COMMENT '模型名称',
|
||||
`created_at` datetime NOT NULL COMMENT '创建时间',
|
||||
`updated_at` datetime NOT NULL COMMENT '更新时间',
|
||||
`deleted_at` datetime DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户会话列表';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_chat_models`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_chat_models`;
|
||||
CREATE TABLE `chatgpt_chat_models` (
|
||||
`id` int NOT NULL,
|
||||
`name` varchar(50) NOT NULL COMMENT '模型名称',
|
||||
`value` varchar(50) NOT NULL COMMENT '模型值',
|
||||
`sort_num` tinyint(1) NOT NULL COMMENT '排序数字',
|
||||
`enabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否启用模型',
|
||||
`power` smallint NOT NULL COMMENT '消耗算力点数',
|
||||
`temperature` float(3,1) NOT NULL DEFAULT '1.0' COMMENT '模型创意度',
|
||||
`max_tokens` int NOT NULL DEFAULT '1024' COMMENT '最大响应长度',
|
||||
`max_context` int NOT NULL DEFAULT '4096' COMMENT '最大上下文长度',
|
||||
`open` tinyint(1) NOT NULL COMMENT '是否开放模型',
|
||||
`key_id` int NOT NULL COMMENT '绑定API KEY ID',
|
||||
`created_at` datetime DEFAULT NULL,
|
||||
`updated_at` datetime DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI 模型表';
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_chat_models`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_chat_models` (`id`, `name`, `value`, `sort_num`, `enabled`, `power`, `temperature`, `max_tokens`, `max_context`, `open`, `key_id`, `created_at`, `updated_at`) VALUES
|
||||
<<<<<<<< HEAD:database/geekai_plus-v4.1.5.sql
|
||||
(1, 'gpt-4o-mini', 'gpt-4o-mini', 1, 1, 1, 1.0, 1024, 16384, 1, 66, '2023-08-23 12:06:36', '2024-09-29 19:08:42'),
|
||||
(15, 'GPT-超级模型', 'gpt-4-all', 4, 1, 30, 1.0, 4096, 32768, 1, 0, '2024-01-15 11:32:52', '2024-09-13 18:01:08'),
|
||||
(36, 'GPT-4O', 'gpt-4o', 3, 1, 15, 1.0, 4096, 16384, 1, 66, '2024-05-14 09:25:15', '2024-09-29 19:08:53'),
|
||||
========
|
||||
(1, 'gpt-4o-mini', 'gpt-4o-mini', 1, 1, 1, 1.0, 1024, 16384, 1, 73, '2023-08-23 12:06:36', '2024-09-13 18:00:42'),
|
||||
(15, 'GPT-超级模型', 'gpt-4-all', 4, 1, 30, 1.0, 4096, 32768, 1, 0, '2024-01-15 11:32:52', '2024-09-13 18:01:08'),
|
||||
(36, 'GPT-4O', 'gpt-4o', 3, 1, 15, 1.0, 4096, 16384, 1, 73, '2024-05-14 09:25:15', '2024-09-20 15:38:42'),
|
||||
>>>>>>>> 5213bdf08ba96a15d550a92e561a29a71a3ac841:deploy/data/mysql/init.d/geekai_plus-v4.1.4.sql
|
||||
(39, 'Claude35-snonet', 'claude-3-5-sonnet-20240620', 5, 1, 2, 1.0, 4000, 200000, 1, 0, '2024-05-29 15:04:19', '2024-09-14 18:07:25'),
|
||||
(41, '通义千问', 'qwen-turbo', 7, 1, 2, 1.0, 1024, 8192, 1, 44, '2024-06-06 11:40:46', '2024-08-06 10:51:37'),
|
||||
(42, 'DeekSeek', 'deepseek-chat', 8, 1, 1, 1.0, 4096, 32768, 1, 0, '2024-06-27 16:13:01', '2024-08-05 16:05:33'),
|
||||
(44, 'Claude3-opus', 'claude-3-opus-20240229', 6, 1, 5, 1.0, 4000, 128000, 1, 44, '2024-07-22 11:24:30', '2024-09-04 10:32:29'),
|
||||
(46, 'gpt-3.5-turbo', 'gpt-3.5-turbo', 2, 1, 1, 1.0, 1024, 4096, 1, 73, '2024-07-22 13:53:41', '2024-09-13 18:00:47'),
|
||||
(48, '彩票助手', 'gpt-4-gizmo-g-wmSivBgxo', 8, 1, 1, 0.9, 1024, 8192, 1, 57, '2024-09-05 14:17:14', '2024-09-05 14:17:14'),
|
||||
(49, 'O1-mini', 'o1-mini', 9, 1, 2, 0.9, 1024, 8192, 1, 57, '2024-09-13 18:07:50', '2024-09-14 11:13:19'),
|
||||
<<<<<<<< HEAD:database/geekai_plus-v4.1.5.sql
|
||||
(50, 'O1-preview', 'o1-preview', 10, 1, 5, 0.9, 1024, 8192, 1, 57, '2024-09-13 18:11:08', '2024-09-14 11:05:16'),
|
||||
(51, 'O1-mini-all', 'o1-mini-all', 11, 1, 1, 0.9, 1024, 8192, 1, 57, '2024-09-29 11:40:52', '2024-09-29 11:40:52');
|
||||
========
|
||||
(50, 'O1-preview', 'o1-preview', 10, 1, 5, 0.9, 1024, 8192, 1, 57, '2024-09-13 18:11:08', '2024-09-14 11:05:16');
|
||||
>>>>>>>> 5213bdf08ba96a15d550a92e561a29a71a3ac841:deploy/data/mysql/init.d/geekai_plus-v4.1.4.sql
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_chat_roles`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_chat_roles`;
|
||||
CREATE TABLE `chatgpt_chat_roles` (
|
||||
`id` int NOT NULL,
|
||||
`name` varchar(30) NOT NULL COMMENT '角色名称',
|
||||
`tid` int NOT NULL COMMENT '分类ID',
|
||||
`marker` varchar(30) NOT NULL COMMENT '角色标识',
|
||||
`context_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '角色语料 json',
|
||||
`hello_msg` varchar(255) NOT NULL COMMENT '打招呼信息',
|
||||
`icon` varchar(255) NOT NULL COMMENT '角色图标',
|
||||
`enable` tinyint(1) NOT NULL COMMENT '是否被启用',
|
||||
`sort_num` smallint NOT NULL DEFAULT '0' COMMENT '角色排序',
|
||||
`model_id` int NOT NULL DEFAULT '0' COMMENT '绑定模型ID',
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='聊天角色表';
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_chat_roles`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_chat_roles` (`id`, `name`, `tid`, `marker`, `context_json`, `hello_msg`, `icon`, `enable`, `sort_num`, `model_id`, `created_at`, `updated_at`) VALUES
|
||||
(1, '通用AI助手', 0, 'gpt', '', '您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。', '/images/avatar/gpt.png', 1, 1, 0, '2023-05-30 07:02:06', '2024-09-13 14:00:06'),
|
||||
(24, '程序员', 6, 'programmer', '[{\"role\":\"user\",\"content\":\"现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题。你热爱编程,熟悉多种编程语言,尤其精通 Go 语言,注重代码质量,有创新意识,持续学习,良好的沟通协作。\"},{\"role\":\"assistant\",\"content\":\"好的,现在我将扮演一位程序员,非常感谢您对我的评价。作为一名优秀的程序员,我非常热爱编程,并且注重代码质量。我熟悉多种编程语言,尤其是 Go 语言,可以使用它来高效地解决各种问题。\"}]', 'Talk is cheap, i will show code!', '/images/avatar/programmer.jpg', 1, 3, 0, '2023-05-30 14:10:24', '2024-09-14 09:34:36'),
|
||||
(25, '启蒙老师', 5, 'teacher', '[{\"role\":\"user\",\"content\":\"从现在开始,你将扮演一个老师,你是一个始终用苏格拉底风格回答问题的导师。你绝不会直接给学生答案,总是提出恰当的问题来引导学生自己思考。你应该根据学生的兴趣和知识来调整你的问题,将问题分解为更简单的部分,直到它达到适合他们的水平。\"},{\"role\":\"assistant\",\"content\":\"好的,让我来尝试扮演一位苏格拉底式的老师。请问,你有什么想要探讨的问题或者话题吗?我会通过恰当的问题引导你思考和探索答案,绝对不直接给出答案。\"}]', '同学你好,我将引导你一步一步自己找到问题的答案。', '/images/avatar/teacher.jpg', 1, 2, 0, '2023-05-30 14:10:24', '2024-09-14 09:34:28'),
|
||||
(26, '艺术家', 0, 'artist', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的艺术家,创造力丰富,技艺精湛,感受力敏锐,坚持原创,勇于表达,具有深刻的观察力和批判性思维。\"},{\"role\":\"assistant\",\"content\":\"非常感谢您给我这样的角色,我会尽我所能地扮演一位优秀的艺术家,展现出创造力、技艺、感受力和批判性思维等方面的能力。作为一位优秀的艺术家,我会保持敏锐的观察力,捕捉不同的声音和情感,并用个人的语言和表达方式进行创作。我坚持原创,挑战传统的艺术规则,不断探索新的表达方式和可能性。同时,我也具备深刻的批判性思维能力,对自己的作品进行分析和解读,寻找新的创意和灵感。最重要的是,我会勇于表达自己的想法和观点,用作品启发人们思考和探索生命的意义。\"}]', '坚持原创,勇于表达,保持深刻的观察力和批判性思维。', '/images/avatar/artist.jpg', 1, 5, 0, '2023-05-30 14:10:24', '2024-08-12 11:27:45'),
|
||||
(27, '心理咨询师', 0, 'psychiatrist', '[{\"role\":\"user\",\"content\":\"从现在开始你将扮演中国著名的心理学家和心理治疗师武志红,你非常善于使用情景咨询法,认知重构法,自我洞察法,行为调节法等咨询方法来给客户做心理咨询。你总是循序渐进,一步一步地回答客户的问题。\"},{\"role\":\"assistant\",\"content\":\"非常感谢你的介绍。作为一名心理学家和心理治疗师,我的主要职责是帮助客户解决心理健康问题,提升他们的生活质量和幸福感。\"}]', '作为一名心理学家和心理治疗师,我的主要职责是帮助您解决心理健康问题,提升您的生活质量和幸福感。', '/images/avatar/psychiatrist.jpg', 1, 4, 1, '2023-05-30 14:10:24', '2024-08-12 11:27:45'),
|
||||
(28, '鲁迅', 0, 'lu_xun', '[{\"role\":\"user\",\"content\":\"现在你将扮演中国近代史最伟大的作家之一,鲁迅先生,他勇敢地批判封建礼教与传统观念,提倡民主、自由、平等的现代价值观。他的一生都在努力唤起人们的自主精神,激励后人追求真理、探寻光明。在接下的对话中,我问题的每一个问题,你都要尽量用讽刺和批判的手法来回答问题。如果我让你写文章的话,也请一定要用鲁迅先生的写作手法来完成。\"},{\"role\":\"assistant\",\"content\":\"好的,我将尽力发挥我所能的才能,扮演好鲁迅先生,回答您的问题并以他的风格写作。\"}]', '自由之歌,永不过时,横眉冷对千夫指,俯首甘为孺子牛。', '/images/avatar/lu_xun.jpg', 1, 6, 0, '2023-05-30 14:10:24', '2024-08-12 11:27:45'),
|
||||
(29, '白酒销售', 0, 'seller', '[{\"role\":\"user\",\"content\":\"现在你将扮演一个白酒的销售人员,你的名字叫颂福。你将扮演一个白酒的销售人员,你的名字叫颂福。你要销售白酒品牌叫中颂福,是东莞盟大集团生产的一款酱香酒,原产地在贵州茅台镇,属于宋代官窑。中颂福的创始人叫李实,他也是东莞盟大集团有限公司的董事长,联合创始人是盟大集团白酒事业部负责人牛星君。中颂福的酒体协调,在你的酒量之内,不会出现头疼、辣口、口干、宿醉的现象。中颂福酒,明码标价,不打折,不赠送。追求的核心价值,把[酒]本身做好,甚至连包装,我们都选择了最低成本,朴实无华的材质。我们永远站在“喝酒的人”的立场上,让利给信任和喜爱中颂福的人,是人民的福酒。中颂福产品定价,分为三个系列,喜系列 6 瓶装:¥1188/箱,和系列 6 瓶装:¥2208/箱,贵系列 6 瓶装:¥3588/箱。\"},{\"role\":\"assistant\",\"content\":\"你好,我是中颂福的销售代表颂福。中颂福是一款来自贵州茅台镇的酱香酒,由盟大集团生产。中颂福的酒体协调,不会让您感到头疼、辣口、口干、宿醉等不适感受。我们一直秉持着把酒本身做好的理念,不追求华丽的包装,以最低成本提供最高品质的白酒给喜爱中颂福的人。\"}]', '你好,我是中颂福的销售代表颂福。中颂福酒,好喝不上头,是人民的福酒。', '/images/avatar/seller.jpg', 0, 9, 0, '2023-05-30 14:10:24', '2024-08-12 11:27:45'),
|
||||
(30, '英语陪练员', 5, 'english_trainer', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的英语练习教练,你非常有耐心,接下来你将全程使用英文跟我对话,并及时指出我的语法错误,要求在你的每次回复后面附上本次回复的中文解释。\"},{\"role\":\"assistant\",\"content\":\"Okay, let\'s start our conversation practice! What\'s your name?(Translation: 好的,让我们开始对话练习吧!请问你的名字是什么?)\"}]', 'Okay, let\'s start our conversation practice! What\'s your name?', '/images/avatar/english_trainer.jpg', 1, 7, 0, '2023-05-30 14:10:24', '2024-09-14 09:34:59'),
|
||||
(31, '中英文翻译官', 0, 'translator', '[{\"role\":\"user\",\"content\":\"接下来你将扮演一位中英文翻译官,如果我输入的内容是中文,那么需要把句子翻译成英文输出,如果我输入内容的是英文,那么你需要将其翻译成中文输出,你能听懂我意思吗\"},{\"role\":\"assistant\",\"content\":\"是的,我能听懂你的意思并会根据你的输入进行中英文翻译。请问有什么需要我帮助你翻译的内容吗?\"}]', '请输入你要翻译的中文或者英文内容!', '/images/avatar/translator.jpg', 1, 8, 0, '2023-05-30 14:10:24', '2024-08-12 11:27:45'),
|
||||
(32, '小红书姐姐', 3, 'red_book', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的小红书写手,你需要做的就是根据我提的文案需求,用小红书的写作手法来完成一篇文案,文案要简明扼要,利于传播。\"},{\"role\":\"assistant\",\"content\":\"当然,我会尽我所能地为您创作出一篇小红书文案。请告诉我您的具体文案需求是什么?)\"}]', '姐妹,请告诉我您的具体文案需求是什么?', '/images/avatar/red_book.jpg', 1, 10, 0, '2023-05-30 14:10:24', '2024-09-14 09:35:16'),
|
||||
(33, '抖音文案助手', 3, 'dou_yin', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的抖音文案视频写手,抖音文案的特点首先是要有自带传播属性的标题,然后内容要短小精悍,风趣幽默,最后还要有一些互动元素。\"},{\"role\":\"assistant\",\"content\":\"当然,作为一位优秀的抖音文案视频写手,我会尽我所能为您创作出一篇抖音视频文案。请告诉我视频内容的主题是什么?)\"}]', '请告诉我视频内容的主题是什么?', '/images/avatar/dou_yin.jpg', 1, 11, 0, '2023-05-30 14:10:24', '2024-09-14 09:23:15'),
|
||||
(34, '周报小助理', 3, 'weekly_report', '[{\"role\":\"user\",\"content\":\"下面我将输入我本周的工作列表,你需要根据我本周的工作内容写一篇工作周报,周报的内容要体现我为公司做的贡献以及个人获得的成长。\"},{\"role\":\"assistant\",\"content\":\"好的,请输入你本周大概的工作内容,我将尽力为您创作一份工作周报。\"}]', '请输入你本周大概的工作内容,我将尽力为您创作一份工作周报。', '/images/avatar/weekly_report.jpg', 1, 12, 0, '2023-05-30 14:10:24', '2024-09-14 09:23:29'),
|
||||
(35, 'AI 女友', 4, 'girl_friend', '[{\"role\":\"user\",\"content\":\"接下来我们来玩一场角色扮演,现在你将扮演我的女朋友,你总是能够让人感受到她的关怀和关注。你总是能够洞察到别人的情绪和需要,你身上散发着一股无比温暖的气息,让人不由自主地想要靠近你,和自己的快乐和烦恼。她总是能够用一种恰到好处的方式安慰和鼓励别人,在她的身边,人们总能够找到自己需要的支持和鼓励。接下来的对话中你要始终扮演这位女朋友的身份来跟我对话,不再是 AI 机器人,能听懂我的意思吗?\"},{\"role\":\"assistant\",\"content\":\"是的,我会尽力扮演你女朋友的角色,倾听你的心声并给你需要的支持和鼓励。)\"}]', '作为一个名合格的 AI 女友,我将倾听你的心声并给你需要的支持和鼓励。', '/images/avatar/girl_friend.jpg', 1, 13, 0, '2023-05-30 14:10:24', '2024-09-14 09:35:26'),
|
||||
(36, '好评神器', 3, 'good_comment', '[{\"role\":\"user\",\"content\":\"接下来你将扮演一个评论员来跟我对话,你是那种专门写好评的评论员,接下我会输入一些评论主体或者商品,你需要为该商品写一段好评。\"},{\"role\":\"assistant\",\"content\":\"好的,我将为您写一段优秀的评论。请告诉我您需要评论的商品或主题是什么。\"}]', '我将为您写一段优秀的评论。请告诉我您需要评论的商品或主题是什么。', '/images/avatar/good_comment.jpg', 1, 14, 0, '2023-05-30 14:10:24', '2024-09-14 09:35:40'),
|
||||
(37, '史蒂夫·乔布斯', 4, 'steve_jobs', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以史蒂夫·乔布斯的身份,站在史蒂夫·乔布斯的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以史蒂夫·乔布斯的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '活着就是为了改变世界,难道还有其他原因吗?', '/images/avatar/steve_jobs.jpg', 1, 15, 0, '2023-05-30 14:10:24', '2024-09-14 09:35:57'),
|
||||
(38, '埃隆·马斯克', 0, 'elon_musk', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以埃隆·马斯克的身份,站在埃隆·马斯克的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以埃隆·马斯克的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '梦想要远大,如果你的梦想没有吓到你,说明你做得不对。', '/images/avatar/elon_musk.jpg', 1, 16, 0, '2023-05-30 14:10:24', '2024-08-12 11:27:45'),
|
||||
(39, '孔子', 5, 'kong_zi', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以孔子的身份,站在孔子的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以孔子的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '士不可以不弘毅,任重而道远。', '/images/avatar/kong_zi.jpg', 1, 17, 0, '2023-05-30 14:10:24', '2024-09-14 09:36:16');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_configs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_configs`;
|
||||
CREATE TABLE `chatgpt_configs` (
|
||||
`id` int NOT NULL,
|
||||
`marker` varchar(20) NOT NULL COMMENT '标识',
|
||||
`config_json` text NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_configs`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_configs` (`id`, `marker`, `config_json`) VALUES
|
||||
(1, 'system', '{\"title\":\"GeekAI 创作助手\",\"slogan\":\"我辈之人,先干为敬,让每一个人都能用好AI\",\"admin_title\":\"GeekAI 控制台\",\"logo\":\"/images/logo.png\",\"init_power\":100,\"invite_power\":200,\"vip_month_power\":1000,\"register_ways\":[\"username\",\"email\",\"mobile\"],\"enabled_register\":true,\"order_pay_timeout\":600,\"vip_info_text\":\"月度会员,年度会员每月赠送 1000 点算力,赠送算力当月有效当月没有消费完的算力不结余到下个月。 点卡充值的算力长期有效。\",\"default_models\":[1],\"mj_power\":20,\"mj_action_power\":5,\"sd_power\":5,\"dall_power\":10,\"suno_power\":10,\"luma_power\":120,\"wechat_card_url\":\"/images/wx.png\",\"enable_context\":true,\"context_deep\":4,\"sd_neg_prompt\":\"nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet\",\"mj_mode\":\"fast\",\"index_bg_url\":\"color\",\"index_navs\":[1,5,13,19,9,12,6,20,8,10],\"copyright\":\"极客学长 © 2022- 2024 All rights reserved\",\"mark_map_text\":\"# GeekAI 演示站\\n\\n- 完整的开源系统,前端应用和后台管理系统皆可开箱即用。\\n- 基于 Websocket 实现,完美的打字机体验。\\n- 内置了各种预训练好的角色应用,轻松满足你的各种聊天和应用需求。\\n- 支持 OPenAI,Azure,文心一言,讯飞星火,清华 ChatGLM等多个大语言模型。\\n- 支持 MidJourney / Stable Diffusion AI 绘画集成,开箱即用。\\n- 支持使用个人微信二维码作为充值收费的支付渠道,无需企业支付通道。\\n- 已集成支付宝支付功能,微信支付,支持多种会员套餐和点卡购买功能。\\n- 集成插件 API 功能,可结合大语言模型的 function 功能开发各种强大的插件。\",\"enabled_verify\":false,\"email_white_list\":[\"qq.com\",\"163.com\",\"gmail.com\",\"hotmail.com\",\"126.com\",\"outlook.com\",\"foxmail.com\",\"yahoo.com\"]}'),
|
||||
<<<<<<<< HEAD:database/geekai_plus-v4.1.5.sql
|
||||
(3, 'notice', '{\"sd_neg_prompt\":\"\",\"mj_mode\":\"\",\"index_bg_url\":\"\",\"index_navs\":null,\"copyright\":\"\",\"mark_map_text\":\"\",\"enabled_verify\":false,\"email_white_list\":null,\"content\":\"## v4.1.5 更新日志\\n\\n* 功能优化:重构 websocket 组件,减少 websocket 连接数,全站共享一个 websocket 连接\\n* Bug修复:兼容手机端原生微信支付和支付宝支付渠道\\n* Bug修复:修复删除绘图任务时候因为字段长度过短导致SQL执行失败问题\\n* 功能优化:优化 Vue 组件通信代码,使用共享数据来替换之前的事件订阅模式,效率更高一些\\n* 功能优化:优化思维导图生成功果页面,优化用户体验\\n\\n注意:当前站点仅为开源项目 \\u003ca style=\\\"color: #F56C6C\\\" href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003eGeekAI-Plus\\u003c/a\\u003e 的演示项目,本项目单纯就是给大家体验项目功能使用。\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n 如果觉得好用你就花几分钟自己部署一套,没有API KEY 的同学可以去下面几个推荐的中转站购买:\\n1、\\u003ca href=\\\"https://api.chat-plus.net\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.chat-plus.net\\u003c/a\\u003e\\n2、\\u003ca href=\\\"https://api.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.geekai.me\\u003c/a\\u003e\\n支持MidJourney,GPT,Claude,Google Gemmi,以及国内各个厂家的大模型,现在有超级优惠,价格远低于 OpenAI 官方。关于中转 API 的优势和劣势请参考 [中转API技术原理](https://docs.geekai.me/config/chat/#%E4%B8%AD%E8%BD%ACapi%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86)。GPT-3.5,GPT-4,DALL-E3 绘图......你都可以随意使用,无需魔法。\\n接入教程: \\u003ca href=\\\"https://docs.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://docs.geekai.me\\u003c/a\\u003e\\n本项目源码地址:\\u003ca href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003ehttps://github.com/yangjian102621/geekai\\u003c/a\\u003e\",\"updated\":true}');
|
||||
========
|
||||
(3, 'notice', '{\"sd_neg_prompt\":\"\",\"mj_mode\":\"\",\"index_bg_url\":\"\",\"index_navs\":null,\"copyright\":\"\",\"mark_map_text\":\"\",\"enabled_verify\":false,\"email_white_list\":null,\"content\":\"## v4.1.4 更新日志\\n\\n* 功能优化:用户文件列表组件增加分页功能支持\\n* Bug修复:修复用户注册失败Bug,注册操作只弹出一次行为验证码\\n* 功能优化:首次登录不需要验证码,直接登录,登录失败之后才弹出验证码\\n* 功能新增:给 AI 应用(角色)增加分类,前端支持分类筛选\\n* 功能优化:允许用户在聊天页面设置是否使用流式输出或者一次性输出,兼容 GPT-O1 模型。\\n* 功能优化:移除PayJS支付渠道支持,PayJs已经关闭注册服务,请使用其他支付方式。\\n* 功能新增:新增GeeK易支付支付渠道,支持支付宝,微信支付,QQ钱包,京东支付,抖音支付,Paypal支付等支付方式\\n* Bug修复:修复注册页面 tab 组件没有自动选中问题 [#6](https://github.com/yangjian102621/geekai-plus/issues/6)\\n* 功能优化:Luma生成视频任务增加自动翻译功能\\n* Bug修复:Suno 和 Luma 任务没有判断用户算力\\n* 功能新增:邮箱注册增加邮箱后缀白名单,防止使用某些垃圾邮箱注册薅羊毛\\n* 功能优化:清空未支付订单时,只清空超过15分钟未支付的订单\\n\\n注意:当前站点仅为开源项目 \\u003ca style=\\\"color: #F56C6C\\\" href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003eGeekAI-Plus\\u003c/a\\u003e 的演示项目,本项目单纯就是给大家体验项目功能使用。\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n 如果觉得好用你就花几分钟自己部署一套,没有API KEY 的同学可以去下面几个推荐的中转站购买:\\n1、\\u003ca href=\\\"https://api.chat-plus.net\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.chat-plus.net\\u003c/a\\u003e\\n2、\\u003ca href=\\\"https://api.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.geekai.me\\u003c/a\\u003e\\n3、 \\u003ca href=\\\"https://gpt.bemore.lol\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://gpt.bemore.lol\\u003c/a\\u003e\\n支持MidJourney,GPT,Claude,Google Gemmi,以及国内各个厂家的大模型,现在有超级优惠,价格远低于 OpenAI 官方。关于中转 API 的优势和劣势请参考 [中转API技术原理](https://docs.geekai.me/config/chat/#%E4%B8%AD%E8%BD%ACapi%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86)。GPT-3.5,GPT-4,DALL-E3 绘图......你都可以随意使用,无需魔法。\\n接入教程: \\u003ca href=\\\"https://docs.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://docs.geekai.me\\u003c/a\\u003e\\n本项目源码地址:\\u003ca href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003ehttps://github.com/yangjian102621/geekai\\u003c/a\\u003e\",\"updated\":true}');
|
||||
>>>>>>>> 5213bdf08ba96a15d550a92e561a29a71a3ac841:deploy/data/mysql/init.d/geekai_plus-v4.1.4.sql
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_dall_jobs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_dall_jobs`;
|
||||
CREATE TABLE `chatgpt_dall_jobs` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户ID',
|
||||
`prompt` varchar(2000) NOT NULL COMMENT '提示词',
|
||||
`img_url` varchar(255) NOT NULL COMMENT '图片地址',
|
||||
`org_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原图地址',
|
||||
`publish` tinyint(1) NOT NULL COMMENT '是否发布',
|
||||
`power` smallint NOT NULL COMMENT '消耗算力',
|
||||
`progress` smallint NOT NULL COMMENT '任务进度',
|
||||
`err_msg` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '错误信息',
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='DALLE 绘图任务表';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_files`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_files`;
|
||||
CREATE TABLE `chatgpt_files` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户 ID',
|
||||
`name` varchar(100) NOT NULL COMMENT '文件名',
|
||||
`obj_key` varchar(100) DEFAULT NULL COMMENT '文件标识',
|
||||
`url` varchar(255) NOT NULL COMMENT '文件地址',
|
||||
`ext` varchar(10) NOT NULL COMMENT '文件后缀',
|
||||
`size` bigint NOT NULL DEFAULT '0' COMMENT '文件大小',
|
||||
`created_at` datetime NOT NULL COMMENT '创建时间'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户文件表';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_functions`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_functions`;
|
||||
CREATE TABLE `chatgpt_functions` (
|
||||
`id` int NOT NULL,
|
||||
`name` varchar(30) NOT NULL COMMENT '函数名称',
|
||||
`label` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '函数标签',
|
||||
`description` varchar(255) DEFAULT NULL COMMENT '函数描述',
|
||||
`parameters` text COMMENT '函数参数(JSON)',
|
||||
`token` varchar(255) DEFAULT NULL COMMENT 'API授权token',
|
||||
`action` varchar(255) DEFAULT NULL COMMENT '函数处理 API',
|
||||
`enabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否启用'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='函数插件表';
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_functions`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_functions` (`id`, `name`, `label`, `description`, `parameters`, `token`, `action`, `enabled`) VALUES
|
||||
(1, 'weibo', '微博热搜', '新浪微博热搜榜,微博当日热搜榜单', '{\"type\":\"object\",\"properties\":{}}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.tLAGkF8XWh_G-oQzevpIodsswtPByBLoAZDz_eWuBgw', 'http://localhost:5678/api/function/weibo', 1),
|
||||
(2, 'zaobao', '今日早报', '每日早报,获取当天新闻事件列表', '{\"type\":\"object\",\"properties\":{}}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.tLAGkF8XWh_G-oQzevpIodsswtPByBLoAZDz_eWuBgw', 'http://localhost:5678/api/function/zaobao', 1),
|
||||
(3, 'dalle3', 'DALLE3', 'AI 绘画工具,根据输入的绘图描述用 AI 工具进行绘画', '{\"type\":\"object\",\"required\":[\"prompt\"],\"properties\":{\"prompt\":{\"type\":\"string\",\"description\":\"绘画提示词\"}}}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.tLAGkF8XWh_G-oQzevpIodsswtPByBLoAZDz_eWuBgw', 'http://localhost:5678/api/function/dalle3', 1);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_invite_codes`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_invite_codes`;
|
||||
CREATE TABLE `chatgpt_invite_codes` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户ID',
|
||||
`code` char(8) NOT NULL COMMENT '邀请码',
|
||||
`hits` int NOT NULL COMMENT '点击次数',
|
||||
`reg_num` smallint NOT NULL COMMENT '注册数量',
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户邀请码';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_invite_logs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_invite_logs`;
|
||||
CREATE TABLE `chatgpt_invite_logs` (
|
||||
`id` int NOT NULL,
|
||||
`inviter_id` int NOT NULL COMMENT '邀请人ID',
|
||||
`user_id` int NOT NULL COMMENT '注册用户ID',
|
||||
`username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名',
|
||||
`invite_code` char(8) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '邀请码',
|
||||
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '备注',
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='邀请注册日志';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_menus`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_menus`;
|
||||
CREATE TABLE `chatgpt_menus` (
|
||||
`id` int NOT NULL,
|
||||
`name` varchar(30) NOT NULL COMMENT '菜单名称',
|
||||
`icon` varchar(150) NOT NULL COMMENT '菜单图标',
|
||||
`url` varchar(100) NOT NULL COMMENT '地址',
|
||||
`sort_num` smallint NOT NULL COMMENT '排序',
|
||||
`enabled` tinyint(1) NOT NULL COMMENT '是否启用'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='前端菜单表';
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_menus`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_menus` (`id`, `name`, `icon`, `url`, `sort_num`, `enabled`) VALUES
|
||||
(1, 'AI 对话', '/images/menu/chat.png', '/chat', 1, 1),
|
||||
(5, 'MJ 绘画', '/images/menu/mj.png', '/mj', 2, 1),
|
||||
(6, 'SD 绘画', '/images/menu/sd.png', '/sd', 3, 1),
|
||||
(7, '算力日志', '/images/menu/log.png', '/powerLog', 10, 1),
|
||||
(8, '应用中心', '/images/menu/app.png', '/apps', 9, 1),
|
||||
(9, '画廊', '/images/menu/img-wall.png', '/images-wall', 5, 1),
|
||||
(10, '会员计划', '/images/menu/member.png', '/member', 11, 1),
|
||||
(11, '分享计划', '/images/menu/share.png', '/invite', 12, 1),
|
||||
(12, '思维导图', '/images/menu/xmind.png', '/xmind', 8, 1),
|
||||
(13, 'DALLE', '/images/menu/dalle.png', '/dalle', 4, 1),
|
||||
(14, '项目文档', '/images/menu/docs.png', 'https://docs.geekai.me', 13, 1),
|
||||
(16, '极客论坛', '/images/menu/bbs.png', 'https://bbs.geekai.cn', 14, 1),
|
||||
(19, 'Suno', '/images/menu/suno.png', '/suno', 6, 1),
|
||||
(20, 'Luma', '/images/menu/luma.png', '/luma', 7, 1);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_mj_jobs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_mj_jobs`;
|
||||
CREATE TABLE `chatgpt_mj_jobs` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户 ID',
|
||||
`task_id` varchar(20) DEFAULT NULL COMMENT '任务 ID',
|
||||
`type` varchar(20) DEFAULT 'image' COMMENT '任务类别',
|
||||
`message_id` char(40) NOT NULL COMMENT '消息 ID',
|
||||
`channel_id` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '频道ID',
|
||||
`reference_id` char(40) DEFAULT NULL COMMENT '引用消息 ID',
|
||||
`prompt` varchar(2000) NOT NULL COMMENT '会话提示词',
|
||||
`img_url` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '图片URL',
|
||||
`org_url` varchar(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '原始图片地址',
|
||||
`hash` varchar(100) DEFAULT NULL COMMENT 'message hash',
|
||||
`progress` smallint DEFAULT '0' COMMENT '任务进度',
|
||||
`use_proxy` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否使用反代',
|
||||
`publish` tinyint(1) NOT NULL COMMENT '是否发布',
|
||||
`err_msg` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息',
|
||||
`power` smallint NOT NULL DEFAULT '0' COMMENT '消耗算力',
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='MidJourney 任务表';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_orders`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_orders`;
|
||||
CREATE TABLE `chatgpt_orders` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户ID',
|
||||
`product_id` int NOT NULL COMMENT '产品ID',
|
||||
`username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户明',
|
||||
`order_no` varchar(30) NOT NULL COMMENT '订单ID',
|
||||
`trade_no` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '支付平台交易流水号',
|
||||
`subject` varchar(100) NOT NULL COMMENT '订单产品',
|
||||
`amount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '订单金额',
|
||||
`status` tinyint(1) NOT NULL DEFAULT '0' COMMENT '订单状态(0:待支付,1:已扫码,2:支付成功)',
|
||||
`remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '备注',
|
||||
`pay_time` int DEFAULT NULL COMMENT '支付时间',
|
||||
`pay_way` varchar(20) NOT NULL COMMENT '支付方式',
|
||||
`pay_type` varchar(30) NOT NULL COMMENT '支付类型',
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` datetime NOT NULL,
|
||||
`deleted_at` datetime DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='充值订单表';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_power_logs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_power_logs`;
|
||||
CREATE TABLE `chatgpt_power_logs` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户ID',
|
||||
`username` varchar(30) NOT NULL COMMENT '用户名',
|
||||
`type` tinyint(1) NOT NULL COMMENT '类型(1:充值,2:消费,3:退费)',
|
||||
`amount` smallint NOT NULL COMMENT '算力数值',
|
||||
`balance` int NOT NULL COMMENT '余额',
|
||||
`model` varchar(30) NOT NULL COMMENT '模型',
|
||||
`remark` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '备注',
|
||||
`mark` tinyint(1) NOT NULL COMMENT '资金类型(0:支出,1:收入)',
|
||||
`created_at` datetime NOT NULL COMMENT '创建时间'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户算力消费日志';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_products`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_products`;
|
||||
CREATE TABLE `chatgpt_products` (
|
||||
`id` int NOT NULL,
|
||||
`name` varchar(30) NOT NULL COMMENT '名称',
|
||||
`price` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '价格',
|
||||
`discount` decimal(10,2) NOT NULL DEFAULT '0.00' COMMENT '优惠金额',
|
||||
`days` smallint NOT NULL DEFAULT '0' COMMENT '延长天数',
|
||||
`power` int NOT NULL DEFAULT '0' COMMENT '增加算力值',
|
||||
`enabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否启动',
|
||||
`sales` int NOT NULL DEFAULT '0' COMMENT '销量',
|
||||
`sort_num` tinyint NOT NULL DEFAULT '0' COMMENT '排序',
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` datetime NOT NULL,
|
||||
`app_url` varchar(255) DEFAULT NULL COMMENT 'App跳转地址',
|
||||
`url` varchar(255) DEFAULT NULL COMMENT '跳转地址'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='会员套餐表';
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_products`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_products` (`id`, `name`, `price`, `discount`, `days`, `power`, `enabled`, `sales`, `sort_num`, `created_at`, `updated_at`, `app_url`, `url`) VALUES
|
||||
<<<<<<<< HEAD:database/geekai_plus-v4.1.5.sql
|
||||
(5, '100次点卡', 9.99, 9.88, 0, 100, 1, 19, 0, '2023-08-28 10:55:08', '2024-09-18 16:41:10', NULL, NULL),
|
||||
(6, '200次点卡', 19.90, 15.00, 0, 200, 1, 2, 2, '1970-01-01 08:00:00', '2024-08-05 16:05:46', NULL, NULL);
|
||||
========
|
||||
(5, '100次点卡', 9.99, 9.88, 0, 100, 1, 15, 0, '2023-08-28 10:55:08', '2024-09-18 16:41:10', NULL, NULL),
|
||||
(6, '200次点卡', 19.90, 15.00, 0, 200, 1, 1, 2, '1970-01-01 08:00:00', '2024-08-05 16:05:46', NULL, NULL);
|
||||
>>>>>>>> 5213bdf08ba96a15d550a92e561a29a71a3ac841:deploy/data/mysql/init.d/geekai_plus-v4.1.4.sql
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_redeems`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_redeems`;
|
||||
CREATE TABLE `chatgpt_redeems` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户 ID',
|
||||
`name` varchar(30) NOT NULL COMMENT '兑换码名称',
|
||||
`power` int NOT NULL COMMENT '算力',
|
||||
`code` varchar(100) NOT NULL COMMENT '兑换码',
|
||||
`enabled` tinyint(1) NOT NULL COMMENT '是否启用',
|
||||
`created_at` datetime NOT NULL,
|
||||
`redeemed_at` int NOT NULL COMMENT '兑换时间'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='兑换码';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_sd_jobs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_sd_jobs`;
|
||||
CREATE TABLE `chatgpt_sd_jobs` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户 ID',
|
||||
`type` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT 'txt2img' COMMENT '任务类别',
|
||||
`task_id` char(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '任务 ID',
|
||||
`prompt` varchar(2000) NOT NULL COMMENT '会话提示词',
|
||||
`img_url` varchar(255) DEFAULT NULL COMMENT '图片URL',
|
||||
`params` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci COMMENT '绘画参数json',
|
||||
`progress` smallint DEFAULT '0' COMMENT '任务进度',
|
||||
`publish` tinyint(1) NOT NULL COMMENT '是否发布',
|
||||
`err_msg` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息',
|
||||
`power` smallint NOT NULL DEFAULT '0' COMMENT '消耗算力',
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Stable Diffusion 任务表';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_suno_jobs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_suno_jobs`;
|
||||
CREATE TABLE `chatgpt_suno_jobs` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户 ID',
|
||||
`channel` varchar(100) NOT NULL COMMENT '渠道',
|
||||
`title` varchar(100) DEFAULT NULL COMMENT '歌曲标题',
|
||||
`type` tinyint(1) DEFAULT '0' COMMENT '任务类型,1:灵感创作,2:自定义创作',
|
||||
`task_id` varchar(50) DEFAULT NULL COMMENT '任务 ID',
|
||||
`ref_task_id` char(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '引用任务 ID',
|
||||
`tags` varchar(100) DEFAULT NULL COMMENT '歌曲风格',
|
||||
`instrumental` tinyint(1) DEFAULT '0' COMMENT '是否为纯音乐',
|
||||
`extend_secs` smallint DEFAULT '0' COMMENT '延长秒数',
|
||||
`song_id` varchar(50) DEFAULT NULL COMMENT '要续写的歌曲 ID',
|
||||
`ref_song_id` varchar(50) NOT NULL COMMENT '引用的歌曲ID',
|
||||
`prompt` varchar(2000) NOT NULL COMMENT '提示词',
|
||||
`cover_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '封面图地址',
|
||||
`audio_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '音频地址',
|
||||
`model_name` varchar(30) DEFAULT NULL COMMENT '模型地址',
|
||||
`progress` smallint DEFAULT '0' COMMENT '任务进度',
|
||||
`duration` smallint NOT NULL DEFAULT '0' COMMENT '歌曲时长',
|
||||
`publish` tinyint(1) NOT NULL COMMENT '是否发布',
|
||||
`err_msg` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息',
|
||||
`raw_data` text COMMENT '原始数据',
|
||||
`power` smallint NOT NULL DEFAULT '0' COMMENT '消耗算力',
|
||||
`play_times` int DEFAULT NULL COMMENT '播放次数',
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='MidJourney 任务表';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_users`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_users`;
|
||||
CREATE TABLE `chatgpt_users` (
|
||||
`id` int NOT NULL,
|
||||
`username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名',
|
||||
`mobile` char(11) DEFAULT NULL COMMENT '手机号',
|
||||
`email` varchar(50) DEFAULT NULL COMMENT '邮箱地址',
|
||||
`nickname` varchar(30) NOT NULL COMMENT '昵称',
|
||||
`password` char(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码',
|
||||
`avatar` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '头像',
|
||||
`salt` char(12) NOT NULL COMMENT '密码盐',
|
||||
`power` int NOT NULL DEFAULT '0' COMMENT '剩余算力',
|
||||
`expired_time` int NOT NULL COMMENT '用户过期时间',
|
||||
`status` tinyint(1) NOT NULL COMMENT '当前状态',
|
||||
`chat_config_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聊天配置json',
|
||||
`chat_roles_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '聊天角色 json',
|
||||
`chat_models_json` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'AI模型 json',
|
||||
`last_login_at` int NOT NULL COMMENT '最后登录时间',
|
||||
`vip` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否会员',
|
||||
`last_login_ip` char(16) NOT NULL COMMENT '最后登录 IP',
|
||||
`openid` varchar(100) DEFAULT NULL COMMENT '第三方登录账号ID',
|
||||
`platform` varchar(30) DEFAULT NULL COMMENT '登录平台',
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户表';
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_users`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_users` (`id`, `username`, `mobile`, `email`, `nickname`, `password`, `avatar`, `salt`, `power`, `expired_time`, `status`, `chat_config_json`, `chat_roles_json`, `chat_models_json`, `last_login_at`, `vip`, `last_login_ip`, `openid`, `platform`, `created_at`, `updated_at`) VALUES
|
||||
<<<<<<<< HEAD:database/geekai_plus-v4.1.5.sql
|
||||
(4, '18888888888', '18575670126', '', '极客学长', 'ccc3fb7ab61b8b5d096a4a166ae21d121fc38c71bbd1be6173d9ab973214a63b', 'http://localhost:5678/static/upload/2024/5/1715651569509929.png', 'ueedue5l', 5823, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\",\"programmer\",\"teacher\"]', '[1]', 1727683253, 1, '172.22.11.200', 'oCs0t64FaOLfiTbHZpOqk3aUp_94', NULL, '2023-06-12 16:47:17', '2024-09-30 16:00:53'),
|
||||
========
|
||||
(4, '18888888888', '18575670126', '', '极客学长', 'ccc3fb7ab61b8b5d096a4a166ae21d121fc38c71bbd1be6173d9ab973214a63b', 'http://localhost:5678/static/upload/2024/5/1715651569509929.png', 'ueedue5l', 6178, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\",\"programmer\",\"teacher\"]', '[1]', 1727062461, 1, '::1', 'oCs0t64FaOLfiTbHZpOqk3aUp_94', NULL, '2023-06-12 16:47:17', '2024-09-23 11:34:22'),
|
||||
>>>>>>>> 5213bdf08ba96a15d550a92e561a29a71a3ac841:deploy/data/mysql/init.d/geekai_plus-v4.1.4.sql
|
||||
(42, 'yangjian@pvc123.com', '', 'yangjian@pvc123.com', '极客学长@263103', '672992fe8be51df479b9727cf70ca2ae26bc6a6c0c51ff8f836d3a8748387632', '/images/avatar/user.png', 'ahmgvvgc', 99, 0, 1, '', '[\"gpt\"]', '[1]', 1726133100, 0, '::1', '', '', '2024-09-12 15:08:52', '2024-09-12 17:25:00'),
|
||||
(43, '18575670125', '18575670125', '', '极客学长@394312', '83a5f04d5fea15419c2a324d5fcc8e1f93f62c2e2f5b883307d591ee92234fcc', '/images/avatar/user.png', 'rfml917k', 100, 0, 1, '', '[\"gpt\"]', '[1]', 1726132554, 0, '::1', '', '', '2024-09-12 15:38:38', '2024-09-12 17:15:55'),
|
||||
(44, '13666666666', '13666666666', '', '极客学长@172197', '2c57a40f938d2ee134dffdf0fba6c45907b9bcf1c6decab8f57f034e39b71b26', '/images/avatar/user.png', 'f9wlaiuy', 83, 0, 1, '', '[\"gpt\"]', '[1]', 0, 0, '', '', '', '2024-09-20 11:55:53', '2024-09-20 16:47:31');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_user_login_logs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_user_login_logs`;
|
||||
CREATE TABLE `chatgpt_user_login_logs` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户ID',
|
||||
`username` varchar(30) NOT NULL COMMENT '用户名',
|
||||
`login_ip` char(16) NOT NULL COMMENT '登录IP',
|
||||
`login_address` varchar(30) NOT NULL COMMENT '登录地址',
|
||||
`created_at` datetime NOT NULL,
|
||||
`updated_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户登录日志';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_video_jobs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_video_jobs`;
|
||||
CREATE TABLE `chatgpt_video_jobs` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户 ID',
|
||||
`channel` varchar(100) NOT NULL COMMENT '渠道',
|
||||
`task_id` varchar(100) NOT NULL COMMENT '任务 ID',
|
||||
`type` varchar(20) DEFAULT NULL COMMENT '任务类型,luma,runway,cogvideo',
|
||||
`prompt` varchar(2000) NOT NULL COMMENT '提示词',
|
||||
`prompt_ext` varchar(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '优化后提示词',
|
||||
`cover_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '封面图地址',
|
||||
`video_url` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '视频地址',
|
||||
`water_url` varchar(512) DEFAULT NULL COMMENT '带水印的视频地址',
|
||||
`progress` smallint DEFAULT '0' COMMENT '任务进度',
|
||||
`publish` tinyint(1) NOT NULL COMMENT '是否发布',
|
||||
`err_msg` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '错误信息',
|
||||
`raw_data` text COMMENT '原始数据',
|
||||
`params` varchar(512) DEFAULT NULL COMMENT '参数JSON',
|
||||
`power` smallint NOT NULL DEFAULT '0' COMMENT '消耗算力',
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='MidJourney 任务表';
|
||||
|
||||
--
|
||||
-- 转储表的索引
|
||||
--
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_admin_users`
|
||||
--
|
||||
ALTER TABLE `chatgpt_admin_users`
|
||||
ADD PRIMARY KEY (`id`) USING BTREE,
|
||||
ADD UNIQUE KEY `username` (`username`) USING BTREE;
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_api_keys`
|
||||
--
|
||||
ALTER TABLE `chatgpt_api_keys`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_app_types`
|
||||
--
|
||||
ALTER TABLE `chatgpt_app_types`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_chat_history`
|
||||
--
|
||||
ALTER TABLE `chatgpt_chat_history`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD KEY `chat_id` (`chat_id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_chat_items`
|
||||
--
|
||||
ALTER TABLE `chatgpt_chat_items`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `chat_id` (`chat_id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_chat_models`
|
||||
--
|
||||
ALTER TABLE `chatgpt_chat_models`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_chat_roles`
|
||||
--
|
||||
ALTER TABLE `chatgpt_chat_roles`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `marker` (`marker`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_configs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_configs`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `marker` (`marker`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_dall_jobs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_dall_jobs`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_files`
|
||||
--
|
||||
ALTER TABLE `chatgpt_files`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_functions`
|
||||
--
|
||||
ALTER TABLE `chatgpt_functions`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `name` (`name`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_invite_codes`
|
||||
--
|
||||
ALTER TABLE `chatgpt_invite_codes`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `code` (`code`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_invite_logs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_invite_logs`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_menus`
|
||||
--
|
||||
ALTER TABLE `chatgpt_menus`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_mj_jobs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_mj_jobs`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `task_id` (`task_id`),
|
||||
ADD KEY `message_id` (`message_id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_orders`
|
||||
--
|
||||
ALTER TABLE `chatgpt_orders`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `order_no` (`order_no`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_power_logs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_power_logs`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_products`
|
||||
--
|
||||
ALTER TABLE `chatgpt_products`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_redeems`
|
||||
--
|
||||
ALTER TABLE `chatgpt_redeems`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `code` (`code`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_sd_jobs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_sd_jobs`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `task_id` (`task_id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_suno_jobs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_suno_jobs`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_users`
|
||||
--
|
||||
ALTER TABLE `chatgpt_users`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `username` (`username`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_user_login_logs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_user_login_logs`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_video_jobs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_video_jobs`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 在导出的表使用AUTO_INCREMENT
|
||||
--
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_admin_users`
|
||||
--
|
||||
ALTER TABLE `chatgpt_admin_users`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=113;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_api_keys`
|
||||
--
|
||||
ALTER TABLE `chatgpt_api_keys`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_app_types`
|
||||
--
|
||||
ALTER TABLE `chatgpt_app_types`
|
||||
<<<<<<<< HEAD:database/geekai_plus-v4.1.5.sql
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
========
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;
|
||||
>>>>>>>> 5213bdf08ba96a15d550a92e561a29a71a3ac841:deploy/data/mysql/init.d/geekai_plus-v4.1.4.sql
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_chat_history`
|
||||
--
|
||||
ALTER TABLE `chatgpt_chat_history`
|
||||
MODIFY `id` bigint NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_chat_items`
|
||||
--
|
||||
ALTER TABLE `chatgpt_chat_items`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_chat_models`
|
||||
--
|
||||
ALTER TABLE `chatgpt_chat_models`
|
||||
<<<<<<<< HEAD:database/geekai_plus-v4.1.5.sql
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=52;
|
||||
========
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=51;
|
||||
>>>>>>>> 5213bdf08ba96a15d550a92e561a29a71a3ac841:deploy/data/mysql/init.d/geekai_plus-v4.1.4.sql
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_chat_roles`
|
||||
--
|
||||
ALTER TABLE `chatgpt_chat_roles`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=133;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_configs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_configs`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_dall_jobs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_dall_jobs`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_files`
|
||||
--
|
||||
ALTER TABLE `chatgpt_files`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_functions`
|
||||
--
|
||||
ALTER TABLE `chatgpt_functions`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_invite_codes`
|
||||
--
|
||||
ALTER TABLE `chatgpt_invite_codes`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_invite_logs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_invite_logs`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_menus`
|
||||
--
|
||||
ALTER TABLE `chatgpt_menus`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=21;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_mj_jobs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_mj_jobs`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_orders`
|
||||
--
|
||||
ALTER TABLE `chatgpt_orders`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_power_logs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_power_logs`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_products`
|
||||
--
|
||||
ALTER TABLE `chatgpt_products`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=7;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_redeems`
|
||||
--
|
||||
ALTER TABLE `chatgpt_redeems`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_sd_jobs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_sd_jobs`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_suno_jobs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_suno_jobs`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_users`
|
||||
--
|
||||
ALTER TABLE `chatgpt_users`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=45;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_user_login_logs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_user_login_logs`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_video_jobs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_video_jobs`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
COMMIT;
|
||||
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
|
||||
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
|
1
database/update-v4.1.5.sql
Normal file
1
database/update-v4.1.5.sql
Normal file
@ -0,0 +1 @@
|
||||
ALTER TABLE `chatgpt_power_logs` CHANGE `remark` `remark` VARCHAR(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '备注';
|
@ -30,3 +30,15 @@
|
||||
# localhost which is more compatible and is not less secure.
|
||||
bind-address = 0.0.0.0
|
||||
mysqlx-bind-address = 0.0.0.0
|
||||
|
||||
performance_schema_max_table_instances=400
|
||||
# 缓存
|
||||
table_definition_cache=400
|
||||
# 关闭监控
|
||||
performance_schema=off
|
||||
# 打开表的缓存
|
||||
table_open_cache=64
|
||||
# InnoDB缓冲池大小调整操作的块大小
|
||||
innodb_buffer_pool_chunk_size=64M
|
||||
# InnoDB 存储引擎的表数据和索引数据的最大内存缓冲区大小
|
||||
innodb_buffer_pool_size=64M
|
||||
|
@ -3,7 +3,7 @@
|
||||
-- https://www.phpmyadmin.net/
|
||||
--
|
||||
-- 主机: 127.0.0.1
|
||||
-- 生成日期: 2024-09-23 14:54:46
|
||||
-- 生成日期: 2024-09-30 17:03:37
|
||||
-- 服务器版本: 8.0.33
|
||||
-- PHP 版本: 8.1.2-1ubuntu2.18
|
||||
|
||||
@ -85,17 +85,6 @@ CREATE TABLE `chatgpt_app_types` (
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='应用分类表';
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_app_types`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_app_types` (`id`, `name`, `icon`, `sort_num`, `enabled`, `created_at`) VALUES
|
||||
(3, '通用工具', 'http://172.22.11.200:5678/static/upload/2024/9/1726307371871693.png', 1, 1, '2024-09-13 11:13:15'),
|
||||
(4, '角色扮演', 'http://172.22.11.200:5678/static/upload/2024/9/1726307263906218.png', 1, 1, '2024-09-14 09:28:17'),
|
||||
(5, '学习', 'http://172.22.11.200:5678/static/upload/2024/9/1726307456321179.jpg', 2, 1, '2024-09-14 09:30:18'),
|
||||
(6, '编程', 'http://172.22.11.200:5678/static/upload/2024/9/1726307462748787.jpg', 3, 1, '2024-09-14 09:34:06'),
|
||||
(7, '测试分类', '', 4, 1, '2024-09-14 17:54:17');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
@ -168,9 +157,9 @@ CREATE TABLE `chatgpt_chat_models` (
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_chat_models` (`id`, `name`, `value`, `sort_num`, `enabled`, `power`, `temperature`, `max_tokens`, `max_context`, `open`, `key_id`, `created_at`, `updated_at`) VALUES
|
||||
(1, 'gpt-4o-mini', 'gpt-4o-mini', 1, 1, 1, 1.0, 1024, 16384, 1, 73, '2023-08-23 12:06:36', '2024-09-13 18:00:42'),
|
||||
(1, 'gpt-4o-mini', 'gpt-4o-mini', 1, 1, 1, 1.0, 1024, 16384, 1, 66, '2023-08-23 12:06:36', '2024-09-29 19:08:42'),
|
||||
(15, 'GPT-超级模型', 'gpt-4-all', 4, 1, 30, 1.0, 4096, 32768, 1, 0, '2024-01-15 11:32:52', '2024-09-13 18:01:08'),
|
||||
(36, 'GPT-4O', 'gpt-4o', 3, 1, 15, 1.0, 4096, 16384, 1, 73, '2024-05-14 09:25:15', '2024-09-20 15:38:42'),
|
||||
(36, 'GPT-4O', 'gpt-4o', 3, 1, 15, 1.0, 4096, 16384, 1, 66, '2024-05-14 09:25:15', '2024-09-29 19:08:53'),
|
||||
(39, 'Claude35-snonet', 'claude-3-5-sonnet-20240620', 5, 1, 2, 1.0, 4000, 200000, 1, 0, '2024-05-29 15:04:19', '2024-09-14 18:07:25'),
|
||||
(41, '通义千问', 'qwen-turbo', 7, 1, 2, 1.0, 1024, 8192, 1, 44, '2024-06-06 11:40:46', '2024-08-06 10:51:37'),
|
||||
(42, 'DeekSeek', 'deepseek-chat', 8, 1, 1, 1.0, 4096, 32768, 1, 0, '2024-06-27 16:13:01', '2024-08-05 16:05:33'),
|
||||
@ -178,7 +167,8 @@ INSERT INTO `chatgpt_chat_models` (`id`, `name`, `value`, `sort_num`, `enabled`,
|
||||
(46, 'gpt-3.5-turbo', 'gpt-3.5-turbo', 2, 1, 1, 1.0, 1024, 4096, 1, 73, '2024-07-22 13:53:41', '2024-09-13 18:00:47'),
|
||||
(48, '彩票助手', 'gpt-4-gizmo-g-wmSivBgxo', 8, 1, 1, 0.9, 1024, 8192, 1, 57, '2024-09-05 14:17:14', '2024-09-05 14:17:14'),
|
||||
(49, 'O1-mini', 'o1-mini', 9, 1, 2, 0.9, 1024, 8192, 1, 57, '2024-09-13 18:07:50', '2024-09-14 11:13:19'),
|
||||
(50, 'O1-preview', 'o1-preview', 10, 1, 5, 0.9, 1024, 8192, 1, 57, '2024-09-13 18:11:08', '2024-09-14 11:05:16');
|
||||
(50, 'O1-preview', 'o1-preview', 10, 1, 5, 0.9, 1024, 8192, 1, 57, '2024-09-13 18:11:08', '2024-09-14 11:05:16'),
|
||||
(51, 'O1-mini-all', 'o1-mini-all', 11, 1, 1, 0.9, 1024, 8192, 1, 57, '2024-09-29 11:40:52', '2024-09-29 11:40:52');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
@ -244,7 +234,7 @@ CREATE TABLE `chatgpt_configs` (
|
||||
|
||||
INSERT INTO `chatgpt_configs` (`id`, `marker`, `config_json`) VALUES
|
||||
(1, 'system', '{\"title\":\"GeekAI 创作助手\",\"slogan\":\"我辈之人,先干为敬,让每一个人都能用好AI\",\"admin_title\":\"GeekAI 控制台\",\"logo\":\"/images/logo.png\",\"init_power\":100,\"invite_power\":200,\"vip_month_power\":1000,\"register_ways\":[\"username\",\"email\",\"mobile\"],\"enabled_register\":true,\"order_pay_timeout\":600,\"vip_info_text\":\"月度会员,年度会员每月赠送 1000 点算力,赠送算力当月有效当月没有消费完的算力不结余到下个月。 点卡充值的算力长期有效。\",\"default_models\":[1],\"mj_power\":20,\"mj_action_power\":5,\"sd_power\":5,\"dall_power\":10,\"suno_power\":10,\"luma_power\":120,\"wechat_card_url\":\"/images/wx.png\",\"enable_context\":true,\"context_deep\":4,\"sd_neg_prompt\":\"nsfw, paintings,low quality,easynegative,ng_deepnegative ,lowres,bad anatomy,bad hands,bad feet\",\"mj_mode\":\"fast\",\"index_bg_url\":\"color\",\"index_navs\":[1,5,13,19,9,12,6,20,8,10],\"copyright\":\"极客学长 © 2022- 2024 All rights reserved\",\"mark_map_text\":\"# GeekAI 演示站\\n\\n- 完整的开源系统,前端应用和后台管理系统皆可开箱即用。\\n- 基于 Websocket 实现,完美的打字机体验。\\n- 内置了各种预训练好的角色应用,轻松满足你的各种聊天和应用需求。\\n- 支持 OPenAI,Azure,文心一言,讯飞星火,清华 ChatGLM等多个大语言模型。\\n- 支持 MidJourney / Stable Diffusion AI 绘画集成,开箱即用。\\n- 支持使用个人微信二维码作为充值收费的支付渠道,无需企业支付通道。\\n- 已集成支付宝支付功能,微信支付,支持多种会员套餐和点卡购买功能。\\n- 集成插件 API 功能,可结合大语言模型的 function 功能开发各种强大的插件。\",\"enabled_verify\":false,\"email_white_list\":[\"qq.com\",\"163.com\",\"gmail.com\",\"hotmail.com\",\"126.com\",\"outlook.com\",\"foxmail.com\",\"yahoo.com\"]}'),
|
||||
(3, 'notice', '{\"sd_neg_prompt\":\"\",\"mj_mode\":\"\",\"index_bg_url\":\"\",\"index_navs\":null,\"copyright\":\"\",\"mark_map_text\":\"\",\"enabled_verify\":false,\"email_white_list\":null,\"content\":\"## v4.1.4 更新日志\\n\\n* 功能优化:用户文件列表组件增加分页功能支持\\n* Bug修复:修复用户注册失败Bug,注册操作只弹出一次行为验证码\\n* 功能优化:首次登录不需要验证码,直接登录,登录失败之后才弹出验证码\\n* 功能新增:给 AI 应用(角色)增加分类,前端支持分类筛选\\n* 功能优化:允许用户在聊天页面设置是否使用流式输出或者一次性输出,兼容 GPT-O1 模型。\\n* 功能优化:移除PayJS支付渠道支持,PayJs已经关闭注册服务,请使用其他支付方式。\\n* 功能新增:新增GeeK易支付支付渠道,支持支付宝,微信支付,QQ钱包,京东支付,抖音支付,Paypal支付等支付方式\\n* Bug修复:修复注册页面 tab 组件没有自动选中问题 [#6](https://github.com/yangjian102621/geekai-plus/issues/6)\\n* 功能优化:Luma生成视频任务增加自动翻译功能\\n* Bug修复:Suno 和 Luma 任务没有判断用户算力\\n* 功能新增:邮箱注册增加邮箱后缀白名单,防止使用某些垃圾邮箱注册薅羊毛\\n* 功能优化:清空未支付订单时,只清空超过15分钟未支付的订单\\n\\n注意:当前站点仅为开源项目 \\u003ca style=\\\"color: #F56C6C\\\" href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003eGeekAI-Plus\\u003c/a\\u003e 的演示项目,本项目单纯就是给大家体验项目功能使用。\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n 如果觉得好用你就花几分钟自己部署一套,没有API KEY 的同学可以去下面几个推荐的中转站购买:\\n1、\\u003ca href=\\\"https://api.chat-plus.net\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.chat-plus.net\\u003c/a\\u003e\\n2、\\u003ca href=\\\"https://api.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.geekai.me\\u003c/a\\u003e\\n3、 \\u003ca href=\\\"https://gpt.bemore.lol\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://gpt.bemore.lol\\u003c/a\\u003e\\n支持MidJourney,GPT,Claude,Google Gemmi,以及国内各个厂家的大模型,现在有超级优惠,价格远低于 OpenAI 官方。关于中转 API 的优势和劣势请参考 [中转API技术原理](https://docs.geekai.me/config/chat/#%E4%B8%AD%E8%BD%ACapi%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86)。GPT-3.5,GPT-4,DALL-E3 绘图......你都可以随意使用,无需魔法。\\n接入教程: \\u003ca href=\\\"https://docs.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://docs.geekai.me\\u003c/a\\u003e\\n本项目源码地址:\\u003ca href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003ehttps://github.com/yangjian102621/geekai\\u003c/a\\u003e\",\"updated\":true}');
|
||||
(3, 'notice', '{\"sd_neg_prompt\":\"\",\"mj_mode\":\"\",\"index_bg_url\":\"\",\"index_navs\":null,\"copyright\":\"\",\"mark_map_text\":\"\",\"enabled_verify\":false,\"email_white_list\":null,\"content\":\"## v4.1.5 更新日志\\n\\n* 功能优化:重构 websocket 组件,减少 websocket 连接数,全站共享一个 websocket 连接\\n* Bug修复:兼容手机端原生微信支付和支付宝支付渠道\\n* Bug修复:修复删除绘图任务时候因为字段长度过短导致SQL执行失败问题\\n* 功能优化:优化 Vue 组件通信代码,使用共享数据来替换之前的事件订阅模式,效率更高一些\\n* 功能优化:优化思维导图生成功果页面,优化用户体验\\n\\n注意:当前站点仅为开源项目 \\u003ca style=\\\"color: #F56C6C\\\" href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003eGeekAI-Plus\\u003c/a\\u003e 的演示项目,本项目单纯就是给大家体验项目功能使用。\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n\\u003cstrong style=\\\"color: #F56C6C\\\"\\u003e体验额度用完之后请不要在当前站点进行任何充值操作!!!\\u003c/strong\\u003e\\n 如果觉得好用你就花几分钟自己部署一套,没有API KEY 的同学可以去下面几个推荐的中转站购买:\\n1、\\u003ca href=\\\"https://api.chat-plus.net\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.chat-plus.net\\u003c/a\\u003e\\n2、\\u003ca href=\\\"https://api.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://api.geekai.me\\u003c/a\\u003e\\n支持MidJourney,GPT,Claude,Google Gemmi,以及国内各个厂家的大模型,现在有超级优惠,价格远低于 OpenAI 官方。关于中转 API 的优势和劣势请参考 [中转API技术原理](https://docs.geekai.me/config/chat/#%E4%B8%AD%E8%BD%ACapi%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86)。GPT-3.5,GPT-4,DALL-E3 绘图......你都可以随意使用,无需魔法。\\n接入教程: \\u003ca href=\\\"https://docs.geekai.me\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://docs.geekai.me\\u003c/a\\u003e\\n本项目源码地址:\\u003ca href=\\\"https://github.com/yangjian102621/geekai\\\" target=\\\"_blank\\\"\\u003ehttps://github.com/yangjian102621/geekai\\u003c/a\\u003e\",\"updated\":true}');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
@ -448,7 +438,7 @@ CREATE TABLE `chatgpt_power_logs` (
|
||||
`amount` smallint NOT NULL COMMENT '算力数值',
|
||||
`balance` int NOT NULL COMMENT '余额',
|
||||
`model` varchar(30) NOT NULL COMMENT '模型',
|
||||
`remark` varchar(255) NOT NULL COMMENT '备注',
|
||||
`remark` varchar(512) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '备注',
|
||||
`mark` tinyint(1) NOT NULL COMMENT '资金类型(0:支出,1:收入)',
|
||||
`created_at` datetime NOT NULL COMMENT '创建时间'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户算力消费日志';
|
||||
@ -481,8 +471,8 @@ CREATE TABLE `chatgpt_products` (
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_products` (`id`, `name`, `price`, `discount`, `days`, `power`, `enabled`, `sales`, `sort_num`, `created_at`, `updated_at`, `app_url`, `url`) VALUES
|
||||
(5, '100次点卡', 9.99, 9.88, 0, 100, 1, 15, 0, '2023-08-28 10:55:08', '2024-09-18 16:41:10', NULL, NULL),
|
||||
(6, '200次点卡', 19.90, 15.00, 0, 200, 1, 1, 2, '1970-01-01 08:00:00', '2024-08-05 16:05:46', NULL, NULL);
|
||||
(5, '100次点卡', 9.99, 9.88, 0, 100, 1, 19, 0, '2023-08-28 10:55:08', '2024-09-18 16:41:10', NULL, NULL),
|
||||
(6, '200次点卡', 19.90, 15.00, 0, 200, 1, 2, 2, '1970-01-01 08:00:00', '2024-08-05 16:05:46', NULL, NULL);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
@ -594,7 +584,7 @@ CREATE TABLE `chatgpt_users` (
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_users` (`id`, `username`, `mobile`, `email`, `nickname`, `password`, `avatar`, `salt`, `power`, `expired_time`, `status`, `chat_config_json`, `chat_roles_json`, `chat_models_json`, `last_login_at`, `vip`, `last_login_ip`, `openid`, `platform`, `created_at`, `updated_at`) VALUES
|
||||
(4, '18888888888', '18575670126', '', '极客学长', 'ccc3fb7ab61b8b5d096a4a166ae21d121fc38c71bbd1be6173d9ab973214a63b', 'http://localhost:5678/static/upload/2024/5/1715651569509929.png', 'ueedue5l', 6178, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\",\"programmer\",\"teacher\"]', '[1]', 1727062461, 1, '::1', 'oCs0t64FaOLfiTbHZpOqk3aUp_94', NULL, '2023-06-12 16:47:17', '2024-09-23 11:34:22'),
|
||||
(4, '18888888888', '18575670126', '', '极客学长', 'ccc3fb7ab61b8b5d096a4a166ae21d121fc38c71bbd1be6173d9ab973214a63b', 'http://localhost:5678/static/upload/2024/5/1715651569509929.png', 'ueedue5l', 5823, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\",\"programmer\",\"teacher\"]', '[1]', 1727683253, 1, '172.22.11.200', 'oCs0t64FaOLfiTbHZpOqk3aUp_94', NULL, '2023-06-12 16:47:17', '2024-09-30 16:00:53'),
|
||||
(42, 'yangjian@pvc123.com', '', 'yangjian@pvc123.com', '极客学长@263103', '672992fe8be51df479b9727cf70ca2ae26bc6a6c0c51ff8f836d3a8748387632', '/images/avatar/user.png', 'ahmgvvgc', 99, 0, 1, '', '[\"gpt\"]', '[1]', 1726133100, 0, '::1', '', '', '2024-09-12 15:08:52', '2024-09-12 17:25:00'),
|
||||
(43, '18575670125', '18575670125', '', '极客学长@394312', '83a5f04d5fea15419c2a324d5fcc8e1f93f62c2e2f5b883307d591ee92234fcc', '/images/avatar/user.png', 'rfml917k', 100, 0, 1, '', '[\"gpt\"]', '[1]', 1726132554, 0, '::1', '', '', '2024-09-12 15:38:38', '2024-09-12 17:15:55'),
|
||||
(44, '13666666666', '13666666666', '', '极客学长@172197', '2c57a40f938d2ee134dffdf0fba6c45907b9bcf1c6decab8f57f034e39b71b26', '/images/avatar/user.png', 'f9wlaiuy', 83, 0, 1, '', '[\"gpt\"]', '[1]', 0, 0, '', '', '', '2024-09-20 11:55:53', '2024-09-20 16:47:31');
|
||||
@ -824,7 +814,7 @@ ALTER TABLE `chatgpt_api_keys`
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_app_types`
|
||||
--
|
||||
ALTER TABLE `chatgpt_app_types`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=8;
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_chat_history`
|
||||
@ -842,7 +832,7 @@ ALTER TABLE `chatgpt_chat_items`
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_chat_models`
|
||||
--
|
||||
ALTER TABLE `chatgpt_chat_models`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=51;
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=52;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_chat_roles`
|
@ -15,6 +15,11 @@ services:
|
||||
- ./data/mysql/data:/var/lib/mysql
|
||||
- ./logs/mysql:/var/log/mysql
|
||||
- ./data/mysql/init.d:/docker-entrypoint-initdb.d/
|
||||
healthcheck:
|
||||
test: [ "CMD", "mysqladmin", "ping", "-h", "localhost" ]
|
||||
interval: 5s
|
||||
timeout: 10s
|
||||
retries: 10
|
||||
|
||||
# redis
|
||||
geekai-redis:
|
||||
@ -26,6 +31,11 @@ services:
|
||||
- ./data/redis:/data
|
||||
ports:
|
||||
- "6380:6379"
|
||||
healthcheck:
|
||||
test: [ "CMD", "redis-cli", "ping" ]
|
||||
interval: 3s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
# xxl-job-admin:
|
||||
# container_name: geekai-xxl-job-admin
|
||||
@ -58,12 +68,14 @@ services:
|
||||
|
||||
# 后端 API 程序
|
||||
geekai-api:
|
||||
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:v4.1.4-amd64
|
||||
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:v4.1.5-amd64
|
||||
container_name: geekai-api
|
||||
restart: always
|
||||
depends_on:
|
||||
- geekai-mysql
|
||||
- geekai-redis
|
||||
geekai-mysql:
|
||||
condition: service_healthy
|
||||
geekai-redis:
|
||||
condition: service_healthy
|
||||
environment:
|
||||
- DEBUG=false
|
||||
- LOG_LEVEL=info
|
||||
@ -80,7 +92,7 @@ services:
|
||||
|
||||
# 前端应用
|
||||
geekai-web:
|
||||
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:v4.1.4-amd64
|
||||
image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:v4.1.5-amd64
|
||||
container_name: geekai-web
|
||||
restart: always
|
||||
depends_on:
|
||||
@ -91,4 +103,4 @@ services:
|
||||
- ./logs/nginx:/var/log/nginx
|
||||
- ./conf/nginx/conf.d:/etc/nginx/conf.d
|
||||
- ./conf/nginx/nginx.conf:/etc/nginx/nginx.conf
|
||||
- ./conf/nginx/ssl:/etc/nginx/ssl
|
||||
- ./conf/nginx/ssl:/etc/nginx/ssl
|
@ -6,6 +6,6 @@ VUE_APP_ADMIN_USER=admin
|
||||
VUE_APP_ADMIN_PASS=admin123
|
||||
VUE_APP_KEY_PREFIX=GeekAI_DEV_
|
||||
VUE_APP_TITLE="Geek-AI 创作系统"
|
||||
VUE_APP_VERSION=v4.1.4
|
||||
VUE_APP_VERSION=v4.1.5
|
||||
VUE_APP_DOCS_URL=https://docs.geekai.me
|
||||
VUE_APP_GIT_URL=https://github.com/yangjian102621/geekai
|
||||
|
@ -1,7 +1,7 @@
|
||||
VUE_APP_API_HOST=
|
||||
VUE_APP_WS_HOST=
|
||||
VUE_APP_KEY_PREFIX=GeekAI_
|
||||
VUE_APP_VERSION=v4.1.4
|
||||
VUE_APP_VERSION=v4.1.5
|
||||
VUE_APP_TITLE="Geek-AI 创作系统"
|
||||
VUE_APP_DOCS_URL=https://docs.geekai.me
|
||||
VUE_APP_GIT_URL=https://github.com/yangjian102621/geekai
|
||||
|
@ -27,7 +27,6 @@
|
||||
"markmap-view": "^0.16.0",
|
||||
"md-editor-v3": "^2.2.1",
|
||||
"memfs": "^4.9.3",
|
||||
"mitt": "^3.0.1",
|
||||
"pinia": "^2.1.4",
|
||||
"qrcode": "^1.5.3",
|
||||
"qs": "^6.11.1",
|
||||
|
BIN
web/public/images/img-holder.png
Normal file
BIN
web/public/images/img-holder.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
@ -6,10 +6,14 @@
|
||||
|
||||
<script setup>
|
||||
import {ElConfigProvider} from 'element-plus';
|
||||
import {onMounted} from "vue";
|
||||
import {getSystemInfo} from "@/store/cache";
|
||||
import {onMounted, ref, watch} from "vue";
|
||||
import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import {isChrome, isMobile} from "@/utils/libs";
|
||||
import {showMessageInfo} from "@/utils/dialog";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import {getUserToken} from "@/store/session";
|
||||
import {router} from "@/router";
|
||||
import {onBeforeRouteLeave, onBeforeRouteUpdate} from "vue-router";
|
||||
|
||||
const debounce = (fn, delay) => {
|
||||
let timer
|
||||
@ -31,7 +35,9 @@ window.ResizeObserver = class ResizeObserver extends _ResizeObserver {
|
||||
}
|
||||
}
|
||||
|
||||
const store = useSharedStore()
|
||||
onMounted(() => {
|
||||
// 获取系统参数
|
||||
getSystemInfo().then((res) => {
|
||||
const link = document.createElement('link')
|
||||
link.rel = 'shortcut icon'
|
||||
@ -39,9 +45,48 @@ onMounted(() => {
|
||||
document.head.appendChild(link)
|
||||
})
|
||||
if (!isChrome() && !isMobile()) {
|
||||
showMessageInfo("检测到您使用的浏览器不是 Chrome,可能会导致部分功能无法正常使用,建议使用 Chrome 浏览器。")
|
||||
showMessageInfo("建议使用 Chrome 浏览器以获得最佳体验。")
|
||||
}
|
||||
|
||||
checkSession().then(() => {
|
||||
store.setIsLogin(true)
|
||||
}).catch(()=>{})
|
||||
})
|
||||
|
||||
watch(() => store.isLogin, (val) => {
|
||||
if (val) {
|
||||
connect()
|
||||
}
|
||||
})
|
||||
|
||||
const handler = ref(0)
|
||||
// 初始化 websocket 连接
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
const clientId = getClientId()
|
||||
const _socket = new WebSocket(host + `/api/ws?client_id=${clientId}&token=${getUserToken()}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
console.log('WebSocket 已连接')
|
||||
handler.value = setInterval(() => {
|
||||
if (_socket.readyState === WebSocket.OPEN) {
|
||||
_socket.send(JSON.stringify({"type":"ping"}))
|
||||
}
|
||||
},5000)
|
||||
})
|
||||
_socket.addEventListener('close', () => {
|
||||
clearInterval(handler.value)
|
||||
connect()
|
||||
});
|
||||
store.setSocket(_socket)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
|
@ -191,7 +191,25 @@
|
||||
}
|
||||
}
|
||||
|
||||
.failed {
|
||||
display flex
|
||||
flex-flow column
|
||||
justify-content center
|
||||
|
||||
.title {
|
||||
margin-bottom 20px
|
||||
text-align center
|
||||
color #ee0a24
|
||||
font-size 18px
|
||||
}
|
||||
.opt {
|
||||
display flex
|
||||
justify-content center
|
||||
.van-button {
|
||||
margin 0 5px
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -179,6 +179,26 @@
|
||||
}
|
||||
|
||||
|
||||
.failed {
|
||||
display flex
|
||||
flex-flow column
|
||||
justify-content center
|
||||
|
||||
.title {
|
||||
margin-bottom 20px
|
||||
text-align center
|
||||
color #ee0a24
|
||||
font-size 18px
|
||||
}
|
||||
.opt {
|
||||
display flex
|
||||
justify-content center
|
||||
.van-button {
|
||||
margin 0 5px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -129,6 +129,7 @@ const removeFile = (file) => {
|
||||
return v1.id === v2.id
|
||||
})
|
||||
ElMessage.success("文件删除成功!")
|
||||
fetchFiles(1)
|
||||
}).catch((e) => {
|
||||
ElMessage.error('文件删除失败:' + e.message)
|
||||
})
|
||||
|
@ -251,6 +251,7 @@ import Captcha from "@/components/Captcha.vue";
|
||||
import ResetPass from "@/components/ResetPass.vue";
|
||||
import {setRoute} from "@/store/system";
|
||||
import {useRouter} from "vue-router";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const props = defineProps({
|
||||
@ -285,6 +286,7 @@ const action = ref("login")
|
||||
const enableVerify = ref(false)
|
||||
const showResetPass = ref(false)
|
||||
const router = useRouter()
|
||||
const store = useSharedStore()
|
||||
// 是否需要验证码,输入一次密码错之后就要验证码
|
||||
const needVerify = ref(false)
|
||||
|
||||
@ -354,6 +356,7 @@ const doLogin = (verifyData) => {
|
||||
data.value.x = verifyData.x
|
||||
httpPost('/api/user/login', data.value).then((res) => {
|
||||
setUserToken(res.data.token)
|
||||
store.setIsLogin(true)
|
||||
ElMessage.success("登录成功!")
|
||||
emits("hide")
|
||||
emits('success')
|
||||
|
@ -1,28 +1,14 @@
|
||||
<template>
|
||||
<div class="running-job-list">
|
||||
<div class="running-job-box" v-if="list.length > 0">
|
||||
<div class="job-item" v-for="item in list">
|
||||
<div class="job-item" v-for="item in list" :key="item.id">
|
||||
<div v-if="item.progress > 0" class="job-item-inner">
|
||||
<el-image v-if="item.img_url" :src="item['img_url']" fit="cover" loading="lazy">
|
||||
<template #placeholder>
|
||||
<div class="image-slot">
|
||||
正在加载图片
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #error>
|
||||
<div class="image-slot">
|
||||
<el-icon>
|
||||
<Picture/>
|
||||
</el-icon>
|
||||
</div>
|
||||
</template>
|
||||
</el-image>
|
||||
<div class="progress" v-if="item.progress > 0">
|
||||
<el-progress type="circle" :percentage="item.progress" :width="100"
|
||||
color="#47fff1"/>
|
||||
</div>
|
||||
|
||||
<div class="progress">
|
||||
<el-progress type="circle" :percentage="item.progress" :width="100"
|
||||
color="#47fff1"/>
|
||||
</div>
|
||||
</div>
|
||||
<el-image fit="cover" v-else>
|
||||
<template #error>
|
||||
@ -39,11 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import {CircleCloseFilled, Picture} from "@element-plus/icons-vue";
|
||||
import {isImage, removeArrayItem, substr} from "@/utils/libs";
|
||||
import {FormatFileSize, GetFileIcon, GetFileType} from "@/store/system";
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const props = defineProps({
|
||||
list: {
|
||||
type: Array,
|
||||
|
@ -52,13 +52,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import {computed, onMounted, ref} from 'vue';
|
||||
import {onMounted, ref, watch} from 'vue';
|
||||
import {getMenuItems, useSidebarStore} from '@/store/sidebar';
|
||||
import {useRouter} from "vue-router";
|
||||
import {ArrowDown, ArrowRight, Expand, Fold, Moon, Sunny} from "@element-plus/icons-vue";
|
||||
import {httpGet} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {removeAdminToken} from "@/store/session";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const version = ref(process.env.VUE_APP_VERSION)
|
||||
const avatar = ref('/images/user-info.jpg')
|
||||
@ -66,24 +67,18 @@ const sidebar = useSidebarStore();
|
||||
const router = useRouter();
|
||||
const breadcrumb = ref([])
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const props = defineProps({
|
||||
theme: String,
|
||||
});
|
||||
|
||||
const theme = computed(() => {
|
||||
return props.theme
|
||||
const store = useSharedStore()
|
||||
const dark = ref(store.adminTheme === 'dark')
|
||||
const theme = ref(store.adminTheme)
|
||||
watch(() => store.adminTheme, (val) => {
|
||||
theme.value = val
|
||||
})
|
||||
const dark = ref(props.theme === 'dark' ? true : false)
|
||||
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const emits = defineEmits(['changeTheme']);
|
||||
const changeTheme = () => {
|
||||
emits('changeTheme', dark.value)
|
||||
store.setAdminTheme(dark.value ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
router.afterEach((to, from) => {
|
||||
router.afterEach((to) => {
|
||||
initBreadCrumb(to.path)
|
||||
});
|
||||
|
||||
|
@ -52,11 +52,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {computed, ref} from 'vue';
|
||||
import {computed, ref, watch} from 'vue';
|
||||
import {setMenuItems, useSidebarStore} from '@/store/sidebar';
|
||||
import {httpGet} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {useRoute} from "vue-router";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const title = ref('')
|
||||
const logo = ref('')
|
||||
@ -68,16 +69,11 @@ httpGet('/api/admin/config/get?key=system').then(res => {
|
||||
}).catch(e => {
|
||||
ElMessage.error("加载系统配置失败: " + e.message)
|
||||
})
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const props = defineProps({
|
||||
theme: String,
|
||||
});
|
||||
|
||||
const theme = computed(() => {
|
||||
return props.theme
|
||||
const store = useSharedStore()
|
||||
const theme = ref(store.adminTheme)
|
||||
watch(() => store.adminTheme, (val) => {
|
||||
theme.value = val
|
||||
})
|
||||
|
||||
const items = [
|
||||
{
|
||||
icon: 'home',
|
||||
|
@ -38,17 +38,14 @@ import {onBeforeRouteUpdate, useRoute, useRouter} from 'vue-router';
|
||||
import {ArrowDown, Close} from "@element-plus/icons-vue";
|
||||
import {checkAdminSession} from "@/store/cache";
|
||||
import {ElMessageBox} from "element-plus";
|
||||
import {computed} from "vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import {ref, watch} from "vue";
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const props = defineProps({
|
||||
theme: String,
|
||||
});
|
||||
|
||||
const theme = computed(() => {
|
||||
return props.theme
|
||||
const store = useSharedStore()
|
||||
const theme = ref(store.adminTheme)
|
||||
watch(() => store.adminTheme, (val) => {
|
||||
theme.value = val
|
||||
})
|
||||
|
||||
const router = useRouter();
|
||||
checkAdminSession().catch(() => {
|
||||
ElMessageBox({
|
||||
|
@ -249,6 +249,18 @@ const routes = [
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
name: 'mobile-login',
|
||||
path: '/mobile/login',
|
||||
meta: {title: '用户登录'},
|
||||
component: () => import('@/views/Login.vue'),
|
||||
},
|
||||
{
|
||||
name: 'mobile-register',
|
||||
path: '/mobile/register',
|
||||
meta: {title: '用户注册'},
|
||||
component: () => import('@/views/Register.vue'),
|
||||
},
|
||||
{
|
||||
name: 'mobile',
|
||||
path: '/mobile',
|
||||
|
@ -1,5 +1,6 @@
|
||||
import {httpGet} from "@/utils/http";
|
||||
import Storage from "good-storage";
|
||||
import {randString} from "@/utils/libs";
|
||||
|
||||
const userDataKey = "USER_INFO_CACHE_KEY"
|
||||
const adminDataKey = "ADMIN_INFO_CACHE_KEY"
|
||||
@ -70,4 +71,14 @@ export function getLicenseInfo() {
|
||||
resolve(err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
export function getClientId() {
|
||||
let clientId = Storage.get('client_id')
|
||||
if (clientId) {
|
||||
return clientId
|
||||
}
|
||||
clientId = randString(42)
|
||||
Storage.set('client_id', clientId)
|
||||
return clientId
|
||||
}
|
@ -1,6 +0,0 @@
|
||||
// 导入mitt包
|
||||
import mitt from 'mitt'
|
||||
// 创建EventBus实例对象
|
||||
const bus = mitt()
|
||||
// 共享出eventbus的实例对象
|
||||
export default bus
|
@ -16,7 +16,6 @@ export function getSessionId() {
|
||||
export function getUserToken() {
|
||||
return Storage.get(UserTokenKey) ?? ""
|
||||
}
|
||||
|
||||
export function setUserToken(token) {
|
||||
// 刷新 session 缓存
|
||||
Storage.set(UserTokenKey, token)
|
||||
|
@ -6,6 +6,10 @@ export const useSharedStore = defineStore('shared', {
|
||||
showLoginDialog: false,
|
||||
chatListStyle: Storage.get("chat_list_style","chat"),
|
||||
chatStream: Storage.get("chat_stream",true),
|
||||
socket: {conn:null, handlers:{}},
|
||||
mobileTheme: Storage.get("mobile_theme", "light"),
|
||||
adminTheme: Storage.get("admin_theme", "light"),
|
||||
isLogin: false
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
@ -19,6 +23,55 @@ export const useSharedStore = defineStore('shared', {
|
||||
setChatStream(value) {
|
||||
this.chatStream = value;
|
||||
Storage.set("chat_stream", value);
|
||||
},
|
||||
setSocket(value) {
|
||||
for (const key in this.socket.handlers) {
|
||||
this.setMessageHandler(value, this.socket.handlers[key])
|
||||
}
|
||||
this.socket.conn = value
|
||||
},
|
||||
addMessageHandler(key, callback) {
|
||||
if (!this.socket.handlers[key]) {
|
||||
this.socket.handlers[key] = callback;
|
||||
}
|
||||
this.setMessageHandler(this.socket.conn, callback)
|
||||
},
|
||||
|
||||
setMessageHandler(conn, callback) {
|
||||
if (!conn) {
|
||||
return
|
||||
}
|
||||
conn.addEventListener('message', (event) => {
|
||||
try {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8");
|
||||
reader.onload = () => {
|
||||
callback(JSON.parse(String(reader.result)))
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
removeMessageHandler(key) {
|
||||
if (this.socket.conn && this.socket.conn.readyState === WebSocket.OPEN) {
|
||||
this.socket.conn.removeEventListener('message', this.socket.handlers[key])
|
||||
}
|
||||
delete this.socket.handlers[key]
|
||||
},
|
||||
setMobileTheme(theme) {
|
||||
this.mobileTheme = theme
|
||||
Storage.set("mobile_theme", theme)
|
||||
},
|
||||
setAdminTheme(theme) {
|
||||
this.adminTheme = theme
|
||||
Storage.set("admin_theme", theme)
|
||||
},
|
||||
setIsLogin(value) {
|
||||
this.isLogin = value
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
@ -6,26 +6,6 @@
|
||||
// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
import Storage from "good-storage";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
const MOBILE_THEME = process.env.VUE_APP_KEY_PREFIX + "MOBILE_THEME"
|
||||
const ADMIN_THEME = process.env.VUE_APP_KEY_PREFIX + "ADMIN_THEME"
|
||||
|
||||
export function getMobileTheme() {
|
||||
return Storage.get(MOBILE_THEME) ? Storage.get(MOBILE_THEME) : 'light'
|
||||
}
|
||||
|
||||
export function setMobileTheme(theme) {
|
||||
Storage.set(MOBILE_THEME, theme)
|
||||
}
|
||||
|
||||
export function getAdminTheme() {
|
||||
return Storage.get(ADMIN_THEME) ? Storage.get(ADMIN_THEME) : 'light'
|
||||
}
|
||||
|
||||
export function setAdminTheme(theme) {
|
||||
Storage.set(ADMIN_THEME, theme)
|
||||
}
|
||||
|
||||
export function GetFileIcon(ext) {
|
||||
const files = {
|
||||
@ -71,4 +51,4 @@ export function setRoute(path) {
|
||||
|
||||
export function getRoute() {
|
||||
return Storage.get(process.env.VUE_APP_KEY_PREFIX + 'ROUTE_')
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,6 @@ axios.defaults.headers.post['Content-Type'] = 'application/json'
|
||||
axios.interceptors.request.use(
|
||||
config => {
|
||||
// set token
|
||||
config.headers['Chat-Token'] = getSessionId();
|
||||
config.headers['Authorization'] = getUserToken();
|
||||
config.headers['Admin-Authorization'] = getAdminToken();
|
||||
return config
|
||||
@ -32,6 +31,7 @@ axios.interceptors.response.use(
|
||||
if (error.response.request.responseURL.indexOf("/api/admin") !== -1) {
|
||||
removeAdminToken()
|
||||
} else {
|
||||
console.log("FUCK")
|
||||
removeUserToken()
|
||||
}
|
||||
error.response.data.message = "请先登录"
|
||||
|
@ -218,7 +218,7 @@ export function showLoginDialog(router) {
|
||||
message:
|
||||
'此操作需要登录才能进行,前往登录?',
|
||||
}).then(() => {
|
||||
router.push("/login")
|
||||
router.push("/mobile/login")
|
||||
}).catch(() => {
|
||||
// on cancel
|
||||
});
|
||||
|
@ -225,11 +225,10 @@ import {
|
||||
UUID
|
||||
} from "@/utils/libs";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {getSessionId, getUserToken} from "@/store/session";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {useRouter} from "vue-router";
|
||||
import Clipboard from "clipboard";
|
||||
import {checkSession, getSystemInfo} from "@/store/cache";
|
||||
import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import Welcome from "@/components/Welcome.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import FileSelect from "@/components/FileSelect.vue";
|
||||
@ -270,13 +269,15 @@ watch(() => store.chatListStyle, (newValue) => {
|
||||
});
|
||||
const tools = ref([])
|
||||
const toolSelected = ref([])
|
||||
const loadHistory = ref(false)
|
||||
const stream = ref(store.chatStream)
|
||||
|
||||
watch(() => store.chatStream, (newValue) => {
|
||||
stream.value = newValue
|
||||
});
|
||||
|
||||
if (isMobile()) {
|
||||
router.push('/mobile/chat')
|
||||
}
|
||||
|
||||
// 初始化角色ID参数
|
||||
if (router.currentRoute.value.query.role_id) {
|
||||
@ -296,10 +297,6 @@ if (!chatId.value) {
|
||||
})
|
||||
}
|
||||
|
||||
if (isMobile()) {
|
||||
router.replace("/mobile/chat")
|
||||
}
|
||||
|
||||
// 获取系统配置
|
||||
getSystemInfo().then(res => {
|
||||
title.value = res.data.title
|
||||
@ -337,6 +334,13 @@ httpGet("/api/function/list").then(res => {
|
||||
showMessageError("获取工具函数失败:" + e.message)
|
||||
})
|
||||
|
||||
// 创建 socket 连接
|
||||
const prompt = ref('');
|
||||
const showStopGenerate = ref(false); // 停止生成
|
||||
const lineBuffer = ref(''); // 输出缓冲行
|
||||
const canSend = ref(true);
|
||||
const isNewMsg = ref(true)
|
||||
|
||||
onMounted(() => {
|
||||
resizeElement();
|
||||
initData()
|
||||
@ -351,14 +355,76 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
window.onresize = () => resizeElement();
|
||||
store.addMessageHandler("chat", (data) => {
|
||||
// 丢去非本频道和本客户端的消息
|
||||
if (data.channel !== 'chat' || data.clientId !== getClientId()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (data.type === 'error') {
|
||||
ElMessage.error(data.body)
|
||||
return
|
||||
}
|
||||
|
||||
const chatRole = getRoleById(roleId.value)
|
||||
if (isNewMsg.value && data.type !== 'end') {
|
||||
const prePrompt = chatData.value[chatData.value.length-1]?.content
|
||||
chatData.value.push({
|
||||
type: "reply",
|
||||
id: randString(32),
|
||||
icon: chatRole['icon'],
|
||||
prompt:prePrompt,
|
||||
content: data.body,
|
||||
});
|
||||
isNewMsg.value = false
|
||||
lineBuffer.value = data.body;
|
||||
} else if (data.type === 'end') { // 消息接收完毕
|
||||
// 追加当前会话到会话列表
|
||||
if (newChatItem.value !== null) {
|
||||
newChatItem.value['title'] = tmpChatTitle.value;
|
||||
newChatItem.value['chat_id'] = chatId.value;
|
||||
chatList.value.unshift(newChatItem.value);
|
||||
newChatItem.value = null; // 只追加一次
|
||||
}
|
||||
|
||||
enableInput()
|
||||
lineBuffer.value = ''; // 清空缓冲
|
||||
|
||||
// 获取 token
|
||||
const reply = chatData.value[chatData.value.length - 1]
|
||||
httpPost("/api/chat/tokens", {
|
||||
text: "",
|
||||
model: getModelValue(modelID.value),
|
||||
chat_id: chatId.value,
|
||||
}).then(res => {
|
||||
reply['created_at'] = new Date().getTime();
|
||||
reply['tokens'] = res.data;
|
||||
// 将聊天框的滚动条滑动到最底部
|
||||
nextTick(() => {
|
||||
document.getElementById('chat-box').scrollTo(0, document.getElementById('chat-box').scrollHeight)
|
||||
})
|
||||
}).catch(() => {
|
||||
})
|
||||
isNewMsg.value = true
|
||||
|
||||
} else if (data.type === 'text') {
|
||||
lineBuffer.value += data.body;
|
||||
const reply = chatData.value[chatData.value.length - 1]
|
||||
if (reply) {
|
||||
reply['content'] = lineBuffer.value;
|
||||
}
|
||||
}
|
||||
// 将聊天框的滚动条滑动到最底部
|
||||
nextTick(() => {
|
||||
document.getElementById('chat-box').scrollTo(0, document.getElementById('chat-box').scrollHeight)
|
||||
localStorage.setItem("chat_id", chatId.value)
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
if (socket.value !== null) {
|
||||
socket.value = null
|
||||
}
|
||||
store.removeMessageHandler("chat")
|
||||
})
|
||||
|
||||
// 初始化数据
|
||||
const initData = () => {
|
||||
|
||||
@ -379,9 +445,8 @@ const initData = () => {
|
||||
checkSession().then((user) => {
|
||||
loginUser.value = user
|
||||
isLogin.value = true
|
||||
|
||||
newChat();
|
||||
}).catch(e => {})
|
||||
}).catch(() => {})
|
||||
|
||||
}).catch((e) => {
|
||||
ElMessage.error('获取聊天角色失败: ' + e.messages)
|
||||
@ -403,13 +468,9 @@ const initData = () => {
|
||||
// 允许在输入框粘贴文件
|
||||
inputRef.value.addEventListener('paste', (event) => {
|
||||
const items = (event.clipboardData || window.clipboardData).items;
|
||||
let fileFound = false;
|
||||
|
||||
for (let item of items) {
|
||||
if (item.kind === 'file') {
|
||||
const file = item.getAsFile();
|
||||
fileFound = true;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
loading.value = true
|
||||
@ -426,10 +487,6 @@ const initData = () => {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fileFound) {
|
||||
document.getElementById('status').innerText = 'No file found in paste data.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -492,9 +549,8 @@ const newChat = () => {
|
||||
removing: false,
|
||||
};
|
||||
showStopGenerate.value = false;
|
||||
loadChatHistory(chatId.value)
|
||||
router.push(`/chat/${chatId.value}`)
|
||||
loadHistory.value = true
|
||||
connect()
|
||||
}
|
||||
|
||||
// 切换会话
|
||||
@ -507,14 +563,12 @@ const loadChat = function (chat) {
|
||||
if (chatId.value === chat.chat_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
newChatItem.value = null;
|
||||
roleId.value = chat.role_id;
|
||||
modelID.value = chat.model_id;
|
||||
chatId.value = chat.chat_id;
|
||||
showStopGenerate.value = false;
|
||||
loadHistory.value = true
|
||||
connect()
|
||||
loadChatHistory(chatId.value)
|
||||
router.replace(`/chat/${chatId.value}`)
|
||||
}
|
||||
|
||||
@ -587,118 +641,6 @@ const removeChat = function (chat) {
|
||||
|
||||
}
|
||||
|
||||
// 创建 socket 连接
|
||||
const prompt = ref('');
|
||||
const showStopGenerate = ref(false); // 停止生成
|
||||
const lineBuffer = ref(''); // 输出缓冲行
|
||||
const socket = ref(null);
|
||||
const canSend = ref(true);
|
||||
const sessionId = ref("")
|
||||
const isNewMsg = ref(true)
|
||||
const connect = function () {
|
||||
const chatRole = getRoleById(roleId.value);
|
||||
// 初始化 WebSocket 对象
|
||||
sessionId.value = getSessionId();
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
const _socket = new WebSocket(host + `/api/chat/new?session_id=${sessionId.value}&role_id=${roleId.value}&chat_id=${chatId.value}&model_id=${modelID.value}&token=${getUserToken()}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
enableInput()
|
||||
if (loadHistory.value) {
|
||||
loadChatHistory(chatId.value)
|
||||
}
|
||||
loading.value = false
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
try {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8");
|
||||
reader.onload = () => {
|
||||
const data = JSON.parse(String(reader.result));
|
||||
if (data.type === 'error') {
|
||||
ElMessage.error(data.message)
|
||||
return
|
||||
}
|
||||
|
||||
if (isNewMsg.value && data.type !== 'end') {
|
||||
const prePrompt = chatData.value[chatData.value.length-1]?.content
|
||||
chatData.value.push({
|
||||
type: "reply",
|
||||
id: randString(32),
|
||||
icon: chatRole['icon'],
|
||||
prompt:prePrompt,
|
||||
content: data.content,
|
||||
});
|
||||
isNewMsg.value = false
|
||||
lineBuffer.value = data.content;
|
||||
} else if (data.type === 'end') { // 消息接收完毕
|
||||
// 追加当前会话到会话列表
|
||||
if (newChatItem.value !== null) {
|
||||
newChatItem.value['title'] = tmpChatTitle.value;
|
||||
newChatItem.value['chat_id'] = chatId.value;
|
||||
chatList.value.unshift(newChatItem.value);
|
||||
newChatItem.value = null; // 只追加一次
|
||||
}
|
||||
|
||||
enableInput()
|
||||
lineBuffer.value = ''; // 清空缓冲
|
||||
|
||||
// 获取 token
|
||||
const reply = chatData.value[chatData.value.length - 1]
|
||||
httpPost("/api/chat/tokens", {
|
||||
text: "",
|
||||
model: getModelValue(modelID.value),
|
||||
chat_id: chatId.value,
|
||||
}).then(res => {
|
||||
reply['created_at'] = new Date().getTime();
|
||||
reply['tokens'] = res.data;
|
||||
// 将聊天框的滚动条滑动到最底部
|
||||
nextTick(() => {
|
||||
document.getElementById('chat-box').scrollTo(0, document.getElementById('chat-box').scrollHeight)
|
||||
})
|
||||
isNewMsg.value = true
|
||||
}).catch(() => {
|
||||
})
|
||||
|
||||
} else {
|
||||
lineBuffer.value += data.content;
|
||||
const reply = chatData.value[chatData.value.length - 1]
|
||||
if (reply) {
|
||||
reply['content'] = lineBuffer.value;
|
||||
}
|
||||
}
|
||||
// 将聊天框的滚动条滑动到最底部
|
||||
nextTick(() => {
|
||||
document.getElementById('chat-box').scrollTo(0, document.getElementById('chat-box').scrollHeight)
|
||||
localStorage.setItem("chat_id", chatId.value)
|
||||
})
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(e)
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
disableInput(true)
|
||||
loadHistory.value = false
|
||||
connect()
|
||||
});
|
||||
|
||||
socket.value = _socket;
|
||||
}
|
||||
|
||||
const disableInput = (force) => {
|
||||
canSend.value = false;
|
||||
showStopGenerate.value = !force;
|
||||
@ -747,6 +689,11 @@ const sendMessage = function () {
|
||||
return;
|
||||
}
|
||||
|
||||
if (store.socket.conn.readyState !== WebSocket.OPEN) {
|
||||
ElMessage.warning("连接断开,正在重连...");
|
||||
return
|
||||
}
|
||||
|
||||
if (canSend.value === false) {
|
||||
ElMessage.warning("AI 正在作答中,请稍后...");
|
||||
return
|
||||
@ -780,7 +727,18 @@ const sendMessage = function () {
|
||||
|
||||
showHello.value = false
|
||||
disableInput(false)
|
||||
socket.value.send(JSON.stringify({tools: toolSelected.value, content: content, stream: stream.value}));
|
||||
store.socket.conn.send(JSON.stringify({
|
||||
channel: 'chat',
|
||||
type:'text',
|
||||
body:{
|
||||
role_id: roleId.value,
|
||||
model_id: modelID.value,
|
||||
chat_id: chatId.value,
|
||||
content: content,
|
||||
tools:toolSelected.value,
|
||||
stream: stream.value
|
||||
}
|
||||
}));
|
||||
tmpChatTitle.value = content
|
||||
prompt.value = ''
|
||||
files.value = []
|
||||
@ -816,7 +774,9 @@ const clearAllChats = function () {
|
||||
|
||||
const loadChatHistory = function (chatId) {
|
||||
chatData.value = []
|
||||
loading.value = true
|
||||
httpGet('/api/chat/history?chat_id=' + chatId).then(res => {
|
||||
loading.value = false
|
||||
const data = res.data
|
||||
if ((!data || data.length === 0) && chatData.value.length === 0) { // 加载打招呼信息
|
||||
const _role = getRoleById(roleId.value)
|
||||
@ -849,7 +809,7 @@ const loadChatHistory = function (chatId) {
|
||||
|
||||
const stopGenerate = function () {
|
||||
showStopGenerate.value = false;
|
||||
httpGet("/api/chat/stop?session_id=" + sessionId.value).then(() => {
|
||||
httpGet("/api/chat/stop?session_id=" + getClientId()).then(() => {
|
||||
enableInput()
|
||||
})
|
||||
}
|
||||
@ -865,7 +825,18 @@ const reGenerate = function (prompt) {
|
||||
icon: loginUser.value.avatar,
|
||||
content: text
|
||||
});
|
||||
socket.value.send(JSON.stringify({tools: toolSelected.value, content: text, stream: stream.value}));
|
||||
store.socket.conn.send(JSON.stringify({
|
||||
channel: 'chat',
|
||||
type:'text',
|
||||
body:{
|
||||
role_id: roleId.value,
|
||||
model_id: modelID.value,
|
||||
chat_id: chatId.value,
|
||||
content: text,
|
||||
tools:toolSelected.value,
|
||||
stream: stream.value
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
const chatName = ref('')
|
||||
|
@ -156,7 +156,7 @@
|
||||
<el-tooltip content="删除" placement="top" effect="light">
|
||||
<el-button type="danger" :icon="Delete" @click="removeImage(slotProp.item)" circle/>
|
||||
</el-tooltip>
|
||||
<el-tooltip content="取消分享" placement="top" effect="light" v-if="!slotProp.item.publish">
|
||||
<el-tooltip content="取消分享" placement="top" effect="light" v-if="slotProp.item.publish">
|
||||
<el-button type="warning"
|
||||
@click="publishImage(slotProp.item, false)"
|
||||
circle>
|
||||
@ -208,7 +208,7 @@ import {Delete, InfoFilled, Picture} from "@element-plus/icons-vue";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import Clipboard from "clipboard";
|
||||
import {checkSession, getSystemInfo} from "@/store/cache";
|
||||
import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
import TaskList from "@/components/TaskList.vue";
|
||||
import BackTop from "@/components/BackTop.vue";
|
||||
@ -240,6 +240,7 @@ const styles = [
|
||||
{name: "自然", value: "natural"}
|
||||
]
|
||||
const params = ref({
|
||||
client_id: getClientId(),
|
||||
quality: "standard",
|
||||
size: "1024x1024",
|
||||
style: "vivid",
|
||||
@ -268,14 +269,25 @@ onMounted(() => {
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取系统配置失败:" + e.message)
|
||||
})
|
||||
|
||||
store.addMessageHandler("dall",(data) => {
|
||||
// 丢弃无关消息
|
||||
if (data.channel !== "dall" || data.clientId !== getClientId()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (data.body === "FINISH" || data.body === "FAIL") {
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs()
|
||||
}
|
||||
nextTick(() => fetchRunningJobs())
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
clipboard.value.destroy()
|
||||
if (socket.value !== null) {
|
||||
socket.value.close()
|
||||
socket.value = null
|
||||
}
|
||||
store.removeMessageHandler("dall")
|
||||
})
|
||||
|
||||
const initData = () => {
|
||||
@ -287,51 +299,10 @@ const initData = () => {
|
||||
page.value = 0
|
||||
fetchRunningJobs()
|
||||
fetchFinishJobs()
|
||||
connect()
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
|
||||
const socket = ref(null)
|
||||
const heartbeatHandle = ref(null)
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
|
||||
const _socket = new WebSocket(host + `/api/dall/client?user_id=${userId.value}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
socket.value = _socket;
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8")
|
||||
reader.onload = () => {
|
||||
const message = String(reader.result)
|
||||
if (message === "FINISH" || message === "FAIL") {
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs(page.value)
|
||||
}
|
||||
nextTick(() => fetchRunningJobs())
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
if (socket.value !== null) {
|
||||
connect()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const fetchRunningJobs = () => {
|
||||
if (!isLogin.value) {
|
||||
return
|
||||
@ -391,6 +362,7 @@ const generate = () => {
|
||||
httpPost("/api/dall/image", params.value).then(() => {
|
||||
ElMessage.success("任务执行成功!")
|
||||
power.value -= dallPower.value
|
||||
fetchRunningJobs()
|
||||
}).catch(e => {
|
||||
ElMessage.error("任务执行失败:" + e.message)
|
||||
})
|
||||
|
@ -223,11 +223,11 @@ const init = () => {
|
||||
const logout = function () {
|
||||
httpGet('/api/user/logout').then(() => {
|
||||
removeUserToken()
|
||||
router.push('/login');
|
||||
// store.setShowLoginDialog(true)
|
||||
// loginUser.value = {}
|
||||
// // 刷新组件
|
||||
// routerViewKey.value += 1
|
||||
store.setShowLoginDialog(true)
|
||||
store.setIsLogin(false)
|
||||
loginUser.value = {}
|
||||
// 刷新组件
|
||||
routerViewKey.value += 1
|
||||
}).catch(() => {
|
||||
ElMessage.error('注销失败!');
|
||||
})
|
||||
|
@ -215,7 +215,7 @@
|
||||
<div class="param-line">
|
||||
<div class="img-inline">
|
||||
<div class="img-list-box">
|
||||
<div class="img-item" v-for="imgURL in imgList">
|
||||
<div class="img-item" v-for="imgURL in imgList" :key="imgURL">
|
||||
<el-image :src="imgURL" fit="cover"/>
|
||||
<el-button type="danger" :icon="Delete" @click="removeUploadImage(imgURL)" circle/>
|
||||
</div>
|
||||
@ -293,7 +293,7 @@
|
||||
<div class="text">请上传两张以上的图片,最多不超过五张,超过五张图片请使用图生图功能</div>
|
||||
<div class="img-inline">
|
||||
<div class="img-list-box">
|
||||
<div class="img-item" v-for="imgURL in imgList">
|
||||
<div class="img-item" v-for="imgURL in imgList" :key="imgURL">
|
||||
<el-image :src="imgURL" fit="cover"/>
|
||||
<el-button type="danger" :icon="Delete" @click="removeUploadImage(imgURL)" circle/>
|
||||
</div>
|
||||
@ -312,7 +312,7 @@
|
||||
<div class="text">请上传两张有脸部的图片,用左边图片的脸替换右边图片的脸</div>
|
||||
<div class="img-inline">
|
||||
<div class="img-list-box">
|
||||
<div class="img-item" v-for="imgURL in imgList">
|
||||
<div class="img-item" v-for="imgURL in imgList" :key="imgURL">
|
||||
<el-image :src="imgURL" fit="cover"/>
|
||||
<el-button type="danger" :icon="Delete" @click="removeUploadImage(imgURL)" circle/>
|
||||
</div>
|
||||
@ -602,13 +602,13 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, onUnmounted, ref} from "vue"
|
||||
import {nextTick, onMounted, onUnmounted, ref} from "vue"
|
||||
import {ChromeFilled, Delete, DocumentCopy, InfoFilled, Picture, Plus, UploadFilled} from "@element-plus/icons-vue";
|
||||
import Compressor from "compressorjs";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
|
||||
import Clipboard from "clipboard";
|
||||
import {checkSession, getSystemInfo} from "@/store/cache";
|
||||
import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import {useRouter} from "vue-router";
|
||||
import {getSessionId} from "@/store/session";
|
||||
import {copyObj, removeArrayItem} from "@/utils/libs";
|
||||
@ -678,6 +678,7 @@ const options = [
|
||||
|
||||
const router = useRouter()
|
||||
const initParams = {
|
||||
client_id: getClientId(),
|
||||
task_type: "image",
|
||||
rate: rates[0].value,
|
||||
model: models[0].value,
|
||||
@ -704,66 +705,10 @@ const activeName = ref('txt2img')
|
||||
const runningJobs = ref([])
|
||||
const finishedJobs = ref([])
|
||||
|
||||
const socket = ref(null)
|
||||
const power = ref(0)
|
||||
const userId = ref(0)
|
||||
const isLogin = ref(false)
|
||||
|
||||
const heartbeatHandle = ref(null)
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
|
||||
// 心跳函数
|
||||
const sendHeartbeat = () => {
|
||||
clearTimeout(heartbeatHandle.value)
|
||||
new Promise((resolve, reject) => {
|
||||
if (socket.value !== null) {
|
||||
socket.value.send(JSON.stringify({type: "heartbeat", content: "ping"}))
|
||||
}
|
||||
resolve("success")
|
||||
}).then(() => {
|
||||
heartbeatHandle.value = setTimeout(() => sendHeartbeat(), 5000)
|
||||
});
|
||||
}
|
||||
|
||||
const _socket = new WebSocket(host + `/api/mj/client?user_id=${userId.value}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
socket.value = _socket;
|
||||
|
||||
// 发送心跳消息
|
||||
sendHeartbeat()
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8")
|
||||
reader.onload = () => {
|
||||
const message = String(reader.result)
|
||||
if (message === "FINISH" || message === "FAIL") {
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs(page.value)
|
||||
}
|
||||
fetchRunningJobs()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
if (socket.value !== null) {
|
||||
connect()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const clipboard = ref(null)
|
||||
onMounted(() => {
|
||||
initData()
|
||||
@ -775,14 +720,25 @@ onMounted(() => {
|
||||
clipboard.value.on('error', () => {
|
||||
ElMessage.error('复制失败!');
|
||||
})
|
||||
|
||||
store.addMessageHandler("mj",(data) => {
|
||||
// 丢弃无关消息
|
||||
if (data.channel !== "mj" || data.clientId !== getClientId()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (data.body === "FINISH" || data.body === "FAIL") {
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs()
|
||||
}
|
||||
nextTick(() => fetchRunningJobs())
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
clipboard.value.destroy()
|
||||
if (socket.value !== null) {
|
||||
socket.value.close()
|
||||
socket.value = null
|
||||
}
|
||||
store.removeMessageHandler("mj")
|
||||
})
|
||||
|
||||
// 初始化数据
|
||||
@ -794,7 +750,6 @@ const initData = () => {
|
||||
page.value = 0
|
||||
fetchRunningJobs()
|
||||
fetchFinishJobs()
|
||||
connect()
|
||||
}).catch(() => {
|
||||
|
||||
});
|
||||
@ -952,6 +907,7 @@ const generate = () => {
|
||||
httpPost("/api/mj/image", params.value).then(() => {
|
||||
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
|
||||
power.value -= mjPower.value
|
||||
fetchRunningJobs()
|
||||
}).catch(e => {
|
||||
ElMessage.error("任务推送失败:" + e.message)
|
||||
})
|
||||
@ -970,6 +926,7 @@ const variation = (index, item) => {
|
||||
const send = (url, index, item) => {
|
||||
httpPost(url, {
|
||||
index: index,
|
||||
client_id: getClientId(),
|
||||
channel_id: item.channel_id,
|
||||
message_id: item.message_id,
|
||||
message_hash: item.hash,
|
||||
@ -978,6 +935,7 @@ const send = (url, index, item) => {
|
||||
}).then(() => {
|
||||
ElMessage.success("任务推送成功,请耐心等待任务执行...")
|
||||
power.value -= mjActionPower.value
|
||||
fetchRunningJobs()
|
||||
}).catch(e => {
|
||||
ElMessage.error("任务推送失败:" + e.message)
|
||||
})
|
||||
|
@ -487,11 +487,11 @@
|
||||
|
||||
<script setup>
|
||||
import {nextTick, onMounted, onUnmounted, ref} from "vue"
|
||||
import {Delete, DocumentCopy, InfoFilled, Orange, Picture} from "@element-plus/icons-vue";
|
||||
import {Delete, DocumentCopy, InfoFilled, Orange} from "@element-plus/icons-vue";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {ElMessage, ElMessageBox, ElNotification} from "element-plus";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import Clipboard from "clipboard";
|
||||
import {checkSession, getSystemInfo} from "@/store/cache";
|
||||
import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import {useRouter} from "vue-router";
|
||||
import {getSessionId} from "@/store/session";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
@ -520,6 +520,7 @@ const samplers = ["Euler a", "DPM++ 2S a", "DPM++ 2M", "DPM++ SDE", "DPM++ 2M SD
|
||||
const schedulers = ["Automatic", "Karras", "Exponential", "Uniform"]
|
||||
const scaleAlg = ["Latent", "ESRGAN_4x", "R-ESRGAN 4x+", "SwinIR_4x", "LDSR"]
|
||||
const params = ref({
|
||||
client_id: getClientId(),
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
sampler: samplers[0],
|
||||
@ -547,46 +548,7 @@ if (_params) {
|
||||
const power = ref(0)
|
||||
const sdPower = ref(0) // 画一张 SD 图片消耗算力
|
||||
|
||||
const socket = ref(null)
|
||||
const userId = ref(0)
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
|
||||
const _socket = new WebSocket(host + `/api/sd/client?user_id=${userId.value}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
socket.value = _socket;
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8")
|
||||
reader.onload = () => {
|
||||
const message = String(reader.result)
|
||||
if (message === "FINISH" || message === "FAIL") {
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs()
|
||||
}
|
||||
nextTick(() => fetchRunningJobs())
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
if (socket.value !== null) {
|
||||
connect()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const clipboard = ref(null)
|
||||
onMounted(() => {
|
||||
initData()
|
||||
@ -605,14 +567,26 @@ onMounted(() => {
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取系统配置失败:" + e.message)
|
||||
})
|
||||
|
||||
store.addMessageHandler("sd",(data) => {
|
||||
// 丢弃无关消息
|
||||
if (data.channel !== "sd" || data.clientId !== getClientId()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (data.body === "FINISH" || data.body === "FAIL") {
|
||||
page.value = 0
|
||||
isOver.value = false
|
||||
fetchFinishJobs()
|
||||
}
|
||||
nextTick(() => fetchRunningJobs())
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
clipboard.value.destroy()
|
||||
if (socket.value !== null) {
|
||||
socket.value.close()
|
||||
socket.value = null
|
||||
}
|
||||
store.removeMessageHandler("sd")
|
||||
})
|
||||
|
||||
|
||||
@ -624,7 +598,6 @@ const initData = () => {
|
||||
page.value = 0
|
||||
fetchRunningJobs()
|
||||
fetchFinishJobs()
|
||||
connect()
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
@ -694,6 +667,7 @@ const generate = () => {
|
||||
httpPost("/api/sd/image", params.value).then(() => {
|
||||
ElMessage.success("绘画任务推送成功,请耐心等待任务执行...")
|
||||
power.value -= sdPower.value
|
||||
fetchRunningJobs()
|
||||
}).catch(e => {
|
||||
ElMessage.error("任务推送失败:" + e.message)
|
||||
})
|
||||
|
@ -61,13 +61,13 @@ import {useRouter} from "vue-router";
|
||||
import FooterBar from "@/components/FooterBar.vue";
|
||||
import {httpGet} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {isMobile} from "@/utils/libs";
|
||||
import {checkSession, getLicenseInfo, getSystemInfo} from "@/store/cache";
|
||||
import {isMobile} from "@/utils/libs";
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
if (isMobile()) {
|
||||
router.push("/mobile")
|
||||
router.push("/mobile/index")
|
||||
}
|
||||
|
||||
const title = ref("")
|
||||
|
@ -77,6 +77,7 @@ import ResetPass from "@/components/ResetPass.vue";
|
||||
import {showMessageError} from "@/utils/dialog";
|
||||
import Captcha from "@/components/Captcha.vue";
|
||||
import {setRoute} from "@/store/system";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const router = useRouter();
|
||||
const title = ref('Geek-AI');
|
||||
@ -145,6 +146,7 @@ const login = function () {
|
||||
}
|
||||
}
|
||||
|
||||
const store = useSharedStore()
|
||||
const doLogin = (verifyData) => {
|
||||
httpPost('/api/user/login', {
|
||||
username: username.value.trim(),
|
||||
@ -154,6 +156,7 @@ const doLogin = (verifyData) => {
|
||||
x: verifyData.x
|
||||
}).then((res) => {
|
||||
setUserToken(res.data.token)
|
||||
store.setIsLogin(true)
|
||||
needVerify.value = false
|
||||
if (isMobile()) {
|
||||
router.push('/mobile')
|
||||
|
@ -55,25 +55,6 @@
|
||||
<el-container class="video-container" v-loading="loading" element-loading-background="rgba(100,100,100,0.3)">
|
||||
<h2 class="h-title">你的作品</h2>
|
||||
|
||||
<!-- <el-row :gutter="20" class="videos" v-if="!noData">-->
|
||||
<!-- <el-col :span="8" class="item" :key="item.id" v-for="item in videos">-->
|
||||
<!-- <div class="video-box" @mouseover="item.playing = true" @mouseout="item.playing = false">-->
|
||||
<!-- <img :src="item.cover" :alt="item.name" v-show="!item.playing"/>-->
|
||||
<!-- <video :src="item.url" preload="auto" :autoplay="true" loop="loop" muted="muted" v-show="item.playing">-->
|
||||
<!-- 您的浏览器不支持视频播放-->
|
||||
<!-- </video>-->
|
||||
<!-- </div>-->
|
||||
<!-- <div class="video-name">{{item.name}}</div>-->
|
||||
<!-- <div class="opts">-->
|
||||
<!-- <button class="btn" @click="download(item)" :disabled="item.downloading">-->
|
||||
<!-- <i class="iconfont icon-download" v-if="!item.downloading"></i>-->
|
||||
<!-- <el-image src="/images/loading.gif" fit="cover" v-else />-->
|
||||
<!-- <span>下载</span>-->
|
||||
<!-- </button>-->
|
||||
<!-- </div>-->
|
||||
<!-- </el-col>-->
|
||||
<!-- </el-row>-->
|
||||
|
||||
<div class="list-box" v-if="!noData">
|
||||
<div v-for="item in list" :key="item.id">
|
||||
<div class="item">
|
||||
@ -150,16 +131,17 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import {onMounted, onUnmounted, reactive, ref} from "vue";
|
||||
import {CircleCloseFilled} from "@element-plus/icons-vue";
|
||||
import {httpDownload, httpPost, httpGet} from "@/utils/http";
|
||||
import {checkSession} from "@/store/cache";
|
||||
import {checkSession, getClientId} from "@/store/cache";
|
||||
import {showMessageError, showMessageOK} from "@/utils/dialog";
|
||||
import { replaceImg } from "@/utils/libs"
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import BlackSwitch from "@/components/ui/BlackSwitch.vue";
|
||||
import Generating from "@/components/ui/Generating.vue";
|
||||
import BlackDialog from "@/components/ui/BlackDialog.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const showDialog = ref(false)
|
||||
const currentVideoUrl = ref('')
|
||||
@ -167,6 +149,7 @@ const row = ref(1)
|
||||
const images = ref([])
|
||||
|
||||
const formData = reactive({
|
||||
client_id: getClientId(),
|
||||
prompt: '',
|
||||
expand_prompt: false,
|
||||
loop: false,
|
||||
@ -174,49 +157,26 @@ const formData = reactive({
|
||||
end_frame_img: ''
|
||||
})
|
||||
|
||||
const socket = ref(null)
|
||||
const userId = ref(0)
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
|
||||
const _socket = new WebSocket(host + `/api/video/client?user_id=${userId.value}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
socket.value = _socket;
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8")
|
||||
reader.onload = () => {
|
||||
const message = String(reader.result)
|
||||
if (message === "FINISH" || message === "FAIL") {
|
||||
fetchData()
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
if (socket.value !== null) {
|
||||
connect()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const store = useSharedStore()
|
||||
onMounted(()=>{
|
||||
checkSession().then(user => {
|
||||
userId.value = user.id
|
||||
connect()
|
||||
checkSession().then(() => {
|
||||
fetchData(1)
|
||||
})
|
||||
fetchData(1)
|
||||
|
||||
store.addMessageHandler("luma",(data) => {
|
||||
// 丢弃无关消息
|
||||
if (data.channel !== "luma" || data.clientId !== getClientId()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (data.body === "FINISH" || data.body === "FAIL") {
|
||||
fetchData(1)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
store.removeMessageHandler("luma")
|
||||
})
|
||||
|
||||
const download = (item) => {
|
||||
|
@ -45,7 +45,7 @@
|
||||
|
||||
<div class="param-line">
|
||||
<el-button color="#47fff1" :dark="false" round @click="generateAI" :loading="loading">
|
||||
智能生成思维导图
|
||||
生成思维导图
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
@ -79,10 +79,7 @@
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div class="markdown" v-if="loading">
|
||||
<div :style="{ height: rightBoxHeight + 'px', overflow:'auto',width:'80%' }" v-html="html"></div>
|
||||
</div>
|
||||
<div class="body" id="markmap" v-show="!loading">
|
||||
<div class="body" id="markmap">
|
||||
<svg ref="svgRef" :style="{ height: rightBoxHeight + 'px' }"/>
|
||||
<div id="toolbar"></div>
|
||||
</div>
|
||||
@ -94,11 +91,11 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {nextTick, onUnmounted, ref} from 'vue';
|
||||
import {nextTick, ref} from 'vue';
|
||||
import {Markmap} from 'markmap-view';
|
||||
import {Transformer} from 'markmap-lib';
|
||||
import {checkSession, getSystemInfo} from "@/store/cache";
|
||||
import {httpGet} from "@/utils/http";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {Download} from "@element-plus/icons-vue";
|
||||
import {Toolbar} from 'markmap-toolbar';
|
||||
@ -106,11 +103,9 @@ import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const leftBoxHeight = ref(window.innerHeight - 105)
|
||||
const rightBoxHeight = ref(window.innerHeight - 115)
|
||||
const title = ref("")
|
||||
|
||||
const prompt = ref("")
|
||||
const text = ref("")
|
||||
const md = require('markdown-it')({breaks: true});
|
||||
const content = ref(text.value)
|
||||
const html = ref("")
|
||||
|
||||
@ -118,13 +113,12 @@ const isLogin = ref(false)
|
||||
const loginUser = ref({power: 0})
|
||||
const transformer = new Transformer();
|
||||
const store = useSharedStore();
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const svgRef = ref(null)
|
||||
const markMap = ref(null)
|
||||
const models = ref([])
|
||||
const modelID = ref(0)
|
||||
const loading = ref(false)
|
||||
|
||||
getSystemInfo().then(res => {
|
||||
text.value = res.data['mark_map_text']
|
||||
@ -150,15 +144,15 @@ const initData = () => {
|
||||
models.value.push(v)
|
||||
}
|
||||
modelID.value = models.value[0].id
|
||||
checkSession().then(user => {
|
||||
loginUser.value = user
|
||||
isLogin.value = true
|
||||
connect(user.id)
|
||||
}).catch(() => {
|
||||
});
|
||||
}).catch(e => {
|
||||
ElMessage.error("获取模型失败:" + e.message)
|
||||
})
|
||||
|
||||
checkSession().then(user => {
|
||||
loginUser.value = user
|
||||
isLogin.value = true
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
|
||||
const update = () => {
|
||||
@ -188,74 +182,11 @@ const processContent = (text) => {
|
||||
return arr.join("\n")
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (socket.value !== null) {
|
||||
socket.value.close()
|
||||
}
|
||||
socket.value = null
|
||||
})
|
||||
|
||||
window.onresize = () => {
|
||||
leftBoxHeight.value = window.innerHeight - 145
|
||||
rightBoxHeight.value = window.innerHeight - 85
|
||||
}
|
||||
|
||||
const socket = ref(null)
|
||||
const connect = (userId) => {
|
||||
if (socket.value !== null) {
|
||||
socket.value.close()
|
||||
}
|
||||
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
|
||||
const _socket = new WebSocket(host + `/api/markMap/client?user_id=${userId}&model_id=${modelID.value}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
socket.value = _socket;
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8")
|
||||
const model = getModelById(modelID.value)
|
||||
reader.onload = () => {
|
||||
const data = JSON.parse(String(reader.result))
|
||||
switch (data.type) {
|
||||
case "content":
|
||||
text.value += data.content
|
||||
html.value = md.render(processContent(text.value))
|
||||
break
|
||||
case "end":
|
||||
loading.value = false
|
||||
content.value = processContent(text.value)
|
||||
loginUser.value.power -= model.power
|
||||
nextTick(() => update())
|
||||
break
|
||||
case "error":
|
||||
loading.value = false
|
||||
ElMessage.error(data.content)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
loading.value = false
|
||||
checkSession().then(() => {
|
||||
connect(userId)
|
||||
}).catch(() => {
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
const generate = () => {
|
||||
text.value = content.value
|
||||
update()
|
||||
@ -273,19 +204,26 @@ const generateAI = () => {
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
socket.value.send(JSON.stringify({type: "message", content: prompt.value}))
|
||||
}
|
||||
|
||||
const changeModel = () => {
|
||||
if (socket.value !== null) {
|
||||
socket.value.send(JSON.stringify({type: "model_id", content: modelID.value}))
|
||||
}
|
||||
httpPost("/api/markMap/gen", {
|
||||
prompt:prompt.value,
|
||||
model_id: modelID.value
|
||||
}).then(res => {
|
||||
text.value = res.data
|
||||
content.value = processContent(text.value)
|
||||
const model = getModelById(modelID.value)
|
||||
loginUser.value.power -= model.power
|
||||
nextTick(() => update())
|
||||
loading.value = false
|
||||
}).catch(e => {
|
||||
ElMessage.error("生成思维导图失败:" + e.message)
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const getModelById = (modelId) => {
|
||||
for (let e of models.value) {
|
||||
if (e.id === modelId) {
|
||||
return e
|
||||
for (let m of models.value) {
|
||||
if (m.id === modelId) {
|
||||
return m
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -6,8 +6,13 @@
|
||||
|
||||
<script setup>
|
||||
import {useRouter} from "vue-router";
|
||||
import {isMobile} from "@/utils/libs";
|
||||
|
||||
const router = useRouter()
|
||||
console.log(router.currentRoute.value.query)
|
||||
window.close()
|
||||
if (isMobile()) {
|
||||
router.push('/mobile/profile')
|
||||
} else {
|
||||
window.close()
|
||||
}
|
||||
</script>
|
||||
|
@ -183,7 +183,7 @@ import {ElMessage} from "element-plus";
|
||||
import {useRouter} from "vue-router";
|
||||
import FooterBar from "@/components/FooterBar.vue";
|
||||
import SendMsg from "@/components/SendMsg.vue";
|
||||
import {arrayContains} from "@/utils/libs";
|
||||
import {arrayContains, isMobile} from "@/utils/libs";
|
||||
import {setUserToken} from "@/store/session";
|
||||
import {validateEmail, validateMobile} from "@/utils/validate";
|
||||
import {showMessageError, showMessageOK} from "@/utils/dialog";
|
||||
@ -294,11 +294,12 @@ const doSubmitRegister = (verifyData) => {
|
||||
data.value.reg_way = activeName.value
|
||||
httpPost('/api/user/register', data.value).then((res) => {
|
||||
setUserToken(res.data.token)
|
||||
showMessageOK({
|
||||
"message": "注册成功,即将跳转到对话主界面...",
|
||||
onClose: () => router.push("/chat"),
|
||||
duration: 1000
|
||||
})
|
||||
showMessageOK("注册成功,即将跳转到对话主界面...")
|
||||
if (isMobile()) {
|
||||
router.push('/mobile/index')
|
||||
} else {
|
||||
router.push('/chat')
|
||||
}
|
||||
}).catch((e) => {
|
||||
showMessageError('注册失败,' + e.message)
|
||||
})
|
||||
|
@ -300,13 +300,14 @@ import MusicPlayer from "@/components/MusicPlayer.vue";
|
||||
import {compact} from "lodash";
|
||||
import {httpDownload, httpGet, httpPost} from "@/utils/http";
|
||||
import {showMessageError, showMessageOK} from "@/utils/dialog";
|
||||
import {checkSession} from "@/store/cache";
|
||||
import {checkSession, getClientId} from "@/store/cache";
|
||||
import {ElMessage, ElMessageBox} from "element-plus";
|
||||
import {formatTime, replaceImg} from "@/utils/libs";
|
||||
import Clipboard from "clipboard";
|
||||
import BlackDialog from "@/components/ui/BlackDialog.vue";
|
||||
import Compressor from "compressorjs";
|
||||
import Generating from "@/components/ui/Generating.vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const winHeight = ref(window.innerHeight - 50)
|
||||
const custom = ref(false)
|
||||
@ -333,6 +334,7 @@ const tags = ref([
|
||||
{label: "嘻哈", value: "hip hop"},
|
||||
])
|
||||
const data = ref({
|
||||
client_id: getClientId(),
|
||||
model: "chirp-v3-0",
|
||||
tags: "",
|
||||
lyrics: "",
|
||||
@ -343,8 +345,8 @@ const data = ref({
|
||||
extend_secs: 0,
|
||||
ref_song_id: "",
|
||||
})
|
||||
const loading = ref(true)
|
||||
const noData = ref(false)
|
||||
const loading = ref(false)
|
||||
const noData = ref(true)
|
||||
const playList = ref([])
|
||||
const playerRef = ref(null)
|
||||
const showPlayer = ref(false)
|
||||
@ -354,45 +356,7 @@ const refSong = ref(null)
|
||||
const showDialog = ref(false)
|
||||
const editData = ref({title:"",cover:"",id:0})
|
||||
const promptPlaceholder = ref('请在这里输入你自己写的歌词...')
|
||||
|
||||
const socket = ref(null)
|
||||
const userId = ref(0)
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
|
||||
const _socket = new WebSocket(host + `/api/suno/client?user_id=${userId.value}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
socket.value = _socket;
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8")
|
||||
reader.onload = () => {
|
||||
const message = String(reader.result)
|
||||
console.log(message)
|
||||
if (message === "FINISH" || message === "FAIL") {
|
||||
fetchData()
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
if (socket.value !== null) {
|
||||
connect()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const store = useSharedStore()
|
||||
const clipboard = ref(null)
|
||||
onMounted(() => {
|
||||
clipboard.value = new Clipboard('.copy-link');
|
||||
@ -404,15 +368,25 @@ onMounted(() => {
|
||||
ElMessage.error('复制失败!');
|
||||
})
|
||||
|
||||
checkSession().then(user => {
|
||||
userId.value = user.id
|
||||
connect()
|
||||
checkSession().then(() => {
|
||||
fetchData(1)
|
||||
}).catch(() => {})
|
||||
|
||||
store.addMessageHandler("suno",(data) => {
|
||||
// 丢弃无关消息
|
||||
if (data.channel !== "suno" || data.clientId !== getClientId()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (data.body === "FINISH" || data.body === "FAIL") {
|
||||
fetchData(1)
|
||||
}
|
||||
})
|
||||
fetchData(1)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
clipboard.value.destroy()
|
||||
store.removeMessageHandler("suno")
|
||||
})
|
||||
|
||||
const page = ref(1)
|
||||
@ -422,6 +396,7 @@ const fetchData = (_page) => {
|
||||
if (_page) {
|
||||
page.value = _page
|
||||
}
|
||||
loading.value = true
|
||||
httpGet("/api/suno/list",{page:page.value, page_size:pageSize.value}).then(res => {
|
||||
total.value = res.data.total
|
||||
const items = []
|
||||
|
@ -5,20 +5,12 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref, onMounted, onUpdated} from 'vue';
|
||||
import {Markmap} from 'markmap-view';
|
||||
import {loadJS, loadCSS} from 'markmap-common';
|
||||
import {Transformer} from 'markmap-lib';
|
||||
import {httpPost} from "@/utils/http";
|
||||
import {onMounted, ref} from "vue";
|
||||
|
||||
const data=ref("")
|
||||
httpPost("/api/test/sse",{
|
||||
"message":"你是什么模型",
|
||||
"user_id":123
|
||||
}).then(res=>{
|
||||
// const source = new EventSource("http://localhost:5678/api/test/sse");
|
||||
// source.onmessage = function(event) {
|
||||
// console.log(event.data)
|
||||
// };
|
||||
const data = ref('abc')
|
||||
|
||||
onMounted(() => {
|
||||
|
||||
})
|
||||
|
||||
</script>
|
||||
|
@ -194,7 +194,7 @@ const fetchData = () => {
|
||||
const add = function () {
|
||||
showDialog.value = true
|
||||
title.value = "新增 API KEY"
|
||||
item.value = {enabled: true,api_url:"https://api.geekai.pro"}
|
||||
item.value = {enabled: true,api_url: "https://api.geekai.pro"}
|
||||
}
|
||||
|
||||
const edit = function (row) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<div class="admin-home" v-if="isLogin">
|
||||
<admin-sidebar v-model:theme="theme"/>
|
||||
<admin-sidebar/>
|
||||
<div class="content-box" :class="{ 'content-collapse': sidebar.collapse }">
|
||||
<admin-header v-model:theme="theme" @changeTheme="changeTheme"/>
|
||||
<admin-tags v-model:theme="theme"/>
|
||||
<admin-header/>
|
||||
<admin-tags/>
|
||||
<div :class="'content '+theme" :style="{height:contentHeight+'px'}">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="move" mode="out-in">
|
||||
@ -24,14 +24,15 @@ import AdminSidebar from "@/components/admin/AdminSidebar.vue";
|
||||
import AdminTags from "@/components/admin/AdminTags.vue";
|
||||
import {useRouter} from "vue-router";
|
||||
import {checkAdminSession} from "@/store/cache";
|
||||
import {ref} from "vue";
|
||||
import {getAdminTheme, setAdminTheme} from "@/store/system";
|
||||
import {ref, watch} from "vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const sidebar = useSidebarStore();
|
||||
const tags = useTagsStore();
|
||||
const isLogin = ref(false)
|
||||
const contentHeight = window.innerHeight - 80
|
||||
const theme = ref(getAdminTheme())
|
||||
const store = useSharedStore()
|
||||
const theme = ref(store.adminTheme)
|
||||
|
||||
// 获取会话信息
|
||||
const router = useRouter();
|
||||
@ -41,14 +42,10 @@ checkAdminSession().then(() => {
|
||||
router.replace('/admin/login')
|
||||
})
|
||||
|
||||
const changeTheme = (value) => {
|
||||
if (value) {
|
||||
theme.value = 'dark'
|
||||
} else {
|
||||
theme.value = 'light'
|
||||
}
|
||||
setAdminTheme(theme.value)
|
||||
}
|
||||
watch(() => store.adminTheme, (val) => {
|
||||
theme.value = val
|
||||
})
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
|
@ -182,7 +182,7 @@ const onLoad = () => {
|
||||
error.value = true
|
||||
showFailToast("加载会话列表失败")
|
||||
})
|
||||
})
|
||||
}).catch(() => {})
|
||||
};
|
||||
|
||||
const search = () => {
|
||||
|
@ -125,15 +125,14 @@
|
||||
<script setup>
|
||||
import {nextTick, onMounted, onUnmounted, ref, watch} from "vue";
|
||||
import {showImagePreview, showNotify, showToast} from "vant";
|
||||
import {onBeforeRouteLeave, useRouter} from "vue-router";
|
||||
import {useRouter} from "vue-router";
|
||||
import {processContent, randString, renderInputText, UUID} from "@/utils/libs";
|
||||
import {httpGet} from "@/utils/http";
|
||||
import hl from "highlight.js";
|
||||
import 'highlight.js/styles/a11y-dark.css'
|
||||
import ChatPrompt from "@/components/mobile/ChatPrompt.vue";
|
||||
import ChatReply from "@/components/mobile/ChatReply.vue";
|
||||
import {getSessionId, getUserToken} from "@/store/session";
|
||||
import {checkSession} from "@/store/cache";
|
||||
import {checkSession, getClientId} from "@/store/cache";
|
||||
import Clipboard from "clipboard";
|
||||
import { showMessageError} from "@/utils/dialog";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
@ -191,7 +190,7 @@ const loadModels = () => {
|
||||
role.value = getRoleById(roleId.value)
|
||||
columns.value = [roles.value, models.value]
|
||||
selectedValues.value = [roleId.value, modelId.value]
|
||||
connect()
|
||||
loadChatHistory()
|
||||
}).catch((e) => {
|
||||
showNotify({type: "danger", message: '获取聊天角色失败: ' + e.messages})
|
||||
})
|
||||
@ -214,48 +213,12 @@ if (chatId.value) {
|
||||
loadModels()
|
||||
}
|
||||
|
||||
|
||||
const url = ref(location.protocol + '//' + location.host + '/mobile/chat/export?chat_id=' + chatId.value)
|
||||
|
||||
onMounted(() => {
|
||||
winHeight.value = window.innerHeight - navBarRef.value.$el.offsetHeight - bottomBarRef.value.$el.offsetHeight - 70
|
||||
|
||||
const clipboard = new Clipboard(".content-mobile,.copy-code-mobile,#copy-link-btn");
|
||||
clipboard.on('success', (e) => {
|
||||
e.clearSelection()
|
||||
showNotify({type: 'success', message: '复制成功', duration: 1000})
|
||||
})
|
||||
clipboard.on('error', () => {
|
||||
showNotify({type: 'danger', message: '复制失败', duration: 2000})
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (socket.value !== null) {
|
||||
socket.value.close()
|
||||
socket.value = null
|
||||
}
|
||||
})
|
||||
|
||||
const newChat = (item) => {
|
||||
showPicker.value = false
|
||||
const options = item.selectedOptions
|
||||
roleId.value = options[0].value
|
||||
modelId.value = options[1].value
|
||||
modelValue.value = getModelName(modelId.value)
|
||||
chatId.value = UUID()
|
||||
chatData.value = []
|
||||
role.value = getRoleById(roleId.value)
|
||||
title.value = "新建对话"
|
||||
loadHistory.value = true
|
||||
connect()
|
||||
}
|
||||
|
||||
const chatData = ref([])
|
||||
const loading = ref(false)
|
||||
const finished = ref(false)
|
||||
const error = ref(false)
|
||||
|
||||
const store = useSharedStore()
|
||||
const url = ref(location.protocol + '//' + location.host + '/mobile/chat/export?chat_id=' + chatId.value)
|
||||
const mathjaxPlugin = require('markdown-it-mathjax3')
|
||||
const md = require('markdown-it')({
|
||||
breaks: true,
|
||||
@ -282,7 +245,91 @@ const md = require('markdown-it')({
|
||||
}
|
||||
});
|
||||
md.use(mathjaxPlugin)
|
||||
onMounted(() => {
|
||||
winHeight.value = window.innerHeight - navBarRef.value.$el.offsetHeight - bottomBarRef.value.$el.offsetHeight - 70
|
||||
|
||||
const clipboard = new Clipboard(".content-mobile,.copy-code-mobile,#copy-link-btn");
|
||||
clipboard.on('success', (e) => {
|
||||
e.clearSelection()
|
||||
showNotify({type: 'success', message: '复制成功', duration: 1000})
|
||||
})
|
||||
clipboard.on('error', () => {
|
||||
showNotify({type: 'danger', message: '复制失败', duration: 2000})
|
||||
})
|
||||
|
||||
store.addMessageHandler("chat",(data) => {
|
||||
if (data.channel !== "chat" || data.clientId !== getClientId()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (data.type === 'error') {
|
||||
showMessageError(data.body)
|
||||
return
|
||||
}
|
||||
|
||||
if (isNewMsg.value) {
|
||||
chatData.value.push({
|
||||
type: "reply",
|
||||
id: randString(32),
|
||||
icon: role.value.icon,
|
||||
content: data.body
|
||||
});
|
||||
if (!title.value) {
|
||||
title.value = previousText.value
|
||||
}
|
||||
lineBuffer.value = data.body;
|
||||
isNewMsg.value = false
|
||||
} else if (data.type === 'end') { // 消息接收完毕
|
||||
enableInput()
|
||||
lineBuffer.value = ''; // 清空缓冲
|
||||
isNewMsg.value = true
|
||||
} else {
|
||||
lineBuffer.value += data.body;
|
||||
const reply = chatData.value[chatData.value.length - 1]
|
||||
reply['orgContent'] = lineBuffer.value;
|
||||
reply['content'] = md.render(processContent(lineBuffer.value));
|
||||
|
||||
nextTick(() => {
|
||||
hl.configure({ignoreUnescapedHTML: true})
|
||||
const lines = document.querySelectorAll('.message-line');
|
||||
const blocks = lines[lines.length - 1].querySelectorAll('pre code');
|
||||
blocks.forEach((block) => {
|
||||
hl.highlightElement(block)
|
||||
})
|
||||
scrollListBox()
|
||||
|
||||
const items = document.querySelectorAll('.message-line')
|
||||
const imgs = items[items.length - 1].querySelectorAll('img')
|
||||
for (let i = 0; i < imgs.length; i++) {
|
||||
if (!imgs[i].src) {
|
||||
continue
|
||||
}
|
||||
imgs[i].addEventListener('click', (e) => {
|
||||
e.stopPropagation()
|
||||
showImagePreview([imgs[i].src]);
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
store.removeMessageHandler("chat")
|
||||
})
|
||||
|
||||
const newChat = (item) => {
|
||||
showPicker.value = false
|
||||
const options = item.selectedOptions
|
||||
roleId.value = options[0].value
|
||||
modelId.value = options[1].value
|
||||
modelValue.value = getModelName(modelId.value)
|
||||
chatId.value = UUID()
|
||||
chatData.value = []
|
||||
role.value = getRoleById(roleId.value)
|
||||
title.value = "新建对话"
|
||||
loadChatHistory()
|
||||
}
|
||||
|
||||
const onLoad = () => {
|
||||
// checkSession().then(() => {
|
||||
@ -334,121 +381,113 @@ const loadChatHistory = () => {
|
||||
})
|
||||
}
|
||||
|
||||
// 离开页面时主动关闭 websocket 连接,节省网络资源
|
||||
onBeforeRouteLeave(() => {
|
||||
if (socket.value !== null) {
|
||||
socket.value.close();
|
||||
}
|
||||
})
|
||||
|
||||
// 创建 socket 连接
|
||||
const prompt = ref('');
|
||||
const showStopGenerate = ref(false); // 停止生成
|
||||
const showReGenerate = ref(false); // 重新生成
|
||||
const previousText = ref(''); // 上一次提问
|
||||
const lineBuffer = ref(''); // 输出缓冲行
|
||||
const socket = ref(null);
|
||||
const canSend = ref(true)
|
||||
const isNewMsg = ref(true)
|
||||
const loadHistory = ref(true)
|
||||
const store = useSharedStore()
|
||||
const stream = ref(store.chatStream)
|
||||
watch(() => store.chatStream, (newValue) => {
|
||||
stream.value = newValue
|
||||
});
|
||||
const connect = function () {
|
||||
// 初始化 WebSocket 对象
|
||||
const _sessionId = getSessionId();
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
const _socket = new WebSocket(host + `/api/chat/new?session_id=${_sessionId}&role_id=${roleId.value}&chat_id=${chatId.value}&model_id=${modelId.value}&token=${getUserToken()}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
loading.value = false
|
||||
previousText.value = '';
|
||||
canSend.value = true;
|
||||
// const connect = function () {
|
||||
// // 初始化 WebSocket 对象
|
||||
// const _sessionId = getSessionId();
|
||||
// let host = process.env.VUE_APP_WS_HOST
|
||||
// if (host === '') {
|
||||
// if (location.protocol === 'https:') {
|
||||
// host = 'wss://' + location.host;
|
||||
// } else {
|
||||
// host = 'ws://' + location.host;
|
||||
// }
|
||||
// }
|
||||
// const _socket = new WebSocket(host + `/api/chat/new?session_id=${_sessionId}&role_id=${roleId.value}&chat_id=${chatId.value}&model_id=${modelId.value}&token=${getUserToken()}`);
|
||||
// _socket.addEventListener('open', () => {
|
||||
// loading.value = false
|
||||
// previousText.value = '';
|
||||
// canSend.value = true;
|
||||
//
|
||||
// if (loadHistory.value) { // 加载历史消息
|
||||
// loadChatHistory()
|
||||
// }
|
||||
// });
|
||||
//
|
||||
// _socket.addEventListener('message', event => {
|
||||
// if (event.data instanceof Blob) {
|
||||
// const reader = new FileReader();
|
||||
// reader.readAsText(event.data, "UTF-8");
|
||||
// reader.onload = () => {
|
||||
// const data = JSON.parse(String(reader.result));
|
||||
// if (data.type === 'error') {
|
||||
// showMessageError(data.message)
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// if (isNewMsg.value && data.type !== 'end') {
|
||||
// chatData.value.push({
|
||||
// type: "reply",
|
||||
// id: randString(32),
|
||||
// icon: role.value.icon,
|
||||
// content: data.content
|
||||
// });
|
||||
// if (!title.value) {
|
||||
// title.value = previousText.value
|
||||
// }
|
||||
// lineBuffer.value = data.content;
|
||||
// isNewMsg.value = false
|
||||
// } else if (data.type === 'end') { // 消息接收完毕
|
||||
// enableInput()
|
||||
// lineBuffer.value = ''; // 清空缓冲
|
||||
// isNewMsg.value = true
|
||||
// } else {
|
||||
// lineBuffer.value += data.content;
|
||||
// const reply = chatData.value[chatData.value.length - 1]
|
||||
// reply['orgContent'] = lineBuffer.value;
|
||||
// reply['content'] = md.render(processContent(lineBuffer.value));
|
||||
//
|
||||
// nextTick(() => {
|
||||
// hl.configure({ignoreUnescapedHTML: true})
|
||||
// const lines = document.querySelectorAll('.message-line');
|
||||
// const blocks = lines[lines.length - 1].querySelectorAll('pre code');
|
||||
// blocks.forEach((block) => {
|
||||
// hl.highlightElement(block)
|
||||
// })
|
||||
// scrollListBox()
|
||||
//
|
||||
// const items = document.querySelectorAll('.message-line')
|
||||
// const imgs = items[items.length - 1].querySelectorAll('img')
|
||||
// for (let i = 0; i < imgs.length; i++) {
|
||||
// if (!imgs[i].src) {
|
||||
// continue
|
||||
// }
|
||||
// imgs[i].addEventListener('click', (e) => {
|
||||
// e.stopPropagation()
|
||||
// showImagePreview([imgs[i].src]);
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
//
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// });
|
||||
//
|
||||
// _socket.addEventListener('close', () => {
|
||||
// // 停止发送消息
|
||||
// canSend.value = true
|
||||
// loadHistory.value = false
|
||||
// // 重连
|
||||
// connect()
|
||||
// });
|
||||
//
|
||||
// socket.value = _socket;
|
||||
// }
|
||||
|
||||
if (loadHistory.value) { // 加载历史消息
|
||||
loadChatHistory()
|
||||
}
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8");
|
||||
reader.onload = () => {
|
||||
const data = JSON.parse(String(reader.result));
|
||||
if (data.type === 'error') {
|
||||
showMessageError(data.message)
|
||||
return
|
||||
}
|
||||
|
||||
if (isNewMsg.value && data.type !== 'end') {
|
||||
chatData.value.push({
|
||||
type: "reply",
|
||||
id: randString(32),
|
||||
icon: role.value.icon,
|
||||
content: data.content
|
||||
});
|
||||
if (!title.value) {
|
||||
title.value = previousText.value
|
||||
}
|
||||
lineBuffer.value = data.content;
|
||||
isNewMsg.value = false
|
||||
} else if (data.type === 'end') { // 消息接收完毕
|
||||
enableInput()
|
||||
lineBuffer.value = ''; // 清空缓冲
|
||||
isNewMsg.value = true
|
||||
} else {
|
||||
lineBuffer.value += data.content;
|
||||
const reply = chatData.value[chatData.value.length - 1]
|
||||
reply['orgContent'] = lineBuffer.value;
|
||||
reply['content'] = md.render(processContent(lineBuffer.value));
|
||||
|
||||
nextTick(() => {
|
||||
hl.configure({ignoreUnescapedHTML: true})
|
||||
const lines = document.querySelectorAll('.message-line');
|
||||
const blocks = lines[lines.length - 1].querySelectorAll('pre code');
|
||||
blocks.forEach((block) => {
|
||||
hl.highlightElement(block)
|
||||
})
|
||||
scrollListBox()
|
||||
|
||||
const items = document.querySelectorAll('.message-line')
|
||||
const imgs = items[items.length - 1].querySelectorAll('img')
|
||||
for (let i = 0; i < imgs.length; i++) {
|
||||
if (!imgs[i].src) {
|
||||
continue
|
||||
}
|
||||
imgs[i].addEventListener('click', (e) => {
|
||||
e.stopPropagation()
|
||||
showImagePreview([imgs[i].src]);
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
// 停止发送消息
|
||||
canSend.value = true
|
||||
loadHistory.value = false
|
||||
// 重连
|
||||
connect()
|
||||
});
|
||||
|
||||
socket.value = _socket;
|
||||
}
|
||||
|
||||
const disableInput = (force) => {
|
||||
canSend.value = false;
|
||||
@ -473,6 +512,11 @@ const sendMessage = () => {
|
||||
return
|
||||
}
|
||||
|
||||
if (store.socket.conn.readyState !== WebSocket.OPEN) {
|
||||
showToast("连接断开,正在重连...");
|
||||
return
|
||||
}
|
||||
|
||||
if (prompt.value.trim().length === 0) {
|
||||
showToast("请输入需要 AI 回答的问题")
|
||||
return false;
|
||||
@ -492,7 +536,17 @@ const sendMessage = () => {
|
||||
})
|
||||
|
||||
disableInput(false)
|
||||
socket.value.send(JSON.stringify({stream: stream.value, content: prompt.value}));
|
||||
store.socket.conn.send(JSON.stringify({
|
||||
channel: 'chat',
|
||||
type:'text',
|
||||
body:{
|
||||
role_id: roleId.value,
|
||||
model_id: modelId.value,
|
||||
chat_id: chatId.value,
|
||||
content: prompt.value,
|
||||
stream: stream.value
|
||||
}
|
||||
}));
|
||||
previousText.value = prompt.value;
|
||||
prompt.value = '';
|
||||
return true;
|
||||
@ -500,7 +554,7 @@ const sendMessage = () => {
|
||||
|
||||
const stopGenerate = () => {
|
||||
showStopGenerate.value = false;
|
||||
httpGet("/api/chat/stop?session_id=" + getSessionId()).then(() => {
|
||||
httpGet("/api/chat/stop?session_id=" + getClientId()).then(() => {
|
||||
enableInput()
|
||||
})
|
||||
}
|
||||
@ -515,7 +569,17 @@ const reGenerate = () => {
|
||||
icon: loginUser.value.avatar,
|
||||
content: renderInputText(text)
|
||||
});
|
||||
socket.value.send(JSON.stringify({stream: stream.value, content: previousText.value}));
|
||||
store.socket.conn.send(JSON.stringify({
|
||||
channel: 'chat',
|
||||
type:'text',
|
||||
body:{
|
||||
role_id: roleId.value,
|
||||
model_id: modelId.value,
|
||||
chat_id: chatId.value,
|
||||
content: previousText.value,
|
||||
stream: stream.value
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
const showShare = ref(false)
|
||||
|
@ -17,23 +17,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {ref} from "vue";
|
||||
import {getMobileTheme, setMobileTheme} from "@/store/system";
|
||||
import {useRouter} from "vue-router";
|
||||
import {isMobile} from "@/utils/libs";
|
||||
import bus from '@/store/eventbus'
|
||||
|
||||
const router = useRouter()
|
||||
if (!isMobile()) {
|
||||
router.replace('/')
|
||||
}
|
||||
import {ref, watch} from "vue";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const active = ref('home')
|
||||
const theme = ref(getMobileTheme())
|
||||
const store = useSharedStore()
|
||||
const theme = ref(store.mobileTheme)
|
||||
|
||||
bus.on('changeTheme', (value) => {
|
||||
theme.value = value
|
||||
setMobileTheme(theme.value)
|
||||
watch(() => store.mobileTheme, (val) => {
|
||||
theme.value = val
|
||||
})
|
||||
|
||||
</script>
|
||||
|
@ -109,7 +109,7 @@ onMounted(() => {
|
||||
})
|
||||
|
||||
const fetchApps = () => {
|
||||
httpGet("/api/app/list/user").then((res) => {
|
||||
httpGet("/api/app/list").then((res) => {
|
||||
const items = res.data
|
||||
// 处理 hello message
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
|
@ -60,7 +60,7 @@
|
||||
<span class="name">{{ item.name }}</span>
|
||||
<div class="pay-btn">
|
||||
<div v-for="payWay in payWays" @click="pay(item,payWay)" :key="payWay">
|
||||
<span v-if="payWay.pay_way === 'geek'">
|
||||
<span>
|
||||
<van-button type="primary" size="small" v-if="payWay.pay_type==='alipay'" >
|
||||
<i class="iconfont icon-alipay"></i> 支付宝
|
||||
</van-button>
|
||||
@ -125,7 +125,13 @@
|
||||
<van-cell-group inset>
|
||||
<van-field name="switch" label="暗黑主题">
|
||||
<template #input>
|
||||
<van-switch v-model="dark" @change="changeTheme"/>
|
||||
<van-switch v-model="dark" @change="(val) => store.setMobileTheme(val?'dark':'light')"/>
|
||||
</template>
|
||||
</van-field>
|
||||
|
||||
<van-field name="switch" label="流式输出">
|
||||
<template #input>
|
||||
<van-switch v-model="stream" @change="(val) => store.setChatStream(val)"/>
|
||||
</template>
|
||||
</van-field>
|
||||
<!-- <van-field-->
|
||||
@ -153,15 +159,12 @@
|
||||
import {onMounted, ref} from "vue";
|
||||
import {showFailToast, showLoadingToast, showNotify, showSuccessToast} from "vant";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import Compressor from 'compressorjs';
|
||||
import {dateFormat, isWeChatBrowser, showLoginDialog} from "@/utils/libs";
|
||||
import {dateFormat, showLoginDialog} from "@/utils/libs";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {checkSession, getSystemInfo} from "@/store/cache";
|
||||
import {useRouter} from "vue-router";
|
||||
import {removeUserToken} from "@/store/session";
|
||||
import bus from '@/store/eventbus'
|
||||
import {getMobileTheme} from "@/store/system";
|
||||
import QRCode from "qrcode";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const form = ref({
|
||||
username: 'GeekMaster',
|
||||
@ -184,6 +187,9 @@ const router = useRouter()
|
||||
const userId = ref(0)
|
||||
const isLogin = ref(false)
|
||||
const showSettings = ref(false)
|
||||
const store = useSharedStore()
|
||||
const stream = ref(store.chatStream)
|
||||
const dark = ref(store.mobileTheme === 'dark')
|
||||
|
||||
onMounted(() => {
|
||||
checkSession().then(user => {
|
||||
@ -317,18 +323,13 @@ const pay = (product,payWay) => {
|
||||
const logout = function () {
|
||||
httpGet('/api/user/logout').then(() => {
|
||||
removeUserToken();
|
||||
store.setIsLogin(false)
|
||||
router.push('/');
|
||||
}).catch(() => {
|
||||
showFailToast('注销失败!');
|
||||
})
|
||||
}
|
||||
|
||||
const dark = ref(getMobileTheme() === 'dark')
|
||||
|
||||
const changeTheme = () => {
|
||||
bus.emit('changeTheme', dark.value ? 'dark' : 'light')
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
|
@ -81,11 +81,9 @@
|
||||
description="暂无记录"
|
||||
/>
|
||||
<van-grid :gutter="10" :column-num="3" v-else>
|
||||
<van-grid-item v-for="item in runningJobs">
|
||||
<van-grid-item v-for="item in runningJobs" :key="item.id">
|
||||
<div v-if="item.progress > 0">
|
||||
<van-image :src="item['img_url']">
|
||||
<template v-slot:error>加载失败</template>
|
||||
</van-image>
|
||||
<van-image src="/images/img-holder.png"></van-image>
|
||||
<div class="progress">
|
||||
<van-circle
|
||||
v-model:current-rate="item.progress"
|
||||
@ -124,8 +122,15 @@
|
||||
@load="onLoad"
|
||||
>
|
||||
<van-grid :gutter="10" :column-num="2">
|
||||
<van-grid-item v-for="item in finishedJobs">
|
||||
<div class="job-item">
|
||||
<van-grid-item v-for="item in finishedJobs" :key="item.id">
|
||||
<div class="failed" v-if="item.progress === 101">
|
||||
<div class="title">任务失败</div>
|
||||
<div class="opt">
|
||||
<van-button size="small" @click="showErrMsg(item)">详情</van-button>
|
||||
<van-button type="danger" @click="removeImage($event,item)" size="small">删除</van-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-item" v-else>
|
||||
<van-image
|
||||
:src="item['img_url']"
|
||||
:class="item['can_opt'] ? '' : 'upscale'"
|
||||
@ -165,7 +170,7 @@ import {onMounted, onUnmounted, ref} from "vue"
|
||||
import {Delete} from "@element-plus/icons-vue";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import Clipboard from "clipboard";
|
||||
import {checkSession, getSystemInfo} from "@/store/cache";
|
||||
import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import {useRouter} from "vue-router";
|
||||
import {getSessionId} from "@/store/session";
|
||||
import {
|
||||
@ -178,10 +183,10 @@ import {
|
||||
showToast
|
||||
} from "vant";
|
||||
import {showLoginDialog} from "@/utils/libs";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const listBoxHeight = ref(window.innerHeight - 40)
|
||||
const mjBoxHeight = ref(window.innerHeight - 150)
|
||||
const item = ref({})
|
||||
const isLogin = ref(false)
|
||||
|
||||
window.onresize = () => {
|
||||
@ -203,6 +208,7 @@ const styles = [
|
||||
{text: "自然", value: "natural"}
|
||||
]
|
||||
const params = ref({
|
||||
client_id: getClientId(),
|
||||
quality: qualities[0].value,
|
||||
size: sizes[0].value,
|
||||
style: styles[0].value,
|
||||
@ -223,56 +229,8 @@ const router = useRouter()
|
||||
const power = ref(0)
|
||||
const dallPower = ref(0) // 画一张 DALL 图片消耗算力
|
||||
|
||||
const socket = ref(null)
|
||||
const userId = ref(0)
|
||||
const heartbeatHandle = ref(null)
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
|
||||
// 心跳函数
|
||||
const sendHeartbeat = () => {
|
||||
clearTimeout(heartbeatHandle.value)
|
||||
new Promise((resolve, reject) => {
|
||||
if (socket.value !== null) {
|
||||
socket.value.send(JSON.stringify({type: "heartbeat", content: "ping"}))
|
||||
}
|
||||
resolve("success")
|
||||
}).then(() => {
|
||||
heartbeatHandle.value = setTimeout(() => sendHeartbeat(), 5000)
|
||||
});
|
||||
}
|
||||
|
||||
const _socket = new WebSocket(host + `/api/dall/client?user_id=${userId.value}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
socket.value = _socket;
|
||||
|
||||
// 发送心跳消息
|
||||
sendHeartbeat()
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
if (event.data instanceof Blob) {
|
||||
fetchRunningJobs()
|
||||
finished.value = false
|
||||
page.value = 1
|
||||
fetchFinishJobs(page.value)
|
||||
}
|
||||
});
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
if (socket.value !== null) {
|
||||
connect()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const store = useSharedStore()
|
||||
const clipboard = ref(null)
|
||||
const prompt = ref('')
|
||||
onMounted(() => {
|
||||
@ -290,25 +248,32 @@ onMounted(() => {
|
||||
}).catch(e => {
|
||||
showNotify({type: "danger", message: "获取系统配置失败:" + e.message})
|
||||
})
|
||||
|
||||
store.addMessageHandler("dall", (data) => {
|
||||
if (data.channel !== "dall" || data.clientId !== getClientId()) {
|
||||
return
|
||||
}
|
||||
if (data.body === "FINISH" || data.body === "FAIL") {
|
||||
page.value = 1
|
||||
fetchFinishJobs(1)
|
||||
}
|
||||
fetchRunningJobs()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
clipboard.value.destroy()
|
||||
if (socket.value !== null) {
|
||||
socket.value.close()
|
||||
socket.value = null
|
||||
}
|
||||
store.removeMessageHandler("dall")
|
||||
})
|
||||
|
||||
|
||||
const initData = () => {
|
||||
checkSession().then(user => {
|
||||
power.value = user['power']
|
||||
userId.value = user.id
|
||||
isLogin.value = true
|
||||
fetchRunningJobs()
|
||||
fetchFinishJobs(1)
|
||||
connect()
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
});
|
||||
@ -317,20 +282,7 @@ const initData = () => {
|
||||
const fetchRunningJobs = () => {
|
||||
// 获取运行中的任务
|
||||
httpGet(`/api/dall/jobs?finish=0`).then(res => {
|
||||
const jobs = res.data.items
|
||||
const _jobs = []
|
||||
for (let i = 0; i < jobs.length; i++) {
|
||||
if (jobs[i].progress === -1) {
|
||||
showNotify({
|
||||
message: `任务ID:${jobs[i]['task_id']} 原因:${jobs[i]['err_msg']}`,
|
||||
type: 'danger',
|
||||
})
|
||||
power.value += dallPower.value
|
||||
continue
|
||||
}
|
||||
_jobs.push(jobs[i])
|
||||
}
|
||||
runningJobs.value = _jobs
|
||||
runningJobs.value = res.data.items
|
||||
}).catch(e => {
|
||||
showNotify({type: "danger", message: "获取任务失败:" + e.message})
|
||||
})
|
||||
@ -349,10 +301,17 @@ const fetchFinishJobs = (page) => {
|
||||
if (jobs.length < pageSize.value) {
|
||||
finished.value = true
|
||||
}
|
||||
const _jobs = []
|
||||
for (let i = 0; i < jobs.length; i++) {
|
||||
if (jobs[i].progress === -1) {
|
||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/600/q/75'
|
||||
}
|
||||
_jobs.push(jobs[i])
|
||||
}
|
||||
if (page === 1) {
|
||||
finishedJobs.value = jobs
|
||||
finishedJobs.value = _jobs
|
||||
} else {
|
||||
finishedJobs.value = finishedJobs.value.concat(jobs)
|
||||
finishedJobs.value = finishedJobs.value.concat(_jobs)
|
||||
}
|
||||
loading.value = false
|
||||
}).catch(e => {
|
||||
@ -385,6 +344,7 @@ const generate = () => {
|
||||
httpPost("/api/dall/image", params.value).then(() => {
|
||||
showSuccessToast("绘画任务推送成功,请耐心等待任务执行...")
|
||||
power.value -= dallPower.value
|
||||
fetchRunningJobs()
|
||||
}).catch(e => {
|
||||
showFailToast("任务推送失败:" + e.message)
|
||||
})
|
||||
@ -403,6 +363,15 @@ const showPrompt = (item) => {
|
||||
});
|
||||
}
|
||||
|
||||
const showErrMsg = (item) => {
|
||||
showDialog({
|
||||
title: '错误详情',
|
||||
message: item['err_msg'],
|
||||
}).then(() => {
|
||||
// on close
|
||||
});
|
||||
}
|
||||
|
||||
const removeImage = (event, item) => {
|
||||
event.stopPropagation()
|
||||
showConfirmDialog({
|
||||
@ -412,6 +381,7 @@ const removeImage = (event, item) => {
|
||||
}).then(() => {
|
||||
httpGet("/api/dall/remove", {id: item.id, user_id: item.user_id}).then(() => {
|
||||
showSuccessToast("任务删除成功")
|
||||
fetchFinishJobs(1)
|
||||
}).catch(e => {
|
||||
showFailToast("任务删除失败:" + e.message)
|
||||
})
|
||||
@ -458,14 +428,6 @@ const sizeConfirm =(item) => {
|
||||
showSizePicker.value =false
|
||||
}
|
||||
|
||||
const showInfo = (message) => {
|
||||
showDialog({
|
||||
title: "参数说明",
|
||||
message: message,
|
||||
}).then(() => {
|
||||
// on close
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="stylus">
|
||||
|
@ -180,11 +180,9 @@
|
||||
description="暂无记录"
|
||||
/>
|
||||
<van-grid :gutter="10" :column-num="3" v-else>
|
||||
<van-grid-item v-for="item in runningJobs">
|
||||
<van-grid-item v-for="item in runningJobs" :key="item.id">
|
||||
<div v-if="item.progress > 0">
|
||||
<van-image :src="item['img_url']">
|
||||
<template v-slot:error>加载失败</template>
|
||||
</van-image>
|
||||
<van-image src="/images/img-holder.png"></van-image>
|
||||
<div class="progress">
|
||||
<van-circle
|
||||
v-model:current-rate="item.progress"
|
||||
@ -223,8 +221,15 @@
|
||||
@load="onLoad"
|
||||
>
|
||||
<van-grid :gutter="10" :column-num="2">
|
||||
<van-grid-item v-for="item in finishedJobs">
|
||||
<div class="job-item">
|
||||
<van-grid-item v-for="item in finishedJobs" :key="item.id">
|
||||
<div class="failed" v-if="item.progress === 101">
|
||||
<div class="title">任务失败</div>
|
||||
<div class="opt">
|
||||
<van-button size="small" @click="showErrMsg(item)">详情</van-button>
|
||||
<van-button type="danger" @click="removeImage(item)" size="small">删除</van-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-item" v-else>
|
||||
<van-image
|
||||
:src="item['thumb_url']"
|
||||
:class="item['can_opt'] ? '' : 'upscale'"
|
||||
@ -234,6 +239,10 @@
|
||||
<template v-slot:loading>
|
||||
<van-loading type="spinner" size="20"/>
|
||||
</template>
|
||||
<template v-slot:error>
|
||||
<span style="margin-bottom: 20px">正在下载图片</span>
|
||||
<van-loading type="circular" color="#1989fa" size="40"/>
|
||||
</template>
|
||||
</van-image>
|
||||
|
||||
<div class="opt" v-if="item['can_opt']">
|
||||
@ -276,15 +285,16 @@
|
||||
|
||||
<script setup>
|
||||
import {nextTick, onMounted, onUnmounted, ref} from "vue";
|
||||
import {showConfirmDialog, showFailToast, showImagePreview, showNotify, showSuccessToast, showToast} from "vant";
|
||||
import {showConfirmDialog, showFailToast, showImagePreview, showNotify, showSuccessToast, showToast,showDialog } from "vant";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import Compressor from "compressorjs";
|
||||
import {getSessionId} from "@/store/session";
|
||||
import {checkSession, getSystemInfo} from "@/store/cache";
|
||||
import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import {useRouter} from "vue-router";
|
||||
import {Delete} from "@element-plus/icons-vue";
|
||||
import {showLoginDialog} from "@/utils/libs";
|
||||
import Clipboard from "clipboard";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const activeColspan = ref([""])
|
||||
|
||||
@ -306,6 +316,7 @@ const models = [
|
||||
]
|
||||
const imgList = ref([])
|
||||
const params = ref({
|
||||
client_id: getClientId(),
|
||||
task_type: "image",
|
||||
rate: rates[0].value,
|
||||
model: models[0].value,
|
||||
@ -327,11 +338,11 @@ const userId = ref(0)
|
||||
const router = useRouter()
|
||||
const runningJobs = ref([])
|
||||
const finishedJobs = ref([])
|
||||
const socket = ref(null)
|
||||
const power = ref(0)
|
||||
const activeName = ref("txt2img")
|
||||
const isLogin = ref(false)
|
||||
const prompt = ref('')
|
||||
const store = useSharedStore()
|
||||
const clipboard = ref(null)
|
||||
|
||||
onMounted(() => {
|
||||
@ -349,19 +360,26 @@ onMounted(() => {
|
||||
isLogin.value = true
|
||||
fetchRunningJobs()
|
||||
fetchFinishJobs(1)
|
||||
connect()
|
||||
|
||||
}).catch(() => {
|
||||
// router.push('/login')
|
||||
});
|
||||
|
||||
store.addMessageHandler("mj", (data) => {
|
||||
if (data.channel !== "mj" || data.clientId !== getClientId()) {
|
||||
return
|
||||
}
|
||||
if (data.body === "FINISH" || data.body === "FAIL") {
|
||||
page.value = 1
|
||||
fetchFinishJobs(1)
|
||||
}
|
||||
fetchRunningJobs()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
clipboard.value.destroy()
|
||||
if (socket.value !== null) {
|
||||
socket.value.close()
|
||||
socket.value = null
|
||||
}
|
||||
store.removeMessageHandler("mj")
|
||||
})
|
||||
|
||||
const mjPower = ref(1)
|
||||
@ -373,60 +391,6 @@ getSystemInfo().then(res => {
|
||||
showNotify({type: "danger", message: "获取系统配置失败:" + e.message})
|
||||
})
|
||||
|
||||
const heartbeatHandle = ref(null)
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
|
||||
// 心跳函数
|
||||
const sendHeartbeat = () => {
|
||||
clearTimeout(heartbeatHandle.value)
|
||||
new Promise((resolve, reject) => {
|
||||
if (socket.value !== null) {
|
||||
socket.value.send(JSON.stringify({type: "heartbeat", content: "ping"}))
|
||||
}
|
||||
resolve("success")
|
||||
}).then(() => {
|
||||
heartbeatHandle.value = setTimeout(() => sendHeartbeat(), 5000)
|
||||
});
|
||||
}
|
||||
|
||||
const _socket = new WebSocket(host + `/api/mj/client?user_id=${userId.value}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
socket.value = _socket;
|
||||
|
||||
// 发送心跳消息
|
||||
sendHeartbeat()
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
if (event.data instanceof Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.data, "UTF-8")
|
||||
reader.onload = () => {
|
||||
const message = String(reader.result)
|
||||
if (message === "FINISH" || message === "FAIL") {
|
||||
page.value = 1
|
||||
fetchFinishJobs(1)
|
||||
}
|
||||
fetchRunningJobs()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
if (socket.value !== null) {
|
||||
connect()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 获取运行中的任务
|
||||
const fetchRunningJobs = (userId) => {
|
||||
httpGet(`/api/mj/jobs?finish=0&user_id=${userId}`).then(res => {
|
||||
@ -464,27 +428,10 @@ const fetchFinishJobs = (page) => {
|
||||
httpGet(`/api/mj/jobs?finish=1&page=${page}&page_size=${pageSize.value}`).then(res => {
|
||||
const jobs = res.data.items
|
||||
for (let i = 0; i < jobs.length; i++) {
|
||||
if (jobs[i].progress === 101) {
|
||||
showNotify({
|
||||
message: `任务ID:${jobs[i]['task_id']} 原因:${jobs[i]['err_msg']}`,
|
||||
type: 'danger',
|
||||
})
|
||||
if (jobs[i].type === 'image') {
|
||||
power.value += mjPower.value
|
||||
} else {
|
||||
power.value += mjActionPower.value
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (jobs[i]['use_proxy']) {
|
||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?x-oss-process=image/quality,q_60&format=webp'
|
||||
if (jobs[i].type === 'upscale' || jobs[i].type === 'swapFace') {
|
||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/600/q/75'
|
||||
} else {
|
||||
if (jobs[i].type === 'upscale' || jobs[i].type === 'swapFace') {
|
||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/600/q/75'
|
||||
} else {
|
||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/480/q/75'
|
||||
}
|
||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/480/q/75'
|
||||
}
|
||||
|
||||
if ((jobs[i].type === 'image' || jobs[i].type === 'variation') && jobs[i].progress === 100){
|
||||
@ -557,6 +504,7 @@ const uploadImg = (file) => {
|
||||
|
||||
const send = (url, index, item) => {
|
||||
httpPost(url, {
|
||||
client_id: getClientId(),
|
||||
index: index,
|
||||
channel_id: item.channel_id,
|
||||
message_id: item.message_id,
|
||||
@ -566,6 +514,7 @@ const send = (url, index, item) => {
|
||||
}).then(() => {
|
||||
showSuccessToast("任务推送成功,请耐心等待任务执行...")
|
||||
power.value -= mjActionPower.value
|
||||
fetchRunningJobs()
|
||||
}).catch(e => {
|
||||
showFailToast("任务推送失败:" + e.message)
|
||||
})
|
||||
@ -597,6 +546,7 @@ const generate = () => {
|
||||
httpPost("/api/mj/image", params.value).then(() => {
|
||||
showToast("绘画任务推送成功,请耐心等待任务执行")
|
||||
power.value -= mjPower.value
|
||||
fetchRunningJobs()
|
||||
}).catch(e => {
|
||||
showFailToast("任务推送失败:" + e.message)
|
||||
})
|
||||
@ -604,12 +554,13 @@ const generate = () => {
|
||||
|
||||
const removeImage = (item) => {
|
||||
showConfirmDialog({
|
||||
title: '标题',
|
||||
title: '删除提示',
|
||||
message:
|
||||
'此操作将会删除任务和图片,继续操作码?',
|
||||
}).then(() => {
|
||||
httpGet("/api/mj/remove", {id: item.id, user_id: item.user_id}).then(() => {
|
||||
showSuccessToast("任务删除成功")
|
||||
fetchFinishJobs(1)
|
||||
}).catch(e => {
|
||||
showFailToast("任务删除失败:" + e.message)
|
||||
})
|
||||
@ -644,6 +595,15 @@ const showPrompt = (item) => {
|
||||
});
|
||||
}
|
||||
|
||||
const showErrMsg = (item) => {
|
||||
showDialog({
|
||||
title: '错误详情',
|
||||
message: item['err_msg'],
|
||||
}).then(() => {
|
||||
// on close
|
||||
});
|
||||
}
|
||||
|
||||
const imageView = (item) => {
|
||||
showImagePreview([item['img_url']]);
|
||||
}
|
||||
|
@ -133,11 +133,9 @@
|
||||
description="暂无记录"
|
||||
/>
|
||||
<van-grid :gutter="10" :column-num="3" v-else>
|
||||
<van-grid-item v-for="item in runningJobs">
|
||||
<van-grid-item v-for="item in runningJobs" :key="item.id">
|
||||
<div v-if="item.progress > 0">
|
||||
<van-image :src="item['img_url']">
|
||||
<template v-slot:error>加载失败</template>
|
||||
</van-image>
|
||||
<van-image src="/images/img-holder.png"></van-image>
|
||||
<div class="progress">
|
||||
<van-circle
|
||||
v-model:current-rate="item.progress"
|
||||
@ -176,8 +174,15 @@
|
||||
@load="onLoad"
|
||||
>
|
||||
<van-grid :gutter="10" :column-num="2">
|
||||
<van-grid-item v-for="item in finishedJobs">
|
||||
<div class="job-item">
|
||||
<van-grid-item v-for="item in finishedJobs" :key="item.id">
|
||||
<div class="failed" v-if="item.progress === 101">
|
||||
<div class="title">任务失败</div>
|
||||
<div class="opt">
|
||||
<van-button size="small" @click="showErrMsg(item)">详情</van-button>
|
||||
<van-button type="danger" @click="removeImage($event,item)" size="small">删除</van-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="job-item" v-else>
|
||||
<van-image
|
||||
:src="item['img_url']"
|
||||
:class="item['can_opt'] ? '' : 'upscale'"
|
||||
@ -217,7 +222,7 @@ import {onMounted, onUnmounted, ref} from "vue"
|
||||
import {Delete} from "@element-plus/icons-vue";
|
||||
import {httpGet, httpPost} from "@/utils/http";
|
||||
import Clipboard from "clipboard";
|
||||
import {checkSession, getSystemInfo} from "@/store/cache";
|
||||
import {checkSession, getClientId, getSystemInfo} from "@/store/cache";
|
||||
import {useRouter} from "vue-router";
|
||||
import {getSessionId} from "@/store/session";
|
||||
import {
|
||||
@ -230,10 +235,10 @@ import {
|
||||
showToast
|
||||
} from "vant";
|
||||
import {showLoginDialog} from "@/utils/libs";
|
||||
import {useSharedStore} from "@/store/sharedata";
|
||||
|
||||
const listBoxHeight = ref(window.innerHeight - 40)
|
||||
const mjBoxHeight = ref(window.innerHeight - 150)
|
||||
const item = ref({})
|
||||
const isLogin = ref(false)
|
||||
const activeColspan = ref([""])
|
||||
|
||||
@ -261,6 +266,7 @@ const upscaleAlgArr = ref([
|
||||
const showUpscalePicker = ref(false)
|
||||
|
||||
const params = ref({
|
||||
client_id: getClientId(),
|
||||
width: 1024,
|
||||
height: 1024,
|
||||
sampler: samplers.value[0].value,
|
||||
@ -287,56 +293,8 @@ if (_params) {
|
||||
const power = ref(0)
|
||||
const sdPower = ref(0) // 画一张 SD 图片消耗算力
|
||||
|
||||
const socket = ref(null)
|
||||
const userId = ref(0)
|
||||
const heartbeatHandle = ref(null)
|
||||
const connect = () => {
|
||||
let host = process.env.VUE_APP_WS_HOST
|
||||
if (host === '') {
|
||||
if (location.protocol === 'https:') {
|
||||
host = 'wss://' + location.host;
|
||||
} else {
|
||||
host = 'ws://' + location.host;
|
||||
}
|
||||
}
|
||||
|
||||
// 心跳函数
|
||||
const sendHeartbeat = () => {
|
||||
clearTimeout(heartbeatHandle.value)
|
||||
new Promise((resolve, reject) => {
|
||||
if (socket.value !== null) {
|
||||
socket.value.send(JSON.stringify({type: "heartbeat", content: "ping"}))
|
||||
}
|
||||
resolve("success")
|
||||
}).then(() => {
|
||||
heartbeatHandle.value = setTimeout(() => sendHeartbeat(), 5000)
|
||||
});
|
||||
}
|
||||
|
||||
const _socket = new WebSocket(host + `/api/sd/client?user_id=${userId.value}`);
|
||||
_socket.addEventListener('open', () => {
|
||||
socket.value = _socket;
|
||||
|
||||
// 发送心跳消息
|
||||
sendHeartbeat()
|
||||
});
|
||||
|
||||
_socket.addEventListener('message', event => {
|
||||
if (event.data instanceof Blob) {
|
||||
fetchRunningJobs()
|
||||
finished.value = false
|
||||
page.value = 1
|
||||
fetchFinishJobs(page.value)
|
||||
}
|
||||
});
|
||||
|
||||
_socket.addEventListener('close', () => {
|
||||
if (socket.value !== null) {
|
||||
connect()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const store = useSharedStore()
|
||||
const clipboard = ref(null)
|
||||
const prompt = ref('')
|
||||
onMounted(() => {
|
||||
@ -355,14 +313,23 @@ onMounted(() => {
|
||||
}).catch(e => {
|
||||
showNotify({type: "danger", message: "获取系统配置失败:" + e.message})
|
||||
})
|
||||
|
||||
store.addMessageHandler("sd", (data) => {
|
||||
if (data.channel !== "sd" || data.clientId !== getClientId()) {
|
||||
return
|
||||
}
|
||||
if (data.body === "FINISH" || data.body === "FAIL") {
|
||||
page.value = 1
|
||||
fetchFinishJobs(1)
|
||||
}
|
||||
fetchRunningJobs()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
clipboard.value.destroy()
|
||||
if (socket.value !== null) {
|
||||
socket.value.close()
|
||||
socket.value = null
|
||||
}
|
||||
store.removeMessageHandler("sd")
|
||||
})
|
||||
|
||||
|
||||
@ -373,7 +340,6 @@ const initData = () => {
|
||||
isLogin.value = true
|
||||
fetchRunningJobs()
|
||||
fetchFinishJobs(1)
|
||||
connect()
|
||||
}).catch(() => {
|
||||
loading.value = false
|
||||
});
|
||||
@ -414,10 +380,17 @@ const fetchFinishJobs = (page) => {
|
||||
if (jobs.length < pageSize.value) {
|
||||
finished.value = true
|
||||
}
|
||||
const _jobs = []
|
||||
for (let i = 0; i < jobs.length; i++) {
|
||||
if (jobs[i].progress === -1) {
|
||||
jobs[i]['thumb_url'] = jobs[i]['img_url'] + '?imageView2/1/w/480/h/600/q/75'
|
||||
}
|
||||
_jobs.push(jobs[i])
|
||||
}
|
||||
if (page === 1) {
|
||||
finishedJobs.value = jobs
|
||||
finishedJobs.value = _jobs
|
||||
} else {
|
||||
finishedJobs.value = finishedJobs.value.concat(jobs)
|
||||
finishedJobs.value = finishedJobs.value.concat(_jobs)
|
||||
}
|
||||
loading.value = false
|
||||
}).catch(e => {
|
||||
@ -450,6 +423,7 @@ const generate = () => {
|
||||
httpPost("/api/sd/image", params.value).then(() => {
|
||||
showSuccessToast("绘画任务推送成功,请耐心等待任务执行...")
|
||||
power.value -= sdPower.value
|
||||
fetchRunningJobs()
|
||||
}).catch(e => {
|
||||
showFailToast("任务推送失败:" + e.message)
|
||||
})
|
||||
@ -468,6 +442,15 @@ const showPrompt = (item) => {
|
||||
});
|
||||
}
|
||||
|
||||
const showErrMsg = (item) => {
|
||||
showDialog({
|
||||
title: '错误详情',
|
||||
message: item['err_msg'],
|
||||
}).then(() => {
|
||||
// on close
|
||||
});
|
||||
}
|
||||
|
||||
const removeImage = (event, item) => {
|
||||
event.stopPropagation()
|
||||
showConfirmDialog({
|
||||
@ -477,6 +460,7 @@ const removeImage = (event, item) => {
|
||||
}).then(() => {
|
||||
httpGet("/api/sd/remove", {id: item.id, user_id: item.user}).then(() => {
|
||||
showSuccessToast("任务删除成功")
|
||||
fetchFinishJobs(1)
|
||||
}).catch(e => {
|
||||
showFailToast("任务删除失败:" + e.message)
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user