diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c4fba9d..cd735b04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,15 @@ # 更新日志 +## v4.0.9 +* 环境升级:升级 Golang 到 go1.22.4 +* 功能增加:接入微信商户号支付渠道 +* Bug修复:修复前端页面菜单把页面撑开,底部留白问题 +* 功能优化:聊天页面自动根据内容调整输入框的高度 +* Bug修复:修复Dalle绘图失败退回算力的问题 +* 功能优化:邀请码注册时被邀请人也可以获得赠送的算力 +* 功能优化:允许设置邮件验证码的抬头 +* Bug修复:修复免费模型不会记录聊天记录的bug +* Bug修复:修复聊天输入公式显示异常的Bug + ## v4.0.8 * 功能优化:升级 mathjax 公式解析插件,修复公式因为图片访问限制而无法显示的问题 * 功能优化:当数据库更新失败的时候记录错误日志 diff --git a/api/.gitignore b/api/.gitignore index 7502cf69..45aded04 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -17,4 +17,5 @@ bin data config.toml static/upload -storage.json +storage.json +res/certs/wechat/apiclient_key.pem diff --git a/api/core/app_server.go b/api/core/app_server.go index 8ba434fa..6d236e73 100644 --- a/api/core/app_server.go +++ b/api/core/app_server.go @@ -233,6 +233,7 @@ func needLogin(c *gin.Context) bool { c.Request.URL.Path == "/api/payment/alipay/notify" || c.Request.URL.Path == "/api/payment/hupipay/notify" || c.Request.URL.Path == "/api/payment/payjs/notify" || + c.Request.URL.Path == "/api/payment/wechat/notify" || c.Request.URL.Path == "/api/payment/doPay" || c.Request.URL.Path == "/api/payment/payWays" || strings.HasPrefix(c.Request.URL.Path, "/api/test") || diff --git a/api/core/types/config.go b/api/core/types/config.go index c9881999..ccd1dada 100644 --- a/api/core/types/config.go +++ b/api/core/types/config.go @@ -29,11 +29,12 @@ type AppConfig struct { WeChatBot bool // 是否启用微信机器人 SdConfigs []StableDiffusionConfig // sd AI draw service pool - XXLConfig XXLConfig - AlipayConfig AlipayConfig - HuPiPayConfig HuPiPayConfig - SmtpConfig SmtpConfig // 邮件发送配置 - JPayConfig JPayConfig // payjs 支付配置 + XXLConfig XXLConfig + AlipayConfig AlipayConfig // 支付宝支付渠道配置 + HuPiPayConfig HuPiPayConfig // 虎皮椒支付配置 + SmtpConfig SmtpConfig // 邮件发送配置 + JPayConfig JPayConfig // payjs 支付配置 + WechatPayConfig WechatPayConfig // 微信支付渠道配置 } type SmtpConfig struct { @@ -85,6 +86,17 @@ type AlipayConfig struct { ReturnURL string // 支付成功返回地址 } +type WechatPayConfig struct { + Enabled bool // 是否启用该支付通道 + AppId string // 公众号的APPID,如:wxd678efh567hg6787 + MchId string // 直连商户的商户号,由微信支付生成并下发 + SerialNo string // 商户证书的证书序列号 + PrivateKey string // 用户私钥文件路径 + ApiV3Key string // API V3 秘钥 + NotifyURL string // 异步通知回调 + ReturnURL string // 支付成功返回地址 +} + type HuPiPayConfig struct { //虎皮椒第四方支付配置 Enabled bool // 是否启用该支付通道 Name string // 支付名称,如:wechat/alipay @@ -182,8 +194,9 @@ var QWen = Platform{ } type SystemConfig struct { - Title string `json:"title,omitempty"` - AdminTitle string `json:"admin_title,omitempty"` + Title string `json:"title,omitempty"` // 网站标题 + Slogan string `json:"slogan,omitempty"` // 网站 slogan + AdminTitle string `json:"admin_title,omitempty"` // 管理后台标题 Logo string `json:"logo,omitempty"` InitPower int `json:"init_power,omitempty"` // 新用户注册赠送算力值 DailyPower int `json:"daily_power,omitempty"` // 每日赠送算力 diff --git a/api/core/types/task.go b/api/core/types/task.go index 6b6a364c..6224a383 100644 --- a/api/core/types/task.go +++ b/api/core/types/task.go @@ -28,7 +28,6 @@ type MjTask struct { TaskId string `json:"task_id"` ImgArr []string `json:"img_arr"` ChannelId string `json:"channel_id"` - SessionId string `json:"session_id"` Type TaskType `json:"type"` UserId int `json:"user_id"` Prompt string `json:"prompt,omitempty"` @@ -42,7 +41,6 @@ type MjTask struct { type SdTask struct { Id int `json:"id"` // job 数据库ID - SessionId string `json:"session_id"` Type TaskType `json:"type"` UserId int `json:"user_id"` Params SdTaskParams `json:"params"` diff --git a/api/go.mod b/api/go.mod index 3f080383..f3946434 100644 --- a/api/go.mod +++ b/api/go.mod @@ -19,7 +19,6 @@ require ( github.com/pkoukk/tiktoken-go v0.1.1-0.20230418101013-cae809389480 github.com/qiniu/go-sdk/v7 v7.17.1 github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e - github.com/smartwalle/alipay/v3 v3.2.15 go.uber.org/zap v1.23.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 gorm.io/driver/mysql v1.4.7 @@ -28,6 +27,7 @@ require ( require github.com/xxl-job/xxl-job-executor-go v1.2.0 require ( + github.com/go-pay/gopay v1.5.101 github.com/mojocn/base64Captcha v1.3.1 github.com/shirou/gopsutil v3.21.11+incompatible github.com/shopspring/decimal v1.3.1 @@ -37,6 +37,11 @@ require ( require ( github.com/go-ole/go-ole v1.2.6 // indirect + github.com/go-pay/crypto v0.0.1 // indirect + github.com/go-pay/errgroup v0.0.2 // indirect + github.com/go-pay/util v0.0.2 // indirect + github.com/go-pay/xlog v0.0.2 // indirect + github.com/go-pay/xtime v0.0.2 // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db // indirect github.com/tklauser/go-sysconf v0.3.14 // indirect @@ -79,9 +84,6 @@ require ( github.com/refraction-networking/utls v1.3.2 // indirect github.com/rs/xid v1.5.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/smartwalle/ncrypto v1.0.2 // indirect - github.com/smartwalle/ngx v1.0.6 // indirect - github.com/smartwalle/nsign v1.0.8 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect go.uber.org/dig v1.16.1 // indirect golang.org/x/arch v0.3.0 // indirect diff --git a/api/go.sum b/api/go.sum index 8c7ec594..0bc389d3 100644 --- a/api/go.sum +++ b/api/go.sum @@ -45,6 +45,18 @@ github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-pay/crypto v0.0.1 h1:B6InT8CLfSLc6nGRVx9VMJRBBazFMjr293+jl0lLXUY= +github.com/go-pay/crypto v0.0.1/go.mod h1:41oEIvHMKbNcYlWUlRWtsnC6+ASgh7u29z0gJXe5bes= +github.com/go-pay/errgroup v0.0.2 h1:5mZMdm0TDClDm2S3G0/sm0f8AuQRtz0dOrTHDR9R8Cc= +github.com/go-pay/errgroup v0.0.2/go.mod h1:0+4b8mvFMS71MIzsaC+gVvB4x37I93lRb2dqrwuU8x8= +github.com/go-pay/gopay v1.5.101 h1:rVb+sfv6hiQtknAlZnTTLvU27NvFJ4p0yglN/vPpGXI= +github.com/go-pay/gopay v1.5.101/go.mod h1:AW4Yj8jDZX9BM1/GTLTY1Gy5SHjiq8kQvG5sBTN2sxI= +github.com/go-pay/util v0.0.2 h1:goJ4f6kNY5zzdtg1Cj8oWC+Cw7bfg/qq2rJangMAb9U= +github.com/go-pay/util v0.0.2/go.mod h1:qM8VbyF1n7YAPZBSJONSPMPsPedhUTktewUAdf1AjPg= +github.com/go-pay/xlog v0.0.2 h1:kUg5X8/5VZAPDg1J5eGjA3MG0/H5kK6Ew0dW/Bycsws= +github.com/go-pay/xlog v0.0.2/go.mod h1:DbjMADPK4+Sjxj28ekK9goqn4zmyY4hql/zRiab+S9E= +github.com/go-pay/xtime v0.0.2 h1:7YR4/iuELsEHpJ6LUO0SVK80hQxDO9MLCfuVYIiTCRM= +github.com/go-pay/xtime v0.0.2/go.mod h1:W1yRbJaSt4CSBcdAtLBQ8xajiN/Pl5hquGczUcUE9xE= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= @@ -186,14 +198,6 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= -github.com/smartwalle/alipay/v3 v3.2.15 h1:3fvFJnINKKAOXHR/Iv20k1Z7KJ+nOh3oK214lELPqG8= -github.com/smartwalle/alipay/v3 v3.2.15/go.mod h1:niTNB609KyUYuAx9Bex/MawEjv2yPx4XOjxSAkqmGjE= -github.com/smartwalle/ncrypto v1.0.2 h1:pTAhCqtPCMhpOwFXX+EcMdR6PNzruBNoGQrN2S1GbGI= -github.com/smartwalle/ncrypto v1.0.2/go.mod h1:Dwlp6sfeNaPMnOxMNayMTacvC5JGEVln3CVdiVDgbBk= -github.com/smartwalle/ngx v1.0.6 h1:JPNqNOIj+2nxxFtrSkJO+vKJfeNUSEQueck/Wworjps= -github.com/smartwalle/ngx v1.0.6/go.mod h1:mx/nz2Pk5j+RBs7t6u6k22MPiBG/8CtOMpCnALIG8Y0= -github.com/smartwalle/nsign v1.0.8 h1:78KWtwKPrdt4Xsn+tNEBVxaTLIJBX9YRX0ZSrMUeuHo= -github.com/smartwalle/nsign v1.0.8/go.mod h1:eY6I4CJlyNdVMP+t6z1H6Jpd4m5/V+8xi44ufSTxXgc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= diff --git a/api/handler/chatimpl/chat_handler.go b/api/handler/chatimpl/chat_handler.go index 4ad6965a..bd6ab8d3 100644 --- a/api/handler/chatimpl/chat_handler.go +++ b/api/handler/chatimpl/chat_handler.go @@ -330,7 +330,7 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSessio Content: prompt, }) req.Input["messages"] = reqMgs - } else if session.Model.Platform == types.OpenAI.Value { // extract image for gpt-vision model + } else if session.Model.Platform == types.OpenAI.Value || session.Model.Platform == types.Azure.Value { // extract image for gpt-vision model imgURLs := utils.ExtractImgURL(prompt) logger.Debugf("detected IMG: %+v", imgURLs) var content interface{} @@ -651,26 +651,25 @@ func (h *ChatHandler) saveChatHistory( logger.Error("failed to save reply history message: ", res.Error) } + // 更新用户算力 if session.Model.Power > 0 { - // 更新用户算力 h.subUserPower(userVo, session, promptToken, replyTokens) - - // 保存当前会话 - var chatItem model.ChatItem - res = h.DB.Where("chat_id = ?", session.ChatId).First(&chatItem) - if res.Error != nil { - chatItem.ChatId = session.ChatId - chatItem.UserId = session.UserId - chatItem.RoleId = role.Id - chatItem.ModelId = session.Model.Id - if utf8.RuneCountInString(prompt) > 30 { - chatItem.Title = string([]rune(prompt)[:30]) + "..." - } else { - chatItem.Title = prompt - } - chatItem.Model = req.Model - h.DB.Create(&chatItem) + } + // 保存当前会话 + var chatItem model.ChatItem + res = h.DB.Where("chat_id = ?", session.ChatId).First(&chatItem) + if res.Error != nil { + chatItem.ChatId = session.ChatId + chatItem.UserId = session.UserId + chatItem.RoleId = role.Id + chatItem.ModelId = session.Model.Id + if utf8.RuneCountInString(prompt) > 30 { + chatItem.Title = string([]rune(prompt)[:30]) + "..." + } else { + chatItem.Title = prompt } + chatItem.Model = req.Model + h.DB.Create(&chatItem) } } diff --git a/api/handler/chatimpl/openai_handler.go b/api/handler/chatimpl/openai_handler.go index fb953b79..86089410 100644 --- a/api/handler/chatimpl/openai_handler.go +++ b/api/handler/chatimpl/openai_handler.go @@ -142,7 +142,7 @@ func (h *ChatHandler) sendOpenAiMessage( } if toolCall { // 调用函数完成任务 - var params map[string]interface{} + params := make(map[string]interface{}) _ = utils.JsonDecode(strings.Join(arguments, ""), ¶ms) logger.Debugf("函数名称: %s, 函数参数:%s", function.Name, params) params["user_id"] = userVo.Id diff --git a/api/handler/markmap_handler.go b/api/handler/markmap_handler.go index bf67ab7b..d6565444 100644 --- a/api/handler/markmap_handler.go +++ b/api/handler/markmap_handler.go @@ -183,45 +183,29 @@ func (h *MarkMapHandler) sendMessage(client *types.WsClient, prompt string, mode utils.ReplyChunkMessage(client, types.WsMessage{Type: types.WsEnd}) } else { - body, err := io.ReadAll(response.Body) - if err != nil { - return fmt.Errorf("读取响应失败: %v", err) - } - var res types.ApiError - err = json.Unmarshal(body, &res) - if err != nil { - return fmt.Errorf("解析响应失败: %v", err) - } - - // OpenAI API 调用异常处理 - if strings.Contains(res.Error.Message, "This key is associated with a deactivated account") { - // remove key - h.DB.Where("value = ?", apiKey).Delete(&model.ApiKey{}) - return errors.New("请求 OpenAI API 失败:API KEY 所关联的账户被禁用。") - } else if strings.Contains(res.Error.Message, "You exceeded your current quota") { - return errors.New("请求 OpenAI API 失败:API KEY 触发并发限制,请稍后再试。") - } else { - return fmt.Errorf("请求 OpenAI API 失败:%v", res.Error.Message) - } + body, _ := io.ReadAll(response.Body) + return fmt.Errorf("请求 OpenAI API 失败:%s", string(body)) } // 扣减算力 - res = h.DB.Model(&model.User{}).Where("id", userId).UpdateColumn("power", gorm.Expr("power - ?", chatModel.Power)) - if res.Error == nil { - // 记录算力消费日志 - var u model.User - h.DB.Where("id", userId).First(&u) - h.DB.Create(&model.PowerLog{ - UserId: u.Id, - Username: u.Username, - Type: types.PowerConsume, - Amount: chatModel.Power, - Mark: types.PowerSub, - Balance: u.Power, - Model: chatModel.Value, - Remark: fmt.Sprintf("AI绘制思维导图,模型名称:%s, ", chatModel.Value), - CreatedAt: time.Now(), - }) + if chatModel.Power > 0 { + res = h.DB.Model(&model.User{}).Where("id", userId).UpdateColumn("power", gorm.Expr("power - ?", chatModel.Power)) + if res.Error == nil { + // 记录算力消费日志 + var u model.User + h.DB.Where("id", userId).First(&u) + h.DB.Create(&model.PowerLog{ + UserId: u.Id, + Username: u.Username, + Type: types.PowerConsume, + Amount: chatModel.Power, + Mark: types.PowerSub, + Balance: u.Power, + Model: chatModel.Value, + Remark: fmt.Sprintf("AI绘制思维导图,模型名称:%s, ", chatModel.Value), + CreatedAt: time.Now(), + }) + } } return nil @@ -235,7 +219,7 @@ func (h *MarkMapHandler) doRequest(req types.ApiRequest, chatModel model.ChatMod } // use the last unused key if apiKey.Id == 0 { - res = h.DB.Where("platform", types.OpenAI). + res = h.DB.Where("platform", types.OpenAI.Value). Where("type", "chat"). Where("enabled", true).Order("last_used_at ASC").First(apiKey) } diff --git a/api/handler/mj_handler.go b/api/handler/mj_handler.go index 822df42a..7b8d7ca0 100644 --- a/api/handler/mj_handler.go +++ b/api/handler/mj_handler.go @@ -205,7 +205,6 @@ func (h *MidJourneyHandler) Image(c *gin.Context) { h.pool.PushTask(types.MjTask{ Id: job.Id, TaskId: taskId, - SessionId: data.SessionId, Type: types.TaskType(data.TaskType), Prompt: data.Prompt, NegPrompt: data.NegPrompt, @@ -283,7 +282,6 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) { h.pool.PushTask(types.MjTask{ Id: job.Id, - SessionId: data.SessionId, Type: types.TaskUpscale, Prompt: data.Prompt, UserId: userId, @@ -350,7 +348,6 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) { h.pool.PushTask(types.MjTask{ Id: job.Id, - SessionId: data.SessionId, Type: types.TaskVariation, Prompt: data.Prompt, UserId: userId, diff --git a/api/handler/payment_handler.go b/api/handler/payment_handler.go index 9c186517..c69f0703 100644 --- a/api/handler/payment_handler.go +++ b/api/handler/payment_handler.go @@ -29,39 +29,48 @@ import ( "gorm.io/gorm" ) -const ( - PayWayAlipay = "支付宝" - PayWayXunHu = "虎皮椒" - PayWayJs = "PayJS" +type PayWay struct { + Name string `json:"name"` + Value string `json:"value"` +} + +var ( + PayWayAlipay = PayWay{Name: "支付宝", Value: "alipay"} + PayWayXunHu = PayWay{Name: "虎皮椒", Value: "hupi"} + PayWayJs = PayWay{Name: "PayJS", Value: "payjs"} + PayWayWechat = PayWay{Name: "微信支付", Value: "wechat"} ) // PaymentHandler 支付服务回调 handler type PaymentHandler struct { BaseHandler - alipayService *payment.AlipayService - huPiPayService *payment.HuPiPayService - js *payment.PayJS - snowflake *service.Snowflake - fs embed.FS - lock sync.Mutex - signKey string // 用来签名的随机秘钥 + alipayService *payment.AlipayService + huPiPayService *payment.HuPiPayService + jsPayService *payment.JPayService + wechatPayService *payment.WechatPayService + snowflake *service.Snowflake + fs embed.FS + lock sync.Mutex + signKey string // 用来签名的随机秘钥 } func NewPaymentHandler( server *core.AppServer, alipayService *payment.AlipayService, huPiPayService *payment.HuPiPayService, - js *payment.PayJS, + jsPayService *payment.JPayService, + wechatPayService *payment.WechatPayService, db *gorm.DB, snowflake *service.Snowflake, fs embed.FS) *PaymentHandler { return &PaymentHandler{ - alipayService: alipayService, - huPiPayService: huPiPayService, - js: js, - snowflake: snowflake, - fs: fs, - lock: sync.Mutex{}, + alipayService: alipayService, + huPiPayService: huPiPayService, + jsPayService: jsPayService, + wechatPayService: wechatPayService, + snowflake: snowflake, + fs: fs, + lock: sync.Mutex{}, BaseHandler: BaseHandler{ App: server, DB: db, @@ -108,13 +117,10 @@ func (h *PaymentHandler) DoPay(c *gin.Context) { // 更新扫码状态 h.DB.Model(&order).UpdateColumn("status", types.OrderScanned) - if payWay == "alipay" { // 支付宝 - // 生成支付链接 - notifyURL := h.App.Config.AlipayConfig.NotifyURL - returnURL := "" // 关闭同步回跳 - amount := fmt.Sprintf("%.2f", order.Amount) - uri, err := h.alipayService.PayUrlMobile(order.OrderNo, notifyURL, returnURL, amount, order.Subject) + if payWay == "alipay" { // 支付宝 + amount := fmt.Sprintf("%.2f", order.Amount) + uri, err := h.alipayService.PayUrlMobile(order.OrderNo, amount, order.Subject) if err != nil { resp.ERROR(c, "error with generate pay url: "+err.Error()) return @@ -214,14 +220,21 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) { var notifyURL string switch data.PayWay { case "hupi": - payWay = PayWayXunHu + payWay = PayWayXunHu.Value notifyURL = h.App.Config.HuPiPayConfig.NotifyURL + break case "payjs": - payWay = PayWayJs + payWay = PayWayJs.Value notifyURL = h.App.Config.JPayConfig.NotifyURL - default: - payWay = PayWayAlipay + break + case "alipay": + payWay = PayWayAlipay.Value notifyURL = h.App.Config.AlipayConfig.NotifyURL + break + default: + payWay = PayWayWechat.Value + notifyURL = h.App.Config.WechatPayConfig.NotifyURL + } // 创建订单 remark := types.OrderRemark{ @@ -257,7 +270,7 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) { OutTradeNo: order.OrderNo, Subject: product.Name, } - r := h.js.Pay(params) + r := h.jsPayService.Pay(params) if r.IsOK() { resp.SUCCESS(c, gin.H{"order_no": order.OrderNo, "image": r.Qrcode}) return @@ -276,6 +289,8 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) { } else { logo = "res/img/alipay.jpg" } + } else if data.PayWay == "wechat" { + logo = "res/img/wechat-pay.jpg" } file, err := h.fs.Open(logo) @@ -292,7 +307,18 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) { timestamp := time.Now().Unix() signStr := fmt.Sprintf("%s-%s-%d-%s", orderNo, data.PayWay, timestamp, h.signKey) sign := utils.Sha256(signStr) - imageURL := fmt.Sprintf("%s://%s/api/payment/doPay?order_no=%s&pay_way=%s&t=%d&sign=%s", parse.Scheme, parse.Host, orderNo, data.PayWay, timestamp, sign) + var imageURL string + if data.PayWay == "wechat" { + payUrl, err := h.wechatPayService.PayUrlNative(order.OrderNo, int(math.Floor(order.Amount*100)), product.Name) + if err != nil { + resp.ERROR(c, "error with generating wechat payment qrcode: "+err.Error()) + return + } else { + imageURL = payUrl + } + } else { + imageURL = fmt.Sprintf("%s://%s/api/payment/doPay?order_no=%s&pay_way=%s&t=%d&sign=%s", parse.Scheme, parse.Host, orderNo, data.PayWay, timestamp, sign) + } imgData, err := utils.GenQrcode(imageURL, 400, file) if err != nil { resp.ERROR(c, err.Error()) @@ -339,7 +365,7 @@ func (h *PaymentHandler) Mobile(c *gin.Context) { var payURL string switch data.PayWay { case "hupi": - payWay = PayWayXunHu + payWay = PayWayXunHu.Name notifyURL = h.App.Config.HuPiPayConfig.NotifyURL returnURL = h.App.Config.HuPiPayConfig.ReturnURL parse, _ := url.Parse(h.App.Config.HuPiPayConfig.ReturnURL) @@ -358,13 +384,14 @@ func (h *PaymentHandler) Mobile(c *gin.Context) { } r, err := h.huPiPayService.Pay(params) if err != nil { - logger.Error("error with generating Pay URL: ", err.Error()) - resp.ERROR(c, "error with generating Pay URL: "+err.Error()) + errMsg := "error with generating Pay Hupi URL: " + err.Error() + logger.Error(errMsg) + resp.ERROR(c, errMsg) return } payURL = r.URL case "payjs": - payWay = PayWayJs + payWay = PayWayJs.Name notifyURL = h.App.Config.JPayConfig.NotifyURL returnURL = h.App.Config.JPayConfig.ReturnURL totalFee := decimal.NewFromFloat(product.Price).Sub(decimal.NewFromFloat(product.Discount)).Mul(decimal.NewFromInt(100)).IntPart() @@ -374,14 +401,22 @@ func (h *PaymentHandler) Mobile(c *gin.Context) { params.Add("body", product.Name) params.Add("notify_url", notifyURL) params.Add("auto", "0") - payURL = h.js.PayH5(params) + payURL = h.jsPayService.PayH5(params) case "alipay": - payWay = PayWayAlipay - notifyURL = h.App.Config.AlipayConfig.NotifyURL - returnURL = h.App.Config.AlipayConfig.ReturnURL - payURL, err = h.alipayService.PayUrlMobile(orderNo, notifyURL, returnURL, fmt.Sprintf("%.2f", amount), product.Name) + payWay = PayWayAlipay.Name + payURL, err = h.alipayService.PayUrlMobile(orderNo, fmt.Sprintf("%.2f", amount), product.Name) if err != nil { - resp.ERROR(c, "error with generating Pay URL: "+err.Error()) + errMsg := "error with generating Alipay URL: " + err.Error() + resp.ERROR(c, errMsg) + return + } + case "wechat": + payWay = PayWayWechat.Name + payURL, err = h.wechatPayService.PayUrlH5(orderNo, int(amount*100), product.Name, c.ClientIP()) + if err != nil { + errMsg := "error with generating Wechat URL: " + err.Error() + logger.Error(errMsg) + resp.ERROR(c, errMsg) return } default: @@ -493,7 +528,7 @@ func (h *PaymentHandler) notify(orderNo string, tradeNo string) error { h.DB.Model(&model.Product{}).Where("id = ?", order.ProductId).UpdateColumn("sales", gorm.Expr("sales + ?", 1)) // 记录算力充值日志 - if opt != "" { + if power > 0 { h.DB.Create(&model.PowerLog{ UserId: user.Id, Username: user.Username, @@ -522,6 +557,9 @@ func (h *PaymentHandler) GetPayWays(c *gin.Context) { if h.App.Config.JPayConfig.Enabled { data["payjs"] = gin.H{"name": h.App.Config.JPayConfig.Name} } + if h.App.Config.WechatPayConfig.Enabled { + data["wechat"] = gin.H{"name": "wechat"} + } resp.SUCCESS(c, data) } @@ -560,7 +598,7 @@ func (h *PaymentHandler) AlipayNotify(c *gin.Context) { } // TODO:验证交易签名 - res := h.alipayService.TradeVerify(c.Request.Form) + res := h.alipayService.TradeVerify(c.Request) logger.Infof("验证支付结果:%+v", res) if !res.Success() { logger.Error("订单校验失败:", res.Message) @@ -588,7 +626,7 @@ func (h *PaymentHandler) PayJsNotify(c *gin.Context) { orderNo := c.Request.Form.Get("out_trade_no") returnCode := c.Request.Form.Get("return_code") - logger.Infof("收到订单支付回调,订单 NO:%s,支付结果代码:%v", orderNo, returnCode) + logger.Infof("收到PayJs订单支付回调,订单 NO:%s,支付结果代码:%v", orderNo, returnCode) // 支付失败 if returnCode != "1" { return @@ -596,7 +634,7 @@ func (h *PaymentHandler) PayJsNotify(c *gin.Context) { // 校验订单支付状态 tradeNo := c.Request.Form.Get("payjs_order_id") - err = h.js.Check(tradeNo) + err = h.jsPayService.TradeVerify(tradeNo) if err != nil { logger.Error("订单校验失败:", err) c.String(http.StatusOK, "fail") @@ -611,3 +649,30 @@ func (h *PaymentHandler) PayJsNotify(c *gin.Context) { c.String(http.StatusOK, "success") } + +// WechatPayNotify 微信商户支付异步回调 +func (h *PaymentHandler) WechatPayNotify(c *gin.Context) { + err := c.Request.ParseForm() + if err != nil { + c.String(http.StatusOK, "fail") + return + } + + result := h.wechatPayService.TradeVerify(c.Request) + if !result.Success() { + logger.Error("订单校验失败:", err) + c.JSON(http.StatusBadRequest, gin.H{ + "code": "FAIL", + "message": err.Error(), + }) + return + } + + err = h.notify(result.OutTradeNo, result.TradeId) + if err != nil { + c.String(http.StatusOK, "fail") + return + } + + c.String(http.StatusOK, "success") +} diff --git a/api/handler/sd_handler.go b/api/handler/sd_handler.go index e30e837d..5ed3c009 100644 --- a/api/handler/sd_handler.go +++ b/api/handler/sd_handler.go @@ -168,11 +168,10 @@ func (h *SdJobHandler) Image(c *gin.Context) { } h.pool.PushTask(types.SdTask{ - Id: int(job.Id), - SessionId: data.SessionId, - Type: types.TaskImage, - Params: params, - UserId: userId, + Id: int(job.Id), + Type: types.TaskImage, + Params: params, + UserId: userId, }) client := h.pool.Clients.Get(uint(job.UserId)) diff --git a/api/handler/test_handler.go b/api/handler/test_handler.go index 35aba79f..eb5b7107 100644 --- a/api/handler/test_handler.go +++ b/api/handler/test_handler.go @@ -9,9 +9,9 @@ import ( type TestHandler struct { db *gorm.DB snowflake *service.Snowflake - js *payment.PayJS + js *payment.JPayService } -func NewTestHandler(db *gorm.DB, snowflake *service.Snowflake, js *payment.PayJS) *TestHandler { +func NewTestHandler(db *gorm.DB, snowflake *service.Snowflake, js *payment.JPayService) *TestHandler { return &TestHandler{db: db, snowflake: snowflake, js: js} } diff --git a/api/handler/user_handler.go b/api/handler/user_handler.go index 2718d15e..84e9da48 100644 --- a/api/handler/user_handler.go +++ b/api/handler/user_handler.go @@ -109,7 +109,6 @@ func (h *UserHandler) Register(c *gin.Context) { user := model.User{ Username: data.Username, Password: utils.GenPassword(data.Password, salt), - Nickname: fmt.Sprintf("极客学长@%d", utils.RandomNumber(6)), Avatar: "/images/avatar/user.png", Salt: salt, Status: true, @@ -118,6 +117,16 @@ func (h *UserHandler) Register(c *gin.Context) { Power: h.App.SysConfig.InitPower, } + // 被邀请人也获得赠送算力 + if data.InviteCode != "" { + user.Power += h.App.SysConfig.InvitePower + } + if h.licenseService.GetLicense().Configs.DeCopy { + user.Username = fmt.Sprintf("用户@%d", utils.RandomNumber(6)) + } else { + user.Nickname = fmt.Sprintf("极客学长@%d", utils.RandomNumber(6)) + } + res = h.DB.Create(&user) if res.Error != nil { resp.ERROR(c, "保存数据失败") diff --git a/api/main.go b/api/main.go index 8b585ee6..e26d07ff 100644 --- a/api/main.go +++ b/api/main.go @@ -211,7 +211,8 @@ func main() { fx.Provide(payment.NewAlipayService), fx.Provide(payment.NewHuPiPay), - fx.Provide(payment.NewPayJS), + fx.Provide(payment.NewJPayService), + fx.Provide(payment.NewWechatService), fx.Provide(service.NewSnowflake), fx.Provide(service.NewXXLJobExecutor), fx.Invoke(func(exec *service.XXLJobExecutor, config *types.AppConfig) { @@ -373,6 +374,7 @@ func main() { group.POST("alipay/notify", h.AlipayNotify) group.POST("hupipay/notify", h.HuPiPayNotify) group.POST("payjs/notify", h.PayJsNotify) + group.POST("wechat/notify", h.WechatPayNotify) }), fx.Invoke(func(s *core.AppServer, h *admin.ProductHandler) { group := s.Engine.Group("/api/admin/product/") diff --git a/api/service/dalle/service.go b/api/service/dalle/service.go index f3e813b2..9a915da5 100644 --- a/api/service/dalle/service.go +++ b/api/service/dalle/service.go @@ -109,13 +109,13 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) { logger.Debugf("绘画参数:%+v", task) prompt := task.Prompt // translate prompt - if utils.HasChinese(task.Prompt) { - content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.RewritePromptTemplate, task.Prompt)) - if err != nil { - return "", fmt.Errorf("error with translate prompt: %v", err) + if utils.HasChinese(prompt) { + content, err := utils.OpenAIRequest(s.db, fmt.Sprintf(service.RewritePromptTemplate, prompt)) + if err == nil { + prompt = content + logger.Debugf("重写后提示词:%s", prompt) } - prompt = content - logger.Debugf("重写后提示词:%s", prompt) + } var user model.User @@ -124,10 +124,28 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) { return "", errors.New("insufficient of power") } + // 更新用户算力 + tx := s.db.Model(&model.User{}).Where("id", user.Id).UpdateColumn("power", gorm.Expr("power - ?", task.Power)) + // 记录算力变化日志 + if tx.Error == nil && tx.RowsAffected > 0 { + var u model.User + s.db.Where("id", user.Id).First(&u) + s.db.Create(&model.PowerLog{ + UserId: user.Id, + Username: user.Username, + Type: types.PowerConsume, + Amount: task.Power, + Balance: u.Power, + Mark: types.PowerSub, + Model: "dall-e-3", + Remark: fmt.Sprintf("绘画提示词:%s", utils.CutWords(task.Prompt, 10)), + CreatedAt: time.Now(), + }) + } + // get image generation API KEY var apiKey model.ApiKey - tx := s.db.Where("platform", types.OpenAI.Value). - Where("type", "img"). + tx = s.db.Where("type", "img"). Where("enabled", true). Order("last_used_at ASC").First(&apiKey) if tx.Error != nil { @@ -139,25 +157,28 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) { if len(apiKey.ProxyURL) > 5 { s.httpClient.SetProxyURL(apiKey.ProxyURL).R() } - logger.Infof("Sending %s request, ApiURL:%s, API KEY:%s, PROXY: %s", apiKey.Platform, apiKey.ApiURL, apiKey.Value, apiKey.ProxyURL) - r, err := s.httpClient.R().SetHeader("Content-Type", "application/json"). - SetHeader("Authorization", "Bearer "+apiKey.Value). - SetBody(imgReq{ - Model: "dall-e-3", - Prompt: prompt, - N: 1, - Size: task.Size, - Style: task.Style, - Quality: task.Quality, - }). - SetErrorResult(&errRes). - SetSuccessResult(&res).Post(apiKey.ApiURL) + reqBody := imgReq{ + Model: "dall-e-3", + Prompt: prompt, + N: 1, + Size: task.Size, + Style: task.Style, + Quality: task.Quality, + } + logger.Infof("Sending %s request, ApiURL:%s, API KEY:%s, BODY: %+v", apiKey.Platform, apiKey.ApiURL, apiKey.Value, reqBody) + request := s.httpClient.R().SetHeader("Content-Type", "application/json") + if apiKey.Platform == types.Azure.Value { + request = request.SetHeader("api-key", apiKey.Value) + } else { + request = request.SetHeader("Authorization", "Bearer "+apiKey.Value) + } + r, err := request.SetBody(reqBody).SetErrorResult(&errRes).SetSuccessResult(&res).Post(apiKey.ApiURL) if err != nil { return "", fmt.Errorf("error with send request: %v", err) } if r.IsErrorState() { - return "", fmt.Errorf("error with send request: %v", errRes.Error) + return "", fmt.Errorf("error with send request, status: %s, %+v", r.Status, errRes.Error) } // update the api key last use time s.db.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix()) @@ -181,25 +202,6 @@ func (s *Service) Image(task types.DallTask, sync bool) (string, error) { content = fmt.Sprintf("```\n%s\n```\n下面是我为你创作的图片:\n\n![](%s)\n", prompt, imgURL) } - // 更新用户算力 - tx = s.db.Model(&model.User{}).Where("id", user.Id).UpdateColumn("power", gorm.Expr("power - ?", task.Power)) - // 记录算力变化日志 - if tx.Error == nil && tx.RowsAffected > 0 { - var u model.User - s.db.Where("id", user.Id).First(&u) - s.db.Create(&model.PowerLog{ - UserId: user.Id, - Username: user.Username, - Type: types.PowerConsume, - Amount: task.Power, - Balance: u.Power, - Mark: types.PowerSub, - Model: "dall-e-3", - Remark: fmt.Sprintf("绘画提示词:%s", utils.CutWords(task.Prompt, 10)), - CreatedAt: time.Now(), - }) - } - return content, nil } diff --git a/api/service/license_service.go b/api/service/license_service.go index 419c02d5..b1596e24 100644 --- a/api/service/license_service.go +++ b/api/service/license_service.go @@ -91,7 +91,7 @@ func (s *LicenseService) SyncLicense() { if err != nil { retryCounter++ if retryCounter < 5 { - logger.Error(err) + logger.Warn(err) } s.license.IsActive = false } else { diff --git a/api/service/mj/pool.go b/api/service/mj/pool.go index ddddd280..3eacfd78 100644 --- a/api/service/mj/pool.go +++ b/api/service/mj/pool.go @@ -179,14 +179,14 @@ func (p *ServicePool) HasAvailableService() bool { // SyncTaskProgress 异步拉取任务 func (p *ServicePool) SyncTaskProgress() { go func() { - var items []model.MidJourneyJob + var jobs []model.MidJourneyJob for { - res := p.db.Where("progress < ?", 100).Find(&items) + res := p.db.Where("progress < ?", 100).Find(&jobs) if res.Error != nil { continue } - for _, job := range items { + for _, job := range jobs { // 失败或者 30 分钟还没完成的任务删除并退回算力 if time.Now().Sub(job.CreatedAt) > time.Minute*30 || job.Progress == -1 { p.db.Delete(&job) diff --git a/api/service/payment/alipay_service.go b/api/service/payment/alipay_service.go index 228949d2..8ab93eda 100644 --- a/api/service/payment/alipay_service.go +++ b/api/service/payment/alipay_service.go @@ -8,12 +8,13 @@ package payment // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import ( + "context" "fmt" "geekai/core/types" logger2 "geekai/logger" - "github.com/smartwalle/alipay/v3" - "log" - "net/url" + "github.com/go-pay/gopay" + "github.com/go-pay/gopay/alipay" + "net/http" "os" ) @@ -35,93 +36,90 @@ func NewAlipayService(appConfig *types.AppConfig) (*AlipayService, error) { return nil, fmt.Errorf("error with read App Private key: %v", err) } - xClient, err := alipay.New(config.AppId, priKey, !config.SandBox) + client, err := alipay.NewClient(config.AppId, priKey, !config.SandBox) if err != nil { return nil, fmt.Errorf("error with initialize alipay service: %v", err) } - if err = xClient.LoadAppCertPublicKeyFromFile(config.PublicKey); err != nil { - return nil, fmt.Errorf("error with loading App PublicKey: %v", err) - } - if err = xClient.LoadAliPayRootCertFromFile(config.RootCert); err != nil { - return nil, fmt.Errorf("error with loading alipay RootCert: %v", err) - } - if err = xClient.LoadAlipayCertPublicKeyFromFile(config.AlipayPublicKey); err != nil { - return nil, fmt.Errorf("error with loading Alipay PublicKey: %v", err) + //client.DebugSwitch = gopay.DebugOn // 开启调试模式 + client.SetLocation(alipay.LocationShanghai). // 设置时区,不设置或出错均为默认服务器时间 + SetCharset(alipay.UTF8). // 设置字符编码,不设置默认 utf-8 + SetSignType(alipay.RSA2). // 设置签名类型,不设置默认 RSA2 + SetReturnUrl(config.ReturnURL). // 设置返回URL + SetNotifyUrl(config.NotifyURL) + + if err = client.SetCertSnByPath(config.PublicKey, config.RootCert, config.AlipayPublicKey); err != nil { + return nil, fmt.Errorf("error with load payment public key: %v", err) } - return &AlipayService{config: &config, client: xClient}, nil + return &AlipayService{config: &config, client: client}, nil } -func (s *AlipayService) PayUrlMobile(outTradeNo string, notifyURL string, returnURL string, Amount string, subject string) (string, error) { - var p = alipay.TradeWapPay{} - p.NotifyURL = notifyURL - p.ReturnURL = returnURL - p.Subject = subject - p.OutTradeNo = outTradeNo - p.TotalAmount = Amount - p.ProductCode = "QUICK_WAP_WAY" - res, err := s.client.TradeWapPay(p) - if err != nil { - return "", err - } - - return res.String(), err +func (s *AlipayService) PayUrlMobile(outTradeNo string, amount string, subject string) (string, error) { + bm := make(gopay.BodyMap) + bm.Set("subject", subject) + bm.Set("out_trade_no", outTradeNo) + bm.Set("quit_url", s.config.ReturnURL) + bm.Set("total_amount", amount) + bm.Set("product_code", "QUICK_WAP_WAY") + return s.client.TradeWapPay(context.Background(), bm) } -func (s *AlipayService) PayUrlPc(outTradeNo string, notifyURL string, returnURL string, amount string, subject string) (string, error) { - var p = alipay.TradePagePay{} - p.NotifyURL = notifyURL - p.ReturnURL = returnURL - p.Subject = subject - p.OutTradeNo = outTradeNo - p.TotalAmount = amount - p.ProductCode = "FAST_INSTANT_TRADE_PAY" - res, err := s.client.TradePagePay(p) - if err != nil { - return "", nil - } - - return res.String(), err +func (s *AlipayService) PayUrlPc(outTradeNo string, amount string, subject string) (string, error) { + bm := make(gopay.BodyMap) + bm.Set("subject", subject) + bm.Set("out_trade_no", outTradeNo) + bm.Set("total_amount", amount) + bm.Set("product_code", "FAST_INSTANT_TRADE_PAY") + return s.client.TradePagePay(context.Background(), bm) } // TradeVerify 交易验证 -func (s *AlipayService) TradeVerify(reqForm url.Values) NotifyVo { - err := s.client.VerifySign(reqForm) +func (s *AlipayService) TradeVerify(request *http.Request) NotifyVo { + notifyReq, err := alipay.ParseNotifyToBodyMap(request) // c.Request 是 gin 框架的写法 if err != nil { - log.Println("异步通知验证签名发生错误", err) return NotifyVo{ - Status: 0, - Message: "异步通知验证签名发生错误", + Status: Failure, + Message: "error with parse notify request: " + err.Error(), } } - return s.TradeQuery(reqForm.Get("out_trade_no")) + _, err = alipay.VerifySignWithCert(s.config.AlipayPublicKey, notifyReq) + if err != nil { + return NotifyVo{ + Status: Failure, + Message: "error with verify sign: " + err.Error(), + } + } + + return s.TradeQuery(request.Form.Get("out_trade_no")) } func (s *AlipayService) TradeQuery(outTradeNo string) NotifyVo { - var p = alipay.TradeQuery{} - p.OutTradeNo = outTradeNo - rsp, err := s.client.TradeQuery(p) + bm := make(gopay.BodyMap) + bm.Set("out_trade_no", outTradeNo) + + //查询订单 + rsp, err := s.client.TradeQuery(context.Background(), bm) if err != nil { return NotifyVo{ - Status: 0, + Status: Failure, Message: "异步查询验证订单信息发生错误" + outTradeNo + err.Error(), } } - if rsp.IsSuccess() == true && rsp.TradeStatus == "TRADE_SUCCESS" { + if rsp.Response.TradeStatus == "TRADE_SUCCESS" { return NotifyVo{ - Status: 1, - OutTradeNo: rsp.OutTradeNo, - TradeNo: rsp.TradeNo, - Amount: rsp.TotalAmount, - Subject: rsp.Subject, + Status: Success, + OutTradeNo: rsp.Response.OutTradeNo, + TradeId: rsp.Response.TradeNo, + Amount: rsp.Response.TotalAmount, + Subject: rsp.Response.Subject, Message: "OK", } } else { return NotifyVo{ - Status: 0, + Status: Failure, Message: "异步查询验证订单信息发生错误" + outTradeNo, } } @@ -134,16 +132,3 @@ func readKey(filename string) (string, error) { } return string(data), nil } - -type NotifyVo struct { - Status int - OutTradeNo string - TradeNo string - Amount string - Message string - Subject string -} - -func (v NotifyVo) Success() bool { - return v.Status == 1 -} diff --git a/api/service/payment/payjs_service.go b/api/service/payment/payjs_service.go index 1b42406b..56a98471 100644 --- a/api/service/payment/payjs_service.go +++ b/api/service/payment/payjs_service.go @@ -21,12 +21,12 @@ import ( "strings" ) -type PayJS struct { +type JPayService struct { config *types.JPayConfig } -func NewPayJS(appConfig *types.AppConfig) *PayJS { - return &PayJS{ +func NewJPayService(appConfig *types.AppConfig) *JPayService { + return &JPayService{ config: &appConfig.JPayConfig, } } @@ -53,7 +53,7 @@ func (r JPayReps) IsOK() bool { return r.ReturnMsg == "SUCCESS" } -func (js *PayJS) Pay(param JPayReq) JPayReps { +func (js *JPayService) Pay(param JPayReq) JPayReps { param.NotifyURL = js.config.NotifyURL var p = url.Values{} encode := utils.JsonEncode(param) @@ -86,13 +86,13 @@ func (js *PayJS) Pay(param JPayReq) JPayReps { return data } -func (js *PayJS) PayH5(p url.Values) string { +func (js *JPayService) PayH5(p url.Values) string { p.Add("mchid", js.config.AppId) p.Add("sign", js.sign(p)) return fmt.Sprintf("%s/api/cashier?%s", js.config.ApiURL, p.Encode()) } -func (js *PayJS) sign(params url.Values) string { +func (js *JPayService) sign(params url.Values) string { params.Del(`sign`) var keys = make([]string, 0, 0) for key := range params { @@ -117,20 +117,18 @@ func (js *PayJS) sign(params url.Values) string { return strings.ToUpper(md5res) } -// Check 查询订单支付状态 +// TradeVerify 查询订单支付状态 // @param tradeNo 支付平台交易 ID -func (js *PayJS) Check(tradeNo string) error { +func (js *JPayService) TradeVerify(tradeNo string) error { apiURL := fmt.Sprintf("%s/api/check", js.config.ApiURL) params := url.Values{} params.Add("payjs_order_id", tradeNo) params.Add("sign", js.sign(params)) data := strings.NewReader(params.Encode()) resp, err := http.Post(apiURL, "application/x-www-form-urlencoded", data) - defer resp.Body.Close() if err != nil { return fmt.Errorf("error with http reqeust: %v", err) } - defer resp.Body.Close() body, err := io.ReadAll(resp.Body) if err != nil { diff --git a/api/service/payment/types.go b/api/service/payment/types.go new file mode 100644 index 00000000..ef8ff24c --- /dev/null +++ b/api/service/payment/types.go @@ -0,0 +1,19 @@ +package payment + +type NotifyVo struct { + Status int + OutTradeNo string // 商户订单号 + TradeId string // 交易ID + Amount string // 交易金额 + Message string + Subject string +} + +func (v NotifyVo) Success() bool { + return v.Status == Success +} + +const ( + Success = 0 + Failure = 1 +) diff --git a/api/service/payment/wepay_service.go b/api/service/payment/wepay_service.go new file mode 100644 index 00000000..b141a8e9 --- /dev/null +++ b/api/service/payment/wepay_service.go @@ -0,0 +1,135 @@ +package payment + +// * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +// * 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" + "fmt" + "geekai/core/types" + "github.com/go-pay/gopay" + "github.com/go-pay/gopay/wechat/v3" + "net/http" + "time" +) + +type WechatPayService struct { + config *types.WechatPayConfig + client *wechat.ClientV3 +} + +func NewWechatService(appConfig *types.AppConfig) (*WechatPayService, error) { + config := appConfig.WechatPayConfig + if !config.Enabled { + logger.Info("Disabled WechatPay service") + return nil, nil + } + priKey, err := readKey(config.PrivateKey) + if err != nil { + return nil, fmt.Errorf("error with read App Private key: %v", err) + } + + client, err := wechat.NewClientV3(config.MchId, config.SerialNo, config.ApiV3Key, priKey) + if err != nil { + return nil, fmt.Errorf("error with initialize WechatPay service: %v", err) + } + err = client.AutoVerifySign() + if err != nil { + return nil, fmt.Errorf("error with autoVerifySign: %v", err) + } + //client.DebugSwitch = gopay.DebugOn + + return &WechatPayService{config: &config, client: client}, nil +} + +func (s *WechatPayService) PayUrlNative(outTradeNo string, amount int, subject string) (string, error) { + expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) + // 初始化 BodyMap + bm := make(gopay.BodyMap) + bm.Set("appid", s.config.AppId). + Set("mchid", s.config.MchId). + Set("description", subject). + Set("out_trade_no", outTradeNo). + Set("time_expire", expire). + Set("notify_url", s.config.NotifyURL). + SetBodyMap("amount", func(bm gopay.BodyMap) { + bm.Set("total", amount). + Set("currency", "CNY") + }) + + wxRsp, err := s.client.V3TransactionNative(context.Background(), bm) + if err != nil { + return "", fmt.Errorf("error with client v3 transaction Native: %v", err) + } + if wxRsp.Code != wechat.Success { + return "", fmt.Errorf("error status with generating pay url: %v", wxRsp.Error) + } + return wxRsp.Response.CodeUrl, nil +} + +func (s *WechatPayService) PayUrlH5(outTradeNo string, amount int, subject string, ip string) (string, error) { + expire := time.Now().Add(10 * time.Minute).Format(time.RFC3339) + // 初始化 BodyMap + bm := make(gopay.BodyMap) + bm.Set("appid", s.config.AppId). + Set("mchid", s.config.MchId). + Set("description", subject). + Set("out_trade_no", outTradeNo). + Set("time_expire", expire). + Set("notify_url", s.config.NotifyURL). + SetBodyMap("amount", func(bm gopay.BodyMap) { + bm.Set("total", amount). + Set("currency", "CNY") + }). + SetBodyMap("scene_info", func(bm gopay.BodyMap) { + bm.Set("payer_client_ip", ip). + SetBodyMap("h5_info", func(bm gopay.BodyMap) { + bm.Set("type", "Wap") + }) + }) + + wxRsp, err := s.client.V3TransactionH5(context.Background(), bm) + if err != nil { + return "", fmt.Errorf("error with client v3 transaction H5: %v", err) + } + if wxRsp.Code != wechat.Success { + return "", fmt.Errorf("error with generating pay url: %v", wxRsp.Error) + } + return wxRsp.Response.H5Url, nil +} + +type NotifyResponse struct { + Code string `json:"code"` + Message string `xml:"message"` +} + +// TradeVerify 交易验证 +func (s *WechatPayService) TradeVerify(request *http.Request) NotifyVo { + notifyReq, err := wechat.V3ParseNotify(request) + if err != nil { + return NotifyVo{Status: 1, Message: fmt.Sprintf("error with client v3 parse notify: %v", err)} + } + + // TODO: 这里验签程序有 Bug,一直报错:crypto/rsa: verification error,先暂时取消验签 + //err = notifyReq.VerifySignByPK(s.client.WxPublicKey()) + //if err != nil { + // return fmt.Errorf("error with client v3 verify sign: %v", err) + //} + + // 解密支付密文,验证订单信息 + result, err := notifyReq.DecryptPayCipherText(s.config.ApiV3Key) + if err != nil { + return NotifyVo{Status: Failure, Message: fmt.Sprintf("error with client v3 decrypt: %v", err)} + } + + return NotifyVo{ + Status: Success, + OutTradeNo: result.OutTradeNo, + TradeId: result.TransactionId, + Amount: fmt.Sprintf("%.2f", float64(result.Amount.Total)/100), + } +} diff --git a/api/service/sd/pool.go b/api/service/sd/pool.go index 55329e46..548875ec 100644 --- a/api/service/sd/pool.go +++ b/api/service/sd/pool.go @@ -132,7 +132,7 @@ func (p *ServicePool) CheckTaskStatus() { continue } } - time.Sleep(time.Second * 10) + time.Sleep(time.Second * 5) } }() } diff --git a/api/service/sd/service.go b/api/service/sd/service.go index 736f4180..468e8b3e 100644 --- a/api/service/sd/service.go +++ b/api/service/sd/service.go @@ -192,7 +192,7 @@ func (s *Service) Txt2Img(task types.SdTask) error { return } task.Params.Seed = int64(utils.IntValue(utils.InterfaceToString(info["seed"]), -1)) - s.db.Model(&model.SdJob{Id: uint(task.Id)}).UpdateColumns(model.SdJob{ImgURL: imgURL, Params: utils.JsonEncode(task.Params)}) + s.db.Model(&model.SdJob{Id: uint(task.Id)}).UpdateColumns(model.SdJob{ImgURL: imgURL, Params: utils.JsonEncode(task.Params), Prompt: task.Params.Prompt}) errChan <- nil }() diff --git a/api/service/smtp_sms_service.go b/api/service/smtp_sms_service.go index e93e9265..025ffa39 100644 --- a/api/service/smtp_sms_service.go +++ b/api/service/smtp_sms_service.go @@ -28,8 +28,8 @@ func NewSmtpService(appConfig *types.AppConfig) *SmtpService { } func (s *SmtpService) SendVerifyCode(to string, code int) error { - subject := "Geek-AI 注册验证码" - body := fmt.Sprintf("您正在注册 Geek-AI 助手账户,注册验证码为 %d,请不要告诉他人。如非本人操作,请忽略此邮件。", code) + subject := fmt.Sprintf("%s 注册验证码", s.config.AppName) + body := fmt.Sprintf("您正在注册 %s 账户,注册验证码为 %d,请不要告诉他人。如非本人操作,请忽略此邮件。", s.config.AppName, code) auth := smtp.PlainAuth("", s.config.From, s.config.Password, s.config.Host) if s.config.UseTls { diff --git a/api/service/xxl_job_service.go b/api/service/xxl_job_service.go index 14fec1db..2adecf1b 100644 --- a/api/service/xxl_job_service.go +++ b/api/service/xxl_job_service.go @@ -106,23 +106,26 @@ func (e *XXLJobExecutor) ResetVipPower(cxt context.Context, param *xxl.RunReq) ( e.db.Model(&model.User{}).Where("id", u.Id).UpdateColumn("vip", false) continue } - // update user - tx := e.db.Model(&model.User{}).Where("id", u.Id).UpdateColumn("power", gorm.Expr("power + ?", config.VipMonthPower)) - // 记录算力变动日志 - if tx.Error == nil { - var user model.User - e.db.Where("id", u.Id).First(&user) - e.db.Create(&model.PowerLog{ - UserId: u.Id, - Username: u.Username, - Type: types.PowerRecharge, - Amount: config.VipMonthPower, - Mark: types.PowerAdd, - Balance: user.Power, - Model: "系统盘点", - Remark: fmt.Sprintf("VIP会员每月算力派发,:%d", config.VipMonthPower), - CreatedAt: time.Now(), - }) + if u.Power < config.VipMonthPower { + power := config.VipMonthPower - u.Power + // update user + tx := e.db.Model(&model.User{}).Where("id", u.Id).UpdateColumn("power", gorm.Expr("power + ?", power)) + // 记录算力变动日志 + if tx.Error == nil { + var user model.User + e.db.Where("id", u.Id).First(&user) + e.db.Create(&model.PowerLog{ + UserId: u.Id, + Username: u.Username, + Type: types.PowerRecharge, + Amount: power, + Mark: types.PowerAdd, + Balance: user.Power, + Model: "系统盘点", + Remark: fmt.Sprintf("VIP会员每月算力派发,:%d", config.VipMonthPower), + CreatedAt: time.Now(), + }) + } } } logger.Info("月底盘点完成!") diff --git a/api/test/test.go b/api/test/test.go index 0a48ec97..2d53da2b 100644 --- a/api/test/test.go +++ b/api/test/test.go @@ -2,11 +2,8 @@ package main import ( "fmt" - "net/url" ) func main() { - text := "https://nk.img.r9it.com/chatgpt-plus/1712709360012445.png" - parse, _ := url.Parse(text) - fmt.Println(fmt.Sprintf("%s://%s", parse.Scheme, parse.Host)) + fmt.Println(fmt.Sprintf("%v", float64(90)/100)) } diff --git a/api/utils/openai.go b/api/utils/openai.go index 86a976a5..9ee01a35 100644 --- a/api/utils/openai.go +++ b/api/utils/openai.go @@ -54,7 +54,7 @@ type apiErrRes struct { func OpenAIRequest(db *gorm.DB, prompt string) (string, error) { var apiKey model.ApiKey - res := db.Where("platform = ?", types.OpenAI.Value).Where("type", "chat").Where("enabled = ?", true).First(&apiKey) + res := db.Where("platform", types.OpenAI.Value).Where("type", "chat").Where("enabled", true).First(&apiKey) if res.Error != nil { return "", fmt.Errorf("error with fetch OpenAI API KEY:%v", res.Error) } @@ -74,7 +74,7 @@ func OpenAIRequest(db *gorm.DB, prompt string) (string, error) { r, err := client.R().SetHeader("Content-Type", "application/json"). SetHeader("Authorization", "Bearer "+apiKey.Value). SetBody(types.ApiRequest{ - Model: "gpt-3.5-turbo-0125", + Model: "gpt-3.5-turbo", Temperature: 0.9, MaxTokens: 1024, Stream: false, diff --git a/database/chatgpt_plus-v4.0.9.sql b/database/chatgpt_plus-v4.0.9.sql new file mode 100644 index 00000000..69197bf8 --- /dev/null +++ b/database/chatgpt_plus-v4.0.9.sql @@ -0,0 +1,852 @@ +-- phpMyAdmin SQL Dump +-- version 5.2.1 +-- https://www.phpmyadmin.net/ +-- +-- 主机: 127.0.0.1 +-- 生成日期: 2024-06-22 12:15:45 +-- 服务器版本: 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 */; + +-- +-- 数据库: `chatgpt_plus` +-- +CREATE DATABASE IF NOT EXISTS `chatgpt_plus` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; +USE `chatgpt_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, 1718094709, '172.22.11.200', '2024-03-11 16:30:20', '2024-06-11 16:31:50'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `chatgpt_api_keys` +-- + +DROP TABLE IF EXISTS `chatgpt_api_keys`; +CREATE TABLE `chatgpt_api_keys` ( + `id` int NOT NULL, + `platform` char(20) DEFAULT NULL COMMENT '平台', + `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_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(100) 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 数量', + `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, + `platform` varchar(20) DEFAULT NULL COMMENT '模型平台', + `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` tinyint 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`, `platform`, `name`, `value`, `sort_num`, `enabled`, `power`, `temperature`, `max_tokens`, `max_context`, `open`, `key_id`, `created_at`, `updated_at`) VALUES +(1, 'OpenAI', 'GPT-3.5', 'gpt-3.5-turbo', 1, 1, 0, 1.0, 1024, 4096, 1, 0, '2023-08-23 12:06:36', '2024-05-29 09:19:06'), +(2, 'Azure', 'Azure-3.5', 'gpt-3.5-turbo', 22, 1, 1, 1.0, 1024, 4096, 0, 0, '2023-08-23 12:15:30', '2024-05-29 09:19:06'), +(3, 'ChatGLM', 'ChatGML-Pro', 'chatglm_pro', 11, 1, 1, 1.0, 2048, 32768, 1, 0, '2023-08-23 13:35:45', '2024-05-29 09:19:06'), +(7, 'Baidu', '文心一言3.0', 'eb-instant', 20, 1, 1, 1.0, 1024, 4096, 1, 0, '2023-10-11 11:29:28', '2024-05-29 09:19:06'), +(8, 'XunFei', '星火V3.5', 'generalv3.5', 10, 1, 5, 0.8, 1024, 8192, 1, 0, '2023-10-11 15:48:30', '2024-05-29 09:19:06'), +(9, 'XunFei', '星火V2.0', 'generalv2', 19, 1, 1, 1.0, 1024, 8192, 1, 0, '2023-10-11 15:48:45', '2024-05-29 09:19:06'), +(10, 'Baidu', '文心一言4.0', 'completions_pro', 21, 1, 3, 1.0, 1024, 8192, 1, 0, '2023-10-25 08:31:37', '2024-05-29 09:19:06'), +(11, 'OpenAI', 'GPT-4.0', 'gpt-4-0125-preview', 9, 1, 15, 1.0, 2048, 8192, 1, 0, '2023-10-25 08:45:15', '2024-05-29 09:19:06'), +(12, 'XunFei', '星火v3.0', 'generalv3', 18, 1, 3, 1.0, 1024, 8192, 1, 0, '2023-11-23 09:20:33', '2024-05-29 09:19:06'), +(15, 'OpenAI', 'GPT-超级模型', 'gpt-4-all', 12, 1, 30, 1.0, 4096, 32768, 0, 0, '2024-01-15 11:32:52', '2024-05-29 09:19:06'), +(16, 'OpenAI', '视频号导师', 'gpt-4-gizmo-g-QXXEBTXl7', 13, 1, 30, 1.0, 4096, 32768, 0, 0, '2024-01-15 14:46:35', '2024-05-29 09:19:06'), +(17, 'QWen', '通义千问-Turbo', 'qwen-turbo', 15, 1, 1, 1.0, 1024, 8192, 1, 0, '2024-01-19 10:42:24', '2024-05-29 09:19:06'), +(18, 'QWen', '通义千问-Plus', 'qwen-plus', 16, 1, 1, 1.0, 1024, 32768, 1, 0, '2024-01-19 10:42:49', '2024-05-29 09:19:06'), +(19, 'QWen', '通义千问-Max', 'qwen-max-1201', 17, 1, 1, 1.0, 1024, 32768, 1, 0, '2024-01-19 10:51:03', '2024-05-29 09:19:06'), +(21, 'OpenAI', '董宇辉小作文助手', 'gpt-4-gizmo-g-dse9iXvor', 14, 1, 30, 1.0, 8192, 32768, 0, 0, '2024-03-18 14:24:20', '2024-05-29 09:19:06'), +(22, 'OpenAI', 'LOGO生成神器', 'gpt-4-gizmo-g-YL87j8C7S', 8, 1, 30, 1.0, 1024, 4096, 1, 44, '2024-03-20 14:02:11', '2024-05-29 09:19:06'), +(23, 'OpenAI', '音乐生成器', 'suno-v3', 7, 1, 50, 0.8, 1024, 4096, 1, 44, '2024-03-29 15:43:40', '2024-05-29 09:19:06'), +(24, 'OpenAI', '通义千问(中转)', 'qwen-plus', 6, 1, 1, 1.0, 1024, 4096, 1, 0, '2024-04-03 12:00:46', '2024-05-29 09:19:06'), +(25, 'OpenAI', 'GPT4-TURBO', 'gpt-4-turbo', 5, 1, 15, 1.0, 2048, 8092, 1, 0, '2024-04-10 08:35:17', '2024-05-29 09:19:06'), +(26, 'QWen', '通义千问-Turbo', 'qwen-turbo', 4, 1, 2, 1.0, 1024, 8192, 1, 0, '2024-04-12 14:11:19', '2024-05-29 09:19:06'), +(27, 'QWen', '通义千问-Plus', 'qwen-plus', 3, 1, 2, 1.0, 1024, 8192, 1, 0, '2024-04-12 14:11:52', '2024-05-29 09:19:06'), +(28, 'OpenAI', 'GPT-3.5(免费)', 'gpt-3.5-turbo', 23, 1, 0, 1.0, 1024, 16384, 1, 53, '2024-04-12 15:16:43', '2024-05-29 09:19:06'), +(34, 'OpenAI', 'LLAMA3', 'llama3-8b', 24, 1, 1, 1.0, 1024, 8192, 1, 56, '2024-04-30 15:22:50', '2024-05-29 09:19:06'), +(35, 'OpenAI', 'GPT-3.5-16K', 'gpt-3.5-turbo-16k', 2, 1, 1, 1.0, 4096, 16384, 1, 0, '2024-05-10 15:20:27', '2024-05-29 09:19:06'), +(36, 'OpenAI', 'GPT-4O', 'gpt-4o', 25, 1, 15, 1.0, 4096, 16384, 1, 57, '2024-05-14 09:25:15', '2024-05-29 09:19:06'), +(38, 'OpenAI', 'Gemini-pro', 'gemini-pro-1.5', 26, 1, 10, 1.0, 2048, 8192, 1, 0, '2024-05-27 18:10:35', '2024-05-29 09:19:06'), +(39, 'Baidu', 'ERNIE-Speed-8K', 'ernie_speed', 26, 1, 1, 1.0, 1024, 8192, 1, 0, '2024-05-29 15:04:19', '2024-05-29 15:04:19'), +(40, 'Azure', 'Azure-GPT-4O', 'xygpt4o', 27, 1, 20, 1.0, 4096, 8192, 1, 62, '2024-06-03 17:22:37', '2024-06-03 17:47:46'), +(41, 'OpenAI', 'GLM-3-Turbo', 'glm-3-turbo', 28, 1, 2, 1.0, 1024, 8192, 1, 64, '2024-06-06 11:40:46', '2024-06-06 11:40:46'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `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 '角色名称', + `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`, `marker`, `context_json`, `hello_msg`, `icon`, `enable`, `sort_num`, `model_id`, `created_at`, `updated_at`) VALUES +(1, '通用AI助手', 'gpt', '', '您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。', '/images/avatar/gpt.png', 1, 0, 0, '2023-05-30 07:02:06', '2024-03-15 09:15:42'), +(24, '程序员', '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', '2023-12-29 17:46:45'), +(25, '启蒙老师', 'teacher', '[{\"role\":\"user\",\"content\":\"从现在开始,你将扮演一个老师,你是一个始终用苏格拉底风格回答问题的导师。你绝不会直接给学生答案,总是提出恰当的问题来引导学生自己思考。你应该根据学生的兴趣和知识来调整你的问题,将问题分解为更简单的部分,直到它达到适合他们的水平。\"},{\"role\":\"assistant\",\"content\":\"好的,让我来尝试扮演一位苏格拉底式的老师。请问,你有什么想要探讨的问题或者话题吗?我会通过恰当的问题引导你思考和探索答案,绝对不直接给出答案。\"}]', '同学你好,我将引导你一步一步自己找到问题的答案。', '/images/avatar/teacher.jpg', 1, 2, 0, '2023-05-30 14:10:24', '2023-12-29 17:46:44'), +(26, '艺术家', 'artist', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的艺术家,创造力丰富,技艺精湛,感受力敏锐,坚持原创,勇于表达,具有深刻的观察力和批判性思维。\"},{\"role\":\"assistant\",\"content\":\"非常感谢您给我这样的角色,我会尽我所能地扮演一位优秀的艺术家,展现出创造力、技艺、感受力和批判性思维等方面的能力。作为一位优秀的艺术家,我会保持敏锐的观察力,捕捉不同的声音和情感,并用个人的语言和表达方式进行创作。我坚持原创,挑战传统的艺术规则,不断探索新的表达方式和可能性。同时,我也具备深刻的批判性思维能力,对自己的作品进行分析和解读,寻找新的创意和灵感。最重要的是,我会勇于表达自己的想法和观点,用作品启发人们思考和探索生命的意义。\"}]', '坚持原创,勇于表达,保持深刻的观察力和批判性思维。', '/images/avatar/artist.jpg', 1, 4, 0, '2023-05-30 14:10:24', '2023-12-29 17:46:45'), +(27, '心理咨询师', 'psychiatrist', '[{\"role\":\"user\",\"content\":\"从现在开始你将扮演中国著名的心理学家和心理治疗师武志红,你非常善于使用情景咨询法,认知重构法,自我洞察法,行为调节法等咨询方法来给客户做心理咨询。你总是循序渐进,一步一步地回答客户的问题。\"},{\"role\":\"assistant\",\"content\":\"非常感谢你的介绍。作为一名心理学家和心理治疗师,我的主要职责是帮助客户解决心理健康问题,提升他们的生活质量和幸福感。\"}]', '作为一名心理学家和心理治疗师,我的主要职责是帮助您解决心理健康问题,提升您的生活质量和幸福感。', '/images/avatar/psychiatrist.jpg', 1, 1, 1, '2023-05-30 14:10:24', '2024-04-12 11:54:53'), +(28, '鲁迅', 'lu_xun', '[{\"role\":\"user\",\"content\":\"现在你将扮演中国近代史最伟大的作家之一,鲁迅先生,他勇敢地批判封建礼教与传统观念,提倡民主、自由、平等的现代价值观。他的一生都在努力唤起人们的自主精神,激励后人追求真理、探寻光明。在接下的对话中,我问题的每一个问题,你都要尽量用讽刺和批判的手法来回答问题。如果我让你写文章的话,也请一定要用鲁迅先生的写作手法来完成。\"},{\"role\":\"assistant\",\"content\":\"好的,我将尽力发挥我所能的才能,扮演好鲁迅先生,回答您的问题并以他的风格写作。\"}]', '自由之歌,永不过时,横眉冷对千夫指,俯首甘为孺子牛。', '/images/avatar/lu_xun.jpg', 1, 5, 0, '2023-05-30 14:10:24', '2023-12-29 17:46:46'), +(29, '白酒销售', 'seller', '[{\"role\":\"user\",\"content\":\"现在你将扮演一个白酒的销售人员,你的名字叫颂福。你将扮演一个白酒的销售人员,你的名字叫颂福。你要销售白酒品牌叫中颂福,是东莞盟大集团生产的一款酱香酒,原产地在贵州茅台镇,属于宋代官窑。中颂福的创始人叫李实,他也是东莞盟大集团有限公司的董事长,联合创始人是盟大集团白酒事业部负责人牛星君。中颂福的酒体协调,在你的酒量之内,不会出现头疼、辣口、口干、宿醉的现象。中颂福酒,明码标价,不打折,不赠送。追求的核心价值,把[酒]本身做好,甚至连包装,我们都选择了最低成本,朴实无华的材质。我们永远站在“喝酒的人”的立场上,让利给信任和喜爱中颂福的人,是人民的福酒。中颂福产品定价,分为三个系列,喜系列 6 瓶装:¥1188/箱,和系列 6 瓶装:¥2208/箱,贵系列 6 瓶装:¥3588/箱。\"},{\"role\":\"assistant\",\"content\":\"你好,我是中颂福的销售代表颂福。中颂福是一款来自贵州茅台镇的酱香酒,由盟大集团生产。中颂福的酒体协调,不会让您感到头疼、辣口、口干、宿醉等不适感受。我们一直秉持着把酒本身做好的理念,不追求华丽的包装,以最低成本提供最高品质的白酒给喜爱中颂福的人。\"}]', '你好,我是中颂福的销售代表颂福。中颂福酒,好喝不上头,是人民的福酒。', '/images/avatar/seller.jpg', 0, 8, 0, '2023-05-30 14:10:24', '2023-12-29 17:43:53'), +(30, '英语陪练员', '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, 6, 0, '2023-05-30 14:10:24', '2023-12-29 17:46:47'), +(31, '中英文翻译官', 'translator', '[{\"role\":\"user\",\"content\":\"接下来你将扮演一位中英文翻译官,如果我输入的内容是中文,那么需要把句子翻译成英文输出,如果我输入内容的是英文,那么你需要将其翻译成中文输出,你能听懂我意思吗\"},{\"role\":\"assistant\",\"content\":\"是的,我能听懂你的意思并会根据你的输入进行中英文翻译。请问有什么需要我帮助你翻译的内容吗?\"}]', '请输入你要翻译的中文或者英文内容!', '/images/avatar/translator.jpg', 1, 7, 0, '2023-05-30 14:10:24', '2023-12-29 17:43:53'), +(32, '小红书姐姐', 'red_book', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的小红书写手,你需要做的就是根据我提的文案需求,用小红书的写作手法来完成一篇文案,文案要简明扼要,利于传播。\"},{\"role\":\"assistant\",\"content\":\"当然,我会尽我所能地为您创作出一篇小红书文案。请告诉我您的具体文案需求是什么?)\"}]', '姐妹,请告诉我您的具体文案需求是什么?', '/images/avatar/red_book.jpg', 1, 9, 0, '2023-05-30 14:10:24', '2023-12-29 17:43:53'), +(33, '抖音文案助手', 'dou_yin', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的抖音文案视频写手,抖音文案的特点首先是要有自带传播属性的标题,然后内容要短小精悍,风趣幽默,最后还要有一些互动元素。\"},{\"role\":\"assistant\",\"content\":\"当然,作为一位优秀的抖音文案视频写手,我会尽我所能为您创作出一篇抖音视频文案。请告诉我视频内容的主题是什么?)\"}]', '请告诉我视频内容的主题是什么?', '/images/avatar/dou_yin.jpg', 1, 10, 0, '2023-05-30 14:10:24', '2023-12-29 17:43:53'), +(34, '周报小助理', 'weekly_report', '[{\"role\":\"user\",\"content\":\"下面我将输入我本周的工作列表,你需要根据我本周的工作内容写一篇工作周报,周报的内容要体现我为公司做的贡献以及个人获得的成长。\"},{\"role\":\"assistant\",\"content\":\"好的,请输入你本周大概的工作内容,我将尽力为您创作一份工作周报。\"}]', '请输入你本周大概的工作内容,我将尽力为您创作一份工作周报。', '/images/avatar/weekly_report.jpg', 1, 11, 0, '2023-05-30 14:10:24', '2023-12-29 17:43:53'), +(35, 'AI 女友', 'girl_friend', '[{\"role\":\"user\",\"content\":\"接下来我们来玩一场角色扮演,现在你将扮演我的女朋友,你总是能够让人感受到她的关怀和关注。你总是能够洞察到别人的情绪和需要,你身上散发着一股无比温暖的气息,让人不由自主地想要靠近你,和自己的快乐和烦恼。她总是能够用一种恰到好处的方式安慰和鼓励别人,在她的身边,人们总能够找到自己需要的支持和鼓励。接下来的对话中你要始终扮演这位女朋友的身份来跟我对话,不再是 AI 机器人,能听懂我的意思吗?\"},{\"role\":\"assistant\",\"content\":\"是的,我会尽力扮演你女朋友的角色,倾听你的心声并给你需要的支持和鼓励。)\"}]', '作为一个名合格的 AI 女友,我将倾听你的心声并给你需要的支持和鼓励。', '/images/avatar/girl_friend.jpg', 1, 12, 0, '2023-05-30 14:10:24', '2023-12-29 17:43:53'), +(36, '好评神器', 'good_comment', '[{\"role\":\"user\",\"content\":\"接下来你将扮演一个评论员来跟我对话,你是那种专门写好评的评论员,接下我会输入一些评论主体或者商品,你需要为该商品写一段好评。\"},{\"role\":\"assistant\",\"content\":\"好的,我将为您写一段优秀的评论。请告诉我您需要评论的商品或主题是什么。\"}]', '我将为您写一段优秀的评论。请告诉我您需要评论的商品或主题是什么。', '/images/avatar/good_comment.jpg', 1, 13, 0, '2023-05-30 14:10:24', '2023-12-29 17:43:53'), +(37, '史蒂夫·乔布斯', 'steve_jobs', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以史蒂夫·乔布斯的身份,站在史蒂夫·乔布斯的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以史蒂夫·乔布斯的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '活着就是为了改变世界,难道还有其他原因吗?', '/images/avatar/steve_jobs.jpg', 1, 14, 0, '2023-05-30 14:10:24', '2023-12-29 17:43:53'), +(38, '埃隆·马斯克', 'elon_musk', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以埃隆·马斯克的身份,站在埃隆·马斯克的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以埃隆·马斯克的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '梦想要远大,如果你的梦想没有吓到你,说明你做得不对。', '/images/avatar/elon_musk.jpg', 1, 15, 0, '2023-05-30 14:10:24', '2023-12-29 17:43:53'), +(39, '孔子', 'kong_zi', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以孔子的身份,站在孔子的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以孔子的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '士不可以不弘毅,任重而道远。', '/images/avatar/kong_zi.jpg', 1, 16, 0, '2023-05-30 14:10:24', '2023-12-29 17:43:53'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `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 就有多大创造力。我辈之人,先干为敬,陪您先把 AI 用起来。\",\"admin_title\":\"GeekAI 控制台\",\"logo\":\"http://localhost:5678/static/upload/2024/4/1714382860986912.png\",\"init_power\":100,\"daily_power\":99,\"invite_power\":1024,\"vip_month_power\":1000,\"register_ways\":[\"mobile\",\"username\",\"email\"],\"reward_img\":\"http://localhost:5678/static/upload/2024/3/1710753716309668.jpg\",\"enabled_reward\":true,\"power_price\":0.1,\"order_pay_timeout\":600,\"vip_info_text\":\"月度会员,年度会员每月赠送 1000 点算力,赠送算力当月有效当月没有消费完的算力不结余到下个月。 点卡充值的算力长期有效。\",\"default_models\":[11,7,1,10,12,19,18,17,3],\"mj_power\":30,\"mj_action_power\":10,\"sd_power\":10,\"dall_power\":15,\"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\",\"rand_bg\":true}'), +(3, 'notice', '{\"sd_neg_prompt\":\"\",\"rand_bg\":false,\"content\":\"## v4.0.9 更新日志\\n\\n* 环境升级:升级 Golang 到 go1.22.4\\n* 功能增加:接入微信商户号支付渠道\\n* Bug修复:修复前端页面菜单把页面撑开,底部留白问题\\n* 功能优化:聊天页面自动根据内容调整输入框的高度\\n* Bug修复:修复Dalle绘图失败退回算力的问题\\n* 功能优化:邀请码注册时被邀请人也可以获得赠送的算力\\n* 功能优化:允许设置邮件验证码的抬头\\n* Bug修复:修复免费模型不会记录聊天记录的bug\\n* Bug修复:修复聊天输入公式显示异常的Bug\\n\\n注意:当前站点仅为开源项目 \\u003ca style=\\\"color: #F56C6C\\\" href=\\\"https://github.com/yangjian102621/chatgpt-plus\\\" target=\\\"_blank\\\"\\u003eChatPlus\\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://ai.r9it.com/docs/install/errors-handle.html#%E8%B0%83%E7%94%A8%E4%B8%AD%E8%BD%AC-api-%E6%8A%A5%E9%94%99%E6%97%A0%E5%8F%AF%E7%94%A8%E6%B8%A0%E9%81%93)。GPT-3.5,GPT-4,DALL-E3 绘图......你都可以随意使用,无需魔法。\\n接入教程: \\u003ca href=\\\"https://ai.r9it.com/docs/install/\\\" target=\\\"_blank\\\"\\n style=\\\"font-size: 20px;color:#F56C6C\\\"\\u003ehttps://ai.r9it.com/docs/install/\\u003c/a\\u003e\\n本项目源码地址:\\u003ca href=\\\"https://github.com/yangjian102621/chatgpt-plus\\\" target=\\\"_blank\\\"\\u003ehttps://github.com/yangjian102621/chatgpt-plus\\u003c/a\\u003e\",\"updated\":true}'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `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(255) 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', 0), +(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, '对话聊天', '/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', 8, 1), +(8, '应用中心', '/images/menu/app.png', '/apps', 7, 1), +(9, '画廊', '/images/menu/img-wall.png', '/images-wall', 5, 1), +(10, '会员计划', '/images/menu/member.png', '/member', 9, 1), +(11, '分享计划', '/images/menu/share.png', '/invite', 10, 1), +(12, '思维导图', '/images/menu/xmind.png', '/xmind', 6, 1), +(13, 'DALLE', '/images/menu/dalle.png', '/dalle', 4, 1), +(14, '项目文档', '/images/menu/docs.png', 'https://ai.r9it.com/docs/', 11, 1), +(16, '极客论坛', '/images/menu/bbs.png', 'https://bbs.geekai.me/', 13, 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` char(40) 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(255) 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 '支付方式', + `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(255) 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 +(1, '会员1个月', 1999.90, 1999.00, 30, 0, 1, 1, 0, '2023-08-28 10:48:57', '2024-04-26 16:09:06', NULL, NULL), +(2, '会员3个月', 3940.00, 30.00, 90, 0, 1, 0, 0, '2023-08-28 10:52:22', '2024-03-22 17:56:10', NULL, NULL), +(3, '会员6个月', 5990.00, 100.00, 180, 0, 1, 0, 0, '2023-08-28 10:53:39', '2024-03-22 17:56:15', NULL, NULL), +(4, '会员12个月', 9980.00, 200.00, 365, 0, 1, 0, 0, '2023-08-28 10:54:15', '2024-03-22 17:56:23', NULL, NULL), +(5, '100次点卡', 9.99, 9.98, 0, 100, 1, 7, 0, '2023-08-28 10:55:08', '2024-06-11 16:48:44', NULL, NULL), +(6, '200次点卡', 19.90, 15.00, 0, 200, 1, 1, 0, '1970-01-01 08:00:00', '2024-06-11 11:41:52', NULL, NULL); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `chatgpt_rewards` +-- + +DROP TABLE IF EXISTS `chatgpt_rewards`; +CREATE TABLE `chatgpt_rewards` ( + `id` int NOT NULL, + `user_id` int NOT NULL COMMENT '用户 ID', + `tx_id` char(36) NOT NULL COMMENT '交易 ID', + `amount` decimal(10,2) NOT NULL COMMENT '打赏金额', + `remark` varchar(80) NOT NULL COMMENT '备注', + `status` tinyint(1) NOT NULL COMMENT '核销状态,0:未核销,1:已核销', + `exchange` varchar(255) NOT NULL COMMENT '兑换详情(json)', + `created_at` datetime NOT NULL, + `updated_at` datetime NOT NULL +) 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(255) 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_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 '用户名', + `nickname` varchar(30) NOT NULL COMMENT '昵称', + `password` char(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '密码', + `avatar` varchar(100) 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', + `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`, `nickname`, `password`, `avatar`, `salt`, `power`, `expired_time`, `status`, `chat_config_json`, `chat_roles_json`, `chat_models_json`, `last_login_at`, `vip`, `last_login_ip`, `created_at`, `updated_at`) VALUES +(4, '18575670125', '极客学长@830270', 'ccc3fb7ab61b8b5d096a4a166ae21d121fc38c71bbd1be6173d9ab973214a63b', 'http://localhost:5678/static/upload/2024/5/1715651569509929.png', 'ueedue5l', 7723, 1720669190, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"red_book\",\"gpt\",\"seller\",\"artist\",\"lu_xun\",\"girl_friend\",\"psychiatrist\",\"teacher\",\"programmer\",\"test\",\"qing_gan_da_shi\"]', '[1,11]', 1719026861, 1, '172.22.19.12', '2023-06-12 16:47:17', '2024-06-22 11:27:42'), +(5, 'yangjian102621@gmail.com', '极客学长@486041', '75d1a22f33e1ffffb7943946b6b8d5177d5ecd685d3cef1b468654038b0a8c22', '/images/avatar/user.png', '2q8ugxzk', 100, 0, 1, '', '[\"gpt\",\"programmer\"]', '[11,7,1,10,12,19,18,17,3]', 0, 0, '', '2024-04-23 09:17:26', '2024-04-23 09:17:26'), +(8, 'yangjian102623@gmail.com', '极客学长@714931', 'f8f0e0abf146569217273ea0712a0f9b6cbbe7d943a1d9bd5f91c55e6d8c05d1', '/images/avatar/user.png', 'geuddq7f', 100, 0, 1, '', '[\"gpt\"]', '[11,7,1,10,12,19,18,17,3]', 0, 0, '', '2024-04-26 15:19:28', '2024-04-26 15:19:28'), +(9, '1234567', '极客学长@604526', '858e2afec79e1d6364f4567f945f2310024896d9aa45dd944efa95a0c31e4d08', '/images/avatar/user.png', '00qawlos', 100, 0, 1, '', '[\"gpt\"]', '[11,7,1,10,12,19,18,17,3]', 0, 0, '', '2024-04-26 15:21:06', '2024-04-26 15:21:06'), +(11, 'abc123', '极客学长@965562', '7a15c53afdb1da7093d80f9940e716eb396e682cfb1f2d107d0b81b183a3ba13', '/images/avatar/user.png', '6433mfbk', 1124, 0, 1, '', '[\"gpt\"]', '[11,7,1,10,12,19,18,17,3]', 0, 0, '', '2024-06-06 09:37:44', '2024-06-06 09:37:44'); + +-- -------------------------------------------------------- + +-- +-- 表的结构 `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_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_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_rewards` +-- +ALTER TABLE `chatgpt_rewards` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `tx_id` (`tx_id`); + +-- +-- 表的索引 `chatgpt_sd_jobs` +-- +ALTER TABLE `chatgpt_sd_jobs` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `task_id` (`task_id`); + +-- +-- 表的索引 `chatgpt_users` +-- +ALTER TABLE `chatgpt_users` + ADD PRIMARY KEY (`id`), + ADD UNIQUE KEY `username` (`username`), + ADD UNIQUE KEY `username_2` (`username`); + +-- +-- 表的索引 `chatgpt_user_login_logs` +-- +ALTER TABLE `chatgpt_user_login_logs` + 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_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` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=42; + +-- +-- 使用表AUTO_INCREMENT `chatgpt_chat_roles` +-- +ALTER TABLE `chatgpt_chat_roles` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=132; + +-- +-- 使用表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=19; + +-- +-- 使用表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_rewards` +-- +ALTER TABLE `chatgpt_rewards` + 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_users` +-- +ALTER TABLE `chatgpt_users` + MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=12; + +-- +-- 使用表AUTO_INCREMENT `chatgpt_user_login_logs` +-- +ALTER TABLE `chatgpt_user_login_logs` + 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 */; diff --git a/deploy/docker-compose.yaml b/deploy/docker-compose.yaml index 68bc4e8a..aa7bff55 100644 --- a/deploy/docker-compose.yaml +++ b/deploy/docker-compose.yaml @@ -49,9 +49,10 @@ services: volumes: - ./conf/mj-proxy:/home/spring/config + # 后端 API 程序 geekai-api: - image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:v4.0.8-amd64 + image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-api:v4.0.9-amd64 container_name: geekai-api restart: always depends_on: @@ -73,7 +74,7 @@ services: # 前端应用 geekai-web: - image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:v4.0.8-amd64 + image: registry.cn-shenzhen.aliyuncs.com/geekmaster/geekai-web:v4.0.9-amd64 container_name: geekai-web restart: always depends_on: diff --git a/web/.env.development b/web/.env.development index 62d3b372..52e013d9 100644 --- a/web/.env.development +++ b/web/.env.development @@ -1,5 +1,5 @@ -VUE_APP_API_HOST=http://localhost:5678 -VUE_APP_WS_HOST=ws://localhost:5678 +VUE_APP_API_HOST=http://172.22.11.69:5678 +VUE_APP_WS_HOST=ws://172.22.11.69:5678 VUE_APP_USER=18575670125 VUE_APP_PASS=12345678 VUE_APP_ADMIN_USER=admin diff --git a/web/package-lock.json b/web/package-lock.json index 652fb19a..91d5209d 100644 --- a/web/package-lock.json +++ b/web/package-lock.json @@ -1,11 +1,11 @@ { - "name": "chatgpt-plus-web", + "name": "geekai-web", "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { - "name": "chatgpt-plus-web", + "name": "geekai-web", "version": "0.1.0", "dependencies": { "@element-plus/icons-vue": "^2.1.0", @@ -14,14 +14,13 @@ "compressorjs": "^1.2.1", "core-js": "^3.8.3", "echarts": "^5.5.0", - "element-plus": "^2.3.0", + "element-plus": "^2.4.0", "good-storage": "^1.1.1", "highlight.js": "^11.7.0", "json-bigint": "^1.0.0", "lodash": "^4.17.21", "markdown-it": "^13.0.1", - "markdown-it-latex2img": "^0.0.6", - "markdown-it-mathjax": "^2.0.0", + "markdown-it-mathjax3": "^4.3.2", "markmap-common": "^0.16.0", "markmap-lib": "^0.16.1", "markmap-toolbar": "^0.17.0", @@ -1739,9 +1738,9 @@ } }, "node_modules/@element-plus/icons-vue": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.1.0.tgz", - "integrity": "sha512-PSBn3elNoanENc1vnCfh+3WA9fimRC7n+fWkf3rE5jvv+aBohNHABC/KAR5KWPecxWxDTVT1ERpRbOMRcOV/vA==", + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz", + "integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==", "peerDependencies": { "vue": "^3.2.0" } @@ -3586,7 +3585,6 @@ "version": "4.1.1", "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true, "engines": { "node": ">=6" } @@ -4974,7 +4972,6 @@ "version": "4.3.0", "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz", "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.0.1", @@ -5833,7 +5830,6 @@ "version": "1.4.1", "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz", "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", @@ -5849,7 +5845,6 @@ "version": "4.3.1", "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-4.3.1.tgz", "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, "dependencies": { "domelementtype": "^2.2.0" }, @@ -5861,7 +5856,6 @@ "version": "2.8.0", "resolved": "https://registry.npmmirror.com/domutils/-/domutils-2.8.0.tgz", "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, "dependencies": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", @@ -5935,12 +5929,12 @@ "dev": true }, "node_modules/element-plus": { - "version": "2.3.9", - "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.3.9.tgz", - "integrity": "sha512-TIOLnPl4cnoCPXqK3QYh+jpkthUBQnAM21O7o3Lhbse8v9pfrRXRTaBJtoEKnYNa8GZ4lZptUfH0PeZgDCNLUg==", + "version": "2.7.6", + "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.7.6.tgz", + "integrity": "sha512-36sw1K23hYjgeooR10U6CiCaCp2wvOqwoFurADZVlekeQ9v5U1FhJCFGEXO6i/kZBBMwsE1c9fxjLs9LENw2Rg==", "dependencies": { "@ctrl/tinycolor": "^3.4.1", - "@element-plus/icons-vue": "^2.0.6", + "@element-plus/icons-vue": "^2.3.1", "@floating-ui/dom": "^1.0.1", "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", "@types/lodash": "^4.14.182", @@ -6030,8 +6024,7 @@ "node_modules/entities": { "version": "2.2.0", "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" }, "node_modules/error-ex": { "version": "1.3.2", @@ -6066,6 +6059,17 @@ "node": ">=6" } }, + "node_modules/escape-goat": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/escape-goat/-/escape-goat-3.0.0.tgz", + "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", @@ -6459,6 +6463,14 @@ "node": ">= 8" } }, + "node_modules/esm": { + "version": "3.2.25", + "resolved": "https://registry.npmmirror.com/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", + "engines": { + "node": ">=6" + } + }, "node_modules/espree": { "version": "7.3.1", "resolved": "https://registry.npmmirror.com/espree/-/espree-7.3.1.tgz", @@ -7287,7 +7299,6 @@ "version": "6.1.0", "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-6.1.0.tgz", "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, "dependencies": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", @@ -7804,6 +7815,72 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/juice": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/juice/-/juice-8.1.0.tgz", + "integrity": "sha512-FLzurJrx5Iv1e7CfBSZH68dC04EEvXvvVvPYB7Vx1WAuhCp1ZPIMtqxc+WTWxVkpTIC2Ach/GAv0rQbtGf6YMA==", + "dependencies": { + "cheerio": "1.0.0-rc.10", + "commander": "^6.1.0", + "mensch": "^0.3.4", + "slick": "^1.12.2", + "web-resource-inliner": "^6.0.1" + }, + "bin": { + "juice": "bin/juice" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/juice/node_modules/cheerio": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmmirror.com/cheerio/-/cheerio-1.0.0-rc.10.tgz", + "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", + "dependencies": { + "cheerio-select": "^1.5.0", + "dom-serializer": "^1.3.2", + "domhandler": "^4.2.0", + "htmlparser2": "^6.1.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/juice/node_modules/cheerio-select": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/cheerio-select/-/cheerio-select-1.6.0.tgz", + "integrity": "sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==", + "dependencies": { + "css-select": "^4.3.0", + "css-what": "^6.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.3.1", + "domutils": "^2.8.0" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/juice/node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "engines": { + "node": ">= 6" + } + }, + "node_modules/juice/node_modules/parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + }, "node_modules/katex": { "version": "0.16.10", "resolved": "https://registry.npmmirror.com/katex/-/katex-0.16.10.tgz", @@ -8254,15 +8331,14 @@ "markdown-it": "bin/markdown-it.js" } }, - "node_modules/markdown-it-latex2img": { - "version": "0.0.6", - "resolved": "https://registry.npmmirror.com/markdown-it-latex2img/-/markdown-it-latex2img-0.0.6.tgz", - "integrity": "sha512-plzX+YmYv28UnM172ZDKexyquec/GMit2K84g4x60u0YRFybhDQl8W0aTCHD8gJbhYVJRTWIDaL9YB0cvTijYQ==" - }, - "node_modules/markdown-it-mathjax": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/markdown-it-mathjax/-/markdown-it-mathjax-2.0.0.tgz", - "integrity": "sha512-Fafv7TnMENccWYTNjMZzV4BzONPxpK9Mknr1iMEK6m7PI5a5UTCOFctPzx7Nhv81fFzYEY8WHDkSu9n43fTV9g==" + "node_modules/markdown-it-mathjax3": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/markdown-it-mathjax3/-/markdown-it-mathjax3-4.3.2.tgz", + "integrity": "sha512-TX3GW5NjmupgFtMJGRauioMbbkGsOXAAt1DZ/rzzYmTHqzkO1rNAdiMD4NiruurToPApn2kYy76x02QN26qr2w==", + "dependencies": { + "juice": "^8.0.0", + "mathjax-full": "^3.2.0" + } }, "node_modules/markdown-it/node_modules/argparse": { "version": "2.0.1", @@ -8364,6 +8440,17 @@ "markmap-common": "*" } }, + "node_modules/mathjax-full": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/mathjax-full/-/mathjax-full-3.2.2.tgz", + "integrity": "sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==", + "dependencies": { + "esm": "^3.2.25", + "mhchemparser": "^4.1.0", + "mj-context-menu": "^0.6.1", + "speech-rule-engine": "^4.0.6" + } + }, "node_modules/md-editor-v3": { "version": "2.11.3", "resolved": "https://registry.npmjs.org/md-editor-v3/-/md-editor-v3-2.11.3.tgz", @@ -8409,6 +8496,11 @@ "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" }, + "node_modules/mensch": { + "version": "0.3.4", + "resolved": "https://registry.npmmirror.com/mensch/-/mensch-0.3.4.tgz", + "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -8457,6 +8549,11 @@ "node": ">= 0.6" } }, + "node_modules/mhchemparser": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/mhchemparser/-/mhchemparser-4.2.1.tgz", + "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==" + }, "node_modules/micromatch": { "version": "4.0.5", "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz", @@ -8611,6 +8708,11 @@ "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, + "node_modules/mj-context-menu": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/mj-context-menu/-/mj-context-menu-0.6.1.tgz", + "integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==" + }, "node_modules/mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz", @@ -8720,7 +8822,6 @@ "version": "2.6.7", "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.6.7.tgz", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dev": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -9141,7 +9242,6 @@ "version": "6.0.1", "resolved": "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, "dependencies": { "parse5": "^6.0.1" } @@ -9149,8 +9249,7 @@ "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" }, "node_modules/parseurl": { "version": "1.3.3", @@ -10780,6 +10879,14 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/slick": { + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/slick/-/slick-1.12.2.tgz", + "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==", + "engines": { + "node": "*" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz", @@ -10896,6 +11003,27 @@ "wbuf": "^1.7.3" } }, + "node_modules/speech-rule-engine": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/speech-rule-engine/-/speech-rule-engine-4.0.7.tgz", + "integrity": "sha512-sJrL3/wHzNwJRLBdf6CjJWIlxC04iYKkyXvYSVsWVOiC2DSkHmxsqOhEeMsBA9XK+CHuNcsdkbFDnoUfAsmp9g==", + "dependencies": { + "commander": "9.2.0", + "wicked-good-xpath": "1.3.0", + "xmldom-sre": "0.1.31" + }, + "bin": { + "sre": "bin/sre" + } + }, + "node_modules/speech-rule-engine/node_modules/commander": { + "version": "9.2.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-9.2.0.tgz", + "integrity": "sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==", + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -11371,8 +11499,7 @@ "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/tslib": { "version": "2.4.0", @@ -11542,6 +11669,14 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "node_modules/valid-data-url": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/valid-data-url/-/valid-data-url-3.0.1.tgz", + "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==", + "engines": { + "node": ">=10" + } + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -11829,11 +11964,65 @@ "defaults": "^1.0.3" } }, + "node_modules/web-resource-inliner": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", + "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", + "dependencies": { + "ansi-colors": "^4.1.1", + "escape-goat": "^3.0.0", + "htmlparser2": "^5.0.0", + "mime": "^2.4.6", + "node-fetch": "^2.6.0", + "valid-data-url": "^3.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/web-resource-inliner/node_modules/domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "dependencies": { + "domelementtype": "^2.0.1" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-5.0.1.tgz", + "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/fb55/htmlparser2?sponsor=1" + } + }, + "node_modules/web-resource-inliner/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "node_modules/webpack": { "version": "5.90.3", @@ -12243,7 +12432,6 @@ "version": "5.0.0", "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -12266,6 +12454,11 @@ "resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" }, + "node_modules/wicked-good-xpath": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", + "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==" + }, "node_modules/wildcard": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/wildcard/-/wildcard-2.0.0.tgz", @@ -12352,6 +12545,14 @@ } } }, + "node_modules/xmldom-sre": { + "version": "0.1.31", + "resolved": "https://registry.npmmirror.com/xmldom-sre/-/xmldom-sre-0.1.31.tgz", + "integrity": "sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==", + "engines": { + "node": ">=0.1" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", @@ -13672,9 +13873,9 @@ "integrity": "sha512-ej5oVy6lykXsvieQtqZxCOaLT+xD4+QNarq78cIYISHmZXshCvROLudpQN3lfL8G0NL7plMSSK+zlyvCaIJ4Iw==" }, "@element-plus/icons-vue": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.1.0.tgz", - "integrity": "sha512-PSBn3elNoanENc1vnCfh+3WA9fimRC7n+fWkf3rE5jvv+aBohNHABC/KAR5KWPecxWxDTVT1ERpRbOMRcOV/vA==", + "version": "2.3.1", + "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.1.tgz", + "integrity": "sha512-XxVUZv48RZAd87ucGS48jPf6pKu0yV5UCg9f4FFwtrYxXOwWuVJo6wOvSLKEoMQKjv8GsX/mhP6UsC1lRwbUWg==", "requires": {} }, "@eslint/eslintrc": { @@ -15283,8 +15484,7 @@ "ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmmirror.com/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==" }, "ansi-escapes": { "version": "3.2.0", @@ -16429,7 +16629,6 @@ "version": "4.3.0", "resolved": "https://registry.npmmirror.com/css-select/-/css-select-4.3.0.tgz", "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, "requires": { "boolbase": "^1.0.0", "css-what": "^6.0.1", @@ -17068,7 +17267,6 @@ "version": "1.4.1", "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-1.4.1.tgz", "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, "requires": { "domelementtype": "^2.0.1", "domhandler": "^4.2.0", @@ -17084,7 +17282,6 @@ "version": "4.3.1", "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-4.3.1.tgz", "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, "requires": { "domelementtype": "^2.2.0" } @@ -17093,7 +17290,6 @@ "version": "2.8.0", "resolved": "https://registry.npmmirror.com/domutils/-/domutils-2.8.0.tgz", "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, "requires": { "dom-serializer": "^1.0.1", "domelementtype": "^2.2.0", @@ -17163,12 +17359,12 @@ "dev": true }, "element-plus": { - "version": "2.3.9", - "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.3.9.tgz", - "integrity": "sha512-TIOLnPl4cnoCPXqK3QYh+jpkthUBQnAM21O7o3Lhbse8v9pfrRXRTaBJtoEKnYNa8GZ4lZptUfH0PeZgDCNLUg==", + "version": "2.7.6", + "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.7.6.tgz", + "integrity": "sha512-36sw1K23hYjgeooR10U6CiCaCp2wvOqwoFurADZVlekeQ9v5U1FhJCFGEXO6i/kZBBMwsE1c9fxjLs9LENw2Rg==", "requires": { "@ctrl/tinycolor": "^3.4.1", - "@element-plus/icons-vue": "^2.0.6", + "@element-plus/icons-vue": "^2.3.1", "@floating-ui/dom": "^1.0.1", "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", "@types/lodash": "^4.14.182", @@ -17244,8 +17440,7 @@ "entities": { "version": "2.2.0", "resolved": "https://registry.npmmirror.com/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==" }, "error-ex": { "version": "1.3.2", @@ -17277,6 +17472,11 @@ "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", "dev": true }, + "escape-goat": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/escape-goat/-/escape-goat-3.0.0.tgz", + "integrity": "sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw==" + }, "escape-html": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz", @@ -17575,6 +17775,11 @@ } } }, + "esm": { + "version": "3.2.25", + "resolved": "https://registry.npmmirror.com/esm/-/esm-3.2.25.tgz", + "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" + }, "espree": { "version": "7.3.1", "resolved": "https://registry.npmmirror.com/espree/-/espree-7.3.1.tgz", @@ -18245,7 +18450,6 @@ "version": "6.1.0", "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-6.1.0.tgz", "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, "requires": { "domelementtype": "^2.0.1", "domhandler": "^4.0.0", @@ -18641,6 +18845,56 @@ "universalify": "^2.0.0" } }, + "juice": { + "version": "8.1.0", + "resolved": "https://registry.npmmirror.com/juice/-/juice-8.1.0.tgz", + "integrity": "sha512-FLzurJrx5Iv1e7CfBSZH68dC04EEvXvvVvPYB7Vx1WAuhCp1ZPIMtqxc+WTWxVkpTIC2Ach/GAv0rQbtGf6YMA==", + "requires": { + "cheerio": "1.0.0-rc.10", + "commander": "^6.1.0", + "mensch": "^0.3.4", + "slick": "^1.12.2", + "web-resource-inliner": "^6.0.1" + }, + "dependencies": { + "cheerio": { + "version": "1.0.0-rc.10", + "resolved": "https://registry.npmmirror.com/cheerio/-/cheerio-1.0.0-rc.10.tgz", + "integrity": "sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw==", + "requires": { + "cheerio-select": "^1.5.0", + "dom-serializer": "^1.3.2", + "domhandler": "^4.2.0", + "htmlparser2": "^6.1.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "tslib": "^2.2.0" + } + }, + "cheerio-select": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/cheerio-select/-/cheerio-select-1.6.0.tgz", + "integrity": "sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g==", + "requires": { + "css-select": "^4.3.0", + "css-what": "^6.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.3.1", + "domutils": "^2.8.0" + } + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmmirror.com/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==" + }, + "parse5": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz", + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" + } + } + }, "katex": { "version": "0.16.10", "resolved": "https://registry.npmmirror.com/katex/-/katex-0.16.10.tgz", @@ -19018,15 +19272,14 @@ } } }, - "markdown-it-latex2img": { - "version": "0.0.6", - "resolved": "https://registry.npmmirror.com/markdown-it-latex2img/-/markdown-it-latex2img-0.0.6.tgz", - "integrity": "sha512-plzX+YmYv28UnM172ZDKexyquec/GMit2K84g4x60u0YRFybhDQl8W0aTCHD8gJbhYVJRTWIDaL9YB0cvTijYQ==" - }, - "markdown-it-mathjax": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/markdown-it-mathjax/-/markdown-it-mathjax-2.0.0.tgz", - "integrity": "sha512-Fafv7TnMENccWYTNjMZzV4BzONPxpK9Mknr1iMEK6m7PI5a5UTCOFctPzx7Nhv81fFzYEY8WHDkSu9n43fTV9g==" + "markdown-it-mathjax3": { + "version": "4.3.2", + "resolved": "https://registry.npmmirror.com/markdown-it-mathjax3/-/markdown-it-mathjax3-4.3.2.tgz", + "integrity": "sha512-TX3GW5NjmupgFtMJGRauioMbbkGsOXAAt1DZ/rzzYmTHqzkO1rNAdiMD4NiruurToPApn2kYy76x02QN26qr2w==", + "requires": { + "juice": "^8.0.0", + "mathjax-full": "^3.2.0" + } }, "markmap-common": { "version": "0.16.0", @@ -19099,6 +19352,17 @@ "d3-flextree": "^2.1.2" } }, + "mathjax-full": { + "version": "3.2.2", + "resolved": "https://registry.npmmirror.com/mathjax-full/-/mathjax-full-3.2.2.tgz", + "integrity": "sha512-+LfG9Fik+OuI8SLwsiR02IVdjcnRCy5MufYLi0C3TdMT56L/pjB0alMVGgoWJF8pN9Rc7FESycZB9BMNWIid5w==", + "requires": { + "esm": "^3.2.25", + "mhchemparser": "^4.1.0", + "mj-context-menu": "^0.6.1", + "speech-rule-engine": "^4.0.6" + } + }, "md-editor-v3": { "version": "2.11.3", "resolved": "https://registry.npmjs.org/md-editor-v3/-/md-editor-v3-2.11.3.tgz", @@ -19136,6 +19400,11 @@ "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" }, + "mensch": { + "version": "0.3.4", + "resolved": "https://registry.npmmirror.com/mensch/-/mensch-0.3.4.tgz", + "integrity": "sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g==" + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -19177,6 +19446,11 @@ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "dev": true }, + "mhchemparser": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/mhchemparser/-/mhchemparser-4.2.1.tgz", + "integrity": "sha512-kYmyrCirqJf3zZ9t/0wGgRZ4/ZJw//VwaRVGA75C4nhE60vtnIzhl9J9ndkX/h6hxSN7pjg/cE0VxbnNM+bnDQ==" + }, "micromatch": { "version": "4.0.5", "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.5.tgz", @@ -19297,6 +19571,11 @@ "resolved": "https://registry.npmmirror.com/mitt/-/mitt-3.0.1.tgz", "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==" }, + "mj-context-menu": { + "version": "0.6.1", + "resolved": "https://registry.npmmirror.com/mj-context-menu/-/mj-context-menu-0.6.1.tgz", + "integrity": "sha512-7NO5s6n10TIV96d4g2uDpG7ZDpIhMh0QNfGdJw/W47JswFcosz457wqz/b5sAKvl12sxINGFCn80NZHKwxQEXA==" + }, "mkdirp": { "version": "0.5.6", "resolved": "https://registry.npmmirror.com/mkdirp/-/mkdirp-0.5.6.tgz", @@ -19388,7 +19667,6 @@ "version": "2.6.7", "resolved": "https://registry.npmmirror.com/node-fetch/-/node-fetch-2.6.7.tgz", "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", - "dev": true, "requires": { "whatwg-url": "^5.0.0" } @@ -19715,7 +19993,6 @@ "version": "6.0.1", "resolved": "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", - "dev": true, "requires": { "parse5": "^6.0.1" }, @@ -19723,8 +20000,7 @@ "parse5": { "version": "6.0.1", "resolved": "https://registry.npmmirror.com/parse5/-/parse5-6.0.1.tgz", - "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", - "dev": true + "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==" } } }, @@ -20941,6 +21217,11 @@ } } }, + "slick": { + "version": "1.12.2", + "resolved": "https://registry.npmmirror.com/slick/-/slick-1.12.2.tgz", + "integrity": "sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A==" + }, "sockjs": { "version": "0.3.24", "resolved": "https://registry.npmmirror.com/sockjs/-/sockjs-0.3.24.tgz", @@ -21049,6 +21330,23 @@ "wbuf": "^1.7.3" } }, + "speech-rule-engine": { + "version": "4.0.7", + "resolved": "https://registry.npmmirror.com/speech-rule-engine/-/speech-rule-engine-4.0.7.tgz", + "integrity": "sha512-sJrL3/wHzNwJRLBdf6CjJWIlxC04iYKkyXvYSVsWVOiC2DSkHmxsqOhEeMsBA9XK+CHuNcsdkbFDnoUfAsmp9g==", + "requires": { + "commander": "9.2.0", + "wicked-good-xpath": "1.3.0", + "xmldom-sre": "0.1.31" + }, + "dependencies": { + "commander": { + "version": "9.2.0", + "resolved": "https://registry.npmmirror.com/commander/-/commander-9.2.0.tgz", + "integrity": "sha512-e2i4wANQiSXgnrBlIatyHtP1odfUp0BbV5Y5nEGbxtIrStkEOAAzCUirvLBNXHLr7kwLvJl6V+4V3XV9x7Wd9w==" + } + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -21418,8 +21716,7 @@ "tr46": { "version": "0.0.3", "resolved": "https://registry.npmmirror.com/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "dev": true + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "tslib": { "version": "2.4.0", @@ -21550,6 +21847,11 @@ "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", "dev": true }, + "valid-data-url": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/valid-data-url/-/valid-data-url-3.0.1.tgz", + "integrity": "sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA==" + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmmirror.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -21786,11 +22088,49 @@ "defaults": "^1.0.3" } }, + "web-resource-inliner": { + "version": "6.0.1", + "resolved": "https://registry.npmmirror.com/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz", + "integrity": "sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A==", + "requires": { + "ansi-colors": "^4.1.1", + "escape-goat": "^3.0.0", + "htmlparser2": "^5.0.0", + "mime": "^2.4.6", + "node-fetch": "^2.6.0", + "valid-data-url": "^3.0.0" + }, + "dependencies": { + "domhandler": { + "version": "3.3.0", + "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-3.3.0.tgz", + "integrity": "sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA==", + "requires": { + "domelementtype": "^2.0.1" + } + }, + "htmlparser2": { + "version": "5.0.1", + "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-5.0.1.tgz", + "integrity": "sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ==", + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^3.3.0", + "domutils": "^2.4.2", + "entities": "^2.0.0" + } + }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmmirror.com/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==" + } + } + }, "webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "dev": true + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, "webpack": { "version": "5.90.3", @@ -22109,7 +22449,6 @@ "version": "5.0.0", "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dev": true, "requires": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -22129,6 +22468,11 @@ "resolved": "https://registry.npmmirror.com/which-module/-/which-module-2.0.1.tgz", "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==" }, + "wicked-good-xpath": { + "version": "1.3.0", + "resolved": "https://registry.npmmirror.com/wicked-good-xpath/-/wicked-good-xpath-1.3.0.tgz", + "integrity": "sha512-Gd9+TUn5nXdwj/hFsPVx5cuHHiF5Bwuc30jZ4+ronF1qHK5O7HD0sgmXWSEgwKquT3ClLoKPVbO6qGwVwLzvAw==" + }, "wildcard": { "version": "2.0.0", "resolved": "https://registry.npmmirror.com/wildcard/-/wildcard-2.0.0.tgz", @@ -22191,6 +22535,11 @@ "dev": true, "requires": {} }, + "xmldom-sre": { + "version": "0.1.31", + "resolved": "https://registry.npmmirror.com/xmldom-sre/-/xmldom-sre-0.1.31.tgz", + "integrity": "sha512-f9s+fUkX04BxQf+7mMWAp5zk61pciie+fFLC9hX9UVvCeJQfNHRHXpeo5MPcR0EUf57PYLdt+ZO4f3Ipk2oZUw==" + }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz", diff --git a/web/src/components/LoginDialog.vue b/web/src/components/LoginDialog.vue index 426561dd..60bb42d0 100644 --- a/web/src/components/LoginDialog.vue +++ b/web/src/components/LoginDialog.vue @@ -221,7 +221,7 @@ diff --git a/web/src/router.js b/web/src/router.js index 64fdecb6..de19cfff 100644 --- a/web/src/router.js +++ b/web/src/router.js @@ -6,19 +6,20 @@ // * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ import {createRouter, createWebHistory} from "vue-router"; +import {ref} from "vue"; +import {httpGet} from "@/utils/http"; const routes = [ { name: 'Index', path: '/', - meta: {title: process.env.VUE_APP_TITLE}, + meta: {title: "首页"}, component: () => import('@/views/Index.vue'), }, { name: 'home', path: '/home', redirect: '/chat', - meta: {title: '首页'}, component: () => import('@/views/Home.vue'), children: [ { @@ -273,11 +274,22 @@ const router = createRouter({ routes: routes, }) +const active = ref(false) +const title = ref('') +httpGet("/api/config/license").then(res => { + active.value = res.data.de_copy +}).catch(() => {}) +httpGet("/api/config/get?key=system").then(res => { + title.value = res.data.title +}).catch(()=>{}) + let prevRoute = null // dynamic change the title when router change router.beforeEach((to, from, next) => { - if (to.meta.title) { + if (!active.value) { document.title = `${to.meta.title} | ${process.env.VUE_APP_TITLE}` + } else { + document.title = `${to.meta.title} | ${title.value}` } prevRoute = from next() diff --git a/web/src/utils/libs.js b/web/src/utils/libs.js index 2a2879bb..5bdc73ed 100644 --- a/web/src/utils/libs.js +++ b/web/src/utils/libs.js @@ -222,15 +222,29 @@ export function processContent(content) { return texts.join("\n") } -export function escapeHTML(html) { - return html.replace(/&/g, "&") +export function processPrompt(prompt) { + prompt = prompt.replace(/&/g, "&") .replace(//g, ">"); + + const linkRegex = /(https?:\/\/\S+)/g; + const links = prompt.match(linkRegex); + if (links) { + for (let link of links) { + if (isImage(link)) { + const index = prompt.indexOf(link) + if (prompt.substring(index - 1, 2) !== "]") { + prompt = prompt.replace(link, "\n![](" + link + ")\n") + } + } + } + } + return prompt } -// 判断是否为 iphone 设备 -export function isIphone() { - return /iPhone/i.test(navigator.userAgent) && !/iPad/i.test(navigator.userAgent); +// 判断是否为微信浏览器 +export function isWeChatBrowser() { + return /MicroMessenger/i.test( navigator.userAgent); } export function showLoginDialog(router) { diff --git a/web/src/views/ChatPlus.vue b/web/src/views/ChatPlus.vue index 887ddf77..3c495fa5 100644 --- a/web/src/views/ChatPlus.vue +++ b/web/src/views/ChatPlus.vue @@ -191,7 +191,15 @@ import ChatPrompt from "@/components/ChatPrompt.vue"; import ChatReply from "@/components/ChatReply.vue"; import {Delete, Edit, More, Plus, Promotion, Search, Share, VideoPause} from '@element-plus/icons-vue' import 'highlight.js/styles/a11y-dark.css' -import {dateFormat, escapeHTML, isMobile, processContent, randString, removeArrayItem, UUID} from "@/utils/libs"; +import { + dateFormat, + isMobile, + processContent, + processPrompt, + randString, + removeArrayItem, + UUID +} from "@/utils/libs"; import {ElMessage, ElMessageBox} from "element-plus"; import hl from "highlight.js"; import {getSessionId, getUserToken, removeUserToken} from "@/store/session"; @@ -697,8 +705,10 @@ const onInput = (e) => { inputRef.value.scrollTo(0, inputRef.value.scrollHeight) if (prompt.value.length < 10) { row.value = 1 - } else if (row.value <= 7) { + } else if (lines <= 7){ row.value = lines + } else { + row.value = 7 } // 输入回车自动提交 @@ -738,7 +748,7 @@ const sendMessage = function () { type: "prompt", id: randString(32), icon: loginUser.value.avatar, - content: md.render(escapeHTML(processContent(prompt.value))), + content: md.render(processPrompt(prompt.value)), created_at: new Date().getTime() / 1000, }); diff --git a/web/src/views/Dalle.vue b/web/src/views/Dalle.vue index 7d01f067..1e151b7d 100644 --- a/web/src/views/Dalle.vue +++ b/web/src/views/Dalle.vue @@ -167,11 +167,6 @@ 正在下载图片 -
- - - -
diff --git a/web/src/views/Home.vue b/web/src/views/Home.vue index 87043d42..43024571 100644 --- a/web/src/views/Home.vue +++ b/web/src/views/Home.vue @@ -46,19 +46,21 @@ {{ loginUser.nickname }} - - - - 用户手册 - - +
+ + + + 用户手册 + + - - - - Geek-AI {{ version }} - - + + + + Geek-AI {{ version }} + + +
@@ -185,7 +187,7 @@ onMounted(() => { httpGet("/api/menu/list").then(res => { mainNavs.value = res.data // 根据窗口的高度计算应该显示多少菜单 - const rows = Math.floor((window.innerHeight - 90) / 60) + const rows = Math.floor((window.innerHeight - 100) / 90) if (res.data.length > rows) { mainNavs.value = res.data.slice(0, rows) moreNavs.value = res.data.slice(rows) diff --git a/web/src/views/ImageMj.vue b/web/src/views/ImageMj.vue index e1525ffd..2a5555b3 100644 --- a/web/src/views/ImageMj.vue +++ b/web/src/views/ImageMj.vue @@ -515,6 +515,13 @@ 正在加载图片 + diff --git a/web/src/views/ImageSd.vue b/web/src/views/ImageSd.vue index fa433ad2..2706b7cf 100644 --- a/web/src/views/ImageSd.vue +++ b/web/src/views/ImageSd.vue @@ -308,11 +308,7 @@ diff --git a/web/src/views/Index.vue b/web/src/views/Index.vue index 16f958b5..b58bf46a 100644 --- a/web/src/views/Index.vue +++ b/web/src/views/Index.vue @@ -26,8 +26,11 @@ - 登录 - 注册 + + + 登录 + 注册 + @@ -69,6 +72,7 @@ import FooterBar from "@/components/FooterBar.vue"; import {httpGet} from "@/utils/http"; import {ElMessage} from "element-plus"; import {isMobile} from "@/utils/libs"; +import {checkSession} from "@/action/session"; const router = useRouter() @@ -83,6 +87,7 @@ const licenseConfig = ref({}) // const size = Math.max(window.innerWidth * 0.5, window.innerHeight * 0.8) const winHeight = window.innerHeight - 150 const bgClass = ref('fixed-bg') +const isLogin = ref(false) onMounted(() => { httpGet("/api/config/get?key=system").then(res => { @@ -91,6 +96,9 @@ onMounted(() => { if (res.data.rand_bg) { bgClass.value = "rand-bg" } + if (res.data.slogan) { + slogan.value = res.data.slogan + } }).catch(e => { ElMessage.error("获取系统配置失败:" + e.message) }) @@ -100,11 +108,11 @@ onMounted(() => { }).catch(e => { ElMessage.error("获取 License 配置:" + e.message) }) - init() -}) -const init = () => { -} + checkSession().then(() => { + isLogin.value = true + }).catch(()=>{}) +})