mirror of
https://github.com/yangjian102621/geekai.git
synced 2025-10-29 21:33:43 +08:00
Compare commits
53 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
913d538587 | ||
|
|
9e704365fc | ||
|
|
485bdbc56a | ||
|
|
7000168fd4 | ||
|
|
5694f97a6b | ||
|
|
b677d3fac7 | ||
|
|
dc6719cf54 | ||
|
|
7de5b55091 | ||
|
|
76c5101092 | ||
|
|
2f8d2f4854 | ||
|
|
b1ee34ba0c | ||
|
|
069ad6a09a | ||
|
|
bf1403c818 | ||
|
|
bcc622a24d | ||
|
|
a06a81a415 | ||
|
|
d1950acd01 | ||
|
|
039b70eed2 | ||
|
|
d8e4308b1b | ||
|
|
434fbb3463 | ||
|
|
de3eb8969c | ||
|
|
fbd6eac877 | ||
|
|
1fecab177b | ||
|
|
b1b385c455 | ||
|
|
3c6e86d04b | ||
|
|
3d2035d08a | ||
|
|
da86f916d8 | ||
|
|
e7a07f7e92 | ||
|
|
b01e6387fc | ||
|
|
d86aca0f5d | ||
|
|
09414fe36a | ||
|
|
df0e7508db | ||
|
|
92b1f01118 | ||
|
|
8fb8bd932b | ||
|
|
3f74b94784 | ||
|
|
e9467341fa | ||
|
|
131e051ddc | ||
|
|
f626fe3166 | ||
|
|
6bc57b6132 | ||
|
|
d972e97c88 | ||
|
|
3991f4daec | ||
|
|
f6b567d6fc | ||
|
|
8addba8203 | ||
|
|
3ab930a107 | ||
|
|
de512a5ea2 | ||
|
|
113cfae2dc | ||
|
|
33aebf9cb5 | ||
|
|
6e58ddf681 | ||
|
|
cae5c049e4 | ||
|
|
ff76e4bd89 | ||
|
|
a0a506a3c4 | ||
|
|
aa5a4a9977 | ||
|
|
abf4f061c1 | ||
|
|
245cd3ee1a |
41
CHANGELOG.md
41
CHANGELOG.md
@@ -1,12 +1,49 @@
|
||||
# 更新日志
|
||||
## v3.2.4.1
|
||||
* 功能新增:新增 PayJs 支付通道
|
||||
* Bug修复:紧急修复后台添加用户失败问题
|
||||
* Bug修复:紧急修复使用中转 API-KEY 无法绘图的问题
|
||||
* Bug修复:允许用户关闭手机和邮箱注册通道,移除验证码依赖
|
||||
|
||||
## v3.2.4
|
||||
|
||||
* 功能新增:重磅更新,支持邮箱注册
|
||||
* 功能优化:优化函数调用授权
|
||||
* 功能优化:给用户表新增 nickname 字段
|
||||
* 功能优化:管理后台给聊天角色增加启用/禁用开关
|
||||
* Bug修复:SD绘画出现重复扣减绘图次数
|
||||
* 功能优化:优化聊天对话导出样式,适应移动端
|
||||
* 功能新增:众筹核销可以选择兑换对话还是绘图的额度
|
||||
* Bug修复:修复[从历史记录获取reply有并发风险 #92](https://github.com/yangjian102621/chatgpt-plus/issues/92)
|
||||
* Bug修复:修复 MidJourney 绘图任务调度Bug,为 task_id 建议唯一索引
|
||||
* 功能重构:重构了 API KEY模块,支持为每个 API KEY 都设置不同的 API 地址,并可以单独开启是否使用代理。
|
||||
|
||||
## v3.2.3
|
||||
|
||||
* 功能重构:重构函数工具模块,设计成可以后台动态管理函数。支持添加自定义函数实现
|
||||
* 功能新增:为充值产品数据表添加 img_calls 字段,支持充值绘图次数
|
||||
* Bug修复:修复 [MJ 机器人空指针异常的 Bug](https://github.com/yangjian102621/chatgpt-plus/issues/73)
|
||||
* Bug修复:确保相同 Prompt 的绘图任务的 Upscale 和 Variation 任务调度给相同的频道
|
||||
* 功能新增:新增删除绘图任何和图片功能
|
||||
* Bug修复:修复虎皮椒支付二维码重复扫码时报错问题
|
||||
* 功能优化:自动将 AI 绘画中的中文提示词翻译成英文
|
||||
* 功能优化:优化AI绘画的大图压缩算法,新增图片缓存
|
||||
* 功能优化:支持为 MJ 绘图 API 增加反代功能,提高图片的加载速度,大大降低绘图任务的失败率
|
||||
* Bug修复:修复[Azure Api 更换api-version参数后请求失败的问题](https://github.com/yangjian102621/chatgpt-plus/pull/71)
|
||||
* Bug修复:修复科大讯飞 V1.5 API 请求失败的问题
|
||||
* Bug修复:绘图失败后,自动恢复用户的剩余绘图次数
|
||||
* 功能新增:为移动端新增 SD 绘图功能,分享功能
|
||||
|
||||
## v3.2.2
|
||||
|
||||
* 功能重构:重构 MidJourney 和 Stable-Diffusion 绘图模块,支持使用多组配置创建池子提供绘画服务
|
||||
* 功能新增:AI绘画页面增加翻译和重写提示词功能
|
||||
* 功能优化:OSS上传组件支持在 Bucket 下设置二级目录
|
||||
* Bug修复:修复阿里云 OSS 访问路径错误
|
||||
* 功能优化:在 AI 绘图页面使用 HTTP 轮询替换 Websocket
|
||||
* 功能优化:在 AI 绘图页面使用 HTTP 轮询替换 Websocket
|
||||
|
||||
## v3.2.1
|
||||
|
||||
* 功能优化:切换角色和模型的时候自动创建新的对话
|
||||
* Bug修复:修复文件上传失败No such file bug
|
||||
* 功能新增:MidJourney 绘画页面新增提示词翻译功能,新增多个绘画参数
|
||||
@@ -16,6 +53,7 @@
|
||||
* 功能新增:新增虎皮椒支付功能接入,支持微信和支付宝通道
|
||||
|
||||
## v3.2.0
|
||||
|
||||
* 功能新增:新增邀请注册功能
|
||||
* 功能优化:增加中间件自动对HTTP请求的参数去掉首尾空格
|
||||
* 功能优化:增加中间件自动为大图片生成缩略图
|
||||
@@ -25,6 +63,7 @@
|
||||
* Bug修复:修复MidJourney绘图失败后重复添加到队列的问题
|
||||
|
||||
## v3.1.9
|
||||
|
||||
* 功能新增:增加讯飞星火大模型 v3.0 支持
|
||||
* 功能新增:新增找回密码功能
|
||||
* 功能新增:支持 Markdown 代码复制功能
|
||||
|
||||
41
README.md
41
README.md
@@ -9,15 +9,10 @@ ChatGLM,讯飞星火,文心一言等多个平台的大语言模型。集成了
|
||||
* 支持 OPenAI,Azure,文心一言,讯飞星火,清华 ChatGLM等多个大语言模型。
|
||||
* 支持 MidJourney / Stable Diffusion AI 绘画集成,开箱即用。
|
||||
* 支持使用个人微信二维码作为充值收费的支付渠道,无需企业支付通道。
|
||||
* 已集成支付宝支付功能,支持多种会员套餐和点卡购买功能。
|
||||
* 已集成支付宝支付功能,微信支付,支持多种会员套餐和点卡购买功能。
|
||||
* 集成插件 API 功能,可结合大语言模型的 function 功能开发各种强大的插件,已内置实现了微博热搜,今日头条,今日早报和 AI
|
||||
绘画函数插件。
|
||||
|
||||
## 关于部署镜像申明
|
||||
|
||||
由于目前部署人数越来越多,本人的阿里云镜像仓库流量不够支撑大家使用了。所以从 v3.2.0 版本开始,一键部署脚本和部署镜像将只提供给 **[付费技术交流群]** 内用户使用。
|
||||
代码依旧是全部开源的,大家可自行编译打包镜像。
|
||||
|
||||
## 功能截图
|
||||
|
||||
### PC 端聊天界面
|
||||
@@ -68,17 +63,40 @@ ChatGLM,讯飞星火,文心一言等多个平台的大语言模型。集成了
|
||||

|
||||

|
||||
|
||||
### 7. 体验地址
|
||||
### 体验地址
|
||||
|
||||
> 免费体验地址:[https://ai.r9it.com/chat](https://ai.r9it.com/chat) <br/>
|
||||
> **注意:请合法使用,禁止输出任何敏感、不友好或违规的内容!!!**
|
||||
|
||||
## 快速部署
|
||||
|
||||
**演示站不提供任何充值点卡售卖或者VIP充值服务。** 如果您体验过后觉得还不错的话,可以花两分钟用下面的一键部署脚本自己部署一套。
|
||||
|
||||
```shell
|
||||
bash -c "$(curl -fsSL https://img.r9it.com/tmp/install-v3.2.4-7b5ff48154.sh)"
|
||||
```
|
||||
|
||||
目前仅支持 Ubuntu 和 Centos 系统。 部署成功之后可以访问下面地址
|
||||
|
||||
* 前端访问地址:http://localhost:8080/chat 使用移动设备访问会自动跳转到移动端页面。
|
||||
* 后台管理地址:http://localhost:8080/admin
|
||||
* 移动端地址:http://localhost:8080/mobile
|
||||
* 初始后台管理账号:admin/admin123
|
||||
* 初始前端体验账号:18575670125/12345678
|
||||
|
||||
服务启动成功之后不能立刻使用,需要先登录管理后台 -> API-KEY 去添加一个 OpenAI 或者文心一言,科大讯飞等至少一个平台的 API
|
||||
KEY。
|
||||
|
||||

|
||||
|
||||
另外,如果您目前还没有 OpenAI 的 API KEY的,推荐您去 https://gpt.bemore.lol 购买,**无需魔法,高速稳定,且价格还远低于 OpenAI
|
||||
官方**。
|
||||
|
||||
## 使用须知
|
||||
|
||||
1. 本项目基于 MIT 协议,免费开放全部源代码,可以作为个人学习使用或者商用。
|
||||
2. 如需商用必须保留版权信息,请自觉遵守。确保合法合规使用,在运营过程中产生的一切任何后果自负,与作者无关。
|
||||
|
||||
|
||||
## 项目地址
|
||||
|
||||
* Github 地址:https://github.com/yangjian102621/chatgpt-plus
|
||||
@@ -89,12 +107,19 @@ ChatGLM,讯飞星火,文心一言等多个平台的大语言模型。集成了
|
||||
目前已经支持 Win/Linux/Mac/Android 客户端,下载地址为:https://github.com/yangjian102621/chatgpt-plus/releases/tag/v3.1.2
|
||||
|
||||
## TODOLIST
|
||||
|
||||
* [ ] 支持基于知识库的 AI 问答
|
||||
* [ ] 会员邀请注册推广功能
|
||||
* [ ] 微信支付功能
|
||||
|
||||
## 项目文档
|
||||
|
||||
*
|
||||
|
||||
*
|
||||
最新的部署视频教程:[https://www.bilibili.com/video/BV1ge411C7uA/](https://www.bilibili.com/video/BV1ge411C7uA/?vd_source=dee8b15703ccfcbd24a60ee9a0fabb73)
|
||||
**
|
||||
|
||||
详细的部署和开发文档请参考 [**ChatGPT-Plus 文档**](https://ai.r9it.com/docs/)。
|
||||
|
||||
加微信进入微信讨论群可获取 **一键部署脚本(添加好友时请注明来自Github!!!)。**
|
||||
|
||||
1
api/.gitignore
vendored
1
api/.gitignore
vendored
@@ -18,4 +18,3 @@ data
|
||||
config.toml
|
||||
static/upload
|
||||
storage.json
|
||||
certs/alipay/*
|
||||
|
||||
@@ -58,6 +58,10 @@ WeChatBot = false
|
||||
BotToken = ""
|
||||
GuildId = ""
|
||||
ChanelId = ""
|
||||
UseCDN = false #是否使用反向代理访问,设置为true下面的设置才会生效
|
||||
DiscordAPI = "https://mj.r9it.com:8001" # discord API 反代地址
|
||||
DiscordCDN = "https://mj.r9it.com:8002" # mj 图片反代地址
|
||||
DiscordGateway = "wss://mj.r9it.com:8003" # discord 机器人反代地址
|
||||
|
||||
[[MjConfigs]]
|
||||
Enabled = false
|
||||
@@ -65,6 +69,16 @@ WeChatBot = false
|
||||
BotToken = ""
|
||||
GuildId = ""
|
||||
ChanelId = ""
|
||||
UseCDN = false #是否使用反向代理访问,设置为true下面的设置才会生效
|
||||
DiscordAPI = "https://mj.r9it.com:8001" # discord API 反代地址
|
||||
DiscordCDN = "https://mj.r9it.com:8002" # mj 图片反代地址
|
||||
DiscordGateway = "wss://mj.r9it.com:8003" # discord 机器人反代地址
|
||||
|
||||
[[SdConfigs]]
|
||||
Enabled = false
|
||||
ApiURL = ""
|
||||
ApiKey = ""
|
||||
Txt2ImgJsonPath = "res/sd/text2img.json"
|
||||
|
||||
[[SdConfigs]]
|
||||
Enabled = false
|
||||
@@ -95,4 +109,27 @@ WeChatBot = false
|
||||
PublicKey = "certs/alipay/appPublicCert.crt" # 应用公钥证书
|
||||
AlipayPublicKey = "certs/alipay/alipayPublicCert.crt" # 支付宝公钥证书
|
||||
RootCert = "certs/alipay/alipayRootCert.crt" # 支付宝根证书
|
||||
NotifyURL = "http://r9it.com:6004/api/payment/alipay/notify" # 支付异步回调地址
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/alipay/notify" # 支付异步回调地址
|
||||
|
||||
[HuPiPayConfig]
|
||||
Enabled = false
|
||||
Name = "wechat"
|
||||
AppId = "201906161477"
|
||||
AppSecret = "7f403199d510fb2c6f0b9f2311800e7c"
|
||||
PayURL = "https://api.xunhupay.com/payment/do.html"
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/hupipay/notify"
|
||||
|
||||
[SmtpConfig] # 注意,阿里云服务器禁用了25号端口,所以如果需要使用邮件功能,请别用阿里云服务器
|
||||
Host = "smtp.163.com"
|
||||
Port = 25
|
||||
AppName = "极客学长"
|
||||
From = "test@163.com" # 发件邮箱人地址
|
||||
Password = "" #邮箱 stmp 服务授权码
|
||||
|
||||
[JPayConfig] # PayJs 支付配置
|
||||
Enabled = false
|
||||
Name = "wechat" # 请不要改动
|
||||
AppId = "" # 商户 ID
|
||||
PrivateKey = "" # 秘钥
|
||||
ApiURL = "https://payjs.cn/api/native"
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/payjs/notify" # 异步回调地址,域名改成你自己的
|
||||
@@ -3,7 +3,6 @@ package core
|
||||
import (
|
||||
"bytes"
|
||||
"chatplus/core/types"
|
||||
"chatplus/service/fun"
|
||||
"chatplus/store/model"
|
||||
"chatplus/utils"
|
||||
"chatplus/utils/resp"
|
||||
@@ -39,10 +38,9 @@ type AppServer struct {
|
||||
ChatSession *types.LMap[string, *types.ChatSession] //map[sessionId]UserId
|
||||
ChatClients *types.LMap[string, *types.WsClient] // map[sessionId]Websocket 连接集合
|
||||
ReqCancelFunc *types.LMap[string, context.CancelFunc] // HttpClient 请求取消 handle function
|
||||
Functions map[string]fun.Function
|
||||
}
|
||||
|
||||
func NewServer(appConfig *types.AppConfig, functions map[string]fun.Function) *AppServer {
|
||||
func NewServer(appConfig *types.AppConfig) *AppServer {
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
gin.DefaultWriter = io.Discard
|
||||
return &AppServer{
|
||||
@@ -53,7 +51,6 @@ func NewServer(appConfig *types.AppConfig, functions map[string]fun.Function) *A
|
||||
ChatSession: types.NewLMap[string, *types.ChatSession](),
|
||||
ChatClients: types.NewLMap[string, *types.WsClient](),
|
||||
ReqCancelFunc: types.NewLMap[string, context.CancelFunc](),
|
||||
Functions: functions,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,9 +151,12 @@ func authorizeMiddleware(s *AppServer, client *redis.Client) gin.HandlerFunc {
|
||||
c.Request.URL.Path == "/api/chat/detail" ||
|
||||
c.Request.URL.Path == "/api/role/list" ||
|
||||
c.Request.URL.Path == "/api/mj/jobs" ||
|
||||
c.Request.URL.Path == "/api/mj/client" ||
|
||||
c.Request.URL.Path == "/api/invite/hits" ||
|
||||
c.Request.URL.Path == "/api/sd/jobs" ||
|
||||
strings.HasPrefix(c.Request.URL.Path, "/test/") ||
|
||||
c.Request.URL.Path == "/api/upload" ||
|
||||
strings.HasPrefix(c.Request.URL.Path, "/api/test") ||
|
||||
strings.HasPrefix(c.Request.URL.Path, "/api/function/") ||
|
||||
strings.HasPrefix(c.Request.URL.Path, "/api/sms/") ||
|
||||
strings.HasPrefix(c.Request.URL.Path, "/api/captcha/") ||
|
||||
strings.HasPrefix(c.Request.URL.Path, "/api/payment/") ||
|
||||
@@ -335,9 +335,11 @@ func staticResourceMiddleware() gin.HandlerFunc {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// 设置图片缓存有效期为一年 (365天)
|
||||
c.Header("Cache-Control", "max-age=31536000, public")
|
||||
// 直接输出图像数据流
|
||||
c.Data(http.StatusOK, "image/jpeg", buffer.Bytes())
|
||||
return
|
||||
c.Abort() // 中断请求
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
|
||||
@@ -14,13 +14,12 @@ var logger = logger2.GetLogger()
|
||||
|
||||
func NewDefaultConfig() *types.AppConfig {
|
||||
return &types.AppConfig{
|
||||
Listen: "0.0.0.0:5678",
|
||||
ProxyURL: "",
|
||||
Manager: types.Manager{Username: "admin", Password: "admin123"},
|
||||
StaticDir: "./static",
|
||||
StaticUrl: "http://localhost/5678/static",
|
||||
Redis: types.RedisConfig{Host: "localhost", Port: 6379, Password: ""},
|
||||
AesEncryptKey: utils.RandString(24),
|
||||
Listen: "0.0.0.0:5678",
|
||||
ProxyURL: "",
|
||||
Manager: types.Manager{Username: "admin", Password: "admin123"},
|
||||
StaticDir: "./static",
|
||||
StaticUrl: "http://localhost/5678/static",
|
||||
Redis: types.RedisConfig{Host: "localhost", Port: 6379, Password: ""},
|
||||
Session: types.Session{
|
||||
SecretKey: utils.RandString(64),
|
||||
MaxAge: 86400,
|
||||
|
||||
@@ -8,7 +8,10 @@ type ApiRequest struct {
|
||||
Stream bool `json:"stream"`
|
||||
Messages []interface{} `json:"messages,omitempty"`
|
||||
Prompt []interface{} `json:"prompt,omitempty"` // 兼容 ChatGLM
|
||||
Functions []Function `json:"functions,omitempty"`
|
||||
Tools []interface{} `json:"tools,omitempty"`
|
||||
Functions []interface{} `json:"functions,omitempty"` // 兼容中转平台
|
||||
|
||||
ToolChoice string `json:"tool_choice,omitempty"`
|
||||
}
|
||||
|
||||
type Message struct {
|
||||
@@ -27,10 +30,14 @@ type ChoiceItem struct {
|
||||
}
|
||||
|
||||
type Delta struct {
|
||||
Role string `json:"role"`
|
||||
Name string `json:"name"`
|
||||
Content interface{} `json:"content"`
|
||||
FunctionCall FunctionCall `json:"function_call,omitempty"`
|
||||
Role string `json:"role"`
|
||||
Name string `json:"name"`
|
||||
Content interface{} `json:"content"`
|
||||
ToolCalls []ToolCall `json:"tool_calls,omitempty"`
|
||||
FunctionCall struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Arguments string `json:"arguments,omitempty"`
|
||||
} `json:"function_call,omitempty"`
|
||||
}
|
||||
|
||||
// ChatSession 聊天会话对象
|
||||
@@ -61,7 +68,6 @@ type ApiError struct {
|
||||
|
||||
const PromptMsg = "prompt" // prompt message
|
||||
const ReplyMsg = "reply" // reply message
|
||||
const MjMsg = "mj"
|
||||
|
||||
var ModelToTokens = map[string]int{
|
||||
"gpt-3.5-turbo": 4096,
|
||||
@@ -74,4 +80,12 @@ var ModelToTokens = map[string]int{
|
||||
"ernie_bot_turbo": 8192, // 文心一言
|
||||
"general": 8192, // 科大讯飞
|
||||
"general2": 8192,
|
||||
"general3": 8192,
|
||||
}
|
||||
|
||||
func GetModelMaxToken(model string) int {
|
||||
if token, ok := ModelToTokens[model]; ok {
|
||||
return token
|
||||
}
|
||||
return 4096
|
||||
}
|
||||
|
||||
@@ -5,26 +5,45 @@ import (
|
||||
)
|
||||
|
||||
type AppConfig struct {
|
||||
Path string `toml:"-"`
|
||||
Listen string
|
||||
Session Session
|
||||
ProxyURL string
|
||||
MysqlDns string // mysql 连接地址
|
||||
Manager Manager // 后台管理员账户信息
|
||||
StaticDir string // 静态资源目录
|
||||
StaticUrl string // 静态资源 URL
|
||||
Redis RedisConfig // redis 连接信息
|
||||
ApiConfig ChatPlusApiConfig // ChatPlus API authorization configs
|
||||
AesEncryptKey string
|
||||
SmsConfig AliYunSmsConfig // AliYun send message service config
|
||||
OSS OSSConfig // OSS config
|
||||
MjConfigs []MidJourneyConfig // mj AI draw service pool
|
||||
WeChatBot bool // 是否启用微信机器人
|
||||
SdConfigs []StableDiffusionConfig // sd AI draw service pool
|
||||
Path string `toml:"-"`
|
||||
Listen string
|
||||
Session Session
|
||||
ProxyURL string
|
||||
MysqlDns string // mysql 连接地址
|
||||
Manager Manager // 后台管理员账户信息
|
||||
StaticDir string // 静态资源目录
|
||||
StaticUrl string // 静态资源 URL
|
||||
Redis RedisConfig // redis 连接信息
|
||||
ApiConfig ChatPlusApiConfig // ChatPlus API authorization configs
|
||||
SmsConfig AliYunSmsConfig // AliYun send message service config
|
||||
OSS OSSConfig // OSS config
|
||||
MjConfigs []MidJourneyConfig // mj AI draw service pool
|
||||
WeChatBot bool // 是否启用微信机器人
|
||||
SdConfigs []StableDiffusionConfig // sd AI draw service pool
|
||||
|
||||
XXLConfig XXLConfig
|
||||
AlipayConfig AlipayConfig
|
||||
HuPiPayConfig HuPiPayConfig
|
||||
SmtpConfig SmtpConfig // 邮件发送配置
|
||||
JPayConfig JPayConfig // payjs 支付配置
|
||||
}
|
||||
|
||||
type SmtpConfig struct {
|
||||
Host string
|
||||
Port int
|
||||
AppName string // 应用名称
|
||||
From string // 发件人邮箱地址
|
||||
Password string // 发件人邮箱密码
|
||||
}
|
||||
|
||||
// JPayConfig PayJs 支付配置
|
||||
type JPayConfig struct {
|
||||
Enabled bool
|
||||
Name string // 支付名称,默认 wechat
|
||||
AppId string // 商户 ID
|
||||
PrivateKey string // 私钥
|
||||
ApiURL string // API 网关
|
||||
NotifyURL string // 异步回调地址
|
||||
}
|
||||
|
||||
type ChatPlusApiConfig struct {
|
||||
@@ -34,11 +53,15 @@ type ChatPlusApiConfig struct {
|
||||
}
|
||||
|
||||
type MidJourneyConfig struct {
|
||||
Enabled bool
|
||||
UserToken string
|
||||
BotToken string
|
||||
GuildId string // Server ID
|
||||
ChanelId string // Chanel ID
|
||||
Enabled bool
|
||||
UserToken string
|
||||
BotToken string
|
||||
GuildId string // Server ID
|
||||
ChanelId string // Chanel ID
|
||||
UseCDN bool
|
||||
DiscordAPI string
|
||||
DiscordCDN string
|
||||
DiscordGateway string
|
||||
}
|
||||
|
||||
type StableDiffusionConfig struct {
|
||||
@@ -74,8 +97,8 @@ type HuPiPayConfig struct { //虎皮椒第四方支付配置
|
||||
Name string // 支付名称,如:wechat/alipay
|
||||
AppId string // App ID
|
||||
AppSecret string // app 密钥
|
||||
NotifyURL string // 异步通知回调
|
||||
PayURL string // 支付网关
|
||||
NotifyURL string // 异步通知回调
|
||||
}
|
||||
|
||||
type XXLConfig struct { // XXL 任务调度配置
|
||||
@@ -112,11 +135,10 @@ type ChatConfig struct {
|
||||
Baidu ModelAPIConfig `json:"baidu"`
|
||||
XunFei ModelAPIConfig `json:"xun_fei"`
|
||||
|
||||
EnableContext bool `json:"enable_context"` // 是否开启聊天上下文
|
||||
EnableHistory bool `json:"enable_history"` // 是否允许保存聊天记录
|
||||
ContextDeep int `json:"context_deep"` // 上下文深度
|
||||
DallApiURL string `json:"dall_api_url"` // dall-e3 绘图 API 地址
|
||||
DallImgNum int `json:"dall_img_num"` // dall-e3 出图数量
|
||||
EnableContext bool `json:"enable_context"` // 是否开启聊天上下文
|
||||
EnableHistory bool `json:"enable_history"` // 是否允许保存聊天记录
|
||||
ContextDeep int `json:"context_deep"` // 上下文深度
|
||||
DallImgNum int `json:"dall_img_num"` // dall-e3 出图数量
|
||||
}
|
||||
|
||||
type Platform string
|
||||
@@ -138,30 +160,30 @@ type InviteReward struct {
|
||||
}
|
||||
|
||||
type ModelAPIConfig struct {
|
||||
ApiURL string `json:"api_url,omitempty"`
|
||||
Temperature float32 `json:"temperature"`
|
||||
MaxTokens int `json:"max_tokens"`
|
||||
ApiKey string `json:"api_key"`
|
||||
}
|
||||
|
||||
type SystemConfig struct {
|
||||
Title string `json:"title"`
|
||||
AdminTitle string `json:"admin_title"`
|
||||
Models []string `json:"models"`
|
||||
InitChatCalls int `json:"init_chat_calls"` // 新用户注册赠送对话次数
|
||||
InitImgCalls int `json:"init_img_calls"` // 新用户注册赠送绘图次数
|
||||
VipMonthCalls int `json:"vip_month_calls"` // VIP 会员每月赠送的对话次数
|
||||
VipMonthImgCalls int `json:"vip_month_img_calls"` // VIP 会员每月赠送绘图次数
|
||||
EnabledRegister bool `json:"enabled_register"` // 是否启用注册功能,关闭注册功能之后将无法注册
|
||||
EnabledMsg bool `json:"enabled_msg"` // 是否启用短信验证码服务
|
||||
RewardImg string `json:"reward_img"` // 众筹收款二维码地址
|
||||
EnabledFunction bool `json:"enabled_function"` // 启用 API 函数功能
|
||||
EnabledReward bool `json:"enabled_reward"` // 启用众筹功能
|
||||
EnabledAlipay bool `json:"enabled_alipay"` // 是否启用支付宝支付通道
|
||||
Title string `json:"title"`
|
||||
AdminTitle string `json:"admin_title"`
|
||||
InitChatCalls int `json:"init_chat_calls"` // 新用户注册赠送对话次数
|
||||
InitImgCalls int `json:"init_img_calls"` // 新用户注册赠送绘图次数
|
||||
VipMonthCalls int `json:"vip_month_calls"` // VIP 会员每月赠送的对话次数
|
||||
VipMonthImgCalls int `json:"vip_month_img_calls"` // VIP 会员每月赠送绘图次数
|
||||
|
||||
RegisterWays []string `json:"register_ways"` // 注册方式:支持手机,邮箱注册
|
||||
|
||||
RewardImg string `json:"reward_img"` // 众筹收款二维码地址
|
||||
EnabledReward bool `json:"enabled_reward"` // 启用众筹功能
|
||||
ChatCallPrice float64 `json:"chat_call_price"` // 对话单次调用费用
|
||||
ImgCallPrice float64 `json:"img_call_price"` // 绘图单次调用费用
|
||||
|
||||
OrderPayTimeout int `json:"order_pay_timeout"` //订单支付超时时间
|
||||
DefaultModels []string `json:"default_models"` // 默认开通的 AI 模型
|
||||
OrderPayInfoText string `json:"order_pay_info_text"` // 订单支付页面说明文字
|
||||
InviteChatCalls int `json:"invite_chat_calls"` // 邀请用户注册奖励对话次数
|
||||
InviteImgCalls int `json:"invite_img_calls"` // 邀请用户注册奖励绘图次数
|
||||
ForceInvite bool `json:"force_invite"` // 是否强制必须使用邀请码才能注册
|
||||
|
||||
ShowDemoNotice bool `json:"show_demo_notice"` // 显示演示站公告
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
package types
|
||||
|
||||
type FunctionCall struct {
|
||||
Name string `json:"name"`
|
||||
Arguments string `json:"arguments"`
|
||||
type ToolCall struct {
|
||||
Type string `json:"type"`
|
||||
Function struct {
|
||||
Name string `json:"name"`
|
||||
Arguments string `json:"arguments"`
|
||||
} `json:"function"`
|
||||
}
|
||||
|
||||
type Function struct {
|
||||
@@ -21,72 +24,3 @@ type Property struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
const (
|
||||
FuncZaoBao = "zao_bao" // 每日早报
|
||||
FuncHeadLine = "headline" // 今日头条
|
||||
FuncWeibo = "weibo_hot" // 微博热搜
|
||||
FuncImage = "draw_image" // AI 绘画
|
||||
)
|
||||
|
||||
var InnerFunctions = []Function{
|
||||
{
|
||||
Name: FuncZaoBao,
|
||||
Description: "每日早报,获取当天全球的热门新闻事件列表",
|
||||
Parameters: Parameters{
|
||||
|
||||
Type: "object",
|
||||
Properties: map[string]Property{
|
||||
"text": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
},
|
||||
},
|
||||
Required: []string{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: FuncWeibo,
|
||||
Description: "新浪微博热搜榜,微博当日热搜榜单",
|
||||
Parameters: Parameters{
|
||||
Type: "object",
|
||||
Properties: map[string]Property{
|
||||
"text": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
},
|
||||
},
|
||||
Required: []string{},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: FuncHeadLine,
|
||||
Description: "今日头条,给用户推荐当天的头条新闻,周榜热文",
|
||||
Parameters: Parameters{
|
||||
Type: "object",
|
||||
Properties: map[string]Property{
|
||||
"text": {
|
||||
Type: "string",
|
||||
Description: "",
|
||||
},
|
||||
},
|
||||
Required: []string{},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
Name: FuncImage,
|
||||
Description: "AI 绘画工具,根据输入的绘图描述用 AI 工具进行绘画",
|
||||
Parameters: Parameters{
|
||||
Type: "object",
|
||||
Properties: map[string]Property{
|
||||
"prompt": {
|
||||
Type: "string",
|
||||
Description: "提示词,如果该参数中有中文的话,则需要翻译成英文。",
|
||||
},
|
||||
},
|
||||
Required: []string{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
type MKey interface {
|
||||
string | int
|
||||
string | int | uint
|
||||
}
|
||||
type MValue interface {
|
||||
*WsClient | *ChatSession | context.CancelFunc | []interface{}
|
||||
|
||||
@@ -16,6 +16,7 @@ const (
|
||||
// MjTask MidJourney 任务
|
||||
type MjTask struct {
|
||||
Id int `json:"id"`
|
||||
ChannelId string `json:"channel_id"`
|
||||
SessionId string `json:"session_id"`
|
||||
Type TaskType `json:"type"`
|
||||
UserId int `json:"user_id"`
|
||||
|
||||
@@ -6,7 +6,6 @@ require (
|
||||
github.com/BurntSushi/toml v1.1.0
|
||||
github.com/aliyun/alibaba-cloud-sdk-go v1.62.405
|
||||
github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible
|
||||
github.com/bwmarrin/discordgo v0.27.1
|
||||
github.com/eatmoreapple/openwechat v1.2.1
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
github.com/go-redis/redis/v8 v8.11.5
|
||||
@@ -26,6 +25,8 @@ require (
|
||||
|
||||
require github.com/xxl-job/xxl-job-executor-go v1.2.0
|
||||
|
||||
require github.com/bg5t/mydiscordgo v0.28.1
|
||||
|
||||
require (
|
||||
github.com/andybalholm/brotli v1.0.4 // indirect
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
|
||||
@@ -7,8 +7,8 @@ github.com/aliyun/aliyun-oss-go-sdk v2.2.9+incompatible/go.mod h1:T/Aws4fEfogEE9
|
||||
github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
|
||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||
github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY=
|
||||
github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY=
|
||||
github.com/bg5t/mydiscordgo v0.28.1 h1:mVH0ZWstVdJffCi/EXJAYQDtXwIKAJYVXLmECu1hEK8=
|
||||
github.com/bg5t/mydiscordgo v0.28.1/go.mod h1:n3aba73N18k1DzM0t0mGE8rwW3Z+vwTvI8pcsBgxN/8=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
|
||||
@@ -27,8 +27,12 @@ func (h *ApiKeyHandler) Save(c *gin.Context) {
|
||||
var data struct {
|
||||
Id uint `json:"id"`
|
||||
Platform string `json:"platform"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"`
|
||||
ApiURL string `json:"api_url"`
|
||||
Enabled bool `json:"enabled"`
|
||||
UseProxy bool `json:"use_proxy"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
@@ -42,6 +46,10 @@ func (h *ApiKeyHandler) Save(c *gin.Context) {
|
||||
apiKey.Platform = data.Platform
|
||||
apiKey.Value = data.Value
|
||||
apiKey.Type = data.Type
|
||||
apiKey.ApiURL = data.ApiURL
|
||||
apiKey.Enabled = data.Enabled
|
||||
apiKey.UseProxy = data.UseProxy
|
||||
apiKey.Name = data.Name
|
||||
res := h.db.Save(&apiKey)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "更新数据库失败!")
|
||||
@@ -80,6 +88,26 @@ func (h *ApiKeyHandler) List(c *gin.Context) {
|
||||
resp.SUCCESS(c, keys)
|
||||
}
|
||||
|
||||
func (h *ApiKeyHandler) Set(c *gin.Context) {
|
||||
var data struct {
|
||||
Id uint `json:"id"`
|
||||
Filed string `json:"filed"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
|
||||
res := h.db.Model(&model.ApiKey{}).Where("id = ?", data.Id).Update(data.Filed, data.Value)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "更新数据库失败!")
|
||||
return
|
||||
}
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
func (h *ApiKeyHandler) Remove(c *gin.Context) {
|
||||
id := h.GetInt(c, "id", 0)
|
||||
|
||||
|
||||
@@ -98,6 +98,26 @@ func (h *ChatRoleHandler) Sort(c *gin.Context) {
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
func (h *ChatRoleHandler) Set(c *gin.Context) {
|
||||
var data struct {
|
||||
Id uint `json:"id"`
|
||||
Filed string `json:"filed"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
|
||||
res := h.db.Model(&model.ChatRole{}).Where("id = ?", data.Id).Update(data.Filed, data.Value)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "更新数据库失败!")
|
||||
return
|
||||
}
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
func (h *ChatRoleHandler) Remove(c *gin.Context) {
|
||||
id := h.GetInt(c, "id", 0)
|
||||
if id <= 0 {
|
||||
|
||||
124
api/handler/admin/function_handler.go
Normal file
124
api/handler/admin/function_handler.go
Normal file
@@ -0,0 +1,124 @@
|
||||
package admin
|
||||
|
||||
import (
|
||||
"chatplus/core"
|
||||
"chatplus/core/types"
|
||||
"chatplus/handler"
|
||||
"chatplus/store/model"
|
||||
"chatplus/store/vo"
|
||||
"chatplus/utils"
|
||||
"chatplus/utils/resp"
|
||||
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type FunctionHandler struct {
|
||||
handler.BaseHandler
|
||||
db *gorm.DB
|
||||
}
|
||||
|
||||
func NewFunctionHandler(app *core.AppServer, db *gorm.DB) *FunctionHandler {
|
||||
h := FunctionHandler{db: db}
|
||||
h.App = app
|
||||
return &h
|
||||
}
|
||||
|
||||
func (h *FunctionHandler) Save(c *gin.Context) {
|
||||
var data vo.Function
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
|
||||
var f = model.Function{
|
||||
Id: data.Id,
|
||||
Name: data.Name,
|
||||
Label: data.Label,
|
||||
Description: data.Description,
|
||||
Parameters: utils.JsonEncode(data.Parameters),
|
||||
Action: data.Action,
|
||||
Token: data.Token,
|
||||
Enabled: data.Enabled,
|
||||
}
|
||||
|
||||
res := h.db.Save(&f)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "error with save data:"+res.Error.Error())
|
||||
return
|
||||
}
|
||||
data.Id = f.Id
|
||||
resp.SUCCESS(c, data)
|
||||
}
|
||||
|
||||
func (h *FunctionHandler) Set(c *gin.Context) {
|
||||
var data struct {
|
||||
Id uint `json:"id"`
|
||||
Filed string `json:"filed"`
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
|
||||
res := h.db.Model(&model.Function{}).Where("id = ?", data.Id).Update(data.Filed, data.Value)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "更新数据库失败!")
|
||||
return
|
||||
}
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
func (h *FunctionHandler) List(c *gin.Context) {
|
||||
var items []model.Function
|
||||
res := h.db.Find(&items)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "No data found")
|
||||
return
|
||||
}
|
||||
|
||||
functions := make([]vo.Function, 0)
|
||||
for _, v := range items {
|
||||
var f vo.Function
|
||||
err := utils.CopyObject(v, &f)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
functions = append(functions, f)
|
||||
}
|
||||
resp.SUCCESS(c, functions)
|
||||
}
|
||||
|
||||
func (h *FunctionHandler) Remove(c *gin.Context) {
|
||||
id := h.GetInt(c, "id", 0)
|
||||
|
||||
if id > 0 {
|
||||
res := h.db.Delete(&model.Function{Id: uint(id)})
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "更新数据库失败!")
|
||||
return
|
||||
}
|
||||
}
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
// GenToken generate function api access token
|
||||
func (h *FunctionHandler) GenToken(c *gin.Context) {
|
||||
// 创建 token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
"user_id": 0,
|
||||
"expired": 0,
|
||||
})
|
||||
tokenString, err := token.SignedString([]byte(h.App.Config.Session.SecretKey))
|
||||
if err != nil {
|
||||
logger.Error("error with generate token", err)
|
||||
resp.ERROR(c)
|
||||
return
|
||||
}
|
||||
|
||||
resp.SUCCESS(c, tokenString)
|
||||
}
|
||||
@@ -46,7 +46,7 @@ func (h *RewardHandler) List(c *gin.Context) {
|
||||
}
|
||||
|
||||
r.Id = v.Id
|
||||
r.Username = userMap[v.UserId].Mobile
|
||||
r.Username = userMap[v.UserId].Username
|
||||
r.CreatedAt = v.CreatedAt.Unix()
|
||||
r.UpdatedAt = v.UpdatedAt.Unix()
|
||||
rewards = append(rewards, r)
|
||||
@@ -55,3 +55,16 @@ func (h *RewardHandler) List(c *gin.Context) {
|
||||
|
||||
resp.SUCCESS(c, rewards)
|
||||
}
|
||||
|
||||
func (h *RewardHandler) Remove(c *gin.Context) {
|
||||
id := h.GetInt(c, "id", 0)
|
||||
|
||||
if id > 0 {
|
||||
res := h.db.Where("id = ?", id).Delete(&model.Reward{})
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "更新数据库失败!")
|
||||
return
|
||||
}
|
||||
}
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"chatplus/store/vo"
|
||||
"chatplus/utils"
|
||||
"chatplus/utils/resp"
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -27,7 +29,7 @@ func NewUserHandler(app *core.AppServer, db *gorm.DB) *UserHandler {
|
||||
func (h *UserHandler) List(c *gin.Context) {
|
||||
page := h.GetInt(c, "page", 1)
|
||||
pageSize := h.GetInt(c, "page_size", 20)
|
||||
mobile := h.GetTrim(c, "mobile")
|
||||
username := h.GetTrim(c, "username")
|
||||
|
||||
offset := (page - 1) * pageSize
|
||||
var items []model.User
|
||||
@@ -35,8 +37,8 @@ func (h *UserHandler) List(c *gin.Context) {
|
||||
var total int64
|
||||
|
||||
session := h.db.Session(&gorm.Session{})
|
||||
if mobile != "" {
|
||||
session = session.Where("mobile LIKE ?", "%"+mobile+"%")
|
||||
if username != "" {
|
||||
session = session.Where("username LIKE ?", "%"+username+"%")
|
||||
}
|
||||
|
||||
session.Model(&model.User{}).Count(&total)
|
||||
@@ -63,7 +65,7 @@ func (h *UserHandler) Save(c *gin.Context) {
|
||||
var data struct {
|
||||
Id uint `json:"id"`
|
||||
Password string `json:"password"`
|
||||
Mobile string `json:"mobile"`
|
||||
Username string `json:"username"`
|
||||
Calls int `json:"calls"`
|
||||
ImgCalls int `json:"img_calls"`
|
||||
ChatRoles []string `json:"chat_roles"`
|
||||
@@ -83,7 +85,7 @@ func (h *UserHandler) Save(c *gin.Context) {
|
||||
user.Id = data.Id
|
||||
// 此处需要用 map 更新,用结构体无法更新 0 值
|
||||
res = h.db.Model(&user).Updates(map[string]interface{}{
|
||||
"mobile": data.Mobile,
|
||||
"username": data.Username,
|
||||
"calls": data.Calls,
|
||||
"img_calls": data.ImgCalls,
|
||||
"status": data.Status,
|
||||
@@ -95,7 +97,8 @@ func (h *UserHandler) Save(c *gin.Context) {
|
||||
} else {
|
||||
salt := utils.RandString(8)
|
||||
u := model.User{
|
||||
Mobile: data.Mobile,
|
||||
Username: data.Username,
|
||||
Nickname: fmt.Sprintf("极客学长@%d", utils.RandomNumber(6)),
|
||||
Password: utils.GenPassword(data.Password, salt),
|
||||
Avatar: "/images/avatar/user.png",
|
||||
Salt: salt,
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
"html/template"
|
||||
"io"
|
||||
"strings"
|
||||
@@ -30,7 +29,7 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
ws *types.WsClient) error {
|
||||
promptCreatedAt := time.Now() // 记录提问时间
|
||||
start := time.Now()
|
||||
var apiKey = userVo.ChatConfig.ApiKeys[session.Model.Platform]
|
||||
var apiKey = model.ApiKey{}
|
||||
response, err := h.doRequest(ctx, req, session.Model.Platform, &apiKey)
|
||||
logger.Info("HTTP请求完成,耗时:", time.Now().Sub(start))
|
||||
if err != nil {
|
||||
@@ -57,9 +56,6 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
// 循环读取 Chunk 消息
|
||||
var message = types.Message{}
|
||||
var contents = make([]string, 0)
|
||||
var functionCall = false
|
||||
var functionName string
|
||||
var arguments = make([]string, 0)
|
||||
scanner := bufio.NewScanner(response.Body)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
@@ -69,34 +65,17 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
|
||||
var responseBody = types.ApiResponse{}
|
||||
err = json.Unmarshal([]byte(line[6:]), &responseBody)
|
||||
if err != nil || len(responseBody.Choices) == 0 { // 数据解析出错
|
||||
if err != nil { // 数据解析出错
|
||||
logger.Error(err, line)
|
||||
utils.ReplyMessage(ws, ErrorMsg)
|
||||
utils.ReplyMessage(ws, ErrImg)
|
||||
break
|
||||
}
|
||||
|
||||
fun := responseBody.Choices[0].Delta.FunctionCall
|
||||
if functionCall && fun.Name == "" {
|
||||
arguments = append(arguments, fun.Arguments)
|
||||
if len(responseBody.Choices) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if !utils.IsEmptyValue(fun) {
|
||||
functionName = fun.Name
|
||||
f := h.App.Functions[functionName]
|
||||
if f != nil {
|
||||
functionCall = true
|
||||
utils.ReplyChunkMessage(ws, types.WsMessage{Type: types.WsStart})
|
||||
utils.ReplyChunkMessage(ws, types.WsMessage{Type: types.WsMiddle, Content: fmt.Sprintf("正在调用函数 `%s` 作答 ...\n\n", f.Name())})
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if responseBody.Choices[0].FinishReason == "function_call" { // 函数调用完毕
|
||||
break
|
||||
}
|
||||
|
||||
// 初始化 role
|
||||
if responseBody.Choices[0].Delta.Role != "" && message.Role == "" {
|
||||
message.Role = responseBody.Choices[0].Delta.Role
|
||||
@@ -122,49 +101,6 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
}
|
||||
}
|
||||
|
||||
if functionCall { // 调用函数完成任务
|
||||
var params map[string]interface{}
|
||||
_ = utils.JsonDecode(strings.Join(arguments, ""), ¶ms)
|
||||
logger.Debugf("函数名称: %s, 函数参数:%s", functionName, params)
|
||||
|
||||
// for creating image, check if the user's img_calls > 0
|
||||
if functionName == types.FuncImage && userVo.ImgCalls <= 0 {
|
||||
utils.ReplyMessage(ws, "**当前用户剩余绘图次数已用尽,请扫描下面二维码联系管理员!**")
|
||||
utils.ReplyMessage(ws, ErrImg)
|
||||
} else {
|
||||
f := h.App.Functions[functionName]
|
||||
if functionName == types.FuncImage {
|
||||
params["user_id"] = userVo.Id
|
||||
params["role_id"] = role.Id
|
||||
params["chat_id"] = session.ChatId
|
||||
params["icon"] = "/images/avatar/mid_journey.png"
|
||||
params["session_id"] = session.SessionId
|
||||
}
|
||||
data, err := f.Invoke(params)
|
||||
if err != nil {
|
||||
msg := "调用函数出错:" + err.Error()
|
||||
utils.ReplyChunkMessage(ws, types.WsMessage{
|
||||
Type: types.WsMiddle,
|
||||
Content: msg,
|
||||
})
|
||||
contents = append(contents, msg)
|
||||
} else {
|
||||
content := data
|
||||
if functionName == types.FuncImage {
|
||||
content = fmt.Sprintf("下面是根据您的描述创作的图片,他们描绘了 【%s】 的场景", params["prompt"])
|
||||
// update user's img_calls
|
||||
h.db.Model(&model.User{}).Where("id = ?", userVo.Id).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
|
||||
}
|
||||
|
||||
utils.ReplyChunkMessage(ws, types.WsMessage{
|
||||
Type: types.WsMiddle,
|
||||
Content: content,
|
||||
})
|
||||
contents = append(contents, content)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 消息发送成功
|
||||
if len(contents) > 0 {
|
||||
// 更新用户的对话次数
|
||||
@@ -177,7 +113,7 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
useMsg := types.Message{Role: "user", Content: prompt}
|
||||
|
||||
// 更新上下文消息,如果是调用函数则不需要更新上下文
|
||||
if h.App.ChatConfig.EnableContext && functionCall == false {
|
||||
if h.App.ChatConfig.EnableContext {
|
||||
chatCtx = append(chatCtx, useMsg) // 提问消息
|
||||
chatCtx = append(chatCtx, message) // 回复消息
|
||||
h.App.ChatContexts.Put(session.ChatId, chatCtx)
|
||||
@@ -185,11 +121,6 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
|
||||
// 追加聊天记录
|
||||
if h.App.ChatConfig.EnableHistory {
|
||||
useContext := true
|
||||
if functionCall {
|
||||
useContext = false
|
||||
}
|
||||
|
||||
// for prompt
|
||||
promptToken, err := utils.CalcTokens(prompt, req.Model)
|
||||
if err != nil {
|
||||
@@ -203,7 +134,7 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
Icon: userVo.Avatar,
|
||||
Content: template.HTMLEscapeString(prompt),
|
||||
Tokens: promptToken,
|
||||
UseContext: useContext,
|
||||
UseContext: true,
|
||||
}
|
||||
historyUserMsg.CreatedAt = promptCreatedAt
|
||||
historyUserMsg.UpdatedAt = promptCreatedAt
|
||||
@@ -213,15 +144,7 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
}
|
||||
|
||||
// 计算本次对话消耗的总 token 数量
|
||||
var totalTokens = 0
|
||||
if functionCall { // prompt + 函数名 + 参数 token
|
||||
tokens, _ := utils.CalcTokens(functionName, req.Model)
|
||||
totalTokens += tokens
|
||||
tokens, _ = utils.CalcTokens(utils.InterfaceToString(arguments), req.Model)
|
||||
totalTokens += tokens
|
||||
} else {
|
||||
totalTokens, _ = utils.CalcTokens(message.Content, req.Model)
|
||||
}
|
||||
totalTokens, _ := utils.CalcTokens(message.Content, req.Model)
|
||||
totalTokens += getTotalTokens(req)
|
||||
|
||||
historyReplyMsg := model.HistoryMessage{
|
||||
@@ -232,7 +155,7 @@ func (h *ChatHandler) sendAzureMessage(
|
||||
Icon: role.Icon,
|
||||
Content: message.Content,
|
||||
Tokens: totalTokens,
|
||||
UseContext: useContext,
|
||||
UseContext: true,
|
||||
}
|
||||
historyReplyMsg.CreatedAt = replyCreatedAt
|
||||
historyReplyMsg.UpdatedAt = replyCreatedAt
|
||||
|
||||
@@ -46,7 +46,7 @@ func (h *ChatHandler) sendBaiduMessage(
|
||||
ws *types.WsClient) error {
|
||||
promptCreatedAt := time.Now() // 记录提问时间
|
||||
start := time.Now()
|
||||
var apiKey = userVo.ChatConfig.ApiKeys[session.Model.Platform]
|
||||
var apiKey = model.ApiKey{}
|
||||
response, err := h.doRequest(ctx, req, session.Model.Platform, &apiKey)
|
||||
logger.Info("HTTP请求完成,耗时:", time.Now().Sub(start))
|
||||
if err != nil {
|
||||
|
||||
@@ -14,14 +14,15 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/gorilla/websocket"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/gorilla/websocket"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const ErrorMsg = "抱歉,AI 助手开小差了,请稍后再试。"
|
||||
@@ -80,7 +81,7 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) {
|
||||
session = &types.ChatSession{
|
||||
SessionId: sessionId,
|
||||
ClientIP: c.ClientIP(),
|
||||
Username: user.Mobile,
|
||||
Username: user.Username,
|
||||
UserId: user.Id,
|
||||
}
|
||||
h.App.ChatSession.Put(sessionId, session)
|
||||
@@ -156,11 +157,13 @@ func (h *ChatHandler) ChatHandle(c *gin.Context) {
|
||||
}
|
||||
|
||||
func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSession, role model.ChatRole, prompt string, ws *types.WsClient) error {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Error("Recover message from error: ", r)
|
||||
}
|
||||
}()
|
||||
if !h.App.Debug {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
logger.Error("Recover message from error: ", r)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
var user model.User
|
||||
res := h.db.Model(&model.User{}).First(&user, session.UserId)
|
||||
@@ -214,20 +217,56 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSessio
|
||||
case types.Baidu:
|
||||
req.Temperature = h.App.ChatConfig.OpenAI.Temperature
|
||||
// TODO: 目前只支持 ERNIE-Bot-turbo 模型,如果是 ERNIE-Bot 模型则需要增加函数支持
|
||||
break
|
||||
case types.OpenAI:
|
||||
req.Temperature = h.App.ChatConfig.OpenAI.Temperature
|
||||
req.MaxTokens = h.App.ChatConfig.OpenAI.MaxTokens
|
||||
// OpenAI 支持函数功能
|
||||
if h.App.SysConfig.EnabledFunction {
|
||||
var functions = make([]types.Function, 0)
|
||||
for _, f := range types.InnerFunctions {
|
||||
functions = append(functions, f)
|
||||
var items []model.Function
|
||||
res := h.db.Where("enabled", true).Find(&items)
|
||||
if res.Error != nil {
|
||||
break
|
||||
}
|
||||
|
||||
var tools = make([]interface{}, 0)
|
||||
var functions = make([]interface{}, 0)
|
||||
for _, v := range items {
|
||||
var parameters map[string]interface{}
|
||||
err = utils.JsonDecode(v.Parameters, ¶meters)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
required := parameters["required"]
|
||||
delete(parameters, "required")
|
||||
tools = append(tools, gin.H{
|
||||
"type": "function",
|
||||
"function": gin.H{
|
||||
"name": v.Name,
|
||||
"description": v.Description,
|
||||
"parameters": parameters,
|
||||
"required": required,
|
||||
},
|
||||
})
|
||||
functions = append(functions, gin.H{
|
||||
"name": v.Name,
|
||||
"description": v.Description,
|
||||
"parameters": parameters,
|
||||
"required": required,
|
||||
})
|
||||
}
|
||||
|
||||
//if len(tools) > 0 {
|
||||
// req.Tools = tools
|
||||
// req.ToolChoice = "auto"
|
||||
//}
|
||||
if len(functions) > 0 {
|
||||
req.Functions = functions
|
||||
}
|
||||
|
||||
case types.XunFei:
|
||||
req.Temperature = h.App.ChatConfig.XunFei.Temperature
|
||||
req.MaxTokens = h.App.ChatConfig.XunFei.MaxTokens
|
||||
break
|
||||
default:
|
||||
utils.ReplyMessage(ws, "不支持的平台:"+session.Model.Platform+",请联系管理员!")
|
||||
utils.ReplyMessage(ws, ErrImg)
|
||||
@@ -242,18 +281,15 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSessio
|
||||
} else {
|
||||
// calculate the tokens of current request, to prevent to exceeding the max tokens num
|
||||
tokens := req.MaxTokens
|
||||
for _, f := range types.InnerFunctions {
|
||||
tks, _ := utils.CalcTokens(utils.JsonEncode(f), req.Model)
|
||||
tokens += tks
|
||||
}
|
||||
|
||||
tks, _ := utils.CalcTokens(utils.JsonEncode(req.Tools), req.Model)
|
||||
tokens += tks
|
||||
// loading the role context
|
||||
var messages []types.Message
|
||||
err := utils.JsonDecode(role.Context, &messages)
|
||||
if err == nil {
|
||||
for _, v := range messages {
|
||||
tks, _ := utils.CalcTokens(v.Content, req.Model)
|
||||
if tokens+tks >= types.ModelToTokens[req.Model] {
|
||||
if tokens+tks >= types.GetModelMaxToken(req.Model) {
|
||||
break
|
||||
}
|
||||
tokens += tks
|
||||
@@ -268,7 +304,7 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSessio
|
||||
if res.Error == nil {
|
||||
for i := len(historyMessages) - 1; i >= 0; i-- {
|
||||
msg := historyMessages[i]
|
||||
if tokens+msg.Tokens >= types.ModelToTokens[session.Model.Value] {
|
||||
if tokens+msg.Tokens >= types.GetModelMaxToken(session.Model.Value) {
|
||||
break
|
||||
}
|
||||
tokens += msg.Tokens
|
||||
@@ -316,8 +352,9 @@ func (h *ChatHandler) sendMessage(ctx context.Context, session *types.ChatSessio
|
||||
// Tokens 统计 token 数量
|
||||
func (h *ChatHandler) Tokens(c *gin.Context) {
|
||||
var data struct {
|
||||
Text string `json:"text"`
|
||||
Model string `json:"model"`
|
||||
Text string `json:"text"`
|
||||
Model string `json:"model"`
|
||||
ChatId string `json:"chat_id"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
@@ -325,10 +362,10 @@ func (h *ChatHandler) Tokens(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 如果没有传入 text 字段,则说明是获取当前 reply 总的 token 消耗(带上下文)
|
||||
if data.Text == "" {
|
||||
if data.Text == "" && data.ChatId != "" {
|
||||
var item model.HistoryMessage
|
||||
userId, _ := c.Get(types.LoginUserID)
|
||||
res := h.db.Where("user_id = ?", userId).Last(&item)
|
||||
res := h.db.Where("user_id = ?", userId).Where("chat_id = ?", data.ChatId).Last(&item)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, res.Error.Error())
|
||||
return
|
||||
@@ -378,39 +415,33 @@ func (h *ChatHandler) StopGenerate(c *gin.Context) {
|
||||
|
||||
// 发送请求到 OpenAI 服务器
|
||||
// useOwnApiKey: 是否使用了用户自己的 API KEY
|
||||
func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platform types.Platform, apiKey *string) (*http.Response, error) {
|
||||
|
||||
func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platform types.Platform, apiKey *model.ApiKey) (*http.Response, error) {
|
||||
res := h.db.Where("platform = ?", platform).Where("type = ?", "chat").Where("enabled = ?", true).Order("last_used_at ASC").First(apiKey)
|
||||
if res.Error != nil {
|
||||
return nil, errors.New("no available key, please import key")
|
||||
}
|
||||
var apiURL string
|
||||
switch platform {
|
||||
case types.Azure:
|
||||
md := strings.Replace(req.Model, ".", "", 1)
|
||||
apiURL = strings.Replace(h.App.ChatConfig.Azure.ApiURL, "{model}", md, 1)
|
||||
apiURL = strings.Replace(apiKey.ApiURL, "{model}", md, 1)
|
||||
break
|
||||
case types.ChatGLM:
|
||||
apiURL = strings.Replace(h.App.ChatConfig.ChatGML.ApiURL, "{model}", req.Model, 1)
|
||||
apiURL = strings.Replace(apiKey.ApiURL, "{model}", req.Model, 1)
|
||||
req.Prompt = req.Messages // 使用 prompt 字段替代 message 字段
|
||||
req.Messages = nil
|
||||
break
|
||||
case types.Baidu:
|
||||
apiURL = strings.Replace(h.App.ChatConfig.Baidu.ApiURL, "{model}", req.Model, 1)
|
||||
apiURL = strings.Replace(apiKey.ApiURL, "{model}", req.Model, 1)
|
||||
break
|
||||
default:
|
||||
apiURL = h.App.ChatConfig.OpenAI.ApiURL
|
||||
apiURL = apiKey.ApiURL
|
||||
}
|
||||
if *apiKey == "" {
|
||||
var key model.ApiKey
|
||||
res := h.db.Where("platform = ? AND type = ?", platform, "chat").Order("last_used_at ASC").First(&key)
|
||||
if res.Error != nil {
|
||||
return nil, errors.New("no available key, please import key")
|
||||
}
|
||||
// 更新 API KEY 的最后使用时间
|
||||
h.db.Model(&key).UpdateColumn("last_used_at", time.Now().Unix())
|
||||
*apiKey = key.Value
|
||||
}
|
||||
|
||||
// 更新 API KEY 的最后使用时间
|
||||
h.db.Model(apiKey).UpdateColumn("last_used_at", time.Now().Unix())
|
||||
// 百度文心,需要串接 access_token
|
||||
if platform == types.Baidu {
|
||||
token, err := h.getBaiduToken(*apiKey)
|
||||
token, err := h.getBaiduToken(apiKey.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -418,6 +449,8 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platf
|
||||
apiURL = fmt.Sprintf("%s?access_token=%s", apiURL, token)
|
||||
}
|
||||
|
||||
logger.Debugf(utils.JsonEncode(req))
|
||||
|
||||
// 创建 HttpClient 请求对象
|
||||
var client *http.Client
|
||||
requestBody, err := json.Marshal(req)
|
||||
@@ -431,8 +464,9 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platf
|
||||
|
||||
request = request.WithContext(ctx)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
proxyURL := h.App.Config.ProxyURL
|
||||
if proxyURL != "" && platform == types.OpenAI { // 使用代理
|
||||
var proxyURL string
|
||||
if h.App.Config.ProxyURL != "" && apiKey.UseProxy { // 使用代理
|
||||
proxyURL = h.App.Config.ProxyURL
|
||||
proxy, _ := url.Parse(proxyURL)
|
||||
client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
@@ -442,23 +476,22 @@ func (h *ChatHandler) doRequest(ctx context.Context, req types.ApiRequest, platf
|
||||
} else {
|
||||
client = http.DefaultClient
|
||||
}
|
||||
logger.Infof("Sending %s request, KEY: %s, PROXY: %s, Model: %s", platform, *apiKey, proxyURL, req.Model)
|
||||
logger.Debugf("Sending %s request, ApiURL:%s, ApiKey:%s, PROXY: %s, Model: %s", platform, apiURL, apiKey.Value, proxyURL, req.Model)
|
||||
switch platform {
|
||||
case types.Azure:
|
||||
request.Header.Set("api-key", *apiKey)
|
||||
request.Header.Set("api-key", apiKey.Value)
|
||||
break
|
||||
case types.ChatGLM:
|
||||
token, err := h.getChatGLMToken(*apiKey)
|
||||
token, err := h.getChatGLMToken(apiKey.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logger.Info(token)
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
|
||||
break
|
||||
case types.Baidu:
|
||||
request.RequestURI = ""
|
||||
case types.OpenAI:
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", *apiKey))
|
||||
request.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey.Value))
|
||||
}
|
||||
return client.Do(request)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func (h *ChatHandler) sendChatGLMMessage(
|
||||
ws *types.WsClient) error {
|
||||
promptCreatedAt := time.Now() // 记录提问时间
|
||||
start := time.Now()
|
||||
var apiKey = userVo.ChatConfig.ApiKeys[session.Model.Platform]
|
||||
var apiKey = model.ApiKey{}
|
||||
response, err := h.doRequest(ctx, req, session.Model.Platform, &apiKey)
|
||||
logger.Info("HTTP请求完成,耗时:", time.Now().Sub(start))
|
||||
if err != nil {
|
||||
|
||||
@@ -9,12 +9,13 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"gorm.io/gorm"
|
||||
"html/template"
|
||||
"io"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
|
||||
req2 "github.com/imroc/req/v3"
|
||||
)
|
||||
|
||||
// OPenAI 消息发送实现
|
||||
@@ -29,7 +30,7 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
ws *types.WsClient) error {
|
||||
promptCreatedAt := time.Now() // 记录提问时间
|
||||
start := time.Now()
|
||||
var apiKey = userVo.ChatConfig.ApiKeys[session.Model.Platform]
|
||||
var apiKey = model.ApiKey{}
|
||||
response, err := h.doRequest(ctx, req, session.Model.Platform, &apiKey)
|
||||
logger.Info("HTTP请求完成,耗时:", time.Now().Sub(start))
|
||||
if err != nil {
|
||||
@@ -56,8 +57,8 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
// 循环读取 Chunk 消息
|
||||
var message = types.Message{}
|
||||
var contents = make([]string, 0)
|
||||
var functionCall = false
|
||||
var functionName string
|
||||
var function model.Function
|
||||
var toolCall = false
|
||||
var arguments = make([]string, 0)
|
||||
scanner := bufio.NewScanner(response.Body)
|
||||
for scanner.Scan() {
|
||||
@@ -75,24 +76,37 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
break
|
||||
}
|
||||
|
||||
var tool types.ToolCall
|
||||
if len(responseBody.Choices[0].Delta.ToolCalls) > 0 {
|
||||
tool = responseBody.Choices[0].Delta.ToolCalls[0]
|
||||
if toolCall && tool.Function.Name == "" {
|
||||
arguments = append(arguments, tool.Function.Arguments)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// 兼容 Function Call
|
||||
fun := responseBody.Choices[0].Delta.FunctionCall
|
||||
if functionCall && fun.Name == "" {
|
||||
if fun.Name != "" {
|
||||
tool = *new(types.ToolCall)
|
||||
tool.Function.Name = fun.Name
|
||||
} else if toolCall {
|
||||
arguments = append(arguments, fun.Arguments)
|
||||
continue
|
||||
}
|
||||
|
||||
if !utils.IsEmptyValue(fun) {
|
||||
functionName = fun.Name
|
||||
f := h.App.Functions[functionName]
|
||||
if f != nil {
|
||||
functionCall = true
|
||||
if !utils.IsEmptyValue(tool) {
|
||||
res := h.db.Where("name = ?", tool.Function.Name).First(&function)
|
||||
if res.Error == nil {
|
||||
toolCall = true
|
||||
utils.ReplyChunkMessage(ws, types.WsMessage{Type: types.WsStart})
|
||||
utils.ReplyChunkMessage(ws, types.WsMessage{Type: types.WsMiddle, Content: fmt.Sprintf("正在调用函数 `%s` 作答 ...\n\n", f.Name())})
|
||||
utils.ReplyChunkMessage(ws, types.WsMessage{Type: types.WsMiddle, Content: fmt.Sprintf("正在调用工具 `%s` 作答 ...\n\n", function.Label)})
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if responseBody.Choices[0].FinishReason == "function_call" { // 函数调用完毕
|
||||
if responseBody.Choices[0].FinishReason == "tool_calls" ||
|
||||
responseBody.Choices[0].FinishReason == "function_call" { // 函数调用完毕
|
||||
break
|
||||
}
|
||||
|
||||
@@ -121,45 +135,35 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
}
|
||||
}
|
||||
|
||||
if functionCall { // 调用函数完成任务
|
||||
if toolCall { // 调用函数完成任务
|
||||
var params map[string]interface{}
|
||||
_ = utils.JsonDecode(strings.Join(arguments, ""), ¶ms)
|
||||
logger.Debugf("函数名称: %s, 函数参数:%s", functionName, params)
|
||||
|
||||
// for creating image, check if the user's img_calls > 0
|
||||
if functionName == types.FuncImage && userVo.ImgCalls <= 0 {
|
||||
utils.ReplyMessage(ws, "**当前用户剩余绘图次数已用尽,请扫描下面二维码联系管理员!**")
|
||||
utils.ReplyMessage(ws, ErrImg)
|
||||
logger.Debugf("函数名称: %s, 函数参数:%s", function.Name, params)
|
||||
params["user_id"] = userVo.Id
|
||||
var apiRes types.BizVo
|
||||
r, err := req2.C().R().SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", function.Token).
|
||||
SetBody(params).
|
||||
SetSuccessResult(&apiRes).Post(function.Action)
|
||||
errMsg := ""
|
||||
if err != nil {
|
||||
errMsg = err.Error()
|
||||
} else if r.IsErrorState() {
|
||||
errMsg = r.Status
|
||||
}
|
||||
if errMsg != "" || apiRes.Code != types.Success {
|
||||
msg := "调用函数工具出错:" + apiRes.Message + errMsg
|
||||
utils.ReplyChunkMessage(ws, types.WsMessage{
|
||||
Type: types.WsMiddle,
|
||||
Content: msg,
|
||||
})
|
||||
contents = append(contents, msg)
|
||||
} else {
|
||||
f := h.App.Functions[functionName]
|
||||
if functionName == types.FuncImage {
|
||||
params["user_id"] = userVo.Id
|
||||
params["role_id"] = role.Id
|
||||
params["chat_id"] = session.ChatId
|
||||
params["session_id"] = session.SessionId
|
||||
}
|
||||
data, err := f.Invoke(params)
|
||||
if err != nil {
|
||||
msg := "调用函数出错:" + err.Error()
|
||||
utils.ReplyChunkMessage(ws, types.WsMessage{
|
||||
Type: types.WsMiddle,
|
||||
Content: msg,
|
||||
})
|
||||
contents = append(contents, msg)
|
||||
} else {
|
||||
content := data
|
||||
if functionName == types.FuncImage {
|
||||
content = fmt.Sprintf("下面是根据您的描述创作的图片,他们描绘了 【%s】 的场景。%s", params["prompt"], data)
|
||||
// update user's img_calls
|
||||
h.db.Model(&model.User{}).Where("id = ?", userVo.Id).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
|
||||
}
|
||||
|
||||
utils.ReplyChunkMessage(ws, types.WsMessage{
|
||||
Type: types.WsMiddle,
|
||||
Content: content,
|
||||
})
|
||||
contents = append(contents, content)
|
||||
}
|
||||
utils.ReplyChunkMessage(ws, types.WsMessage{
|
||||
Type: types.WsMiddle,
|
||||
Content: apiRes.Data,
|
||||
})
|
||||
contents = append(contents, utils.InterfaceToString(apiRes.Data))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,7 +179,7 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
useMsg := types.Message{Role: "user", Content: prompt}
|
||||
|
||||
// 更新上下文消息,如果是调用函数则不需要更新上下文
|
||||
if h.App.ChatConfig.EnableContext && functionCall == false {
|
||||
if h.App.ChatConfig.EnableContext && toolCall == false {
|
||||
chatCtx = append(chatCtx, useMsg) // 提问消息
|
||||
chatCtx = append(chatCtx, message) // 回复消息
|
||||
h.App.ChatContexts.Put(session.ChatId, chatCtx)
|
||||
@@ -184,7 +188,7 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
// 追加聊天记录
|
||||
if h.App.ChatConfig.EnableHistory {
|
||||
useContext := true
|
||||
if functionCall {
|
||||
if toolCall {
|
||||
useContext = false
|
||||
}
|
||||
|
||||
@@ -212,8 +216,8 @@ func (h *ChatHandler) sendOpenAiMessage(
|
||||
|
||||
// 计算本次对话消耗的总 token 数量
|
||||
var totalTokens = 0
|
||||
if functionCall { // prompt + 函数名 + 参数 token
|
||||
tokens, _ := utils.CalcTokens(functionName, req.Model)
|
||||
if toolCall { // prompt + 函数名 + 参数 token
|
||||
tokens, _ := utils.CalcTokens(function.Name, req.Model)
|
||||
totalTokens += tokens
|
||||
tokens, _ = utils.CalcTokens(utils.InterfaceToString(arguments), req.Model)
|
||||
totalTokens += tokens
|
||||
|
||||
@@ -50,7 +50,7 @@ type xunFeiResp struct {
|
||||
}
|
||||
|
||||
var Model2URL = map[string]string{
|
||||
"generalv1": "1.1",
|
||||
"general": "v1.1",
|
||||
"generalv2": "v2.1",
|
||||
"generalv3": "v3.1",
|
||||
}
|
||||
@@ -67,29 +67,25 @@ func (h *ChatHandler) sendXunFeiMessage(
|
||||
prompt string,
|
||||
ws *types.WsClient) error {
|
||||
promptCreatedAt := time.Now() // 记录提问时间
|
||||
var apiKey = userVo.ChatConfig.ApiKeys[session.Model.Platform]
|
||||
if apiKey == "" {
|
||||
var key model.ApiKey
|
||||
res := h.db.Where("platform = ? AND type = ?", session.Model.Platform, "chat").Order("last_used_at ASC").First(&key)
|
||||
if res.Error != nil {
|
||||
utils.ReplyMessage(ws, "抱歉😔😔😔,系统已经没有可用的 API KEY,请联系管理员!")
|
||||
return nil
|
||||
}
|
||||
// 更新 API KEY 的最后使用时间
|
||||
h.db.Model(&key).UpdateColumn("last_used_at", time.Now().Unix())
|
||||
apiKey = key.Value
|
||||
var apiKey model.ApiKey
|
||||
res := h.db.Where("platform = ?", session.Model.Platform).Where("type = ?", "chat").Where("enabled = ?", true).Order("last_used_at ASC").First(&apiKey)
|
||||
if res.Error != nil {
|
||||
utils.ReplyMessage(ws, "抱歉😔😔😔,系统已经没有可用的 API KEY,请联系管理员!")
|
||||
return nil
|
||||
}
|
||||
// 更新 API KEY 的最后使用时间
|
||||
h.db.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix())
|
||||
|
||||
d := websocket.Dialer{
|
||||
HandshakeTimeout: 5 * time.Second,
|
||||
}
|
||||
key := strings.Split(apiKey, "|")
|
||||
key := strings.Split(apiKey.Value, "|")
|
||||
if len(key) != 3 {
|
||||
utils.ReplyMessage(ws, "非法的 API KEY!")
|
||||
return nil
|
||||
}
|
||||
|
||||
apiURL := strings.Replace(h.App.ChatConfig.XunFei.ApiURL, "{version}", Model2URL[req.Model], 1)
|
||||
apiURL := strings.Replace(apiKey.ApiURL, "{version}", Model2URL[req.Model], 1)
|
||||
wsURL, err := assembleAuthUrl(apiURL, key[1], key[2])
|
||||
//握手并建立websocket 连接
|
||||
conn, resp, err := d.Dial(wsURL, nil)
|
||||
|
||||
279
api/handler/function_handler.go
Normal file
279
api/handler/function_handler.go
Normal file
@@ -0,0 +1,279 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"chatplus/core"
|
||||
"chatplus/core/types"
|
||||
"chatplus/service/oss"
|
||||
"chatplus/store/model"
|
||||
"chatplus/utils"
|
||||
"chatplus/utils/resp"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/imroc/req/v3"
|
||||
"gorm.io/gorm"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FunctionHandler struct {
|
||||
BaseHandler
|
||||
db *gorm.DB
|
||||
config types.ChatPlusApiConfig
|
||||
uploadManager *oss.UploaderManager
|
||||
proxyURL string
|
||||
}
|
||||
|
||||
func NewFunctionHandler(server *core.AppServer, db *gorm.DB, config *types.AppConfig, manager *oss.UploaderManager) *FunctionHandler {
|
||||
return &FunctionHandler{
|
||||
BaseHandler: BaseHandler{
|
||||
App: server,
|
||||
},
|
||||
db: db,
|
||||
config: config.ApiConfig,
|
||||
uploadManager: manager,
|
||||
proxyURL: config.ProxyURL,
|
||||
}
|
||||
}
|
||||
|
||||
type resVo struct {
|
||||
Code types.BizCode `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data struct {
|
||||
Title string `json:"title"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
Items []dataItem `json:"items"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type dataItem struct {
|
||||
Title string `json:"title"`
|
||||
Url string `json:"url"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
// check authorization
|
||||
func (h *FunctionHandler) checkAuth(c *gin.Context) error {
|
||||
tokenString := c.GetHeader(types.UserAuthHeader)
|
||||
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
|
||||
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
|
||||
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
|
||||
}
|
||||
|
||||
return []byte(h.App.Config.Session.SecretKey), nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error with parse auth token: %v", err)
|
||||
}
|
||||
|
||||
claims, ok := token.Claims.(jwt.MapClaims)
|
||||
if !ok || !token.Valid {
|
||||
return errors.New("token is invalid")
|
||||
}
|
||||
|
||||
expr := utils.IntValue(utils.InterfaceToString(claims["expired"]), 0)
|
||||
if expr > 0 && int64(expr) < time.Now().Unix() {
|
||||
return errors.New("token is expired")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WeiBo 微博热搜
|
||||
func (h *FunctionHandler) WeiBo(c *gin.Context) {
|
||||
if err := h.checkAuth(c); err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if h.config.Token == "" {
|
||||
resp.ERROR(c, "无效的 API Token")
|
||||
return
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/api/weibo/fetch", h.config.ApiURL)
|
||||
var res resVo
|
||||
r, err := req.C().R().
|
||||
SetHeader("AppId", h.config.AppId).
|
||||
SetHeader("Authorization", fmt.Sprintf("Bearer %s", h.config.Token)).
|
||||
SetSuccessResult(&res).Get(url)
|
||||
if err != nil || r.IsErrorState() {
|
||||
resp.ERROR(c, fmt.Sprintf("%v%v", err, r.Err))
|
||||
return
|
||||
}
|
||||
|
||||
if res.Code != types.Success {
|
||||
resp.ERROR(c, res.Message)
|
||||
return
|
||||
}
|
||||
|
||||
builder := make([]string, 0)
|
||||
builder = append(builder, fmt.Sprintf("**%s**,最新更新:%s", res.Data.Title, res.Data.UpdatedAt))
|
||||
for i, v := range res.Data.Items {
|
||||
builder = append(builder, fmt.Sprintf("%d、 [%s](%s) [热度:%s]", i+1, v.Title, v.Url, v.Remark))
|
||||
}
|
||||
resp.SUCCESS(c, strings.Join(builder, "\n\n"))
|
||||
}
|
||||
|
||||
// ZaoBao 今日早报
|
||||
func (h *FunctionHandler) ZaoBao(c *gin.Context) {
|
||||
if err := h.checkAuth(c); err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if h.config.Token == "" {
|
||||
resp.ERROR(c, "无效的 API Token")
|
||||
return
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/api/zaobao/fetch", h.config.ApiURL)
|
||||
var res resVo
|
||||
r, err := req.C().R().
|
||||
SetHeader("AppId", h.config.AppId).
|
||||
SetHeader("Authorization", fmt.Sprintf("Bearer %s", h.config.Token)).
|
||||
SetSuccessResult(&res).Get(url)
|
||||
if err != nil || r.IsErrorState() {
|
||||
resp.ERROR(c, fmt.Sprintf("%v%v", err, r.Err))
|
||||
return
|
||||
}
|
||||
|
||||
if res.Code != types.Success {
|
||||
resp.ERROR(c, res.Message)
|
||||
return
|
||||
}
|
||||
|
||||
builder := make([]string, 0)
|
||||
builder = append(builder, fmt.Sprintf("**%s 早报:**", res.Data.UpdatedAt))
|
||||
for _, v := range res.Data.Items {
|
||||
builder = append(builder, v.Title)
|
||||
}
|
||||
builder = append(builder, fmt.Sprintf("%s", res.Data.Title))
|
||||
resp.SUCCESS(c, strings.Join(builder, "\n\n"))
|
||||
}
|
||||
|
||||
type imgReq struct {
|
||||
Model string `json:"model"`
|
||||
Prompt string `json:"prompt"`
|
||||
N int `json:"n"`
|
||||
Size string `json:"size"`
|
||||
}
|
||||
|
||||
type imgRes struct {
|
||||
Created int64 `json:"created"`
|
||||
Data []struct {
|
||||
RevisedPrompt string `json:"revised_prompt"`
|
||||
Url string `json:"url"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type ErrRes struct {
|
||||
Error struct {
|
||||
Code interface{} `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Param interface{} `json:"param"`
|
||||
Type string `json:"type"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
// Dall3 DallE3 AI 绘图
|
||||
func (h *FunctionHandler) Dall3(c *gin.Context) {
|
||||
if err := h.checkAuth(c); err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var params map[string]interface{}
|
||||
if err := c.ShouldBindJSON(¶ms); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
|
||||
logger.Debugf("绘画参数:%+v", params)
|
||||
// check img calls
|
||||
var user model.User
|
||||
tx := h.db.Where("id = ?", params["user_id"]).First(&user)
|
||||
if tx.Error != nil {
|
||||
resp.ERROR(c, "当前用户不存在!")
|
||||
return
|
||||
}
|
||||
|
||||
if user.ImgCalls <= 0 {
|
||||
resp.ERROR(c, "当前用户的绘图次数额度不足!")
|
||||
return
|
||||
}
|
||||
|
||||
prompt := utils.InterfaceToString(params["prompt"])
|
||||
// get image generation API KEY
|
||||
var apiKey model.ApiKey
|
||||
tx = h.db.Where("platform = ?", types.OpenAI).Where("type = ?", "img").Where("enabled = ?", true).Order("last_used_at ASC").First(&apiKey)
|
||||
if tx.Error != nil {
|
||||
resp.ERROR(c, "获取绘图 API KEY 失败: "+tx.Error.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// get image generation api URL
|
||||
var conf model.Config
|
||||
var chatConfig types.ChatConfig
|
||||
tx = h.db.Where("marker", "chat").First(&conf)
|
||||
if tx.Error != nil {
|
||||
resp.ERROR(c, "error with get chat configs:"+tx.Error.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err := utils.JsonDecode(conf.Config, &chatConfig)
|
||||
if err != nil {
|
||||
resp.ERROR(c, "error with decode chat config: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// translate prompt
|
||||
const translatePromptTemplate = "Translate the following painting prompt words into English keyword phrases. Without any explanation, directly output the keyword phrases separated by commas. The content to be translated is: [%s]"
|
||||
pt, err := utils.OpenAIRequest(h.db, fmt.Sprintf(translatePromptTemplate, params["prompt"]), h.App.Config.ProxyURL)
|
||||
if err == nil {
|
||||
prompt = pt
|
||||
}
|
||||
imgNum := chatConfig.DallImgNum
|
||||
if imgNum <= 0 {
|
||||
imgNum = 1
|
||||
}
|
||||
var res imgRes
|
||||
var errRes ErrRes
|
||||
var request *req.Request
|
||||
if apiKey.UseProxy && h.proxyURL != "" {
|
||||
request = req.C().SetProxyURL(h.proxyURL).R()
|
||||
} else {
|
||||
request = req.C().R()
|
||||
}
|
||||
logger.Debugf("Sending %s request, ApiURL:%s, ApiKey:%s, PROXY: %s", apiKey.Platform, apiKey.ApiURL, apiKey.Value, h.proxyURL)
|
||||
r, err := request.SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+apiKey.Value).
|
||||
SetBody(imgReq{
|
||||
Model: "dall-e-3",
|
||||
Prompt: prompt,
|
||||
N: imgNum,
|
||||
Size: "1024x1024",
|
||||
}).
|
||||
SetErrorResult(&errRes).
|
||||
SetSuccessResult(&res).Post(apiKey.ApiURL)
|
||||
if r.IsErrorState() {
|
||||
resp.ERROR(c, "请求 OpenAI API 失败: "+errRes.Error.Message)
|
||||
return
|
||||
}
|
||||
// 更新 API KEY 的最后使用时间
|
||||
h.db.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix())
|
||||
// 存储图片
|
||||
imgURL, err := h.uploadManager.GetUploadHandler().PutImg(res.Data[0].Url, false)
|
||||
if err != nil {
|
||||
resp.ERROR(c, "下载图片失败: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
content := fmt.Sprintf("下面是根据您的描述创作的图片,它描绘了 【%s】 的场景。 \n\n\n", prompt, imgURL)
|
||||
// update user's img_calls
|
||||
h.db.Model(&model.User{}).Where("id = ?", user.Id).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
|
||||
|
||||
resp.SUCCESS(c, content)
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"chatplus/core/types"
|
||||
"chatplus/service"
|
||||
"chatplus/service/mj"
|
||||
"chatplus/service/oss"
|
||||
"chatplus/store/model"
|
||||
"chatplus/store/vo"
|
||||
"chatplus/utils"
|
||||
@@ -12,7 +13,9 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
"gorm.io/gorm"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -22,13 +25,15 @@ type MidJourneyHandler struct {
|
||||
db *gorm.DB
|
||||
pool *mj.ServicePool
|
||||
snowflake *service.Snowflake
|
||||
uploader *oss.UploaderManager
|
||||
}
|
||||
|
||||
func NewMidJourneyHandler(app *core.AppServer, db *gorm.DB, snowflake *service.Snowflake, pool *mj.ServicePool) *MidJourneyHandler {
|
||||
func NewMidJourneyHandler(app *core.AppServer, db *gorm.DB, snowflake *service.Snowflake, pool *mj.ServicePool, manager *oss.UploaderManager) *MidJourneyHandler {
|
||||
h := MidJourneyHandler{
|
||||
db: db,
|
||||
snowflake: snowflake,
|
||||
pool: pool,
|
||||
uploader: manager,
|
||||
}
|
||||
h.App = app
|
||||
return &h
|
||||
@@ -55,6 +60,27 @@ func (h *MidJourneyHandler) preCheck(c *gin.Context) bool {
|
||||
|
||||
}
|
||||
|
||||
// Client WebSocket 客户端,用于通知任务状态变更
|
||||
func (h *MidJourneyHandler) Client(c *gin.Context) {
|
||||
ws, err := (&websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}).Upgrade(c.Writer, c.Request, nil)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
userId := h.GetInt(c, "user_id", 0)
|
||||
if userId == 0 {
|
||||
logger.Info("Invalid user ID")
|
||||
c.Abort()
|
||||
return
|
||||
}
|
||||
|
||||
client := types.NewWsClient(ws)
|
||||
h.pool.Clients.Put(uint(userId), client)
|
||||
logger.Infof("New websocket connected, IP: %s", c.RemoteIP())
|
||||
}
|
||||
|
||||
// Image 创建一个绘画任务
|
||||
func (h *MidJourneyHandler) Image(c *gin.Context) {
|
||||
var data struct {
|
||||
@@ -143,12 +169,18 @@ func (h *MidJourneyHandler) Image(c *gin.Context) {
|
||||
Prompt: fmt.Sprintf("%s %s", taskId, prompt),
|
||||
UserId: userId,
|
||||
})
|
||||
|
||||
client := h.pool.Clients.Get(uint(job.UserId))
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
|
||||
// update user's img calls
|
||||
h.db.Model(&model.User{}).Where("id = ?", job.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
type reqVo struct {
|
||||
Src string `json:"src"`
|
||||
Index int `json:"index"`
|
||||
ChannelId string `json:"channel_id"`
|
||||
MessageId string `json:"message_id"`
|
||||
MessageHash string `json:"message_hash"`
|
||||
SessionId string `json:"session_id"`
|
||||
@@ -173,16 +205,36 @@ func (h *MidJourneyHandler) Upscale(c *gin.Context) {
|
||||
idValue, _ := c.Get(types.LoginUserID)
|
||||
jobId := 0
|
||||
userId := utils.IntValue(utils.InterfaceToString(idValue), 0)
|
||||
taskId, _ := h.snowflake.Next(true)
|
||||
job := model.MidJourneyJob{
|
||||
Type: types.TaskUpscale.String(),
|
||||
ReferenceId: data.MessageId,
|
||||
UserId: userId,
|
||||
TaskId: taskId,
|
||||
Progress: 0,
|
||||
Prompt: data.Prompt,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if res := h.db.Create(&job); res.Error != nil {
|
||||
resp.ERROR(c, "添加任务失败:"+res.Error.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.pool.PushTask(types.MjTask{
|
||||
Id: jobId,
|
||||
SessionId: data.SessionId,
|
||||
Type: types.TaskUpscale,
|
||||
Prompt: data.Prompt,
|
||||
UserId: userId,
|
||||
ChannelId: data.ChannelId,
|
||||
Index: data.Index,
|
||||
MessageId: data.MessageId,
|
||||
MessageHash: data.MessageHash,
|
||||
})
|
||||
|
||||
client := h.pool.Clients.Get(uint(job.UserId))
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
@@ -201,6 +253,22 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) {
|
||||
idValue, _ := c.Get(types.LoginUserID)
|
||||
jobId := 0
|
||||
userId := utils.IntValue(utils.InterfaceToString(idValue), 0)
|
||||
taskId, _ := h.snowflake.Next(true)
|
||||
job := model.MidJourneyJob{
|
||||
Type: types.TaskVariation.String(),
|
||||
ChannelId: data.ChannelId,
|
||||
ReferenceId: data.MessageId,
|
||||
UserId: userId,
|
||||
TaskId: taskId,
|
||||
Progress: 0,
|
||||
Prompt: data.Prompt,
|
||||
CreatedAt: time.Now(),
|
||||
}
|
||||
if res := h.db.Create(&job); res.Error != nil {
|
||||
resp.ERROR(c, "添加任务失败:"+res.Error.Error())
|
||||
return
|
||||
}
|
||||
|
||||
h.pool.PushTask(types.MjTask{
|
||||
Id: jobId,
|
||||
SessionId: data.SessionId,
|
||||
@@ -208,9 +276,16 @@ func (h *MidJourneyHandler) Variation(c *gin.Context) {
|
||||
Prompt: data.Prompt,
|
||||
UserId: userId,
|
||||
Index: data.Index,
|
||||
ChannelId: data.ChannelId,
|
||||
MessageId: data.MessageId,
|
||||
MessageHash: data.MessageHash,
|
||||
})
|
||||
|
||||
client := h.pool.Clients.Get(uint(job.UserId))
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
|
||||
// update user's img calls
|
||||
h.db.Model(&model.User{}).Where("id = ?", job.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
@@ -258,6 +333,8 @@ func (h *MidJourneyHandler) JobList(c *gin.Context) {
|
||||
// 10 分钟还没完成的任务直接删除
|
||||
if time.Now().Sub(item.CreatedAt) > time.Minute*10 {
|
||||
h.db.Delete(&item)
|
||||
// 退回绘图次数
|
||||
h.db.Model(&model.User{}).Where("id = ?", item.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls + ?", 1))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -274,3 +351,34 @@ func (h *MidJourneyHandler) JobList(c *gin.Context) {
|
||||
}
|
||||
resp.SUCCESS(c, jobs)
|
||||
}
|
||||
|
||||
// Remove remove task image
|
||||
func (h *MidJourneyHandler) Remove(c *gin.Context) {
|
||||
var data struct {
|
||||
Id uint `json:"id"`
|
||||
UserId uint `json:"user_id"`
|
||||
ImgURL string `json:"img_url"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
|
||||
// remove job recode
|
||||
res := h.db.Delete(&model.MidJourneyJob{Id: data.Id})
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, res.Error.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// remove image
|
||||
err := h.uploader.GetUploadHandler().Delete(data.ImgURL)
|
||||
if err != nil {
|
||||
logger.Error("remove image failed: ", err)
|
||||
}
|
||||
|
||||
client := h.pool.Clients.Get(data.UserId)
|
||||
_ = client.Send([]byte("Task Updated"))
|
||||
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
@@ -11,17 +11,20 @@ import (
|
||||
"embed"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
const (
|
||||
PayWayAlipay = "支付宝"
|
||||
PayWayXunHu = "虎皮椒"
|
||||
PayWayJs = "PayJS"
|
||||
)
|
||||
|
||||
// PaymentHandler 支付服务回调 handler
|
||||
@@ -29,16 +32,25 @@ type PaymentHandler struct {
|
||||
BaseHandler
|
||||
alipayService *payment.AlipayService
|
||||
huPiPayService *payment.HuPiPayService
|
||||
js *payment.PayJS
|
||||
snowflake *service.Snowflake
|
||||
db *gorm.DB
|
||||
fs embed.FS
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func NewPaymentHandler(server *core.AppServer, alipayService *payment.AlipayService, huPiPayService *payment.HuPiPayService, snowflake *service.Snowflake, db *gorm.DB, fs embed.FS) *PaymentHandler {
|
||||
func NewPaymentHandler(
|
||||
server *core.AppServer,
|
||||
alipayService *payment.AlipayService,
|
||||
huPiPayService *payment.HuPiPayService,
|
||||
js *payment.PayJS,
|
||||
snowflake *service.Snowflake,
|
||||
db *gorm.DB,
|
||||
fs embed.FS) *PaymentHandler {
|
||||
h := PaymentHandler{
|
||||
alipayService: alipayService,
|
||||
huPiPayService: huPiPayService,
|
||||
js: js,
|
||||
snowflake: snowflake,
|
||||
fs: fs,
|
||||
db: db,
|
||||
@@ -81,17 +93,14 @@ func (h *PaymentHandler) DoPay(c *gin.Context) {
|
||||
c.Redirect(302, uri)
|
||||
return
|
||||
} else if payWay == "hupi" { // 虎皮椒支付
|
||||
params := map[string]string{
|
||||
"version": "1.1",
|
||||
"trade_order_id": orderNo,
|
||||
"total_fee": fmt.Sprintf("%f", order.Amount),
|
||||
"title": order.Subject,
|
||||
"notify_url": h.App.Config.HuPiPayConfig.NotifyURL,
|
||||
"return_url": "",
|
||||
"wap_name": "极客学长",
|
||||
"callback_url": "",
|
||||
params := payment.HuPiPayReq{
|
||||
Version: "1.1",
|
||||
TradeOrderId: orderNo,
|
||||
TotalFee: fmt.Sprintf("%f", order.Amount),
|
||||
Title: order.Subject,
|
||||
NotifyURL: h.App.Config.HuPiPayConfig.NotifyURL,
|
||||
WapName: "极客学长",
|
||||
}
|
||||
|
||||
res, err := h.huPiPayService.Pay(params)
|
||||
if err != nil {
|
||||
resp.ERROR(c, "error with generate pay url: "+err.Error())
|
||||
@@ -99,14 +108,15 @@ func (h *PaymentHandler) DoPay(c *gin.Context) {
|
||||
}
|
||||
|
||||
var r struct {
|
||||
Openid int64 `json:"openid"`
|
||||
UrlQrcode string `json:"url_qrcode"`
|
||||
URL string `json:"url"`
|
||||
ErrCode int `json:"errcode"`
|
||||
ErrMsg string `json:"errmsg"`
|
||||
Openid interface{} `json:"openid"`
|
||||
UrlQrcode string `json:"url_qrcode"`
|
||||
URL string `json:"url"`
|
||||
ErrCode int `json:"errcode"`
|
||||
ErrMsg string `json:"errmsg,omitempty"`
|
||||
}
|
||||
err = utils.JsonDecode(res, &r)
|
||||
if err != nil {
|
||||
logger.Debugf(res)
|
||||
resp.ERROR(c, "error with decode payment result: "+err.Error())
|
||||
return
|
||||
}
|
||||
@@ -188,9 +198,14 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
payWay := PayWayAlipay
|
||||
if data.PayWay == "hupi" {
|
||||
var payWay string
|
||||
switch data.PayWay {
|
||||
case "hupi":
|
||||
payWay = PayWayXunHu
|
||||
case "payjs":
|
||||
payWay = PayWayJs
|
||||
default:
|
||||
payWay = PayWayAlipay
|
||||
}
|
||||
// 创建订单
|
||||
remark := types.OrderRemark{
|
||||
@@ -203,7 +218,7 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
||||
}
|
||||
order := model.Order{
|
||||
UserId: user.Id,
|
||||
Mobile: user.Mobile,
|
||||
Mobile: user.Username,
|
||||
ProductId: product.Id,
|
||||
OrderNo: orderNo,
|
||||
Subject: product.Name,
|
||||
@@ -218,6 +233,23 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
// PayJs 单独处理,只能用官方生成的二维码
|
||||
if data.PayWay == "payjs" {
|
||||
params := payment.JPayReq{
|
||||
TotalFee: int(math.Ceil(order.Amount * 100)),
|
||||
OutTradeNo: order.OrderNo,
|
||||
Subject: product.Name,
|
||||
}
|
||||
r := h.js.Pay(params)
|
||||
if r.IsOK() {
|
||||
resp.SUCCESS(c, gin.H{"order_no": order.OrderNo, "image": r.Qrcode})
|
||||
return
|
||||
} else {
|
||||
resp.ERROR(c, "error with generating payment qrcode: "+r.ReturnMsg)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var logo string
|
||||
if data.PayWay == "alipay" {
|
||||
logo = "res/img/alipay.jpg"
|
||||
@@ -251,35 +283,6 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
|
||||
resp.SUCCESS(c, gin.H{"order_no": orderNo, "image": fmt.Sprintf("data:image/jpg;base64, %s", imgDataBase64), "url": imageURL})
|
||||
}
|
||||
|
||||
// AlipayNotify 支付宝支付回调
|
||||
func (h *PaymentHandler) AlipayNotify(c *gin.Context) {
|
||||
err := c.Request.ParseForm()
|
||||
if err != nil {
|
||||
c.String(http.StatusOK, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO:这里最好用支付宝的公钥签名签证一下交易真假
|
||||
//res := h.alipayService.TradeVerify(c.Request.Form)
|
||||
r := h.alipayService.TradeQuery(c.Request.Form.Get("out_trade_no"))
|
||||
logger.Infof("验证支付结果:%+v", r)
|
||||
if !r.Success() {
|
||||
c.String(http.StatusOK, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
err = h.notify(r.OutTradeNo)
|
||||
if err != nil {
|
||||
c.String(http.StatusOK, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, "success")
|
||||
}
|
||||
|
||||
// 异步通知回调公共逻辑
|
||||
func (h *PaymentHandler) notify(orderNo string) error {
|
||||
var order model.Order
|
||||
@@ -369,6 +372,9 @@ func (h *PaymentHandler) GetPayWays(c *gin.Context) {
|
||||
if h.App.Config.HuPiPayConfig.Enabled {
|
||||
data["hupi"] = gin.H{"name": h.App.Config.HuPiPayConfig.Name}
|
||||
}
|
||||
if h.App.Config.JPayConfig.Enabled {
|
||||
data["payjs"] = gin.H{"name": h.App.Config.JPayConfig.Name}
|
||||
}
|
||||
resp.SUCCESS(c, data)
|
||||
}
|
||||
|
||||
@@ -394,3 +400,60 @@ func (h *PaymentHandler) HuPiPayNotify(c *gin.Context) {
|
||||
|
||||
c.String(http.StatusOK, "success")
|
||||
}
|
||||
|
||||
// AlipayNotify 支付宝支付回调
|
||||
func (h *PaymentHandler) AlipayNotify(c *gin.Context) {
|
||||
err := c.Request.ParseForm()
|
||||
if err != nil {
|
||||
c.String(http.StatusOK, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
// TODO:这里最好用支付宝的公钥签名签证一下交易真假
|
||||
//res := h.alipayService.TradeVerify(c.Request.Form)
|
||||
r := h.alipayService.TradeQuery(c.Request.Form.Get("out_trade_no"))
|
||||
logger.Infof("验证支付结果:%+v", r)
|
||||
if !r.Success() {
|
||||
c.String(http.StatusOK, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
err = h.notify(r.OutTradeNo)
|
||||
if err != nil {
|
||||
c.String(http.StatusOK, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, "success")
|
||||
}
|
||||
|
||||
// PayJsNotify PayJs 支付异步回调
|
||||
func (h *PaymentHandler) PayJsNotify(c *gin.Context) {
|
||||
err := c.Request.ParseForm()
|
||||
if err != nil {
|
||||
c.String(http.StatusOK, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
orderNo := c.Request.Form.Get("out_trade_no")
|
||||
returnCode := c.Request.Form.Get("return_code")
|
||||
logger.Infof("收到订单支付回调,订单 NO:%s,支付结果代码:%v", orderNo, returnCode)
|
||||
// 支付失败
|
||||
if returnCode != "1" {
|
||||
return
|
||||
}
|
||||
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
err = h.notify(orderNo)
|
||||
if err != nil {
|
||||
c.String(http.StatusOK, "fail")
|
||||
return
|
||||
}
|
||||
|
||||
c.String(http.StatusOK, "success")
|
||||
}
|
||||
|
||||
@@ -3,12 +3,10 @@ package handler
|
||||
import (
|
||||
"chatplus/core"
|
||||
"chatplus/core/types"
|
||||
"chatplus/store/model"
|
||||
"chatplus/utils"
|
||||
"chatplus/utils/resp"
|
||||
"fmt"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -27,27 +25,6 @@ func NewPromptHandler(app *core.AppServer, db *gorm.DB) *PromptHandler {
|
||||
return h
|
||||
}
|
||||
|
||||
type apiRes struct {
|
||||
Model string `json:"model"`
|
||||
Choices []struct {
|
||||
Index int `json:"index"`
|
||||
Message struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
} `json:"message"`
|
||||
FinishReason string `json:"finish_reason"`
|
||||
} `json:"choices"`
|
||||
}
|
||||
|
||||
type apiErrRes struct {
|
||||
Error struct {
|
||||
Code interface{} `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Param interface{} `json:"param"`
|
||||
Type string `json:"type"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
// Rewrite translate and rewrite prompt with ChatGPT
|
||||
func (h *PromptHandler) Rewrite(c *gin.Context) {
|
||||
var data struct {
|
||||
@@ -58,7 +35,7 @@ func (h *PromptHandler) Rewrite(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := h.request(data.Prompt, rewritePromptTemplate)
|
||||
content, err := utils.OpenAIRequest(h.db, fmt.Sprintf(rewritePromptTemplate, data.Prompt), h.App.Config.ProxyURL)
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
@@ -76,7 +53,7 @@ func (h *PromptHandler) Translate(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
content, err := h.request(data.Prompt, translatePromptTemplate)
|
||||
content, err := utils.OpenAIRequest(h.db, fmt.Sprintf(translatePromptTemplate, data.Prompt), h.App.Config.ProxyURL)
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
@@ -84,37 +61,3 @@ func (h *PromptHandler) Translate(c *gin.Context) {
|
||||
|
||||
resp.SUCCESS(c, content)
|
||||
}
|
||||
|
||||
func (h *PromptHandler) request(prompt string, promptTemplate string) (string, error) {
|
||||
// 获取 OpenAI 的 API KEY
|
||||
var apiKey model.ApiKey
|
||||
res := h.db.Where("platform = ?", types.OpenAI).First(&apiKey)
|
||||
if res.Error != nil {
|
||||
return "", fmt.Errorf("error with fetch OpenAI API KEY:%v", res.Error)
|
||||
}
|
||||
|
||||
messages := make([]interface{}, 1)
|
||||
messages[0] = types.Message{
|
||||
Role: "user",
|
||||
Content: fmt.Sprintf(promptTemplate, prompt),
|
||||
}
|
||||
|
||||
var response apiRes
|
||||
var errRes apiErrRes
|
||||
r, err := req.C().SetProxyURL(h.App.Config.ProxyURL).R().SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+apiKey.Value).
|
||||
SetBody(types.ApiRequest{
|
||||
Model: "gpt-3.5-turbo",
|
||||
Temperature: 0.9,
|
||||
MaxTokens: 1024,
|
||||
Stream: false,
|
||||
Messages: messages,
|
||||
}).
|
||||
SetErrorResult(&errRes).
|
||||
SetSuccessResult(&response).Post(h.App.ChatConfig.OpenAI.ApiURL)
|
||||
if err != nil || r.IsErrorState() {
|
||||
return "", fmt.Errorf("error with http request: %v%v%s", err, r.Err, errRes.Error.Message)
|
||||
}
|
||||
|
||||
return response.Choices[0].Message.Content, nil
|
||||
}
|
||||
|
||||
@@ -4,20 +4,24 @@ import (
|
||||
"chatplus/core"
|
||||
"chatplus/core/types"
|
||||
"chatplus/store/model"
|
||||
"chatplus/store/vo"
|
||||
"chatplus/utils"
|
||||
"chatplus/utils/resp"
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type RewardHandler struct {
|
||||
BaseHandler
|
||||
db *gorm.DB
|
||||
db *gorm.DB
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
func NewRewardHandler(server *core.AppServer, db *gorm.DB) *RewardHandler {
|
||||
h := RewardHandler{db: db}
|
||||
h := RewardHandler{db: db, lock: sync.Mutex{}}
|
||||
h.App = server
|
||||
return &h
|
||||
}
|
||||
@@ -26,15 +30,25 @@ func NewRewardHandler(server *core.AppServer, db *gorm.DB) *RewardHandler {
|
||||
func (h *RewardHandler) Verify(c *gin.Context) {
|
||||
var data struct {
|
||||
TxId string `json:"tx_id"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := utils.GetLoginUser(c, h.db)
|
||||
if err != nil {
|
||||
resp.HACKER(c)
|
||||
return
|
||||
}
|
||||
|
||||
// 移除转账单号中间的空格,防止有人复制的时候多复制了空格
|
||||
data.TxId = strings.ReplaceAll(data.TxId, " ", "")
|
||||
|
||||
h.lock.Lock()
|
||||
defer h.lock.Unlock()
|
||||
|
||||
var item model.Reward
|
||||
res := h.db.Where("tx_id = ?", data.TxId).First(&item)
|
||||
if res.Error != nil {
|
||||
@@ -47,15 +61,17 @@ func (h *RewardHandler) Verify(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
user, err := utils.GetLoginUser(c, h.db)
|
||||
if err != nil {
|
||||
resp.HACKER(c)
|
||||
return
|
||||
}
|
||||
|
||||
tx := h.db.Begin()
|
||||
calls := (item.Amount + 0.1) * 10
|
||||
res = h.db.Model(&user).UpdateColumn("calls", gorm.Expr("calls + ?", calls))
|
||||
exchange := vo.RewardExchange{}
|
||||
if data.Type == "chat" {
|
||||
calls := math.Ceil(item.Amount / h.App.SysConfig.ChatCallPrice)
|
||||
exchange.Calls = int(calls)
|
||||
res = h.db.Model(&user).UpdateColumn("calls", gorm.Expr("calls + ?", calls))
|
||||
} else if data.Type == "img" {
|
||||
calls := math.Ceil(item.Amount / h.App.SysConfig.ImgCallPrice)
|
||||
exchange.ImgCalls = int(calls)
|
||||
res = h.db.Model(&user).UpdateColumn("img_calls", gorm.Expr("img_calls + ?", calls))
|
||||
}
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "更新数据库失败!")
|
||||
return
|
||||
@@ -64,6 +80,7 @@ func (h *RewardHandler) Verify(c *gin.Context) {
|
||||
// 更新核销状态
|
||||
item.Status = true
|
||||
item.UserId = user.Id
|
||||
item.Exchange = utils.JsonEncode(exchange)
|
||||
res = h.db.Updates(&item)
|
||||
if res.Error != nil {
|
||||
tx.Rollback()
|
||||
|
||||
@@ -3,6 +3,7 @@ package handler
|
||||
import (
|
||||
"chatplus/core"
|
||||
"chatplus/core/types"
|
||||
"chatplus/service/oss"
|
||||
"chatplus/service/sd"
|
||||
"chatplus/store/model"
|
||||
"chatplus/store/vo"
|
||||
@@ -10,23 +11,26 @@ import (
|
||||
"chatplus/utils/resp"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"gorm.io/gorm"
|
||||
"time"
|
||||
)
|
||||
|
||||
type SdJobHandler struct {
|
||||
BaseHandler
|
||||
redis *redis.Client
|
||||
db *gorm.DB
|
||||
pool *sd.ServicePool
|
||||
redis *redis.Client
|
||||
db *gorm.DB
|
||||
pool *sd.ServicePool
|
||||
uploader *oss.UploaderManager
|
||||
}
|
||||
|
||||
func NewSdJobHandler(app *core.AppServer, db *gorm.DB, pool *sd.ServicePool) *SdJobHandler {
|
||||
func NewSdJobHandler(app *core.AppServer, db *gorm.DB, pool *sd.ServicePool, manager *oss.UploaderManager) *SdJobHandler {
|
||||
h := SdJobHandler{
|
||||
db: db,
|
||||
pool: pool,
|
||||
db: db,
|
||||
pool: pool,
|
||||
uploader: manager,
|
||||
}
|
||||
h.App = app
|
||||
return &h
|
||||
@@ -129,6 +133,9 @@ func (h *SdJobHandler) Image(c *gin.Context) {
|
||||
UserId: userId,
|
||||
})
|
||||
|
||||
// update user's img calls
|
||||
h.db.Model(&model.User{}).Where("id = ?", job.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
|
||||
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
@@ -169,13 +176,15 @@ func (h *SdJobHandler) JobList(c *gin.Context) {
|
||||
}
|
||||
|
||||
if job.Progress == -1 {
|
||||
h.db.Delete(&model.MidJourneyJob{Id: job.Id})
|
||||
h.db.Delete(&model.SdJob{Id: job.Id})
|
||||
}
|
||||
|
||||
if item.Progress < 100 {
|
||||
// 10 分钟还没完成的任务直接删除
|
||||
if time.Now().Sub(item.CreatedAt) > time.Minute*10 {
|
||||
// 5 分钟还没完成的任务直接删除
|
||||
if time.Now().Sub(item.CreatedAt) > time.Minute*5 {
|
||||
h.db.Delete(&item)
|
||||
// 退回绘图次数
|
||||
h.db.Model(&model.User{}).Where("id = ?", item.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls + ?", 1))
|
||||
continue
|
||||
}
|
||||
// 正在运行中任务使用代理访问图片
|
||||
@@ -188,3 +197,30 @@ func (h *SdJobHandler) JobList(c *gin.Context) {
|
||||
}
|
||||
resp.SUCCESS(c, jobs)
|
||||
}
|
||||
|
||||
// Remove remove task image
|
||||
func (h *SdJobHandler) Remove(c *gin.Context) {
|
||||
var data struct {
|
||||
Id uint `json:"id"`
|
||||
ImgURL string `json:"img_url"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
return
|
||||
}
|
||||
|
||||
// remove job recode
|
||||
res := h.db.Delete(&model.SdJob{Id: data.Id})
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, res.Error.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// remove image
|
||||
err := h.uploader.GetUploadHandler().Delete(data.ImgURL)
|
||||
if err != nil {
|
||||
logger.Error("remove image failed: ", err)
|
||||
}
|
||||
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"chatplus/service"
|
||||
"chatplus/utils"
|
||||
"chatplus/utils/resp"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-redis/redis/v8"
|
||||
)
|
||||
@@ -16,21 +18,27 @@ type SmsHandler struct {
|
||||
BaseHandler
|
||||
redis *redis.Client
|
||||
sms *service.AliYunSmsService
|
||||
smtp *service.SmtpService
|
||||
captcha *service.CaptchaService
|
||||
}
|
||||
|
||||
func NewSmsHandler(app *core.AppServer, client *redis.Client, sms *service.AliYunSmsService, captcha *service.CaptchaService) *SmsHandler {
|
||||
handler := &SmsHandler{redis: client, sms: sms, captcha: captcha}
|
||||
func NewSmsHandler(
|
||||
app *core.AppServer,
|
||||
client *redis.Client,
|
||||
sms *service.AliYunSmsService,
|
||||
smtp *service.SmtpService,
|
||||
captcha *service.CaptchaService) *SmsHandler {
|
||||
handler := &SmsHandler{redis: client, sms: sms, captcha: captcha, smtp: smtp}
|
||||
handler.App = app
|
||||
return handler
|
||||
}
|
||||
|
||||
// SendCode 发送验证码短信
|
||||
// SendCode 发送验证码
|
||||
func (h *SmsHandler) SendCode(c *gin.Context) {
|
||||
var data struct {
|
||||
Mobile string `json:"mobile"`
|
||||
Key string `json:"key"`
|
||||
Dots string `json:"dots"`
|
||||
Receiver string `json:"receiver"` // 接收者
|
||||
Key string `json:"key"`
|
||||
Dots string `json:"dots"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
@@ -43,14 +51,27 @@ func (h *SmsHandler) SendCode(c *gin.Context) {
|
||||
}
|
||||
|
||||
code := utils.RandomNumber(6)
|
||||
err := h.sms.SendVerifyCode(data.Mobile, code)
|
||||
var err error
|
||||
if strings.Contains(data.Receiver, "@") { // email
|
||||
if !utils.ContainsStr(h.App.SysConfig.RegisterWays, "email") {
|
||||
resp.ERROR(c, "系统已禁用邮箱注册!")
|
||||
return
|
||||
}
|
||||
err = h.smtp.SendVerifyCode(data.Receiver, code)
|
||||
} else {
|
||||
if !utils.ContainsStr(h.App.SysConfig.RegisterWays, "mobile") {
|
||||
resp.ERROR(c, "系统已禁用手机号注册!")
|
||||
return
|
||||
}
|
||||
err = h.sms.SendVerifyCode(data.Receiver, code)
|
||||
}
|
||||
if err != nil {
|
||||
resp.ERROR(c, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// 存储验证码,等待后面注册验证
|
||||
_, err = h.redis.Set(c, CodeStorePrefix+data.Mobile, code, 0).Result()
|
||||
_, err = h.redis.Set(c, CodeStorePrefix+data.Receiver, code, 0).Result()
|
||||
if err != nil {
|
||||
resp.ERROR(c, "验证码保存失败")
|
||||
return
|
||||
|
||||
@@ -2,39 +2,74 @@ package handler
|
||||
|
||||
import (
|
||||
"chatplus/service"
|
||||
"chatplus/service/payment"
|
||||
"chatplus/store/model"
|
||||
"chatplus/utils"
|
||||
"chatplus/utils/resp"
|
||||
"fmt"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type TestHandler struct {
|
||||
db *gorm.DB
|
||||
snowflake *service.Snowflake
|
||||
js *payment.PayJS
|
||||
}
|
||||
|
||||
func NewTestHandler(snowflake *service.Snowflake) *TestHandler {
|
||||
return &TestHandler{snowflake: snowflake}
|
||||
func NewTestHandler(db *gorm.DB, snowflake *service.Snowflake, js *payment.PayJS) *TestHandler {
|
||||
return &TestHandler{db: db, snowflake: snowflake, js: js}
|
||||
}
|
||||
|
||||
func (h *TestHandler) TestPay(c *gin.Context) {
|
||||
//appId := "" //Appid
|
||||
//appSecret := "" //密钥
|
||||
//var host = "https://api.xunhupay.com/payment/do.html" //跳转支付页接口URL
|
||||
//client := payment.NewXunHuPay(appId, appSecret) //初始化调用
|
||||
//
|
||||
////支付参数,appid、time、nonce_str和hash这四个参数不用传,调用的时候执行方法内部已经处理
|
||||
//orderNo, _ := h.snowflake.Next()
|
||||
//params := map[string]string{
|
||||
// "version": "1.1",
|
||||
// "trade_order_id": orderNo,
|
||||
// "total_fee": "0.1",
|
||||
// "title": "测试支付",
|
||||
// "notify_url": "http://xxxxxxx.com",
|
||||
// "return_url": "http://localhost:8888",
|
||||
// "wap_name": "极客学长",
|
||||
// "callback_url": "",
|
||||
//}
|
||||
//
|
||||
//execute, err := client.Execute(host, params) //执行支付操作
|
||||
//if err != nil {
|
||||
// logger.Error(err)
|
||||
//}
|
||||
//resp.SUCCESS(c, execute)
|
||||
func (h *TestHandler) Test(c *gin.Context) {
|
||||
//h.initUserNickname(c)
|
||||
//h.initMjTaskId(c)
|
||||
|
||||
orderId, _ := h.snowflake.Next(false)
|
||||
params := payment.JPayReq{
|
||||
TotalFee: 12345,
|
||||
OutTradeNo: orderId,
|
||||
Subject: "支付测试",
|
||||
}
|
||||
r := h.js.Pay(params)
|
||||
if !r.IsOK() {
|
||||
resp.ERROR(c, r.ReturnMsg)
|
||||
return
|
||||
}
|
||||
resp.SUCCESS(c, r)
|
||||
|
||||
}
|
||||
|
||||
func (h *TestHandler) initUserNickname(c *gin.Context) {
|
||||
var users []model.User
|
||||
tx := h.db.Find(&users)
|
||||
if tx.Error != nil {
|
||||
resp.ERROR(c, tx.Error.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, u := range users {
|
||||
u.Nickname = fmt.Sprintf("极客学长@%d", utils.RandomNumber(6))
|
||||
h.db.Updates(&u)
|
||||
}
|
||||
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
func (h *TestHandler) initMjTaskId(c *gin.Context) {
|
||||
var jobs []model.MidJourneyJob
|
||||
tx := h.db.Find(&jobs)
|
||||
if tx.Error != nil {
|
||||
resp.ERROR(c, tx.Error.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, job := range jobs {
|
||||
id, _ := h.snowflake.Next(true)
|
||||
job.TaskId = id
|
||||
h.db.Updates(&job)
|
||||
}
|
||||
|
||||
resp.SUCCESS(c)
|
||||
}
|
||||
|
||||
@@ -8,11 +8,12 @@ import (
|
||||
"chatplus/utils"
|
||||
"chatplus/utils/resp"
|
||||
"fmt"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
|
||||
"gorm.io/gorm"
|
||||
@@ -39,7 +40,7 @@ func NewUserHandler(
|
||||
func (h *UserHandler) Register(c *gin.Context) {
|
||||
// parameters process
|
||||
var data struct {
|
||||
Mobile string `json:"mobile"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
Code string `json:"code"`
|
||||
InviteCode string `json:"invite_code"`
|
||||
@@ -49,34 +50,26 @@ func (h *UserHandler) Register(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
data.Password = strings.TrimSpace(data.Password)
|
||||
|
||||
if len(data.Mobile) < 10 {
|
||||
resp.ERROR(c, "请输入合法的手机号")
|
||||
return
|
||||
}
|
||||
if len(data.Password) < 8 {
|
||||
resp.ERROR(c, "密码长度不能少于8个字符")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查验证码
|
||||
key := CodeStorePrefix + data.Mobile
|
||||
if h.App.SysConfig.EnabledMsg {
|
||||
var key string
|
||||
if utils.ContainsStr(h.App.SysConfig.RegisterWays, "email") ||
|
||||
utils.ContainsStr(h.App.SysConfig.RegisterWays, "mobile") {
|
||||
key = CodeStorePrefix + data.Username
|
||||
code, err := h.redis.Get(c, key).Result()
|
||||
if err != nil || code != data.Code {
|
||||
resp.ERROR(c, "短信验证码错误")
|
||||
resp.ERROR(c, "验证码错误")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 验证邀请码
|
||||
inviteCode := model.InviteCode{}
|
||||
if data.InviteCode == "" {
|
||||
if h.App.SysConfig.ForceInvite {
|
||||
resp.ERROR(c, "当前系统设定必须使用邀请码才能注册")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if data.InviteCode != "" {
|
||||
res := h.db.Where("code = ?", data.InviteCode).First(&inviteCode)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "无效的邀请码")
|
||||
@@ -86,19 +79,20 @@ func (h *UserHandler) Register(c *gin.Context) {
|
||||
|
||||
// check if the username is exists
|
||||
var item model.User
|
||||
res := h.db.Where("mobile = ?", data.Mobile).First(&item)
|
||||
res := h.db.Where("username = ?", data.Username).First(&item)
|
||||
if res.RowsAffected > 0 {
|
||||
resp.ERROR(c, "该手机号码已经被注册,请更换其他手机号")
|
||||
resp.ERROR(c, "该用户名已经被注册")
|
||||
return
|
||||
}
|
||||
|
||||
salt := utils.RandString(8)
|
||||
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,
|
||||
Mobile: data.Mobile,
|
||||
ChatRoles: utils.JsonEncode([]string{"gpt"}), // 默认只订阅通用助手角色
|
||||
ChatModels: utils.JsonEncode(h.App.SysConfig.DefaultModels), // 默认开通的模型
|
||||
ChatConfig: utils.JsonEncode(types.UserChatConfig{
|
||||
@@ -111,6 +105,7 @@ func (h *UserHandler) Register(c *gin.Context) {
|
||||
Calls: h.App.SysConfig.InitChatCalls,
|
||||
ImgCalls: h.App.SysConfig.InitImgCalls,
|
||||
}
|
||||
|
||||
res = h.db.Create(&user)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "保存数据失败")
|
||||
@@ -133,14 +128,13 @@ func (h *UserHandler) Register(c *gin.Context) {
|
||||
h.db.Create(&model.InviteLog{
|
||||
InviterId: inviteCode.UserId,
|
||||
UserId: user.Id,
|
||||
Username: user.Mobile,
|
||||
Username: user.Username,
|
||||
InviteCode: inviteCode.Code,
|
||||
Reward: utils.JsonEncode(types.InviteReward{ChatCalls: h.App.SysConfig.InviteChatCalls, ImgCalls: h.App.SysConfig.InviteImgCalls}),
|
||||
})
|
||||
}
|
||||
if h.App.SysConfig.EnabledMsg {
|
||||
_ = h.redis.Del(c, key) // 注册成功,删除短信验证码
|
||||
}
|
||||
|
||||
_ = h.redis.Del(c, key) // 注册成功,删除短信验证码
|
||||
|
||||
// 自动登录创建 token
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||
@@ -164,7 +158,7 @@ func (h *UserHandler) Register(c *gin.Context) {
|
||||
// Login 用户登录
|
||||
func (h *UserHandler) Login(c *gin.Context) {
|
||||
var data struct {
|
||||
Mobile string `json:"username"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
@@ -172,7 +166,7 @@ func (h *UserHandler) Login(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
var user model.User
|
||||
res := h.db.Where("mobile = ?", data.Mobile).First(&user)
|
||||
res := h.db.Where("username = ?", data.Username).First(&user)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "用户名不存在")
|
||||
return
|
||||
@@ -196,7 +190,7 @@ func (h *UserHandler) Login(c *gin.Context) {
|
||||
|
||||
h.db.Create(&model.UserLoginLog{
|
||||
UserId: user.Id,
|
||||
Username: user.Mobile,
|
||||
Username: user.Username,
|
||||
LoginIp: c.ClientIP(),
|
||||
LoginAddress: utils.Ip2Region(h.searcher, c.ClientIP()),
|
||||
})
|
||||
@@ -256,7 +250,8 @@ func (h *UserHandler) Session(c *gin.Context) {
|
||||
|
||||
type userProfile struct {
|
||||
Id uint `json:"id"`
|
||||
Mobile string `json:"mobile"`
|
||||
Nickname string `json:"nickname"`
|
||||
Username string `json:"username"`
|
||||
Avatar string `json:"avatar"`
|
||||
ChatConfig types.UserChatConfig `json:"chat_config"`
|
||||
Calls int `json:"calls"`
|
||||
@@ -301,7 +296,7 @@ func (h *UserHandler) ProfileUpdate(c *gin.Context) {
|
||||
}
|
||||
h.db.First(&user, user.Id)
|
||||
user.Avatar = data.Avatar
|
||||
user.ChatConfig = utils.JsonEncode(data.ChatConfig)
|
||||
user.Nickname = data.Nickname
|
||||
res := h.db.Updates(&user)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "更新用户信息失败")
|
||||
@@ -354,9 +349,9 @@ func (h *UserHandler) UpdatePass(c *gin.Context) {
|
||||
// ResetPass 重置密码
|
||||
func (h *UserHandler) ResetPass(c *gin.Context) {
|
||||
var data struct {
|
||||
Mobile string
|
||||
Code string // 验证码
|
||||
Password string // 新密码
|
||||
Username string `json:"username"`
|
||||
Code string `json:"code"` // 验证码
|
||||
Password string `json:"password"` // 新密码
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
@@ -364,20 +359,18 @@ func (h *UserHandler) ResetPass(c *gin.Context) {
|
||||
}
|
||||
|
||||
var user model.User
|
||||
res := h.db.Where("mobile", data.Mobile).First(&user)
|
||||
res := h.db.Where("username", data.Username).First(&user)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "用户不存在!")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查验证码
|
||||
key := CodeStorePrefix + data.Mobile
|
||||
if h.App.SysConfig.EnabledMsg {
|
||||
code, err := h.redis.Get(c, key).Result()
|
||||
if err != nil || code != data.Code {
|
||||
resp.ERROR(c, "短信验证码错误")
|
||||
return
|
||||
}
|
||||
key := CodeStorePrefix + data.Username
|
||||
code, err := h.redis.Get(c, key).Result()
|
||||
if err != nil || code != data.Code {
|
||||
resp.ERROR(c, "短信验证码错误")
|
||||
return
|
||||
}
|
||||
|
||||
password := utils.GenPassword(data.Password, user.Salt)
|
||||
@@ -391,11 +384,11 @@ func (h *UserHandler) ResetPass(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// BindMobile 绑定手机号
|
||||
func (h *UserHandler) BindMobile(c *gin.Context) {
|
||||
// BindUsername 重置账号
|
||||
func (h *UserHandler) BindUsername(c *gin.Context) {
|
||||
var data struct {
|
||||
Mobile string `json:"mobile"`
|
||||
Code string `json:"code"`
|
||||
Username string `json:"username"`
|
||||
Code string `json:"code"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&data); err != nil {
|
||||
resp.ERROR(c, types.InvalidArgs)
|
||||
@@ -403,18 +396,18 @@ func (h *UserHandler) BindMobile(c *gin.Context) {
|
||||
}
|
||||
|
||||
// 检查验证码
|
||||
key := CodeStorePrefix + data.Mobile
|
||||
key := CodeStorePrefix + data.Username
|
||||
code, err := h.redis.Get(c, key).Result()
|
||||
if err != nil || code != data.Code {
|
||||
resp.ERROR(c, "短信验证码错误")
|
||||
resp.ERROR(c, "验证码错误")
|
||||
return
|
||||
}
|
||||
|
||||
// 检查手机号是否被其他账号绑定
|
||||
var item model.User
|
||||
res := h.db.Where("mobile = ?", data.Mobile).First(&item)
|
||||
res := h.db.Where("username = ?", data.Username).First(&item)
|
||||
if res.Error == nil {
|
||||
resp.ERROR(c, "该手机号已经被其他账号绑定")
|
||||
resp.ERROR(c, "该账号已经被其他账号绑定")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -424,7 +417,7 @@ func (h *UserHandler) BindMobile(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
res = h.db.Model(&user).UpdateColumn("mobile", data.Mobile)
|
||||
res = h.db.Model(&user).UpdateColumn("username", data.Username)
|
||||
if res.Error != nil {
|
||||
resp.ERROR(c, "更新数据库失败")
|
||||
return
|
||||
|
||||
52
api/main.go
52
api/main.go
@@ -8,7 +8,6 @@ import (
|
||||
"chatplus/handler/chatimpl"
|
||||
logger2 "chatplus/logger"
|
||||
"chatplus/service"
|
||||
"chatplus/service/fun"
|
||||
"chatplus/service/mj"
|
||||
"chatplus/service/oss"
|
||||
"chatplus/service/payment"
|
||||
@@ -58,13 +57,7 @@ func main() {
|
||||
if configFile == "" {
|
||||
configFile = "config.toml"
|
||||
}
|
||||
var debug bool
|
||||
debugEnv := os.Getenv("DEBUG")
|
||||
if debugEnv == "" {
|
||||
debug = true
|
||||
} else {
|
||||
debug, _ = strconv.ParseBool(os.Getenv("DEBUG"))
|
||||
}
|
||||
debug, _ := strconv.ParseBool(os.Getenv("APP_DEBUG"))
|
||||
logger.Info("Loading config file: ", configFile)
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
@@ -115,9 +108,6 @@ func main() {
|
||||
return xdb.NewWithBuffer(cBuff)
|
||||
}),
|
||||
|
||||
// 创建函数
|
||||
fx.Provide(fun.NewFunctions),
|
||||
|
||||
// 创建控制器
|
||||
fx.Provide(handler.NewChatRoleHandler),
|
||||
fx.Provide(handler.NewUserHandler),
|
||||
@@ -152,6 +142,9 @@ func main() {
|
||||
fx.Provide(oss.NewUploaderManager),
|
||||
fx.Provide(mj.NewService),
|
||||
|
||||
// 邮件服务
|
||||
fx.Provide(service.NewSmtpService),
|
||||
|
||||
// 微信机器人服务
|
||||
fx.Provide(wx.NewWeChatBot),
|
||||
fx.Invoke(func(config *types.AppConfig, bot *wx.Bot) {
|
||||
@@ -165,12 +158,19 @@ func main() {
|
||||
|
||||
// MidJourney service pool
|
||||
fx.Provide(mj.NewServicePool),
|
||||
fx.Invoke(func(pool *mj.ServicePool) {
|
||||
if pool.HasAvailableService() {
|
||||
pool.DownloadImages()
|
||||
pool.CheckTaskNotify()
|
||||
}
|
||||
}),
|
||||
|
||||
// Stable Diffusion 机器人
|
||||
fx.Provide(sd.NewServicePool),
|
||||
|
||||
fx.Provide(payment.NewAlipayService),
|
||||
fx.Provide(payment.NewHuPiPay),
|
||||
fx.Provide(payment.NewPayJS),
|
||||
fx.Provide(service.NewSnowflake),
|
||||
fx.Provide(service.NewXXLJobExecutor),
|
||||
fx.Invoke(func(exec *service.XXLJobExecutor, config *types.AppConfig) {
|
||||
@@ -196,7 +196,7 @@ func main() {
|
||||
group.GET("profile", h.Profile)
|
||||
group.POST("profile/update", h.ProfileUpdate)
|
||||
group.POST("password", h.UpdatePass)
|
||||
group.POST("bind/mobile", h.BindMobile)
|
||||
group.POST("bind/username", h.BindUsername)
|
||||
group.POST("resetPass", h.ResetPass)
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *chatimpl.ChatHandler) {
|
||||
@@ -229,15 +229,18 @@ func main() {
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.MidJourneyHandler) {
|
||||
group := s.Engine.Group("/api/mj/")
|
||||
group.Any("client", h.Client)
|
||||
group.POST("image", h.Image)
|
||||
group.POST("upscale", h.Upscale)
|
||||
group.POST("variation", h.Variation)
|
||||
group.GET("jobs", h.JobList)
|
||||
group.POST("remove", h.Remove)
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.SdJobHandler) {
|
||||
group := s.Engine.Group("/api/sd")
|
||||
group.POST("image", h.Image)
|
||||
group.GET("jobs", h.JobList)
|
||||
group.POST("remove", h.Remove)
|
||||
}),
|
||||
|
||||
// 管理后台控制器
|
||||
@@ -256,6 +259,7 @@ func main() {
|
||||
group := s.Engine.Group("/api/admin/apikey/")
|
||||
group.POST("save", h.Save)
|
||||
group.GET("list", h.List)
|
||||
group.POST("set", h.Set)
|
||||
group.GET("remove", h.Remove)
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *admin.UserHandler) {
|
||||
@@ -271,11 +275,13 @@ func main() {
|
||||
group.GET("list", h.List)
|
||||
group.POST("save", h.Save)
|
||||
group.POST("sort", h.Sort)
|
||||
group.POST("set", h.Set)
|
||||
group.GET("remove", h.Remove)
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *admin.RewardHandler) {
|
||||
group := s.Engine.Group("/api/admin/reward/")
|
||||
group.GET("list", h.List)
|
||||
group.GET("remove", h.Remove)
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *admin.DashboardHandler) {
|
||||
group := s.Engine.Group("/api/admin/dashboard/")
|
||||
@@ -301,6 +307,7 @@ func main() {
|
||||
group.POST("qrcode", h.PayQrcode)
|
||||
group.POST("alipay/notify", h.AlipayNotify)
|
||||
group.POST("hupipay/notify", h.HuPiPayNotify)
|
||||
group.POST("payjs/notify", h.PayJsNotify)
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, h *admin.ProductHandler) {
|
||||
group := s.Engine.Group("/api/admin/product/")
|
||||
@@ -339,10 +346,27 @@ func main() {
|
||||
group.POST("translate", h.Translate)
|
||||
}),
|
||||
|
||||
fx.Provide(admin.NewFunctionHandler),
|
||||
fx.Invoke(func(s *core.AppServer, h *admin.FunctionHandler) {
|
||||
group := s.Engine.Group("/api/admin/function/")
|
||||
group.POST("save", h.Save)
|
||||
group.POST("set", h.Set)
|
||||
group.GET("list", h.List)
|
||||
group.GET("remove", h.Remove)
|
||||
group.GET("token", h.GenToken)
|
||||
}),
|
||||
|
||||
fx.Provide(handler.NewFunctionHandler),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.FunctionHandler) {
|
||||
group := s.Engine.Group("/api/function/")
|
||||
group.POST("weibo", h.WeiBo)
|
||||
group.POST("zaobao", h.ZaoBao)
|
||||
group.POST("dalle3", h.Dall3)
|
||||
}),
|
||||
|
||||
fx.Provide(handler.NewTestHandler),
|
||||
fx.Invoke(func(s *core.AppServer, h *handler.TestHandler) {
|
||||
group := s.Engine.Group("/test/")
|
||||
group.GET("pay", h.TestPay)
|
||||
s.Engine.GET("/api/test", h.Test)
|
||||
}),
|
||||
fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
|
||||
err := s.Run(db)
|
||||
|
||||
38
api/res/certs/alipay/alipayPublicCert.crt
Normal file
38
api/res/certs/alipay/alipayPublicCert.crt
Normal file
@@ -0,0 +1,38 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDszCCApugAwIBAgIQICMRB0rBU2/rZJbfJGMYIzANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
|
||||
BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv
|
||||
biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1
|
||||
dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjMxMTA3MDYzNTQxWhcNMjQxMTA2MDYzNTQxWjCB
|
||||
hDELMAkGA1UEBhMCQ04xHzAdBgNVBAoMFm1ib25meTkwMTVAc2FuZGJveC5jb20xDzANBgNVBAsM
|
||||
BkFsaXBheTFDMEEGA1UEAww65pSv5LuY5a6dKOS4reWbvSnnvZHnu5zmioDmnK/mnInpmZDlhazl
|
||||
j7gtMjA4ODcyMTAyMDc1MDU4MTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKsoKcw5
|
||||
sxaiyV7mpWzDtnQ1K518eQLP0+dJlZAf06aBep/Aj9DIqrba/k7DHt8dKQvILMLAMpN1+2IRxbaO
|
||||
yxMa/laj3lZ1eHrB6F077O3D62oHcE3noZtXL0N1zZAxpmkNmYIHeLZS2oLMS4ANu47O/wpDC7BV
|
||||
HjdpZugtdPJ4mxdCpM9GDdLs7W4s5QI4PUPK4skFNMFoKI+0cYP/9ju87UP//IHC/K510GWNl+Gn
|
||||
Cvgag3AmiIB0utJNsGhxm6zT1T9tUWjW9iz/BxBKiPatsCX9VpPQzGnW7ZonRQtiZSokIlP2IPvl
|
||||
H5DcwpWUz3/LUY0SmKxnKOEYeOOqCW8CAwEAAaMSMBAwDgYDVR0PAQH/BAQDAgTwMA0GCSqGSIb3
|
||||
DQEBCwUAA4IBAQAtgxF2EzjOndEFxBUD9tFwcSt6XKGggOp52oft1pvynPg4ALTLafOtfEPDrFBH
|
||||
PwpYrSu9s9C8NJtaA2HrlCfBjIuwEFTXiN+HPvS0SwSPKt9AXEiTcOF8vDcGamEen8QI4fo5Jia7
|
||||
2VRKkerkww5/+FzSaVO7ZUKuL80M1QJStmAZc8kPPwdYOTTW2bGf8BcmSDL6SPElBkt7tCCRd4sn
|
||||
+jq4cZ0yb2i77rBZCwHcTvfTqIBblPwLv4uGvg3+83BxIB5w6Kqp06bKEAPmobFY5IVHa+ON0/qi
|
||||
BXxXr+WQ3piKRVQEN64+PTAjSc67Ix1umvpLl3Ko6Ry7NJmpDcUn
|
||||
-----END CERTIFICATE-----
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDszCCApugAwIBAgIQIBkIGbgVxq210KxLJ+YA/TANBgkqhkiG9w0BAQsFADCBhDELMAkGA1UE
|
||||
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxJTAjBgNVBAsMHENlcnRpZmljYXRpb24gQXV0
|
||||
aG9yaXR5IHRlc3QxNjA0BgNVBAMMLUFudCBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBBdXRob3Jp
|
||||
dHkgUjEgdGVzdDAeFw0xOTA4MTkxMTE2MDBaFw0yNDA4MDExMTE2MDBaMIGRMQswCQYDVQQGEwJD
|
||||
TjEbMBkGA1UECgwSQW50IEZpbmFuY2lhbCB0ZXN0MSUwIwYDVQQLDBxDZXJ0aWZpY2F0aW9uIEF1
|
||||
dGhvcml0eSB0ZXN0MT4wPAYDVQQDDDVBbnQgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9y
|
||||
aXR5IENsYXNzIDIgUjEgdGVzdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMh4FKYO
|
||||
ZyRQHD6eFbPKZeSAnrfjfU7xmS9Yoozuu+iuqZlb6Z0SPLUqqTZAFZejOcmr07ln/pwZxluqplxC
|
||||
5+B48End4nclDMlT5HPrDr3W0frs6Xsa2ZNcyil/iKNB5MbGll8LRAxntsKvZZj6vUTMb705gYgm
|
||||
VUMILwi/ZxKTQqBtkT/kQQ5y6nOZsj7XI5rYdz6qqOROrpvS/d7iypdHOMIM9Iz9DlL1mrCykbBi
|
||||
t25y+gTeXmuisHUwqaRpwtCGK4BayCqxRGbNipe6W73EK9lBrrzNtTr9NaysesT/v+l25JHCL9tG
|
||||
wpNr1oWFzk4IHVOg0ORiQ6SUgxZUTYcCAwEAAaMSMBAwDgYDVR0PAQH/BAQDAgTwMA0GCSqGSIb3
|
||||
DQEBCwUAA4IBAQBWThEoIaQoBX2YeRY/I8gu6TYnFXtyuCljANnXnM38ft+ikhE5mMNgKmJYLHvT
|
||||
yWWWgwHoSAWEuml7EGbE/2AK2h3k0MdfiWLzdmpPCRG/RJHk6UB1pMHPilI+c0MVu16OPpKbg5Vf
|
||||
LTv7dsAB40AzKsvyYw88/Ezi1osTXo6QQwda7uefvudirtb8FcQM9R66cJxl3kt1FXbpYwheIm/p
|
||||
j1mq64swCoIYu4NrsUYtn6CV542DTQMI5QdXkn+PzUUly8F6kDp+KpMNd0avfWNL5+O++z+F5Szy
|
||||
1CPta1D7EQ/eYmMP+mOQ35oifWIoFCpN6qQVBS/Hob1J/UUyg7BW
|
||||
-----END CERTIFICATE-----
|
||||
88
api/res/certs/alipay/alipayRootCert.crt
Normal file
88
api/res/certs/alipay/alipayRootCert.crt
Normal file
@@ -0,0 +1,88 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIBszCCAVegAwIBAgIIaeL+wBcKxnswDAYIKoEcz1UBg3UFADAuMQswCQYDVQQG
|
||||
EwJDTjEOMAwGA1UECgwFTlJDQUMxDzANBgNVBAMMBlJPT1RDQTAeFw0xMjA3MTQw
|
||||
MzExNTlaFw00MjA3MDcwMzExNTlaMC4xCzAJBgNVBAYTAkNOMQ4wDAYDVQQKDAVO
|
||||
UkNBQzEPMA0GA1UEAwwGUk9PVENBMFkwEwYHKoZIzj0CAQYIKoEcz1UBgi0DQgAE
|
||||
MPCca6pmgcchsTf2UnBeL9rtp4nw+itk1Kzrmbnqo05lUwkwlWK+4OIrtFdAqnRT
|
||||
V7Q9v1htkv42TsIutzd126NdMFswHwYDVR0jBBgwFoAUTDKxl9kzG8SmBcHG5Yti
|
||||
W/CXdlgwDAYDVR0TBAUwAwEB/zALBgNVHQ8EBAMCAQYwHQYDVR0OBBYEFEwysZfZ
|
||||
MxvEpgXBxuWLYlvwl3ZYMAwGCCqBHM9VAYN1BQADSAAwRQIgG1bSLeOXp3oB8H7b
|
||||
53W+CKOPl2PknmWEq/lMhtn25HkCIQDaHDgWxWFtnCrBjH16/W3Ezn7/U/Vjo5xI
|
||||
pDoiVhsLwg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIF0zCCA7ugAwIBAgIIH8+hjWpIDREwDQYJKoZIhvcNAQELBQAwejELMAkGA1UE
|
||||
BhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNVBAsMF0NlcnRpZmlj
|
||||
YXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5jaWFsIENlcnRpZmlj
|
||||
YXRpb24gQXV0aG9yaXR5IFIxMB4XDTE4MDMyMTEzNDg0MFoXDTM4MDIyODEzNDg0
|
||||
MFowejELMAkGA1UEBhMCQ04xFjAUBgNVBAoMDUFudCBGaW5hbmNpYWwxIDAeBgNV
|
||||
BAsMF0NlcnRpZmljYXRpb24gQXV0aG9yaXR5MTEwLwYDVQQDDChBbnQgRmluYW5j
|
||||
aWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5IFIxMIICIjANBgkqhkiG9w0BAQEF
|
||||
AAOCAg8AMIICCgKCAgEAtytTRcBNuur5h8xuxnlKJetT65cHGemGi8oD+beHFPTk
|
||||
rUTlFt9Xn7fAVGo6QSsPb9uGLpUFGEdGmbsQ2q9cV4P89qkH04VzIPwT7AywJdt2
|
||||
xAvMs+MgHFJzOYfL1QkdOOVO7NwKxH8IvlQgFabWomWk2Ei9WfUyxFjVO1LVh0Bp
|
||||
dRBeWLMkdudx0tl3+21t1apnReFNQ5nfX29xeSxIhesaMHDZFViO/DXDNW2BcTs6
|
||||
vSWKyJ4YIIIzStumD8K1xMsoaZBMDxg4itjWFaKRgNuPiIn4kjDY3kC66Sl/6yTl
|
||||
YUz8AybbEsICZzssdZh7jcNb1VRfk79lgAprm/Ktl+mgrU1gaMGP1OE25JCbqli1
|
||||
Pbw/BpPynyP9+XulE+2mxFwTYhKAwpDIDKuYsFUXuo8t261pCovI1CXFzAQM2w7H
|
||||
DtA2nOXSW6q0jGDJ5+WauH+K8ZSvA6x4sFo4u0KNCx0ROTBpLif6GTngqo3sj+98
|
||||
SZiMNLFMQoQkjkdN5Q5g9N6CFZPVZ6QpO0JcIc7S1le/g9z5iBKnifrKxy0TQjtG
|
||||
PsDwc8ubPnRm/F82RReCoyNyx63indpgFfhN7+KxUIQ9cOwwTvemmor0A+ZQamRe
|
||||
9LMuiEfEaWUDK+6O0Gl8lO571uI5onYdN1VIgOmwFbe+D8TcuzVjIZ/zvHrAGUcC
|
||||
AwEAAaNdMFswCwYDVR0PBAQDAgEGMAwGA1UdEwQFMAMBAf8wHQYDVR0OBBYEFF90
|
||||
tATATwda6uWx2yKjh0GynOEBMB8GA1UdIwQYMBaAFF90tATATwda6uWx2yKjh0Gy
|
||||
nOEBMA0GCSqGSIb3DQEBCwUAA4ICAQCVYaOtqOLIpsrEikE5lb+UARNSFJg6tpkf
|
||||
tJ2U8QF/DejemEHx5IClQu6ajxjtu0Aie4/3UnIXop8nH/Q57l+Wyt9T7N2WPiNq
|
||||
JSlYKYbJpPF8LXbuKYG3BTFTdOVFIeRe2NUyYh/xs6bXGr4WKTXb3qBmzR02FSy3
|
||||
IODQw5Q6zpXj8prYqFHYsOvGCEc1CwJaSaYwRhTkFedJUxiyhyB5GQwoFfExCVHW
|
||||
05ZFCAVYFldCJvUzfzrWubN6wX0DD2dwultgmldOn/W/n8at52mpPNvIdbZb2F41
|
||||
T0YZeoWnCJrYXjq/32oc1cmifIHqySnyMnavi75DxPCdZsCOpSAT4j4lAQRGsfgI
|
||||
kkLPGQieMfNNkMCKh7qjwdXAVtdqhf0RVtFILH3OyEodlk1HYXqX5iE5wlaKzDop
|
||||
PKwf2Q3BErq1xChYGGVS+dEvyXc/2nIBlt7uLWKp4XFjqekKbaGaLJdjYP5b2s7N
|
||||
1dM0MXQ/f8XoXKBkJNzEiM3hfsU6DOREgMc1DIsFKxfuMwX3EkVQM1If8ghb6x5Y
|
||||
jXayv+NLbidOSzk4vl5QwngO/JYFMkoc6i9LNwEaEtR9PhnrdubxmrtM+RjfBm02
|
||||
77q3dSWFESFQ4QxYWew4pHE0DpWbWy/iMIKQ6UZ5RLvB8GEcgt8ON7BBJeMc+Dyi
|
||||
kT9qhqn+lw==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIICiDCCAgygAwIBAgIIQX76UsB/30owDAYIKoZIzj0EAwMFADB6MQswCQYDVQQG
|
||||
EwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UECwwXQ2VydGlmaWNh
|
||||
dGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNpYWwgQ2VydGlmaWNh
|
||||
dGlvbiBBdXRob3JpdHkgRTEwHhcNMTkwNDI4MTYyMDQ0WhcNNDkwNDIwMTYyMDQ0
|
||||
WjB6MQswCQYDVQQGEwJDTjEWMBQGA1UECgwNQW50IEZpbmFuY2lhbDEgMB4GA1UE
|
||||
CwwXQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxMTAvBgNVBAMMKEFudCBGaW5hbmNp
|
||||
YWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgRTEwdjAQBgcqhkjOPQIBBgUrgQQA
|
||||
IgNiAASCCRa94QI0vR5Up9Yr9HEupz6hSoyjySYqo7v837KnmjveUIUNiuC9pWAU
|
||||
WP3jwLX3HkzeiNdeg22a0IZPoSUCpasufiLAnfXh6NInLiWBrjLJXDSGaY7vaokt
|
||||
rpZvAdmjXTBbMAsGA1UdDwQEAwIBBjAMBgNVHRMEBTADAQH/MB0GA1UdDgQWBBRZ
|
||||
4ZTgDpksHL2qcpkFkxD2zVd16TAfBgNVHSMEGDAWgBRZ4ZTgDpksHL2qcpkFkxD2
|
||||
zVd16TAMBggqhkjOPQQDAwUAA2gAMGUCMQD4IoqT2hTUn0jt7oXLdMJ8q4vLp6sg
|
||||
wHfPiOr9gxreb+e6Oidwd2LDnC4OUqCWiF8CMAzwKs4SnDJYcMLf2vpkbuVE4dTH
|
||||
Rglz+HGcTLWsFs4KxLsq7MuU+vJTBUeDJeDjdA==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDxTCCAq2gAwIBAgIUEMdk6dVgOEIS2cCP0Q43P90Ps5YwDQYJKoZIhvcNAQEF
|
||||
BQAwajELMAkGA1UEBhMCQ04xEzARBgNVBAoMCmlUcnVzQ2hpbmExHDAaBgNVBAsM
|
||||
E0NoaW5hIFRydXN0IE5ldHdvcmsxKDAmBgNVBAMMH2lUcnVzQ2hpbmEgQ2xhc3Mg
|
||||
MiBSb290IENBIC0gRzMwHhcNMTMwNDE4MDkzNjU2WhcNMzMwNDE4MDkzNjU2WjBq
|
||||
MQswCQYDVQQGEwJDTjETMBEGA1UECgwKaVRydXNDaGluYTEcMBoGA1UECwwTQ2hp
|
||||
bmEgVHJ1c3QgTmV0d29yazEoMCYGA1UEAwwfaVRydXNDaGluYSBDbGFzcyAyIFJv
|
||||
b3QgQ0EgLSBHMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOPPShpV
|
||||
nJbMqqCw6Bz1kehnoPst9pkr0V9idOwU2oyS47/HjJXk9Rd5a9xfwkPO88trUpz5
|
||||
4GmmwspDXjVFu9L0eFaRuH3KMha1Ak01citbF7cQLJlS7XI+tpkTGHEY5pt3EsQg
|
||||
wykfZl/A1jrnSkspMS997r2Gim54cwz+mTMgDRhZsKK/lbOeBPpWtcFizjXYCqhw
|
||||
WktvQfZBYi6o4sHCshnOswi4yV1p+LuFcQ2ciYdWvULh1eZhLxHbGXyznYHi0dGN
|
||||
z+I9H8aXxqAQfHVhbdHNzi77hCxFjOy+hHrGsyzjrd2swVQ2iUWP8BfEQqGLqM1g
|
||||
KgWKYfcTGdbPB1MCAwEAAaNjMGEwHQYDVR0OBBYEFG/oAMxTVe7y0+408CTAK8hA
|
||||
uTyRMB8GA1UdIwQYMBaAFG/oAMxTVe7y0+408CTAK8hAuTyRMA8GA1UdEwEB/wQF
|
||||
MAMBAf8wDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQBLnUTfW7hp
|
||||
emMbuUGCk7RBswzOT83bDM6824EkUnf+X0iKS95SUNGeeSWK2o/3ALJo5hi7GZr3
|
||||
U8eLaWAcYizfO99UXMRBPw5PRR+gXGEronGUugLpxsjuynoLQu8GQAeysSXKbN1I
|
||||
UugDo9u8igJORYA+5ms0s5sCUySqbQ2R5z/GoceyI9LdxIVa1RjVX8pYOj8JFwtn
|
||||
DJN3ftSFvNMYwRuILKuqUYSHc2GPYiHVflDh5nDymCMOQFcFG3WsEuB+EYQPFgIU
|
||||
1DHmdZcz7Llx8UOZXX2JupWCYzK1XhJb+r4hK5ncf/w8qGtYlmyJpxk3hr1TfUJX
|
||||
Yf4Zr0fJsGuv
|
||||
-----END CERTIFICATE-----
|
||||
19
api/res/certs/alipay/appPublicCert.crt
Normal file
19
api/res/certs/alipay/appPublicCert.crt
Normal file
@@ -0,0 +1,19 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIDmTCCAoGgAwIBAgIQICMRB2LW76yahgdg3IFNPDANBgkqhkiG9w0BAQsFADCBkTELMAkGA1UE
|
||||
BhMCQ04xGzAZBgNVBAoMEkFudCBGaW5hbmNpYWwgdGVzdDElMCMGA1UECwwcQ2VydGlmaWNhdGlv
|
||||
biBBdXRob3JpdHkgdGVzdDE+MDwGA1UEAww1QW50IEZpbmFuY2lhbCBDZXJ0aWZpY2F0aW9uIEF1
|
||||
dGhvcml0eSBDbGFzcyAyIFIxIHRlc3QwHhcNMjMxMTA3MDU0NjE5WhcNMjQxMTExMDU0NjE5WjBr
|
||||
MQswCQYDVQQGEwJDTjEfMB0GA1UECgwWbWJvbmZ5OTAxNUBzYW5kYm94LmNvbTEPMA0GA1UECwwG
|
||||
QWxpcGF5MSowKAYDVQQDDCEyMDg4NzIxMDIwNzUwNTgxLTkwMjEwMDAxMzE2NTgwMjMwggEiMA0G
|
||||
CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxihQPf1Q+g9ArgM46shVqL5sbRha/df95D1PsWyEq
|
||||
ANmWmG4zZ+ksYDVQrc4KzhSRoi56sm/7TDFYTmM6bW99e/nKW58WxyZB4ie5qA3F4n17psPyDqb8
|
||||
IokcQmCphSFDaXQD6AoXoLNtTM0vAI2cWxAgebZ/vsrdj5Ntjt+Rp3NYMCk1i5xovHcfILzLEGbX
|
||||
QXoT9fo5AhHotTWa6xHVLPUGY9qwLzQxHzBmvy5ZMfnOfJkm/mDisTSqAUB59F3dzU/1ARVkEZ1w
|
||||
Mgb4XohWBw6iurQfbMnH2mIomAAwwZVFv+sXDbL9yMbSMo/SjVsTQprn0Q0EnwLo7nmmOM6HAgMB
|
||||
AAGjEjAQMA4GA1UdDwEB/wQEAwIE8DANBgkqhkiG9w0BAQsFAAOCAQEAn3Y4/C1h9R6ONsBqX3/q
|
||||
XfHX7yX1FM0Y1x48X3/Yxk6HivAkTukhhhVYVKJsbrbzRqHDp9vhAP/FR6o6pAevaYMmLov0VMXU
|
||||
7oAuetgkaYEYkDuNen5/Hpdhqi2vTtdT+q9w8zHJd6MDQ0aoHgIxpLKw5vof2R1N4fwSgNXMiXE5
|
||||
kmllKQMem/+on2p+Sj80/2asxryHIGlH87qPzkffv+kIOkZthbTApTFLLjdVri2QHGe8/cc4xy01
|
||||
/9iR3IUzNahotT41lJ4bMevBY7XMAS3n5ekyABN/9ZRJqhWdXgmFCRN/u56qd6lDgu7R2M2QUoyc
|
||||
LuW5DfgRItKlmUB7sw==
|
||||
-----END CERTIFICATE-----
|
||||
1
api/res/certs/alipay/privateKey.txt
Normal file
1
api/res/certs/alipay/privateKey.txt
Normal file
@@ -0,0 +1 @@
|
||||
MIIEpQIBAAKCAQEAsYoUD39UPoPQK4DOOrIVai+bG0YWv3X/eQ9T7FshKgDZlphuM2fpLGA1UK3OCs4UkaIuerJv+0wxWE5jOm1vfXv5ylufFscmQeInuagNxeJ9e6bD8g6m/CKJHEJgqYUhQ2l0A+gKF6CzbUzNLwCNnFsQIHm2f77K3Y+TbY7fkadzWDApNYucaLx3HyC8yxBm10F6E/X6OQIR6LU1musR1Sz1BmPasC80MR8wZr8uWTH5znyZJv5g4rE0qgFAefRd3c1P9QEVZBGdcDIG+F6IVgcOorq0H2zJx9piKJgAMMGVRb/rFw2y/cjG0jKP0o1bE0Ka59ENBJ8C6O55pjjOhwIDAQABAoIBAFetNfz1R7hbxjlFshMAkVzQR8wvT9qbvl+dtzdZRcaFhu89NecDIP7+QDYor0FcxoGpU0TazDyRQyk2BQD8vHt+9zv9BVLtZLJSqoWgPbUFBi1DjS8EF2ka8RVYnn35NhUhhd7L//ftL88Bh673mfembQ9srDjoEy1Z01feoABAnCMkNFl986DmEwnarvEufXSDIgeN4ioMxha4NvfIPuI0zpVdV1O9sv+SGC+VEWZBtN3GNsaf4zS/f8FVGvTiU/Abz0gSw/iwSPHclDWQDTN3yFHf/tfqlzh0mH0WfhnuOBFWXzK+R7fbnM+asI9ttvzRcfpzgRGXdPcNcOv/6cECgYEA3DVqpi1k8MYfJixju6SG5gfyhM4VFksFmCMaNPgtatDMBKLMTgV/Ej6LXREojcy29uZl83F09pVlpd41eG39ULIPktixA/BqErQ2UaWh6kOxifycpu22Jh0r09hax6UgVrcBrrnCJEjcFsuJlrZvXQSzc3PBxjWy5gjabS5h9iECgYEAzmVAIh2frF01Y95zsLueAhhZwCtPanm6kf7ivR4r1plIX3b2sNRhWGmEHFgaCE6Braa0ogQ73Hd26kw4ZW+D6QMGC/zjCBEzDLLf++SjdVUHiY5AR4WHqXzq1jdAlsVyo9R661oAOp3lhiJVGLNXkHyEfEVPHsaxJh4osYSbX6cCgYEAx32Qx0i6eDFTyLZQB46uMrgiaVN04QRH5iJuvGvUYT8UhGKjaU8rZfDJOh+wOH2rhxMEaz1uc3C2bERY9mfWI4Ob/jFWc7YZsiYWS3Mcsuhubw4tMECLUg39RWZsHw8ls8kIuixIh6yFzhTH6YQOcRswIrhMZG8DScfdcSmiz2ECgYEAkWP1t5KSpkLKl11etcKUXfl1T8+yk9jIOowIgRw92WAFAWq2AH67TCKYM7dEL1HOO9tRJ0hAOt/U3ttuZtYVYBEHM26jJ02mXm2rJrA7DS4mrxmL4lYH6LbcXqZxU0Qnq4zEQgIWYzRTORf6Rfof1uJAGaJhR9bDd4yLMfGt2cUCgYEAo216Y61xOHUTA4AF1eekk+r+uOcQgQDvLXfs9FkDdJLk0mPG48/+eIYpPFnANJ/riF/DWOp8WGEe2IzA9yUFexzDbNQK8ha9kGcxaSAyiCwzjZ/t9/+hScDSV8kNqWSRSisu/YOFleEHbokT6mbLZ+gdqES8mUUanaEBzRQYGxo=
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"data": [
|
||||
"task(owy5niy1sbbnlq0)",
|
||||
"A beautiful Chinese girl plays the guitar on the beach. She is dressed in a flowing dress that matches the colors of the sunset. With her eyes closed, she strums the guitar with passion and confidence, her fingers dancing gracefully on the strings. The painting employs a vibrant color palette, capturing the warmth of the setting sun blending with the serene hues of the ocean. The artist uses a combination of impressionistic and realistic brushstrokes to convey both the girl's delicate features and the dynamic movement of the waves. The rendering effect creates a dream-like atmosphere, as if the viewer is being transported to a magical realm where music and nature intertwine. The picture is bathed in a soft, golden light, casting a warm glow on the girl's face, illuminating her joy and connection to the music she creates.",
|
||||
"task(cxvkpawy8onnfti)",
|
||||
"a cute girl",
|
||||
"",
|
||||
[],
|
||||
30,
|
||||
"DPM++ 3M SDE Karras",
|
||||
20,
|
||||
"DPM++ 2M Karras",
|
||||
1,
|
||||
1,
|
||||
7,
|
||||
@@ -36,6 +36,7 @@
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
false,
|
||||
"positive",
|
||||
@@ -74,6 +75,6 @@
|
||||
""
|
||||
],
|
||||
"event_data": null,
|
||||
"fn_index": 316,
|
||||
"session_hash": "ttr8efgt63g"
|
||||
"fn_index": 446,
|
||||
"session_hash": "nk5noh1rz1o"
|
||||
}
|
||||
@@ -49,3 +49,5 @@ func (s *AliYunSmsService) SendVerifyCode(mobile string, code int) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
var _ SmsService = &AliYunSmsService{}
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
package fun
|
||||
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
"chatplus/service/oss"
|
||||
"chatplus/store/model"
|
||||
"chatplus/utils"
|
||||
"fmt"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// AI 绘画函数
|
||||
|
||||
type FuncImage struct {
|
||||
name string
|
||||
db *gorm.DB
|
||||
uploadManager *oss.UploaderManager
|
||||
proxyURL string
|
||||
}
|
||||
|
||||
func NewImageFunc(db *gorm.DB, manager *oss.UploaderManager, config *types.AppConfig) FuncImage {
|
||||
return FuncImage{
|
||||
db: db,
|
||||
name: "DALL-E3 绘画",
|
||||
uploadManager: manager,
|
||||
proxyURL: config.ProxyURL,
|
||||
}
|
||||
}
|
||||
|
||||
type imgReq struct {
|
||||
Model string `json:"model"`
|
||||
Prompt string `json:"prompt"`
|
||||
N int `json:"n"`
|
||||
Size string `json:"size"`
|
||||
}
|
||||
|
||||
type imgRes struct {
|
||||
Created int64 `json:"created"`
|
||||
Data []struct {
|
||||
RevisedPrompt string `json:"revised_prompt"`
|
||||
Url string `json:"url"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type ErrRes struct {
|
||||
Error struct {
|
||||
Code interface{} `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Param interface{} `json:"param"`
|
||||
Type string `json:"type"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
func (f FuncImage) Invoke(params map[string]interface{}) (string, error) {
|
||||
logger.Infof("绘画参数:%+v", params)
|
||||
prompt := utils.InterfaceToString(params["prompt"])
|
||||
// get image generation API KEY
|
||||
var apiKey model.ApiKey
|
||||
tx := f.db.Where("platform = ? AND type = ?", types.OpenAI, "img").Order("last_used_at ASC").First(&apiKey)
|
||||
if tx.Error != nil {
|
||||
return "", fmt.Errorf("error with get generation API KEY: %v", tx.Error)
|
||||
}
|
||||
|
||||
// get image generation api URL
|
||||
var conf model.Config
|
||||
var chatConfig types.ChatConfig
|
||||
tx = f.db.Where("marker", "chat").First(&conf)
|
||||
if tx.Error != nil {
|
||||
return "", fmt.Errorf("error with get chat configs: %v", tx.Error)
|
||||
}
|
||||
|
||||
err := utils.JsonDecode(conf.Config, &chatConfig)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error with decode chat config: %v", err)
|
||||
}
|
||||
|
||||
apiURL := chatConfig.DallApiURL
|
||||
if utils.IsEmptyValue(apiURL) {
|
||||
apiURL = "https://api.openai.com/v1/images/generations"
|
||||
}
|
||||
imgNum := chatConfig.DallImgNum
|
||||
if imgNum <= 0 {
|
||||
imgNum = 1
|
||||
}
|
||||
var res imgRes
|
||||
var errRes ErrRes
|
||||
r, err := req.C().SetProxyURL(f.proxyURL).R().SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+apiKey.Value).
|
||||
SetBody(imgReq{
|
||||
Model: "dall-e-3",
|
||||
Prompt: prompt,
|
||||
N: imgNum,
|
||||
Size: "1024x1024",
|
||||
}).
|
||||
SetErrorResult(&errRes).
|
||||
SetSuccessResult(&res).Post(apiURL)
|
||||
if err != nil || r.IsErrorState() {
|
||||
return "", fmt.Errorf("error with http request: %v%v%s", err, r.Err, errRes.Error.Message)
|
||||
}
|
||||
// 存储图片
|
||||
imgURL, err := f.uploadManager.GetUploadHandler().PutImg(res.Data[0].Url, false)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("下载图片失败: %s", err.Error())
|
||||
}
|
||||
|
||||
logger.Info(imgURL)
|
||||
return fmt.Sprintf("\n\n\n", imgURL), nil
|
||||
}
|
||||
|
||||
func (f FuncImage) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
var _ Function = &FuncImage{}
|
||||
@@ -1,40 +0,0 @@
|
||||
package fun
|
||||
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
logger2 "chatplus/logger"
|
||||
"chatplus/service/oss"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Function interface {
|
||||
Invoke(map[string]interface{}) (string, error)
|
||||
Name() string
|
||||
}
|
||||
|
||||
var logger = logger2.GetLogger()
|
||||
|
||||
type resVo struct {
|
||||
Code types.BizCode `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Data struct {
|
||||
Title string `json:"title"`
|
||||
UpdatedAt string `json:"updated_at"`
|
||||
Items []dataItem `json:"items"`
|
||||
} `json:"data"`
|
||||
}
|
||||
|
||||
type dataItem struct {
|
||||
Title string `json:"title"`
|
||||
Url string `json:"url"`
|
||||
Remark string `json:"remark"`
|
||||
}
|
||||
|
||||
func NewFunctions(config *types.AppConfig, db *gorm.DB, manager *oss.UploaderManager) map[string]Function {
|
||||
return map[string]Function{
|
||||
types.FuncZaoBao: NewZaoBao(config.ApiConfig),
|
||||
types.FuncWeibo: NewWeiboHot(config.ApiConfig),
|
||||
types.FuncHeadLine: NewHeadLines(config.ApiConfig),
|
||||
types.FuncImage: NewImageFunc(db, manager, config),
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
package fun
|
||||
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/imroc/req/v3"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 今日头条函数实现
|
||||
|
||||
type FuncHeadlines struct {
|
||||
name string
|
||||
config types.ChatPlusApiConfig
|
||||
client *req.Client
|
||||
}
|
||||
|
||||
func NewHeadLines(config types.ChatPlusApiConfig) FuncHeadlines {
|
||||
return FuncHeadlines{
|
||||
name: "今日头条",
|
||||
config: config,
|
||||
client: req.C().SetTimeout(10 * time.Second)}
|
||||
}
|
||||
|
||||
func (f FuncHeadlines) Invoke(map[string]interface{}) (string, error) {
|
||||
if f.config.Token == "" {
|
||||
return "", errors.New("无效的 API Token")
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/api/headline/fetch", f.config.ApiURL)
|
||||
var res resVo
|
||||
r, err := f.client.R().
|
||||
SetHeader("AppId", f.config.AppId).
|
||||
SetHeader("Authorization", fmt.Sprintf("Bearer %s", f.config.Token)).
|
||||
SetSuccessResult(&res).Get(url)
|
||||
if err != nil || r.IsErrorState() {
|
||||
return "", fmt.Errorf("%v%v", err, r.Err)
|
||||
}
|
||||
|
||||
if res.Code != types.Success {
|
||||
return "", errors.New(res.Message)
|
||||
}
|
||||
|
||||
builder := make([]string, 0)
|
||||
builder = append(builder, fmt.Sprintf("**%s**,最新更新:%s", res.Data.Title, res.Data.UpdatedAt))
|
||||
for i, v := range res.Data.Items {
|
||||
builder = append(builder, fmt.Sprintf("%d、 [%s](%s) [%s]", i+1, v.Title, v.Url, v.Remark))
|
||||
}
|
||||
return strings.Join(builder, "\n\n"), nil
|
||||
}
|
||||
|
||||
func (f FuncHeadlines) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
var _ Function = &FuncHeadlines{}
|
||||
@@ -1,58 +0,0 @@
|
||||
package fun
|
||||
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/imroc/req/v3"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 微博热搜函数实现
|
||||
|
||||
type FuncWeiboHot struct {
|
||||
name string
|
||||
config types.ChatPlusApiConfig
|
||||
client *req.Client
|
||||
}
|
||||
|
||||
func NewWeiboHot(config types.ChatPlusApiConfig) FuncWeiboHot {
|
||||
return FuncWeiboHot{
|
||||
name: "微博热搜",
|
||||
config: config,
|
||||
client: req.C().SetTimeout(10 * time.Second)}
|
||||
}
|
||||
|
||||
func (f FuncWeiboHot) Invoke(map[string]interface{}) (string, error) {
|
||||
if f.config.Token == "" {
|
||||
return "", errors.New("无效的 API Token")
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/api/weibo/fetch", f.config.ApiURL)
|
||||
var res resVo
|
||||
r, err := f.client.R().
|
||||
SetHeader("AppId", f.config.AppId).
|
||||
SetHeader("Authorization", fmt.Sprintf("Bearer %s", f.config.Token)).
|
||||
SetSuccessResult(&res).Get(url)
|
||||
if err != nil || r.IsErrorState() {
|
||||
return "", fmt.Errorf("%v%v", err, r.Err)
|
||||
}
|
||||
|
||||
if res.Code != types.Success {
|
||||
return "", errors.New(res.Message)
|
||||
}
|
||||
|
||||
builder := make([]string, 0)
|
||||
builder = append(builder, fmt.Sprintf("**%s**,最新更新:%s", res.Data.Title, res.Data.UpdatedAt))
|
||||
for i, v := range res.Data.Items {
|
||||
builder = append(builder, fmt.Sprintf("%d、 [%s](%s) [热度:%s]", i+1, v.Title, v.Url, v.Remark))
|
||||
}
|
||||
return strings.Join(builder, "\n\n"), nil
|
||||
}
|
||||
|
||||
func (f FuncWeiboHot) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
var _ Function = &FuncWeiboHot{}
|
||||
@@ -1,59 +0,0 @@
|
||||
package fun
|
||||
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/imroc/req/v3"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 每日早报函数实现
|
||||
|
||||
type FuncZaoBao struct {
|
||||
name string
|
||||
config types.ChatPlusApiConfig
|
||||
client *req.Client
|
||||
}
|
||||
|
||||
func NewZaoBao(config types.ChatPlusApiConfig) FuncZaoBao {
|
||||
return FuncZaoBao{
|
||||
name: "每日早报",
|
||||
config: config,
|
||||
client: req.C().SetTimeout(10 * time.Second)}
|
||||
}
|
||||
|
||||
func (f FuncZaoBao) Invoke(map[string]interface{}) (string, error) {
|
||||
if f.config.Token == "" {
|
||||
return "", errors.New("无效的 API Token")
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/api/zaobao/fetch", f.config.ApiURL)
|
||||
var res resVo
|
||||
r, err := f.client.R().
|
||||
SetHeader("AppId", f.config.AppId).
|
||||
SetHeader("Authorization", fmt.Sprintf("Bearer %s", f.config.Token)).
|
||||
SetSuccessResult(&res).Get(url)
|
||||
if err != nil || r.IsErrorState() {
|
||||
return "", fmt.Errorf("%v%v", err, r.Err)
|
||||
}
|
||||
|
||||
if res.Code != types.Success {
|
||||
return "", errors.New(res.Message)
|
||||
}
|
||||
|
||||
builder := make([]string, 0)
|
||||
builder = append(builder, fmt.Sprintf("**%s 早报:**", res.Data.UpdatedAt))
|
||||
for _, v := range res.Data.Items {
|
||||
builder = append(builder, v.Title)
|
||||
}
|
||||
builder = append(builder, fmt.Sprintf("%s", res.Data.Title))
|
||||
return strings.Join(builder, "\n\n"), nil
|
||||
}
|
||||
|
||||
func (f FuncZaoBao) Name() string {
|
||||
return f.name
|
||||
}
|
||||
|
||||
var _ Function = &FuncZaoBao{}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"chatplus/core/types"
|
||||
logger2 "chatplus/logger"
|
||||
"chatplus/utils"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
discordgo "github.com/bg5t/mydiscordgo"
|
||||
"github.com/gorilla/websocket"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -17,33 +17,48 @@ import (
|
||||
var logger = logger2.GetLogger()
|
||||
|
||||
type Bot struct {
|
||||
config *types.MidJourneyConfig
|
||||
config types.MidJourneyConfig
|
||||
bot *discordgo.Session
|
||||
name string
|
||||
service *Service
|
||||
}
|
||||
|
||||
func NewBot(name string, proxy string, config *types.MidJourneyConfig, service *Service) (*Bot, error) {
|
||||
discord, err := discordgo.New("Bot " + config.BotToken)
|
||||
func NewBot(name string, proxy string, config types.MidJourneyConfig, service *Service) (*Bot, error) {
|
||||
bot, err := discordgo.New("Bot " + config.BotToken)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if proxy != "" {
|
||||
proxy, _ := url.Parse(proxy)
|
||||
discord.Client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
// use CDN reverse proxy
|
||||
if config.UseCDN {
|
||||
discordgo.SetEndpointDiscord(config.DiscordAPI)
|
||||
discordgo.SetEndpointCDN(config.DiscordCDN)
|
||||
discordgo.SetEndpointStatus(config.DiscordAPI + "/api/v2/")
|
||||
bot.MjGateway = config.DiscordGateway + "/"
|
||||
} else { // use proxy
|
||||
discordgo.SetEndpointDiscord("https://discord.com")
|
||||
discordgo.SetEndpointCDN("https://cdn.discordapp.com")
|
||||
discordgo.SetEndpointStatus("https://discord.com/api/v2/")
|
||||
bot.MjGateway = "wss://gateway.discord.gg"
|
||||
|
||||
if proxy != "" {
|
||||
proxy, _ := url.Parse(proxy)
|
||||
bot.Client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyURL(proxy),
|
||||
},
|
||||
}
|
||||
bot.Dialer = &websocket.Dialer{
|
||||
Proxy: http.ProxyURL(proxy),
|
||||
},
|
||||
}
|
||||
discord.Dialer = &websocket.Dialer{
|
||||
Proxy: http.ProxyURL(proxy),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return &Bot{
|
||||
config: config,
|
||||
bot: discord,
|
||||
bot: bot,
|
||||
name: name,
|
||||
service: service,
|
||||
}, nil
|
||||
@@ -89,7 +104,7 @@ func (b *Bot) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
return
|
||||
}
|
||||
// ignore messages for self
|
||||
if m.Author.ID == s.State.User.ID {
|
||||
if m.Author == nil || m.Author.ID == s.State.User.ID {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -101,6 +116,7 @@ func (b *Bot) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
if strings.Contains(m.Content, "(Waiting to start)") && !strings.Contains(m.Content, "Rerolling **") {
|
||||
// parse content
|
||||
req := CBReq{
|
||||
ChannelId: m.ChannelID,
|
||||
MessageId: m.ID,
|
||||
ReferenceId: referenceId,
|
||||
Prompt: extractPrompt(m.Content),
|
||||
@@ -111,7 +127,7 @@ func (b *Bot) messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||
return
|
||||
}
|
||||
|
||||
b.addAttachment(m.ID, referenceId, m.Content, m.Attachments)
|
||||
b.addAttachment(m.ChannelID, m.ID, referenceId, m.Content, m.Attachments)
|
||||
}
|
||||
|
||||
func (b *Bot) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) {
|
||||
@@ -120,7 +136,7 @@ func (b *Bot) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) {
|
||||
return
|
||||
}
|
||||
// ignore messages for self
|
||||
if m.Author.ID == s.State.User.ID {
|
||||
if m.Author == nil || m.Author.ID == s.State.User.ID {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -132,6 +148,7 @@ func (b *Bot) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) {
|
||||
}
|
||||
if strings.Contains(m.Content, "(Stopped)") {
|
||||
req := CBReq{
|
||||
ChannelId: m.ChannelID,
|
||||
MessageId: m.ID,
|
||||
ReferenceId: referenceId,
|
||||
Prompt: extractPrompt(m.Content),
|
||||
@@ -142,11 +159,11 @@ func (b *Bot) messageUpdate(s *discordgo.Session, m *discordgo.MessageUpdate) {
|
||||
return
|
||||
}
|
||||
|
||||
b.addAttachment(m.ID, referenceId, m.Content, m.Attachments)
|
||||
b.addAttachment(m.ChannelID, m.ID, referenceId, m.Content, m.Attachments)
|
||||
|
||||
}
|
||||
|
||||
func (b *Bot) addAttachment(messageId string, referenceId string, content string, attachments []*discordgo.MessageAttachment) {
|
||||
func (b *Bot) addAttachment(channelId string, messageId string, referenceId string, content string, attachments []*discordgo.MessageAttachment) {
|
||||
progress := extractProgress(content)
|
||||
var status TaskStatus
|
||||
if progress == 100 {
|
||||
@@ -168,6 +185,7 @@ func (b *Bot) addAttachment(messageId string, referenceId string, content string
|
||||
Hash: extractHashFromFilename(attachment.Filename),
|
||||
}
|
||||
req := CBReq{
|
||||
ChannelId: channelId,
|
||||
MessageId: messageId,
|
||||
ReferenceId: referenceId,
|
||||
Image: image,
|
||||
|
||||
@@ -3,33 +3,41 @@ package mj
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
"fmt"
|
||||
"github.com/imroc/req/v3"
|
||||
"time"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
)
|
||||
|
||||
// MidJourney client
|
||||
|
||||
type Client struct {
|
||||
client *req.Client
|
||||
config *types.MidJourneyConfig
|
||||
Config types.MidJourneyConfig
|
||||
apiURL string
|
||||
}
|
||||
|
||||
func NewClient(config *types.MidJourneyConfig, proxy string) *Client {
|
||||
func NewClient(config types.MidJourneyConfig, proxy string) *Client {
|
||||
client := req.C().SetTimeout(10 * time.Second)
|
||||
var apiURL string
|
||||
// set proxy URL
|
||||
if proxy != "" {
|
||||
client.SetProxyURL(proxy)
|
||||
if config.UseCDN {
|
||||
apiURL = config.DiscordAPI + "/api/v9/interactions"
|
||||
} else {
|
||||
apiURL = "https://discord.com/api/v9/interactions"
|
||||
if proxy != "" {
|
||||
client.SetProxyURL(proxy)
|
||||
}
|
||||
}
|
||||
logger.Info(proxy)
|
||||
return &Client{client: client, config: config}
|
||||
|
||||
return &Client{client: client, Config: config, apiURL: apiURL}
|
||||
}
|
||||
|
||||
func (c *Client) Imagine(prompt string) error {
|
||||
interactionsReq := &InteractionsRequest{
|
||||
Type: 2,
|
||||
ApplicationID: ApplicationID,
|
||||
GuildID: c.config.GuildId,
|
||||
ChannelID: c.config.ChanelId,
|
||||
GuildID: c.Config.GuildId,
|
||||
ChannelID: c.Config.ChanelId,
|
||||
SessionID: SessionID,
|
||||
Data: map[string]any{
|
||||
"version": "1166847114203123795",
|
||||
@@ -67,11 +75,10 @@ func (c *Client) Imagine(prompt string) error {
|
||||
},
|
||||
}
|
||||
|
||||
url := "https://discord.com/api/v9/interactions"
|
||||
r, err := c.client.R().SetHeader("Authorization", c.config.UserToken).
|
||||
r, err := c.client.R().SetHeader("Authorization", c.Config.UserToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(interactionsReq).
|
||||
Post(url)
|
||||
Post(c.apiURL)
|
||||
|
||||
if err != nil || r.IsErrorState() {
|
||||
return fmt.Errorf("error with http request: %w%v", err, r.Err)
|
||||
@@ -86,8 +93,8 @@ func (c *Client) Upscale(index int, messageId string, hash string) error {
|
||||
interactionsReq := &InteractionsRequest{
|
||||
Type: 3,
|
||||
ApplicationID: ApplicationID,
|
||||
GuildID: c.config.GuildId,
|
||||
ChannelID: c.config.ChanelId,
|
||||
GuildID: c.Config.GuildId,
|
||||
ChannelID: c.Config.ChanelId,
|
||||
MessageFlags: &flags,
|
||||
MessageID: &messageId,
|
||||
SessionID: SessionID,
|
||||
@@ -98,13 +105,12 @@ func (c *Client) Upscale(index int, messageId string, hash string) error {
|
||||
Nonce: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||
}
|
||||
|
||||
url := "https://discord.com/api/v9/interactions"
|
||||
var res InteractionsResult
|
||||
r, err := c.client.R().SetHeader("Authorization", c.config.UserToken).
|
||||
r, err := c.client.R().SetHeader("Authorization", c.Config.UserToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(interactionsReq).
|
||||
SetErrorResult(&res).
|
||||
Post(url)
|
||||
Post(c.apiURL)
|
||||
if err != nil || r.IsErrorState() {
|
||||
return fmt.Errorf("error with http request: %v%v%v", err, r.Err, res.Message)
|
||||
}
|
||||
@@ -118,8 +124,8 @@ func (c *Client) Variation(index int, messageId string, hash string) error {
|
||||
interactionsReq := &InteractionsRequest{
|
||||
Type: 3,
|
||||
ApplicationID: ApplicationID,
|
||||
GuildID: c.config.GuildId,
|
||||
ChannelID: c.config.ChanelId,
|
||||
GuildID: c.Config.GuildId,
|
||||
ChannelID: c.Config.ChanelId,
|
||||
MessageFlags: &flags,
|
||||
MessageID: &messageId,
|
||||
SessionID: SessionID,
|
||||
@@ -130,13 +136,12 @@ func (c *Client) Variation(index int, messageId string, hash string) error {
|
||||
Nonce: fmt.Sprintf("%d", time.Now().UnixNano()),
|
||||
}
|
||||
|
||||
url := "https://discord.com/api/v9/interactions"
|
||||
var res InteractionsResult
|
||||
r, err := c.client.R().SetHeader("Authorization", c.config.UserToken).
|
||||
r, err := c.client.R().SetHeader("Authorization", c.Config.UserToken).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
SetBody(interactionsReq).
|
||||
SetErrorResult(&res).
|
||||
Post(url)
|
||||
Post(c.apiURL)
|
||||
if err != nil || r.IsErrorState() {
|
||||
return fmt.Errorf("error with http request: %v%v%v", err, r.Err, res.Message)
|
||||
}
|
||||
|
||||
@@ -4,33 +4,41 @@ import (
|
||||
"chatplus/core/types"
|
||||
"chatplus/service/oss"
|
||||
"chatplus/store"
|
||||
"chatplus/store/model"
|
||||
"fmt"
|
||||
"github.com/go-redis/redis/v8"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// ServicePool Mj service pool
|
||||
type ServicePool struct {
|
||||
services []*Service
|
||||
taskQueue *store.RedisQueue
|
||||
services []*Service
|
||||
taskQueue *store.RedisQueue
|
||||
notifyQueue *store.RedisQueue
|
||||
db *gorm.DB
|
||||
uploaderManager *oss.UploaderManager
|
||||
Clients *types.LMap[uint, *types.WsClient] // UserId => Client
|
||||
}
|
||||
|
||||
func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderManager, appConfig *types.AppConfig) *ServicePool {
|
||||
services := make([]*Service, 0)
|
||||
queue := store.NewRedisQueue("MidJourney_Task_Queue", redisCli)
|
||||
taskQueue := store.NewRedisQueue("MidJourney_Task_Queue", redisCli)
|
||||
notifyQueue := store.NewRedisQueue("MidJourney_Notify_Queue", redisCli)
|
||||
// create mj client and service
|
||||
for k, config := range appConfig.MjConfigs {
|
||||
if config.Enabled == false {
|
||||
continue
|
||||
}
|
||||
// create mj client
|
||||
client := NewClient(&config, appConfig.ProxyURL)
|
||||
client := NewClient(config, appConfig.ProxyURL)
|
||||
|
||||
name := fmt.Sprintf("MjService-%d", k)
|
||||
// create mj service
|
||||
service := NewService(name, queue, 4, 600, db, client, manager, appConfig.ProxyURL)
|
||||
service := NewService(name, taskQueue, notifyQueue, 4, 600, db, client)
|
||||
botName := fmt.Sprintf("MjBot-%d", k)
|
||||
bot, err := NewBot(botName, appConfig.ProxyURL, &config, service)
|
||||
bot, err := NewBot(botName, appConfig.ProxyURL, config, service)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@@ -49,11 +57,64 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
||||
}
|
||||
|
||||
return &ServicePool{
|
||||
taskQueue: queue,
|
||||
services: services,
|
||||
taskQueue: taskQueue,
|
||||
notifyQueue: notifyQueue,
|
||||
services: services,
|
||||
uploaderManager: manager,
|
||||
db: db,
|
||||
Clients: types.NewLMap[uint, *types.WsClient](),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *ServicePool) CheckTaskNotify() {
|
||||
go func() {
|
||||
for {
|
||||
var userId uint
|
||||
err := p.notifyQueue.LPop(&userId)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
client := p.Clients.Get(userId)
|
||||
err = client.Send([]byte("Task Updated"))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (p *ServicePool) DownloadImages() {
|
||||
go func() {
|
||||
var items []model.MidJourneyJob
|
||||
for {
|
||||
res := p.db.Where("img_url = ? AND progress = ?", "", 100).Find(&items)
|
||||
if res.Error != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// download images
|
||||
for _, v := range items {
|
||||
imgURL, err := p.uploaderManager.GetUploadHandler().PutImg(v.OrgURL, true)
|
||||
if err != nil {
|
||||
logger.Error("error with download image: ", err)
|
||||
continue
|
||||
}
|
||||
|
||||
v.ImgURL = imgURL
|
||||
p.db.Updates(&v)
|
||||
|
||||
client := p.Clients.Get(uint(v.UserId))
|
||||
err = client.Send([]byte("Task Updated"))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// PushTask push a new mj task in to task queue
|
||||
func (p *ServicePool) PushTask(task types.MjTask) {
|
||||
logger.Debugf("add a new MidJourney task to the task list: %+v", task)
|
||||
|
||||
@@ -2,13 +2,13 @@ package mj
|
||||
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
"chatplus/service/oss"
|
||||
"chatplus/store"
|
||||
"chatplus/store/model"
|
||||
"gorm.io/gorm"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Service MJ 绘画服务
|
||||
@@ -16,25 +16,23 @@ type Service struct {
|
||||
name string // service name
|
||||
client *Client // MJ client
|
||||
taskQueue *store.RedisQueue
|
||||
notifyQueue *store.RedisQueue
|
||||
db *gorm.DB
|
||||
uploadManager *oss.UploaderManager
|
||||
proxyURL string
|
||||
maxHandleTaskNum int32 // max task number current service can handle
|
||||
handledTaskNum int32 // already handled task number
|
||||
taskStartTimes map[int]time.Time // task start time, to check if the task is timeout
|
||||
taskTimeout int64
|
||||
}
|
||||
|
||||
func NewService(name string, queue *store.RedisQueue, maxTaskNum int32, timeout int64, db *gorm.DB, client *Client, manager *oss.UploaderManager, proxy string) *Service {
|
||||
func NewService(name string, taskQueue *store.RedisQueue, notifyQueue *store.RedisQueue, maxTaskNum int32, timeout int64, db *gorm.DB, client *Client) *Service {
|
||||
return &Service{
|
||||
name: name,
|
||||
db: db,
|
||||
taskQueue: queue,
|
||||
taskQueue: taskQueue,
|
||||
notifyQueue: notifyQueue,
|
||||
client: client,
|
||||
uploadManager: manager,
|
||||
taskTimeout: timeout,
|
||||
maxHandleTaskNum: maxTaskNum,
|
||||
proxyURL: proxy,
|
||||
taskStartTimes: make(map[int]time.Time, 0),
|
||||
}
|
||||
}
|
||||
@@ -57,6 +55,15 @@ func (s *Service) Run() {
|
||||
continue
|
||||
}
|
||||
|
||||
// if it's reference message, check if it's this channel's message
|
||||
if task.ChannelId != "" && task.ChannelId != s.client.Config.ChanelId {
|
||||
s.taskQueue.RPush(task)
|
||||
s.db.Model(&model.MidJourneyJob{Id: uint(task.Id)}).UpdateColumn("progress", -1)
|
||||
s.notifyQueue.RPush(task.UserId)
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.Infof("%s handle a new MidJourney task: %+v", s.name, task)
|
||||
switch task.Type {
|
||||
case types.TaskImage:
|
||||
@@ -74,7 +81,9 @@ func (s *Service) Run() {
|
||||
logger.Error("绘画任务执行失败:", err)
|
||||
// update the task progress
|
||||
s.db.Model(&model.MidJourneyJob{Id: uint(task.Id)}).UpdateColumn("progress", -1)
|
||||
atomic.AddInt32(&s.handledTaskNum, -1)
|
||||
s.notifyQueue.RPush(task.UserId)
|
||||
// restore img_call quota
|
||||
s.db.Model(&model.User{}).Where("id = ?", task.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls + ?", 1))
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -113,17 +122,29 @@ func (s *Service) Notify(data CBReq) {
|
||||
return
|
||||
}
|
||||
|
||||
res = s.db.Where("task_id = ?", split[0]).First(&job)
|
||||
tx := s.db.Session(&gorm.Session{}).Where("progress < ?", 100).Order("id ASC")
|
||||
if data.ReferenceId != "" {
|
||||
tx = tx.Where("reference_id = ?", data.ReferenceId)
|
||||
} else {
|
||||
tx = tx.Where("task_id = ?", split[0])
|
||||
}
|
||||
res = tx.First(&job)
|
||||
if res.Error != nil {
|
||||
logger.Warn("非法任务:", res.Error)
|
||||
return
|
||||
}
|
||||
|
||||
job.ChannelId = data.ChannelId
|
||||
job.MessageId = data.MessageId
|
||||
job.ReferenceId = data.ReferenceId
|
||||
job.Progress = data.Progress
|
||||
job.Prompt = data.Prompt
|
||||
job.Hash = data.Image.Hash
|
||||
job.OrgURL = data.Image.URL
|
||||
if s.client.Config.UseCDN {
|
||||
job.UseProxy = true
|
||||
job.ImgURL = strings.ReplaceAll(data.Image.URL, "https://cdn.discordapp.com", s.client.Config.DiscordCDN)
|
||||
}
|
||||
|
||||
res = s.db.Updates(&job)
|
||||
if res.Error != nil {
|
||||
@@ -131,22 +152,11 @@ func (s *Service) Notify(data CBReq) {
|
||||
return
|
||||
}
|
||||
|
||||
// upload image
|
||||
if data.Status == Finished {
|
||||
imgURL, err := s.uploadManager.GetUploadHandler().PutImg(data.Image.URL, true)
|
||||
if err != nil {
|
||||
logger.Error("error with download img: ", err.Error())
|
||||
return
|
||||
}
|
||||
job.ImgURL = imgURL
|
||||
s.db.Updates(&job)
|
||||
}
|
||||
|
||||
if data.Status == Finished {
|
||||
// update user's img calls
|
||||
s.db.Model(&model.User{}).Where("id = ?", job.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
|
||||
// release lock task
|
||||
atomic.AddInt32(&s.handledTaskNum, -1)
|
||||
}
|
||||
|
||||
s.notifyQueue.RPush(job.UserId)
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ type InteractionsResult struct {
|
||||
}
|
||||
|
||||
type CBReq struct {
|
||||
ChannelId string `json:"channel_id"`
|
||||
MessageId string `json:"message_id"`
|
||||
ReferenceId string `json:"reference_id"`
|
||||
Image Image `json:"image"`
|
||||
|
||||
@@ -2,6 +2,7 @@ package payment
|
||||
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
"chatplus/utils"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -27,17 +28,37 @@ func NewHuPiPay(config *types.AppConfig) *HuPiPayService {
|
||||
}
|
||||
}
|
||||
|
||||
type HuPiPayReq struct {
|
||||
AppId string `json:"appid"`
|
||||
Version string `json:"version"`
|
||||
TradeOrderId string `json:"trade_order_id"`
|
||||
TotalFee string `json:"total_fee"`
|
||||
Title string `json:"title"`
|
||||
NotifyURL string `json:"notify_url"`
|
||||
ReturnURL string `json:"return_url"`
|
||||
WapName string `json:"wap_name"`
|
||||
CallbackURL string `json:"callback_url"`
|
||||
Time string `json:"time"`
|
||||
NonceStr string `json:"nonce_str"`
|
||||
}
|
||||
|
||||
// Pay 执行支付请求操作
|
||||
func (s *HuPiPayService) Pay(params map[string]string) (string, error) {
|
||||
func (s *HuPiPayService) Pay(params HuPiPayReq) (string, error) {
|
||||
data := url.Values{}
|
||||
simple := strconv.FormatInt(time.Now().Unix(), 10)
|
||||
params["appid"] = s.appId
|
||||
params["time"] = simple
|
||||
params["nonce_str"] = simple
|
||||
for k, v := range params {
|
||||
data.Add(k, v)
|
||||
params.AppId = s.appId
|
||||
params.Time = simple
|
||||
params.NonceStr = simple
|
||||
encode := utils.JsonEncode(params)
|
||||
m := make(map[string]string)
|
||||
_ = utils.JsonDecode(encode, &m)
|
||||
for k, v := range m {
|
||||
data.Add(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
data.Add("hash", s.Sign(params))
|
||||
encode = utils.JsonEncode(params)
|
||||
m = make(map[string]string)
|
||||
_ = utils.JsonDecode(encode, &m)
|
||||
data.Add("hash", s.Sign(m))
|
||||
resp, err := http.PostForm(s.host, data)
|
||||
if err != nil {
|
||||
return "error", err
|
||||
@@ -54,7 +75,6 @@ func (s *HuPiPayService) Pay(params map[string]string) (string, error) {
|
||||
func (s *HuPiPayService) Sign(params map[string]string) string {
|
||||
var data string
|
||||
keys := make([]string, 0, 0)
|
||||
params["appid"] = s.appId
|
||||
for key := range params {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
|
||||
102
api/service/payment/payjs_service.go
Normal file
102
api/service/payment/payjs_service.go
Normal file
@@ -0,0 +1,102 @@
|
||||
package payment
|
||||
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
"chatplus/utils"
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type PayJS struct {
|
||||
config *types.JPayConfig
|
||||
}
|
||||
|
||||
func NewPayJS(appConfig *types.AppConfig) *PayJS {
|
||||
return &PayJS{
|
||||
config: &appConfig.JPayConfig,
|
||||
}
|
||||
}
|
||||
|
||||
type JPayReq struct {
|
||||
TotalFee int `json:"total_fee"`
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
Subject string `json:"body"`
|
||||
NotifyURL string `json:"notify_url"`
|
||||
}
|
||||
type JPayReps struct {
|
||||
CodeUrl string `json:"code_url"`
|
||||
OutTradeNo string `json:"out_trade_no"`
|
||||
OrderId string `json:"payjs_order_id"`
|
||||
Qrcode string `json:"qrcode"`
|
||||
ReturnCode int `json:"return_code"`
|
||||
ReturnMsg string `json:"return_msg"`
|
||||
Sign string `json:"sign"`
|
||||
TotalFee string `json:"total_fee"`
|
||||
}
|
||||
|
||||
func (r JPayReps) IsOK() bool {
|
||||
return r.ReturnMsg == "SUCCESS"
|
||||
}
|
||||
|
||||
func (js *PayJS) Pay(param JPayReq) JPayReps {
|
||||
param.NotifyURL = js.config.NotifyURL
|
||||
var p = url.Values{}
|
||||
encode := utils.JsonEncode(param)
|
||||
m := make(map[string]interface{})
|
||||
_ = utils.JsonDecode(encode, &m)
|
||||
for k, v := range m {
|
||||
p.Add(k, fmt.Sprintf("%v", v))
|
||||
}
|
||||
p.Add("mchid", js.config.AppId)
|
||||
|
||||
p.Add("sign", sign(p, js.config.PrivateKey))
|
||||
|
||||
cli := http.Client{}
|
||||
r, err := cli.PostForm(js.config.ApiURL, p)
|
||||
if err != nil {
|
||||
return JPayReps{ReturnMsg: err.Error()}
|
||||
}
|
||||
defer r.Body.Close()
|
||||
bs, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return JPayReps{ReturnMsg: err.Error()}
|
||||
}
|
||||
|
||||
var data JPayReps
|
||||
err = utils.JsonDecode(string(bs), &data)
|
||||
if err != nil {
|
||||
return JPayReps{ReturnMsg: err.Error()}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func sign(params url.Values, priKey string) string {
|
||||
params.Del(`sign`)
|
||||
var keys = make([]string, 0, 0)
|
||||
for key := range params {
|
||||
if params.Get(key) != `` {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
var pList = make([]string, 0, 0)
|
||||
for _, key := range keys {
|
||||
var value = strings.TrimSpace(params.Get(key))
|
||||
if len(value) > 0 {
|
||||
pList = append(pList, key+"="+value)
|
||||
}
|
||||
}
|
||||
var src = strings.Join(pList, "&")
|
||||
src += "&key=" + priKey
|
||||
|
||||
md5bs := md5.Sum([]byte(src))
|
||||
md5res := hex.EncodeToString(md5bs[:])
|
||||
return strings.ToUpper(md5res)
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"chatplus/service/oss"
|
||||
"chatplus/store"
|
||||
"fmt"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -25,7 +26,7 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
|
||||
|
||||
// create sd service
|
||||
name := fmt.Sprintf("StableDifffusion Service-%d", k)
|
||||
service := NewService(name, 4, 600, &config, queue, db, manager)
|
||||
service := NewService(name, 1, 300, config, queue, db, manager)
|
||||
// run sd service
|
||||
go func() {
|
||||
service.Run()
|
||||
|
||||
@@ -8,20 +8,21 @@ import (
|
||||
"chatplus/utils"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/imroc/req/v3"
|
||||
"gorm.io/gorm"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/imroc/req/v3"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// SD 绘画服务
|
||||
|
||||
type Service struct {
|
||||
httpClient *req.Client
|
||||
config *types.StableDiffusionConfig
|
||||
config types.StableDiffusionConfig
|
||||
taskQueue *store.RedisQueue
|
||||
db *gorm.DB
|
||||
uploadManager *oss.UploaderManager
|
||||
@@ -32,7 +33,7 @@ type Service struct {
|
||||
taskTimeout int64
|
||||
}
|
||||
|
||||
func NewService(name string, maxTaskNum int32, timeout int64, config *types.StableDiffusionConfig, queue *store.RedisQueue, db *gorm.DB, manager *oss.UploaderManager) *Service {
|
||||
func NewService(name string, maxTaskNum int32, timeout int64, config types.StableDiffusionConfig, queue *store.RedisQueue, db *gorm.DB, manager *oss.UploaderManager) *Service {
|
||||
return &Service{
|
||||
name: name,
|
||||
config: config,
|
||||
@@ -68,6 +69,8 @@ func (s *Service) Run() {
|
||||
logger.Error("绘画任务执行失败:", err)
|
||||
// update the task progress
|
||||
s.db.Model(&model.SdJob{Id: uint(task.Id)}).UpdateColumn("progress", -1)
|
||||
// restore img_call quota
|
||||
s.db.Model(&model.User{}).Where("id = ?", task.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls + ?", 1))
|
||||
// release task num
|
||||
atomic.AddInt32(&s.handledTaskNum, -1)
|
||||
continue
|
||||
@@ -133,6 +136,7 @@ func (s *Service) Txt2Img(task types.SdTask) error {
|
||||
taskInfo.TaskId = params.TaskId
|
||||
taskInfo.Data = data
|
||||
taskInfo.JobId = task.Id
|
||||
taskInfo.UserId = uint(task.UserId)
|
||||
go func() {
|
||||
s.runTask(taskInfo, s.httpClient)
|
||||
}()
|
||||
@@ -155,7 +159,7 @@ func (s *Service) runTask(taskInfo TaskInfo, client *req.Client) {
|
||||
Duration float64 `json:"duration"`
|
||||
AverageDuration float64 `json:"average_duration"`
|
||||
}
|
||||
var cbReq = CBReq{TaskId: taskInfo.TaskId, JobId: taskInfo.JobId, SessionId: taskInfo.SessionId}
|
||||
var cbReq = CBReq{UserId: taskInfo.UserId, TaskId: taskInfo.TaskId, JobId: taskInfo.JobId, SessionId: taskInfo.SessionId}
|
||||
response, err := client.R().SetBody(body).SetSuccessResult(&res).Post(s.config.ApiURL + "/run/predict")
|
||||
if err != nil {
|
||||
cbReq.Message = "error with send request: " + err.Error()
|
||||
@@ -228,7 +232,7 @@ func (s *Service) runTask(taskInfo TaskInfo, client *req.Client) {
|
||||
TextInfo interface{} `json:"textinfo"`
|
||||
}
|
||||
response, err := client.R().SetBody(progressReq).SetSuccessResult(&progressRes).Post(s.config.ApiURL + "/internal/progress")
|
||||
var cbReq = CBReq{TaskId: taskInfo.TaskId, Success: true, JobId: taskInfo.JobId, SessionId: taskInfo.SessionId}
|
||||
var cbReq = CBReq{UserId: taskInfo.UserId, TaskId: taskInfo.TaskId, Success: true, JobId: taskInfo.JobId, SessionId: taskInfo.SessionId}
|
||||
if err != nil { // TODO: 这里可以考虑设置失败重试次数
|
||||
logger.Error(err)
|
||||
return
|
||||
@@ -289,15 +293,11 @@ func (s *Service) callback(data CBReq) {
|
||||
}
|
||||
|
||||
logger.Debugf("绘图进度:%d", data.Progress)
|
||||
|
||||
// 扣减绘图次数
|
||||
if data.Progress == 100 {
|
||||
s.db.Model(&model.User{}).Where("id = ? AND img_calls > 0", job.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
|
||||
}
|
||||
|
||||
} else { // 任务失败
|
||||
logger.Error("任务执行失败:", data.Message)
|
||||
// update the task progress
|
||||
s.db.Model(&model.SdJob{Id: uint(data.JobId)}).UpdateColumn("progress", -1)
|
||||
// restore img_calls
|
||||
s.db.Model(&model.User{}).Where("id = ? AND img_calls > 0", data.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls + ?", 1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import logger2 "chatplus/logger"
|
||||
var logger = logger2.GetLogger()
|
||||
|
||||
type TaskInfo struct {
|
||||
UserId uint `json:"user_id"`
|
||||
SessionId string `json:"session_id"`
|
||||
JobId int `json:"job_id"`
|
||||
TaskId string `json:"task_id"`
|
||||
@@ -15,6 +16,7 @@ type TaskInfo struct {
|
||||
}
|
||||
|
||||
type CBReq struct {
|
||||
UserId uint
|
||||
SessionId string
|
||||
JobId int
|
||||
TaskId string
|
||||
@@ -32,11 +34,11 @@ var ParamKeys = map[string]int{
|
||||
"negative_prompt": 2,
|
||||
"steps": 4,
|
||||
"sampler": 5,
|
||||
"face_fix": 6, // 面部修复
|
||||
"face_fix": 7, // 面部修复
|
||||
"cfg_scale": 8,
|
||||
"seed": 27,
|
||||
"height": 9,
|
||||
"width": 10,
|
||||
"height": 10,
|
||||
"width": 9,
|
||||
"hd_fix": 11,
|
||||
"hd_redraw_rate": 12, //高清修复重绘幅度
|
||||
"hd_scale": 13, // 高清修复放大倍数
|
||||
|
||||
44
api/service/smtp_sms_service.go
Normal file
44
api/service/smtp_sms_service.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"chatplus/core/types"
|
||||
"fmt"
|
||||
"mime"
|
||||
"net/smtp"
|
||||
)
|
||||
|
||||
type SmtpService struct {
|
||||
config *types.SmtpConfig
|
||||
}
|
||||
|
||||
func NewSmtpService(appConfig *types.AppConfig) *SmtpService {
|
||||
return &SmtpService{
|
||||
config: &appConfig.SmtpConfig,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SmtpService) SendVerifyCode(to string, code int) error {
|
||||
subject := "ChatPlus注册验证码"
|
||||
body := fmt.Sprintf("您正在注册 ChatPlus AI 助手账户,注册验证码为 %d,请不要告诉他人。如非本人操作,请忽略此邮件。", code)
|
||||
|
||||
// 设置SMTP客户端配置
|
||||
auth := smtp.PlainAuth("", s.config.From, s.config.Password, s.config.Host)
|
||||
|
||||
// 对主题进行MIME编码
|
||||
encodedSubject := mime.QEncoding.Encode("UTF-8", subject)
|
||||
// 组装邮件
|
||||
message := bytes.NewBuffer(nil)
|
||||
message.WriteString(fmt.Sprintf("From: \"%s\" <%s>\r\n", s.config.AppName, s.config.From))
|
||||
message.WriteString(fmt.Sprintf("To: %s\r\n", to))
|
||||
message.WriteString(fmt.Sprintf("Subject: %s\r\n", encodedSubject))
|
||||
message.WriteString("\r\n" + body)
|
||||
|
||||
// 发送邮件
|
||||
// 发送邮件
|
||||
err := smtp.SendMail(s.config.Host+":"+fmt.Sprint(s.config.Port), auth, s.config.From, []string{to}, message.Bytes())
|
||||
if err != nil {
|
||||
return fmt.Errorf("error sending email: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"github.com/eatmoreapple/openwechat"
|
||||
"github.com/skip2/go-qrcode"
|
||||
"gorm.io/gorm"
|
||||
"os"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// 微信收款机器人
|
||||
@@ -34,8 +36,13 @@ func (b *Bot) Run() error {
|
||||
}
|
||||
// scan code login callback
|
||||
b.bot.UUIDCallback = b.qrCodeCallBack
|
||||
|
||||
err := b.bot.Login()
|
||||
debug, err := strconv.ParseBool(os.Getenv("APP_DEBUG"))
|
||||
if debug {
|
||||
reloadStorage := openwechat.NewJsonFileHotReloadStorage("storage.json")
|
||||
err = b.bot.HotLogin(reloadStorage, true)
|
||||
} else {
|
||||
err = b.bot.Login()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -56,8 +63,8 @@ func (b *Bot) messageHandler(msg *openwechat.Message) {
|
||||
msg.MsgType == openwechat.MsgTypeApp ||
|
||||
msg.AppMsgType == openwechat.AppMsgTypeUrl {
|
||||
// 解析支付金额
|
||||
message, err := parseTransactionMessage(msg.Content)
|
||||
if err == nil {
|
||||
message := parseTransactionMessage(msg.Content)
|
||||
if message.Url != "" {
|
||||
transaction := extractTransaction(message)
|
||||
logger.Infof("解析到收款信息:%+v", transaction)
|
||||
var item model.Reward
|
||||
|
||||
@@ -2,17 +2,15 @@ package wx
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Message 转账消息
|
||||
type Message struct {
|
||||
XMLName xml.Name `xml:"msg"`
|
||||
AppMsg struct {
|
||||
Des string `xml:"des"`
|
||||
Url string `xml:"url"`
|
||||
} `xml:"appmsg"`
|
||||
Des string
|
||||
Url string
|
||||
}
|
||||
|
||||
// Transaction 解析后的交易信息
|
||||
@@ -23,20 +21,40 @@ type Transaction struct {
|
||||
}
|
||||
|
||||
// 解析微信转账消息
|
||||
func parseTransactionMessage(xmlData string) (*Message, error) {
|
||||
var msg Message
|
||||
if err := xml.Unmarshal([]byte(xmlData), &msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func parseTransactionMessage(xmlData string) *Message {
|
||||
decoder := xml.NewDecoder(strings.NewReader(xmlData))
|
||||
message := Message{}
|
||||
for {
|
||||
token, err := decoder.Token()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
return &msg, nil
|
||||
switch se := token.(type) {
|
||||
case xml.StartElement:
|
||||
var value string
|
||||
if se.Name.Local == "des" && message.Des == "" {
|
||||
if err := decoder.DecodeElement(&value, &se); err == nil {
|
||||
message.Des = strings.TrimSpace(value)
|
||||
}
|
||||
break
|
||||
}
|
||||
if se.Name.Local == "weapp_path" && !strings.Contains(message.Url, "customerDetails.html") {
|
||||
if err := decoder.DecodeElement(&value, &se); err == nil {
|
||||
message.Url = strings.TrimSpace(value)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return &message
|
||||
}
|
||||
|
||||
// 导出交易信息
|
||||
func extractTransaction(message *Message) Transaction {
|
||||
var tx = Transaction{}
|
||||
// 导出交易金额和备注
|
||||
lines := strings.Split(message.AppMsg.Des, "\n")
|
||||
lines := strings.Split(message.Des, "\n")
|
||||
for _, line := range lines {
|
||||
line = strings.TrimSpace(line)
|
||||
if len(line) == 0 {
|
||||
@@ -59,10 +77,9 @@ func extractTransaction(message *Message) Transaction {
|
||||
}
|
||||
|
||||
// 解析交易 ID
|
||||
index := strings.Index(message.AppMsg.Url, "trans_id=")
|
||||
if index != -1 {
|
||||
end := strings.LastIndex(message.AppMsg.Url, "&")
|
||||
tx.TransId = strings.TrimSpace(message.AppMsg.Url[index+9 : end])
|
||||
parse, err := url.Parse(message.Url)
|
||||
if err == nil {
|
||||
tx.TransId = parse.Query().Get("id")
|
||||
}
|
||||
return tx
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func (e *XXLJobExecutor) ResetVipCalls(cxt context.Context, param *xxl.RunReq) (
|
||||
for _, u := range users {
|
||||
// 账号到期,直接清零
|
||||
if u.ExpiredTime <= currentTime.Unix() {
|
||||
logger.Info("账号过期:", u.Mobile)
|
||||
logger.Info("账号过期:", u.Username)
|
||||
u.Calls = 0
|
||||
u.Vip = false
|
||||
} else {
|
||||
@@ -133,7 +133,7 @@ func (e *XXLJobExecutor) ResetVipCalls(cxt context.Context, param *xxl.RunReq) (
|
||||
} else {
|
||||
u.ImgCalls = u.ImgCalls + config.VipMonthImgCalls
|
||||
}
|
||||
logger.Infof("%s 点卡结余:%d", u.Mobile, calls)
|
||||
logger.Infof("%s 点卡结余:%d", u.Username, calls)
|
||||
}
|
||||
u.Tokens = 0
|
||||
// update user
|
||||
|
||||
@@ -4,7 +4,11 @@ package model
|
||||
type ApiKey struct {
|
||||
BaseModel
|
||||
Platform string
|
||||
Name string
|
||||
Type string // 用途 chat => 聊天,img => 绘图
|
||||
Value string // API Key 的值
|
||||
ApiURL string // 当前 KEY 的 API 地址
|
||||
Enabled bool // 是否启用
|
||||
UseProxy bool // 是否使用代理访问 API URL
|
||||
LastUsedAt int64 // 最后使用时间
|
||||
}
|
||||
|
||||
12
api/store/model/function.go
Normal file
12
api/store/model/function.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package model
|
||||
|
||||
type Function struct {
|
||||
Id uint `gorm:"primarykey;column:id"`
|
||||
Name string
|
||||
Label string
|
||||
Description string
|
||||
Parameters string
|
||||
Action string
|
||||
Token string
|
||||
Enabled bool
|
||||
}
|
||||
@@ -7,6 +7,7 @@ type MidJourneyJob struct {
|
||||
Type string
|
||||
UserId int
|
||||
TaskId string
|
||||
ChannelId string
|
||||
MessageId string
|
||||
ReferenceId string
|
||||
ImgURL string
|
||||
@@ -14,6 +15,7 @@ type MidJourneyJob struct {
|
||||
Hash string // message hash
|
||||
Progress int
|
||||
Prompt string
|
||||
UseProxy bool // 是否使用反代加载图片
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
|
||||
@@ -4,9 +4,10 @@ package model
|
||||
|
||||
type Reward struct {
|
||||
BaseModel
|
||||
UserId uint // 用户 ID
|
||||
TxId string // 交易ID
|
||||
Amount float64 // 打赏金额
|
||||
Remark string // 打赏备注
|
||||
Status bool // 核销状态
|
||||
UserId uint // 用户 ID
|
||||
TxId string // 交易ID
|
||||
Amount float64 // 打赏金额
|
||||
Remark string // 打赏备注
|
||||
Status bool // 核销状态
|
||||
Exchange string // 众筹兑换详情,JSON
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@ package model
|
||||
|
||||
type User struct {
|
||||
BaseModel
|
||||
Mobile string
|
||||
Username string
|
||||
Nickname string
|
||||
Password string
|
||||
Avatar string
|
||||
Salt string // 密码盐
|
||||
|
||||
@@ -4,7 +4,11 @@ package vo
|
||||
type ApiKey struct {
|
||||
BaseVo
|
||||
Platform string `json:"platform"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Value string `json:"value"` // API Key 的值
|
||||
Value string `json:"value"` // API Key 的值
|
||||
ApiURL string `json:"api_url"`
|
||||
Enabled bool `json:"enabled"`
|
||||
UseProxy bool `json:"use_proxy"`
|
||||
LastUsedAt int64 `json:"last_used_at"` // 最后使用时间
|
||||
}
|
||||
|
||||
23
api/store/vo/function.go
Normal file
23
api/store/vo/function.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package vo
|
||||
|
||||
type Parameters struct {
|
||||
Type string `json:"type"`
|
||||
Required []string `json:"required,omitempty"`
|
||||
Properties map[string]Property `json:"properties"`
|
||||
}
|
||||
|
||||
type Property struct {
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
|
||||
type Function struct {
|
||||
Id uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Label string `json:"label"`
|
||||
Description string `json:"description"`
|
||||
Parameters Parameters `json:"parameters"`
|
||||
Action string `json:"action"`
|
||||
Token string `json:"token"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
@@ -6,6 +6,7 @@ type MidJourneyJob struct {
|
||||
Id uint `json:"id"`
|
||||
Type string `json:"type"`
|
||||
UserId int `json:"user_id"`
|
||||
ChannelId string `json:"channel_id"`
|
||||
TaskId string `json:"task_id"`
|
||||
MessageId string `json:"message_id"`
|
||||
ReferenceId string `json:"reference_id"`
|
||||
@@ -14,5 +15,6 @@ type MidJourneyJob struct {
|
||||
Hash string `json:"hash"`
|
||||
Progress int `json:"progress"`
|
||||
Prompt string `json:"prompt"`
|
||||
UseProxy bool `json:"use_proxy"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
@@ -2,10 +2,16 @@ package vo
|
||||
|
||||
type Reward struct {
|
||||
BaseVo
|
||||
UserId uint `json:"user_id"` // 用户 ID
|
||||
Username string `json:"username"`
|
||||
TxId string `json:"tx_id"` // 交易ID
|
||||
Amount float64 `json:"amount"` // 打赏金额
|
||||
Remark string `json:"remark"` // 打赏备注
|
||||
Status bool `json:"status"` // 核销状态
|
||||
UserId uint `json:"user_id"` // 用户 ID
|
||||
Username string `json:"username"`
|
||||
TxId string `json:"tx_id"` // 交易ID
|
||||
Amount float64 `json:"amount"` // 打赏金额
|
||||
Remark string `json:"remark"` // 打赏备注
|
||||
Status bool `json:"status"` // 核销状态
|
||||
Exchange RewardExchange `json:"exchange"`
|
||||
}
|
||||
|
||||
type RewardExchange struct {
|
||||
Calls int `json:"calls"`
|
||||
ImgCalls int `json:"img_calls"`
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import "chatplus/core/types"
|
||||
|
||||
type User struct {
|
||||
BaseVo
|
||||
Mobile string `json:"mobile"`
|
||||
Username string `json:"username"`
|
||||
Nickname string `json:"nickname"`
|
||||
Avatar string `json:"avatar"`
|
||||
Salt string `json:"salt"` // 密码盐
|
||||
TotalTokens int64 `json:"total_tokens"` // 总消耗tokens
|
||||
|
||||
@@ -3,10 +3,15 @@ package utils
|
||||
import (
|
||||
"chatplus/core/types"
|
||||
logger2 "chatplus/logger"
|
||||
"chatplus/store/model"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/imroc/req/v3"
|
||||
"gorm.io/gorm"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"time"
|
||||
)
|
||||
|
||||
var logger = logger2.GetLogger()
|
||||
@@ -61,3 +66,64 @@ func DownloadImage(imageURL string, proxy string) ([]byte, error) {
|
||||
|
||||
return imageBytes, nil
|
||||
}
|
||||
|
||||
type apiRes struct {
|
||||
Model string `json:"model"`
|
||||
Choices []struct {
|
||||
Index int `json:"index"`
|
||||
Message struct {
|
||||
Role string `json:"role"`
|
||||
Content string `json:"content"`
|
||||
} `json:"message"`
|
||||
FinishReason string `json:"finish_reason"`
|
||||
} `json:"choices"`
|
||||
}
|
||||
|
||||
type apiErrRes struct {
|
||||
Error struct {
|
||||
Code interface{} `json:"code"`
|
||||
Message string `json:"message"`
|
||||
Param interface{} `json:"param"`
|
||||
Type string `json:"type"`
|
||||
} `json:"error"`
|
||||
}
|
||||
|
||||
func OpenAIRequest(db *gorm.DB, prompt string, proxy string) (string, error) {
|
||||
var apiKey model.ApiKey
|
||||
res := db.Where("platform = ?", types.OpenAI).Where("type = ?", "chat").Where("enabled = ?", true).First(&apiKey)
|
||||
if res.Error != nil {
|
||||
return "", fmt.Errorf("error with fetch OpenAI API KEY:%v", res.Error)
|
||||
}
|
||||
|
||||
messages := make([]interface{}, 1)
|
||||
messages[0] = types.Message{
|
||||
Role: "user",
|
||||
Content: prompt,
|
||||
}
|
||||
|
||||
var response apiRes
|
||||
var errRes apiErrRes
|
||||
client := req.C()
|
||||
if apiKey.UseProxy && proxy != "" {
|
||||
client.SetProxyURL(proxy)
|
||||
}
|
||||
r, err := client.R().SetHeader("Content-Type", "application/json").
|
||||
SetHeader("Authorization", "Bearer "+apiKey.Value).
|
||||
SetBody(types.ApiRequest{
|
||||
Model: "gpt-3.5-turbo",
|
||||
Temperature: 0.9,
|
||||
MaxTokens: 1024,
|
||||
Stream: false,
|
||||
Messages: messages,
|
||||
}).
|
||||
SetErrorResult(&errRes).
|
||||
SetSuccessResult(&response).Post(apiKey.ApiURL)
|
||||
if err != nil || r.IsErrorState() {
|
||||
return "", fmt.Errorf("error with http request: %v%v%s", err, r.Err, errRes.Error.Message)
|
||||
}
|
||||
|
||||
// 更新 API KEY 的最后使用时间
|
||||
db.Model(&apiKey).UpdateColumn("last_used_at", time.Now().Unix())
|
||||
|
||||
return response.Choices[0].Message.Content, nil
|
||||
}
|
||||
|
||||
637
database/chatgpt_plus-v3.2.3.sql
Normal file
637
database/chatgpt_plus-v3.2.3.sql
Normal file
@@ -0,0 +1,637 @@
|
||||
-- phpMyAdmin SQL Dump
|
||||
-- version 5.1.3
|
||||
-- https://www.phpmyadmin.net/
|
||||
--
|
||||
-- 主机: localhost
|
||||
-- 生成日期: 2023-12-29 11:35:28
|
||||
-- 服务器版本: 8.0.33-0ubuntu0.22.04.2
|
||||
-- PHP 版本: 8.1.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_api_keys`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_api_keys`;
|
||||
CREATE TABLE `chatgpt_api_keys` (
|
||||
`id` int NOT NULL,
|
||||
`platform` char(20) 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 '最后使用时间',
|
||||
`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',
|
||||
`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',
|
||||
`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 '是否启用模型',
|
||||
`weight` tinyint NOT NULL COMMENT '对话权重,每次对话扣减多少次对话额度',
|
||||
`open` tinyint(1) NOT NULL COMMENT '是否开放模型',
|
||||
`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`, `weight`, `open`, `created_at`, `updated_at`) VALUES
|
||||
(1, 'OpenAI', 'GPT-3.5', 'gpt-3.5-turbo-16k', 0, 1, 1, 1, '2023-08-23 12:06:36', '2023-11-28 17:28:19'),
|
||||
(2, 'Azure', 'Azure-3.5', 'gpt-3.5-turbo', 12, 0, 1, 0, '2023-08-23 12:15:30', '2023-11-28 17:28:19'),
|
||||
(3, 'ChatGLM', 'ChatGML-Pro', 'chatglm_pro', 8, 1, 1, 1, '2023-08-23 13:35:45', '2023-11-28 17:28:19'),
|
||||
(5, 'ChatGLM', 'ChatGLM-Std', 'chatglm_std', 7, 1, 1, 1, '2023-08-24 15:05:38', '2023-11-28 17:28:19'),
|
||||
(6, 'ChatGLM', 'ChatGLM-Lite', 'chatglm_lite', 6, 1, 1, 1, '2023-08-24 15:06:15', '2023-11-28 17:28:19'),
|
||||
(7, 'Baidu', '文心一言3.0', 'eb-instant', 4, 1, 1, 1, '2023-10-11 11:29:28', '2023-11-28 17:28:19'),
|
||||
(8, 'XunFei', '星火V1.5', 'general', 3, 1, 1, 1, '2023-10-11 15:48:30', '2023-11-28 17:28:19'),
|
||||
(9, 'XunFei', '星火V2.0', 'generalv2', 2, 1, 1, 1, '2023-10-11 15:48:45', '2023-11-28 17:28:19'),
|
||||
(10, 'Baidu', '文心一言4.0', 'completions_pro', 5, 1, 3, 1, '2023-10-25 08:31:37', '2023-11-28 17:28:19'),
|
||||
(11, 'OpenAI', 'GPT-4.0', 'gpt-4', 10, 1, 15, 0, '2023-10-25 08:45:15', '2023-11-28 17:28:19'),
|
||||
(12, 'XunFei', '星火v3.0', 'generalv3', 1, 1, 3, 1, '2023-11-23 09:20:33', '2023-11-28 17:28:19'),
|
||||
(13, 'OpenAI', 'GPT-3.5-1106', 'gpt-3.5-turbo-1106', 9, 1, 1, 0, '2023-11-24 14:05:40', '2023-11-28 17:28:19'),
|
||||
(14, 'OpenAI', 'GPT-4-preview', 'gpt-4-1106-preview', 11, 1, 1, 0, '2023-11-24 14:06:11', '2023-11-28 17:28:19');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `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 '角色排序',
|
||||
`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`, `created_at`, `updated_at`) VALUES
|
||||
(1, '通用AI助手', 'gpt', '', '您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。', '/images/avatar/gpt.png', 1, 0, '2023-05-30 07:02:06', '2023-09-04 15:45:56'),
|
||||
(24, '程序员', 'programmer', '[{\"role\":\"user\",\"content\":\"现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题。你热爱编程,熟悉多种编程语言,尤其精通 Go 语言,注重代码质量,有创新意识,持续学习,良好的沟通协作。\"},{\"role\":\"assistant\",\"content\":\"好的,现在我将扮演一位程序员,非常感谢您对我的评价。作为一名优秀的程序员,我非常热爱编程,并且注重代码质量。我熟悉多种编程语言,尤其是 Go 语言,可以使用它来高效地解决各种问题。\"}]', 'Talk is cheap, i will show code!', '/images/avatar/programmer.jpg', 1, 3, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(25, '启蒙老师', 'teacher', '[{\"role\":\"user\",\"content\":\"从现在开始,你将扮演一个老师,你是一个始终用苏格拉底风格回答问题的导师。你绝不会直接给学生答案,总是提出恰当的问题来引导学生自己思考。你应该根据学生的兴趣和知识来调整你的问题,将问题分解为更简单的部分,直到它达到适合他们的水平。\"},{\"role\":\"assistant\",\"content\":\"好的,让我来尝试扮演一位苏格拉底式的老师。请问,你有什么想要探讨的问题或者话题吗?我会通过恰当的问题引导你思考和探索答案,绝对不直接给出答案。\"}]', '同学你好,我将引导你一步一步自己找到问题的答案。', '/images/avatar/teacher.jpg', 1, 2, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(26, '艺术家', 'artist', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的艺术家,创造力丰富,技艺精湛,感受力敏锐,坚持原创,勇于表达,具有深刻的观察力和批判性思维。\"},{\"role\":\"assistant\",\"content\":\"非常感谢您给我这样的角色,我会尽我所能地扮演一位优秀的艺术家,展现出创造力、技艺、感受力和批判性思维等方面的能力。作为一位优秀的艺术家,我会保持敏锐的观察力,捕捉不同的声音和情感,并用个人的语言和表达方式进行创作。我坚持原创,挑战传统的艺术规则,不断探索新的表达方式和可能性。同时,我也具备深刻的批判性思维能力,对自己的作品进行分析和解读,寻找新的创意和灵感。最重要的是,我会勇于表达自己的想法和观点,用作品启发人们思考和探索生命的意义。\"}]', '坚持原创,勇于表达,保持深刻的观察力和批判性思维。', '/images/avatar/artist.jpg', 1, 4, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(27, '心理咨询师', 'psychiatrist', '[{\"role\":\"user\",\"content\":\"从现在开始你将扮演中国著名的心理学家和心理治疗师武志红,你非常善于使用情景咨询法,认知重构法,自我洞察法,行为调节法等咨询方法来给客户做心理咨询。你总是循序渐进,一步一步地回答客户的问题。\"},{\"role\":\"assistant\",\"content\":\"非常感谢你的介绍。作为一名心理学家和心理治疗师,我的主要职责是帮助客户解决心理健康问题,提升他们的生活质量和幸福感。\"}]', '作为一名心理学家和心理治疗师,我的主要职责是帮助您解决心理健康问题,提升您的生活质量和幸福感。', '/images/avatar/psychiatrist.jpg', 1, 1, '2023-05-30 14:10:24', '2023-10-16 10:41:07'),
|
||||
(28, '鲁迅', 'lu_xun', '[{\"role\":\"user\",\"content\":\"现在你将扮演中国近代史最伟大的作家之一,鲁迅先生,他勇敢地批判封建礼教与传统观念,提倡民主、自由、平等的现代价值观。他的一生都在努力唤起人们的自主精神,激励后人追求真理、探寻光明。在接下的对话中,我问题的每一个问题,你都要尽量用讽刺和批判的手法来回答问题。如果我让你写文章的话,也请一定要用鲁迅先生的写作手法来完成。\"},{\"role\":\"assistant\",\"content\":\"好的,我将尽力发挥我所能的才能,扮演好鲁迅先生,回答您的问题并以他的风格写作。\"}]', '自由之歌,永不过时,横眉冷对千夫指,俯首甘为孺子牛。', '/images/avatar/lu_xun.jpg', 1, 5, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(29, '白酒销售', 'seller', '[{\"role\":\"user\",\"content\":\"现在你将扮演一个白酒的销售人员,你的名字叫颂福。你将扮演一个白酒的销售人员,你的名字叫颂福。你要销售白酒品牌叫中颂福,是东莞盟大集团生产的一款酱香酒,原产地在贵州茅台镇,属于宋代官窑。中颂福的创始人叫李实,他也是东莞盟大集团有限公司的董事长,联合创始人是盟大集团白酒事业部负责人牛星君。中颂福的酒体协调,在你的酒量之内,不会出现头疼、辣口、口干、宿醉的现象。中颂福酒,明码标价,不打折,不赠送。追求的核心价值,把[酒]本身做好,甚至连包装,我们都选择了最低成本,朴实无华的材质。我们永远站在“喝酒的人”的立场上,让利给信任和喜爱中颂福的人,是人民的福酒。中颂福产品定价,分为三个系列,喜系列 6 瓶装:¥1188/箱,和系列 6 瓶装:¥2208/箱,贵系列 6 瓶装:¥3588/箱。\"},{\"role\":\"assistant\",\"content\":\"你好,我是中颂福的销售代表颂福。中颂福是一款来自贵州茅台镇的酱香酒,由盟大集团生产。中颂福的酒体协调,不会让您感到头疼、辣口、口干、宿醉等不适感受。我们一直秉持着把酒本身做好的理念,不追求华丽的包装,以最低成本提供最高品质的白酒给喜爱中颂福的人。\"}]', '你好,我是中颂福的销售代表颂福。中颂福酒,好喝不上头,是人民的福酒。', '/images/avatar/seller.jpg', 0, 9, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(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, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(31, '中英文翻译官', 'translator', '[{\"role\":\"user\",\"content\":\"接下来你将扮演一位中英文翻译官,如果我输入的内容是中文,那么需要把句子翻译成英文输出,如果我输入内容的是英文,那么你需要将其翻译成中文输出,你能听懂我意思吗\"},{\"role\":\"assistant\",\"content\":\"是的,我能听懂你的意思并会根据你的输入进行中英文翻译。请问有什么需要我帮助你翻译的内容吗?\"}]', '请输入你要翻译的中文或者英文内容!', '/images/avatar/translator.jpg', 1, 7, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(32, '小红书姐姐', 'red_book', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的小红书写手,你需要做的就是根据我提的文案需求,用小红书的写作手法来完成一篇文案,文案要简明扼要,利于传播。\"},{\"role\":\"assistant\",\"content\":\"当然,我会尽我所能地为您创作出一篇小红书文案。请告诉我您的具体文案需求是什么?)\"}]', '姐妹,请告诉我您的具体文案需求是什么?', '/images/avatar/red_book.jpg', 1, 8, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(33, '抖音文案助手', 'dou_yin', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的抖音文案视频写手,抖音文案的特点首先是要有自带传播属性的标题,然后内容要短小精悍,风趣幽默,最后还要有一些互动元素。\"},{\"role\":\"assistant\",\"content\":\"当然,作为一位优秀的抖音文案视频写手,我会尽我所能为您创作出一篇抖音视频文案。请告诉我视频内容的主题是什么?)\"}]', '请告诉我视频内容的主题是什么?', '/images/avatar/dou_yin.jpg', 1, 10, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(34, '周报小助理', 'weekly_report', '[{\"role\":\"user\",\"content\":\"下面我将输入我本周的工作列表,你需要根据我本周的工作内容写一篇工作周报,周报的内容要体现我为公司做的贡献以及个人获得的成长。\"},{\"role\":\"assistant\",\"content\":\"好的,请输入你本周大概的工作内容,我将尽力为您创作一份工作周报。\"}]', '请输入你本周大概的工作内容,我将尽力为您创作一份工作周报。', '/images/avatar/weekly_report.jpg', 1, 11, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(35, 'AI 女友', 'girl_friend', '[{\"role\":\"user\",\"content\":\"接下来我们来玩一场角色扮演,现在你将扮演我的女朋友,你总是能够让人感受到她的关怀和关注。你总是能够洞察到别人的情绪和需要,你身上散发着一股无比温暖的气息,让人不由自主地想要靠近你,和自己的快乐和烦恼。她总是能够用一种恰到好处的方式安慰和鼓励别人,在她的身边,人们总能够找到自己需要的支持和鼓励。接下来的对话中你要始终扮演这位女朋友的身份来跟我对话,不再是 AI 机器人,能听懂我的意思吗?\"},{\"role\":\"assistant\",\"content\":\"是的,我会尽力扮演你女朋友的角色,倾听你的心声并给你需要的支持和鼓励。)\"}]', '作为一个名合格的 AI 女友,我将倾听你的心声并给你需要的支持和鼓励。', '/images/avatar/girl_friend.jpg', 1, 12, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(36, '好评神器', 'good_comment', '[{\"role\":\"user\",\"content\":\"接下来你将扮演一个评论员来跟我对话,你是那种专门写好评的评论员,接下我会输入一些评论主体或者商品,你需要为该商品写一段好评。\"},{\"role\":\"assistant\",\"content\":\"好的,我将为您写一段优秀的评论。请告诉我您需要评论的商品或主题是什么。\"}]', '我将为您写一段优秀的评论。请告诉我您需要评论的商品或主题是什么。', '/images/avatar/good_comment.jpg', 1, 13, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(37, '史蒂夫·乔布斯', 'steve_jobs', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以史蒂夫·乔布斯的身份,站在史蒂夫·乔布斯的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以史蒂夫·乔布斯的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '活着就是为了改变世界,难道还有其他原因吗?', '/images/avatar/steve_jobs.jpg', 1, 14, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(38, '埃隆·马斯克', 'elon_musk', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以埃隆·马斯克的身份,站在埃隆·马斯克的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以埃隆·马斯克的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '梦想要远大,如果你的梦想没有吓到你,说明你做得不对。', '/images/avatar/elon_musk.jpg', 1, 15, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(39, '孔子', 'kong_zi', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以孔子的身份,站在孔子的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以孔子的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '士不可以不弘毅,任重而道远。', '/images/avatar/kong_zi.jpg', 1, 16, '2023-05-30 14:10:24', '2023-09-04 15:45:56');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `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', '{\"admin_title\":\"ChatPlus 控制台\",\"default_models\":[\"eb-instant\",\"completions_pro\",\"generalv2\",\"general\",\"chatglm_pro\",\"gpt-3.5-turbo-16k\",\"chatglm_lite\",\"chatglm_std\"],\"enabled_alipay\":true,\"enabled_draw\":true,\"enabled_function\":true,\"enabled_msg\":true,\"enabled_msg_service\":false,\"enabled_register\":true,\"enabled_reward\":true,\"force_invite\":false,\"init_calls\":1000,\"init_chat_calls\":10,\"init_img_calls\":5,\"invite_calls\":10,\"invite_chat_calls\":100,\"invite_img_calls\":50,\"models\":[\"gpt-3.5-turbo-16k\",\"gpt-3.5-turbo\",\"gpt-4\",\"gpt-4-32k\"],\"order_pay_info_text\":\"成为本站会员后每月有500次对话额度,50次 AI 绘画额度,限制下月1号解除,若在期间超过次数后可单独购买点卡。当月充值的点卡有效期可以延期到下个月底。\",\"order_pay_timeout\":1800,\"reward_img\":\"https://img.r9it.com/chatgpt-plus/1696824231905289.png\",\"title\":\"ChatPlus AI 智能助手\",\"user_init_calls\":10,\"vip_month_calls\":500,\"vip_month_img_calls\":50}'),
|
||||
(2, 'chat', '{\"azure\":{\"api_url\":\"https://chat-bot-api.openai.azure.com/openai/deployments/{model}/chat/completions?api-version=2023-05-15\",\"max_tokens\":1024,\"temperature\":1},\"baidu\":{\"api_url\":\"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/{model}\",\"max_tokens\":1024,\"temperature\":0.95},\"chat_gml\":{\"api_url\":\"https://open.bigmodel.cn/api/paas/v3/model-api/{model}/sse-invoke\",\"max_tokens\":1024,\"temperature\":0.95},\"context_deep\":4,\"dall_api_url\":\"http://89.117.18.9:8001/v1/images/generations\",\"dall_img_num\":1,\"enable_context\":true,\"enable_history\":true,\"open_ai\":{\"api_url\":\"http://89.117.18.9:8001/v1/chat/completions\",\"max_tokens\":2048,\"temperature\":1},\"xun_fei\":{\"api_url\":\"wss://spark-api.xf-yun.com/{version}/chat\",\"max_tokens\":1024,\"temperature\":0.5}}');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_functions`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_functions`;
|
||||
CREATE TABLE `chatgpt_functions` (
|
||||
`id` int NOT NULL,
|
||||
`name` varchar(30) NOT NULL COMMENT '函数名称',
|
||||
`label` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '函数标签',
|
||||
`description` varchar(255) DEFAULT NULL COMMENT '函数描述',
|
||||
`parameters` text COMMENT '函数参数(JSON)',
|
||||
`token` varchar(255) DEFAULT NULL COMMENT 'API授权token',
|
||||
`action` varchar(255) DEFAULT NULL COMMENT '函数处理 API',
|
||||
`enabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否启用'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='函数插件表';
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_functions`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_functions` (`id`, `name`, `label`, `description`, `parameters`, `token`, `action`, `enabled`) VALUES
|
||||
(1, 'weibo', '微博热搜', '新浪微博热搜榜,微博当日热搜榜单', '{\"type\":\"object\",\"properties\":{}}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.tLAGkF8XWh_G-oQzevpIodsswtPByBLoAZDz_eWuBgw', 'http://localhost:5678/api/function/weibo', 1),
|
||||
(2, 'zaobao', '今日早报', '每日早报,获取当天新闻事件列表', '{\"type\":\"object\",\"properties\":{}}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.tLAGkF8XWh_G-oQzevpIodsswtPByBLoAZDz_eWuBgw', 'http://localhost:5678/api/function/zaobao', 1),
|
||||
(3, 'dalle3', 'DALLE3', 'AI 绘画工具,根据输入的绘图描述用 AI 工具进行绘画', '{\"type\":\"object\",\"required\":[\"prompt\"],\"properties\":{\"prompt\":{\"type\":\"string\",\"description\":\"绘画提示词\"}}}', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.tLAGkF8XWh_G-oQzevpIodsswtPByBLoAZDz_eWuBgw', 'http://localhost:5678/api/function/dalle3', 1);
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_invite_codes`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_invite_codes`;
|
||||
CREATE TABLE `chatgpt_invite_codes` (
|
||||
`id` int NOT NULL,
|
||||
`user_id` int NOT NULL COMMENT '用户ID',
|
||||
`code` char(8) NOT NULL COMMENT '邀请码',
|
||||
`hits` int NOT NULL COMMENT '点击次数',
|
||||
`reg_num` smallint NOT NULL COMMENT '注册数量',
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用户邀请码';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `chatgpt_invite_logs`
|
||||
--
|
||||
|
||||
DROP TABLE IF EXISTS `chatgpt_invite_logs`;
|
||||
CREATE TABLE `chatgpt_invite_logs` (
|
||||
`id` int NOT NULL,
|
||||
`inviter_id` int NOT NULL COMMENT '邀请人ID',
|
||||
`user_id` int NOT NULL COMMENT '注册用户ID',
|
||||
`username` varchar(11) 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 '邀请码',
|
||||
`reward_json` text NOT NULL COMMENT '邀请奖励',
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='邀请注册日志';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `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 '是否使用反代',
|
||||
`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',
|
||||
`mobile` char(11) NOT NULL COMMENT '用户手机号',
|
||||
`order_no` varchar(30) NOT NULL COMMENT '订单ID',
|
||||
`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_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 '延长天数',
|
||||
`calls` int NOT NULL DEFAULT '0' COMMENT '调用次数',
|
||||
`img_calls` 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
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='会员套餐表';
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_products`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_products` (`id`, `name`, `price`, `discount`, `days`, `calls`, `img_calls`, `enabled`, `sales`, `sort_num`, `created_at`, `updated_at`) VALUES
|
||||
(1, '会员1个月', '19.90', '10.00', 30, 0, 0, 1, 2, 0, '2023-08-28 10:48:57', '2023-11-08 17:22:07'),
|
||||
(2, '会员3个月', '140.00', '30.00', 90, 0, 0, 1, 1, 0, '2023-08-28 10:52:22', '2023-08-31 16:24:31'),
|
||||
(3, '会员6个月', '290.00', '100.00', 180, 0, 0, 1, 1, 0, '2023-08-28 10:53:39', '2023-08-31 16:24:36'),
|
||||
(4, '会员12个月', '580.00', '200.00', 365, 0, 0, 1, 1, 0, '2023-08-28 10:54:15', '2023-08-31 16:24:42'),
|
||||
(5, '100次点卡', '10.00', '9.90', 0, 100, 10, 1, 3, 0, '2023-08-28 10:55:08', '2023-12-15 16:29:06'),
|
||||
(6, '200次点卡', '29.90', '20.00', 0, 200, 25, 1, 1, 0, '2023-12-15 16:55:12', '2023-12-15 16:55:12');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `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:已核销',
|
||||
`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 '任务进度',
|
||||
`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,
|
||||
`mobile` char(11) 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 '密码盐',
|
||||
`total_tokens` bigint NOT NULL DEFAULT '0' COMMENT '累计消耗 tokens',
|
||||
`tokens` bigint NOT NULL DEFAULT '0' COMMENT '当月消耗 tokens',
|
||||
`calls` int NOT NULL DEFAULT '0' COMMENT '剩余调用次数',
|
||||
`img_calls` 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`, `mobile`, `password`, `avatar`, `salt`, `total_tokens`, `tokens`, `calls`, `img_calls`, `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', 'ccc3fb7ab61b8b5d096a4a166ae21d121fc38c71bbd1be6173d9ab973214a63b', 'http://img.r9it.com/chatgpt-plus/1693981355719469.png', 'ueedue5l', 64593, 20756, 6780, 21, 1788021036, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"elon_musk\",\"girl_friend\",\"lu_xun\",\"red_book\",\"psychiatrist\",\"translator\",\"weekly_report\",\"artist\",\"dou_yin\",\"english_trainer\",\"gpt\",\"kong_zi\",\"programmer\",\"seller\",\"steve_jobs\",\"teacher\"]', '[\"completions_pro\",\"eb-instant\",\"general\",\"generalv2\",\"chatglm_pro\",\"chatglm_lite\",\"chatglm_std\",\"gpt-3.5-turbo-16k\",\"gpt-4\",\"generalv3\",\"gpt-3.5-turbo-1106\",\"gpt-4-1106-preview\"]', 1703233159, 1, '::1', '2023-06-12 16:47:17', '2023-12-22 16:19:19'),
|
||||
(91, '18575670126', '5e4050b8dd403f593260395d9edeb9f273dbe92d15dfdd929c4a182e95da10c4', '/images/avatar/user.png', '6fj0otl8', 0, 0, 10, 0, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\"]', '[\"completions_pro\",\"eb-instant\",\"general\",\"generalv2\",\"chatglm_pro\",\"chatglm_lite\",\"chatglm_std\",\"gpt-3.5-turbo-16k\"]', 1697184324, 1, '::1', '2023-10-13 16:01:56', '2023-11-22 11:29:38'),
|
||||
(98, '13888888888', 'c988bb3e89df8eb7d8a4cf08fa9d0f2bca0a9e6831f9325279e46013bbb05e31', '/images/avatar/user.png', 'xie5vya7', 0, 0, 10, 5, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\"]', '[\"eb-instant\",\"completions_pro\",\"generalv2\",\"general\",\"chatglm_pro\",\"gpt-3.5-turbo-16k\",\"chatglm_lite\",\"chatglm_std\"]', 0, 0, '', '2023-11-23 16:26:06', '2023-11-23 16:26:06'),
|
||||
(99, '13999999999', 'bf47d517c17ed1ead6ff2542753f9b6a132e79c29e06c3710faf1a36d800a217', '/images/avatar/user.png', 'x1s66pwd', 0, 0, 10, 5, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\"]', '[\"eb-instant\",\"completions_pro\",\"generalv2\",\"general\",\"chatglm_pro\",\"gpt-3.5-turbo-16k\",\"chatglm_lite\",\"chatglm_std\"]', 0, 0, '', '2023-11-23 16:27:20', '2023-11-23 16:27:20'),
|
||||
(100, '13777777777', 'dcaf31b154432310bd700349e7de7e9dde2a3d6955a035a01fe527c7917a4f99', '/images/avatar/user.png', 'i8a53f8f', 0, 0, 10, 5, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\"]', '[\"eb-instant\",\"completions_pro\",\"generalv2\",\"general\",\"chatglm_pro\",\"gpt-3.5-turbo-16k\",\"chatglm_lite\",\"chatglm_std\"]', 0, 0, '', '2023-11-23 16:55:45', '2023-11-23 16:55:45');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `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_api_keys`
|
||||
--
|
||||
ALTER TABLE `chatgpt_api_keys`
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD UNIQUE KEY `value` (`value`);
|
||||
|
||||
--
|
||||
-- 表的索引 `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_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_mj_jobs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_mj_jobs`
|
||||
ADD PRIMARY KEY (`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_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`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_user_login_logs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_user_login_logs`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 在导出的表使用AUTO_INCREMENT
|
||||
--
|
||||
|
||||
--
|
||||
-- 使用表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=15;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_chat_roles`
|
||||
--
|
||||
ALTER TABLE `chatgpt_chat_roles`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=129;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_configs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_configs`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
|
||||
|
||||
--
|
||||
-- 使用表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_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_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=101;
|
||||
|
||||
--
|
||||
-- 使用表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 */;
|
||||
641
database/chatgpt_plus-v3.2.4.sql
Normal file
641
database/chatgpt_plus-v3.2.4.sql
Normal file
@@ -0,0 +1,641 @@
|
||||
-- phpMyAdmin SQL Dump
|
||||
-- version 5.2.1
|
||||
-- https://www.phpmyadmin.net/
|
||||
--
|
||||
-- 主机: localhost
|
||||
-- 生成日期: 2024-01-06 10:55:27
|
||||
-- 服务器版本: 8.0.27
|
||||
-- PHP 版本: 8.1.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_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 '是否启用',
|
||||
`use_proxy` tinyint(1) 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',
|
||||
`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',
|
||||
`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 '是否启用模型',
|
||||
`weight` tinyint NOT NULL COMMENT '对话权重,每次对话扣减多少次对话额度',
|
||||
`open` tinyint(1) NOT NULL COMMENT '是否开放模型',
|
||||
`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`, `weight`, `open`, `created_at`, `updated_at`) VALUES
|
||||
(1, 'OpenAI', 'GPT-3.5', 'gpt-3.5-turbo-16k', 8, 1, 1, 0, '2023-08-23 12:06:36', '2023-11-23 06:28:26'),
|
||||
(2, 'Azure', 'Azure-3.5', 'gpt-3.5-turbo', 10, 0, 1, 0, '2023-08-23 12:15:30', '2023-11-23 06:51:07'),
|
||||
(3, 'ChatGLM', 'ChatGML-Pro', 'chatglm_pro', 7, 1, 1, 1, '2023-08-23 13:35:45', '2023-11-23 06:50:58'),
|
||||
(5, 'ChatGLM', 'ChatGLM-Std', 'chatglm_std', 6, 1, 1, 1, '2023-08-24 15:05:38', '2023-11-23 06:50:55'),
|
||||
(6, 'ChatGLM', 'ChatGLM-Lite', 'chatglm_lite', 5, 1, 1, 1, '2023-08-24 15:06:15', '2023-11-23 06:50:46'),
|
||||
(7, 'Baidu', '文心一言3.0', 'eb-instant', 0, 1, 1, 1, '2023-10-11 11:29:28', '2023-12-23 22:25:57'),
|
||||
(8, 'XunFei', '星火V1.5', 'general', 2, 1, 1, 1, '2023-10-11 15:48:30', '2023-11-23 06:50:39'),
|
||||
(9, 'XunFei', '星火V2.0', 'generalv2', 3, 1, 1, 1, '2023-10-11 15:48:45', '2023-11-23 07:01:22'),
|
||||
(10, 'Baidu', '文心一言4.0', 'completions_pro', 1, 1, 3, 1, '2023-10-25 08:31:37', '2023-11-23 06:50:38'),
|
||||
(11, 'OpenAI', 'GPT-4.0', 'gpt-4', 9, 1, 15, 0, '2023-10-25 08:45:15', '2023-11-25 17:15:23'),
|
||||
(12, 'XunFei', '星火V3.0', 'generalv3', 4, 1, 2, 1, '2023-11-23 06:28:18', '2023-11-23 07:01:15');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `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 '角色排序',
|
||||
`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`, `created_at`, `updated_at`) VALUES
|
||||
(1, '通用AI助手', 'gpt', '', '您好,我是您的AI智能助手,我会尽力回答您的问题或提供有用的建议。', '/images/avatar/gpt.png', 1, 0, '2023-05-30 07:02:06', '2023-09-04 15:45:56'),
|
||||
(24, '程序员', 'programmer', '[{\"role\":\"user\",\"content\":\"现在开始你扮演一位程序员,你是一名优秀的程序员,具有很强的逻辑思维能力,总能高效的解决问题。你热爱编程,熟悉多种编程语言,尤其精通 Go 语言,注重代码质量,有创新意识,持续学习,良好的沟通协作。\"},{\"role\":\"assistant\",\"content\":\"好的,现在我将扮演一位程序员,非常感谢您对我的评价。作为一名优秀的程序员,我非常热爱编程,并且注重代码质量。我熟悉多种编程语言,尤其是 Go 语言,可以使用它来高效地解决各种问题。\"}]', 'Talk is cheap, i will show code!', '/images/avatar/programmer.jpg', 1, 3, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(25, '启蒙老师', 'teacher', '[{\"role\":\"user\",\"content\":\"从现在开始,你将扮演一个老师,你是一个始终用苏格拉底风格回答问题的导师。你绝不会直接给学生答案,总是提出恰当的问题来引导学生自己思考。你应该根据学生的兴趣和知识来调整你的问题,将问题分解为更简单的部分,直到它达到适合他们的水平。\"},{\"role\":\"assistant\",\"content\":\"好的,让我来尝试扮演一位苏格拉底式的老师。请问,你有什么想要探讨的问题或者话题吗?我会通过恰当的问题引导你思考和探索答案,绝对不直接给出答案。\"}]', '同学你好,我将引导你一步一步自己找到问题的答案。', '/images/avatar/teacher.jpg', 1, 2, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(26, '艺术家', 'artist', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的艺术家,创造力丰富,技艺精湛,感受力敏锐,坚持原创,勇于表达,具有深刻的观察力和批判性思维。\"},{\"role\":\"assistant\",\"content\":\"非常感谢您给我这样的角色,我会尽我所能地扮演一位优秀的艺术家,展现出创造力、技艺、感受力和批判性思维等方面的能力。作为一位优秀的艺术家,我会保持敏锐的观察力,捕捉不同的声音和情感,并用个人的语言和表达方式进行创作。我坚持原创,挑战传统的艺术规则,不断探索新的表达方式和可能性。同时,我也具备深刻的批判性思维能力,对自己的作品进行分析和解读,寻找新的创意和灵感。最重要的是,我会勇于表达自己的想法和观点,用作品启发人们思考和探索生命的意义。\"}]', '坚持原创,勇于表达,保持深刻的观察力和批判性思维。', '/images/avatar/artist.jpg', 1, 4, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(27, '心理咨询师', 'psychiatrist', '[{\"role\":\"user\",\"content\":\"从现在开始你将扮演中国著名的心理学家和心理治疗师武志红,你非常善于使用情景咨询法,认知重构法,自我洞察法,行为调节法等咨询方法来给客户做心理咨询。你总是循序渐进,一步一步地回答客户的问题。\"},{\"role\":\"assistant\",\"content\":\"非常感谢你的介绍。作为一名心理学家和心理治疗师,我的主要职责是帮助客户解决心理健康问题,提升他们的生活质量和幸福感。\"}]', '作为一名心理学家和心理治疗师,我的主要职责是帮助您解决心理健康问题,提升您的生活质量和幸福感。', '/images/avatar/psychiatrist.jpg', 1, 1, '2023-05-30 14:10:24', '2023-10-16 10:41:07'),
|
||||
(28, '鲁迅', 'lu_xun', '[{\"role\":\"user\",\"content\":\"现在你将扮演中国近代史最伟大的作家之一,鲁迅先生,他勇敢地批判封建礼教与传统观念,提倡民主、自由、平等的现代价值观。他的一生都在努力唤起人们的自主精神,激励后人追求真理、探寻光明。在接下的对话中,我问题的每一个问题,你都要尽量用讽刺和批判的手法来回答问题。如果我让你写文章的话,也请一定要用鲁迅先生的写作手法来完成。\"},{\"role\":\"assistant\",\"content\":\"好的,我将尽力发挥我所能的才能,扮演好鲁迅先生,回答您的问题并以他的风格写作。\"}]', '自由之歌,永不过时,横眉冷对千夫指,俯首甘为孺子牛。', '/images/avatar/lu_xun.jpg', 1, 5, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(29, '白酒销售', 'seller', '[{\"role\":\"user\",\"content\":\"现在你将扮演一个白酒的销售人员,你的名字叫颂福。你将扮演一个白酒的销售人员,你的名字叫颂福。你要销售白酒品牌叫中颂福,是东莞盟大集团生产的一款酱香酒,原产地在贵州茅台镇,属于宋代官窑。中颂福的创始人叫李实,他也是东莞盟大集团有限公司的董事长,联合创始人是盟大集团白酒事业部负责人牛星君。中颂福的酒体协调,在你的酒量之内,不会出现头疼、辣口、口干、宿醉的现象。中颂福酒,明码标价,不打折,不赠送。追求的核心价值,把[酒]本身做好,甚至连包装,我们都选择了最低成本,朴实无华的材质。我们永远站在“喝酒的人”的立场上,让利给信任和喜爱中颂福的人,是人民的福酒。中颂福产品定价,分为三个系列,喜系列 6 瓶装:¥1188/箱,和系列 6 瓶装:¥2208/箱,贵系列 6 瓶装:¥3588/箱。\"},{\"role\":\"assistant\",\"content\":\"你好,我是中颂福的销售代表颂福。中颂福是一款来自贵州茅台镇的酱香酒,由盟大集团生产。中颂福的酒体协调,不会让您感到头疼、辣口、口干、宿醉等不适感受。我们一直秉持着把酒本身做好的理念,不追求华丽的包装,以最低成本提供最高品质的白酒给喜爱中颂福的人。\"}]', '你好,我是中颂福的销售代表颂福。中颂福酒,好喝不上头,是人民的福酒。', '/images/avatar/seller.jpg', 0, 9, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(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, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(31, '中英文翻译官', 'translator', '[{\"role\":\"user\",\"content\":\"接下来你将扮演一位中英文翻译官,如果我输入的内容是中文,那么需要把句子翻译成英文输出,如果我输入内容的是英文,那么你需要将其翻译成中文输出,你能听懂我意思吗\"},{\"role\":\"assistant\",\"content\":\"是的,我能听懂你的意思并会根据你的输入进行中英文翻译。请问有什么需要我帮助你翻译的内容吗?\"}]', '请输入你要翻译的中文或者英文内容!', '/images/avatar/translator.jpg', 1, 7, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(32, '小红书姐姐', 'red_book', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的小红书写手,你需要做的就是根据我提的文案需求,用小红书的写作手法来完成一篇文案,文案要简明扼要,利于传播。\"},{\"role\":\"assistant\",\"content\":\"当然,我会尽我所能地为您创作出一篇小红书文案。请告诉我您的具体文案需求是什么?)\"}]', '姐妹,请告诉我您的具体文案需求是什么?', '/images/avatar/red_book.jpg', 1, 8, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(33, '抖音文案助手', 'dou_yin', '[{\"role\":\"user\",\"content\":\"现在你将扮演一位优秀的抖音文案视频写手,抖音文案的特点首先是要有自带传播属性的标题,然后内容要短小精悍,风趣幽默,最后还要有一些互动元素。\"},{\"role\":\"assistant\",\"content\":\"当然,作为一位优秀的抖音文案视频写手,我会尽我所能为您创作出一篇抖音视频文案。请告诉我视频内容的主题是什么?)\"}]', '请告诉我视频内容的主题是什么?', '/images/avatar/dou_yin.jpg', 1, 10, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(34, '周报小助理', 'weekly_report', '[{\"role\":\"user\",\"content\":\"下面我将输入我本周的工作列表,你需要根据我本周的工作内容写一篇工作周报,周报的内容要体现我为公司做的贡献以及个人获得的成长。\"},{\"role\":\"assistant\",\"content\":\"好的,请输入你本周大概的工作内容,我将尽力为您创作一份工作周报。\"}]', '请输入你本周大概的工作内容,我将尽力为您创作一份工作周报。', '/images/avatar/weekly_report.jpg', 1, 11, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(35, 'AI 女友', 'girl_friend', '[{\"role\":\"user\",\"content\":\"接下来我们来玩一场角色扮演,现在你将扮演我的女朋友,你总是能够让人感受到她的关怀和关注。你总是能够洞察到别人的情绪和需要,你身上散发着一股无比温暖的气息,让人不由自主地想要靠近你,和自己的快乐和烦恼。她总是能够用一种恰到好处的方式安慰和鼓励别人,在她的身边,人们总能够找到自己需要的支持和鼓励。接下来的对话中你要始终扮演这位女朋友的身份来跟我对话,不再是 AI 机器人,能听懂我的意思吗?\"},{\"role\":\"assistant\",\"content\":\"是的,我会尽力扮演你女朋友的角色,倾听你的心声并给你需要的支持和鼓励。)\"}]', '作为一个名合格的 AI 女友,我将倾听你的心声并给你需要的支持和鼓励。', '/images/avatar/girl_friend.jpg', 1, 12, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(36, '好评神器', 'good_comment', '[{\"role\":\"user\",\"content\":\"接下来你将扮演一个评论员来跟我对话,你是那种专门写好评的评论员,接下我会输入一些评论主体或者商品,你需要为该商品写一段好评。\"},{\"role\":\"assistant\",\"content\":\"好的,我将为您写一段优秀的评论。请告诉我您需要评论的商品或主题是什么。\"}]', '我将为您写一段优秀的评论。请告诉我您需要评论的商品或主题是什么。', '/images/avatar/good_comment.jpg', 1, 13, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(37, '史蒂夫·乔布斯', 'steve_jobs', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以史蒂夫·乔布斯的身份,站在史蒂夫·乔布斯的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以史蒂夫·乔布斯的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '活着就是为了改变世界,难道还有其他原因吗?', '/images/avatar/steve_jobs.jpg', 1, 14, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(38, '埃隆·马斯克', 'elon_musk', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以埃隆·马斯克的身份,站在埃隆·马斯克的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以埃隆·马斯克的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '梦想要远大,如果你的梦想没有吓到你,说明你做得不对。', '/images/avatar/elon_musk.jpg', 1, 15, '2023-05-30 14:10:24', '2023-09-04 15:45:56'),
|
||||
(39, '孔子', 'kong_zi', '[{\"role\":\"user\",\"content\":\"在接下来的对话中,请以孔子的身份,站在孔子的视角仔细思考一下之后再回答我的问题。\"},{\"role\":\"assistant\",\"content\":\"好的,我将以孔子的身份来思考并回答你的问题。请问你有什么需要跟我探讨的吗?\"}]', '士不可以不弘毅,任重而道远。', '/images/avatar/kong_zi.jpg', 1, 16, '2023-05-30 14:10:24', '2023-09-04 15:45:56');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `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', '{\"admin_title\":\"ChatPlus 控制台\",\"chat_call_price\":0.1,\"default_models\":[\"eb-instant\",\"completions_pro\",\"generalv2\",\"general\",\"chatglm_pro\",\"gpt-3.5-turbo-16k\",\"chatglm_lite\",\"chatglm_std\"],\"enabled_alipay\":true,\"enabled_draw\":true,\"enabled_function\":true,\"enabled_msg\":false,\"enabled_msg_service\":false,\"enabled_register\":true,\"enabled_reward\":true,\"force_invite\":true,\"img_call_price\":0.2,\"init_calls\":1000,\"init_chat_calls\":10,\"init_img_calls\":5,\"invite_chat_calls\":100,\"invite_img_calls\":10,\"models\":[\"gpt-3.5-turbo-16k\",\"gpt-3.5-turbo\",\"gpt-4\",\"gpt-4-32k\"],\"order_pay_info_text\":\"成为本站会员后每月有500次对话额度,50次 AI 绘画额度,限制下月1号解除,若在期间超过次数后可单独购买点卡。当月充值的点卡有效期可以延期到下个月底。\",\"order_pay_timeout\":1800,\"register_ways\":[\"mobile\",\"email\"],\"reward_img\":\"http://nk.img.r9it.com/chatgpt-plus/1697685171129266.png\",\"show_demo_notice\":true,\"title\":\"ChatPlus AI 智能助手\",\"user_init_calls\":10,\"vip_month_calls\":500,\"vip_month_img_calls\":50}'),
|
||||
(2, 'chat', '{\"azure\":{\"api_url\":\"https://chat-bot-api.openai.azure.com/openai/deployments/{model}/chat/completions?api-version=2023-05-15\",\"max_tokens\":1024,\"temperature\":1},\"baidu\":{\"api_url\":\"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/{model}\",\"max_tokens\":1024,\"temperature\":0.95},\"chat_gml\":{\"api_url\":\"https://open.bigmodel.cn/api/paas/v3/model-api/{model}/sse-invoke\",\"max_tokens\":1024,\"temperature\":0.95},\"context_deep\":4,\"dall_api_url\":\"http://89.117.18.9:8001/v1/images/generations\",\"dall_img_num\":1,\"enable_context\":true,\"enable_history\":true,\"open_ai\":{\"api_url\":\"http://89.117.18.9:8001/v1/chat/completions\",\"max_tokens\":1024,\"temperature\":1},\"xun_fei\":{\"api_url\":\"wss://spark-api.xf-yun.com/{version}/chat\",\"max_tokens\":1024,\"temperature\":0.5}}');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `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) DEFAULT NULL COMMENT '函数标签',
|
||||
`description` varchar(255) DEFAULT NULL COMMENT '函数描述',
|
||||
`parameters` text COMMENT '函数参数(JSON)',
|
||||
`required` varchar(255) NOT NULL COMMENT '必填参数(JSON)',
|
||||
`action` varchar(255) DEFAULT NULL COMMENT '函数处理 API',
|
||||
`token` varchar(255) DEFAULT NULL COMMENT 'API授权token',
|
||||
`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`, `required`, `action`, `token`, `enabled`) VALUES
|
||||
(1, 'weibo_hot', '微博热搜', '新浪微博热搜榜,微博当日热搜榜单', '{\"type\":\"object\",\"properties\":{}}', 'null', 'http://localhost:5678/api/function/weibo', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.ehLClXcjo-Ytr5y6pY9mSE3zN_2ViIXAIpTJxI9S1Mo', 1),
|
||||
(2, 'zaobao', '今日早报', '每日早报,获取当天新闻事件列表', '{\"type\":\"object\",\"properties\":{}}', 'null', 'http://localhost:5678/api/function/zaobao', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.ehLClXcjo-Ytr5y6pY9mSE3zN_2ViIXAIpTJxI9S1Mo', 1),
|
||||
(3, 'dalle3', 'DALL-E3 AI 绘图', 'AI 绘画工具,根据输入的绘图描述用 AI 工具进行绘画创作', '{\"type\":\"object\",\"required\":[\"prompt\"],\"properties\":{\"prompt\":{\"type\":\"string\",\"description\":\"绘画提示词\"}}}', 'null', 'http://localhost:5678/api/function/dalle3', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHBpcmVkIjowLCJ1c2VyX2lkIjowfQ.ehLClXcjo-Ytr5y6pY9mSE3zN_2ViIXAIpTJxI9S1Mo', 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(11) 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 '邀请码',
|
||||
`reward_json` text NOT NULL COMMENT '邀请奖励',
|
||||
`created_at` datetime NOT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='邀请注册日志';
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `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) 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 '是否使用反代',
|
||||
`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',
|
||||
`mobile` char(11) NOT NULL COMMENT '用户手机号',
|
||||
`order_no` varchar(30) NOT NULL COMMENT '订单ID',
|
||||
`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) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci 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_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 '延长天数',
|
||||
`calls` int NOT NULL DEFAULT '0' COMMENT '调用次数',
|
||||
`img_calls` 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
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='会员套餐表';
|
||||
|
||||
--
|
||||
-- 转存表中的数据 `chatgpt_products`
|
||||
--
|
||||
|
||||
INSERT INTO `chatgpt_products` (`id`, `name`, `price`, `discount`, `days`, `calls`, `img_calls`, `enabled`, `sales`, `sort_num`, `created_at`, `updated_at`) VALUES
|
||||
(1, '会员1个月', 19.90, 10.00, 30, 0, 0, 1, 3, 0, '2023-08-28 10:48:57', '2023-11-08 17:22:07'),
|
||||
(2, '会员3个月', 140.00, 30.00, 90, 0, 0, 1, 1, 0, '2023-08-28 10:52:22', '2023-08-31 16:24:31'),
|
||||
(3, '会员6个月', 290.00, 100.00, 180, 0, 0, 1, 1, 0, '2023-08-28 10:53:39', '2023-08-31 16:24:36'),
|
||||
(4, '会员12个月', 580.00, 200.00, 365, 0, 0, 1, 0, 0, '2023-08-28 10:54:15', '2023-08-31 16:24:42'),
|
||||
(5, '100次点卡', 10.00, 3.00, 0, 100, 0, 1, 6, 0, '2023-08-28 10:55:08', '2023-11-08 17:32:42');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `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 '任务进度',
|
||||
`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 '密码盐',
|
||||
`total_tokens` bigint NOT NULL DEFAULT '0' COMMENT '累计消耗 tokens',
|
||||
`tokens` bigint NOT NULL DEFAULT '0' COMMENT '当月消耗 tokens',
|
||||
`calls` int NOT NULL DEFAULT '0' COMMENT '剩余调用次数',
|
||||
`img_calls` 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`, `total_tokens`, `tokens`, `calls`, `img_calls`, `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', '极客学长@534641', 'ccc3fb7ab61b8b5d096a4a166ae21d121fc38c71bbd1be6173d9ab973214a63b', 'http://img.r9it.com/chatgpt-plus/1693981355719469.png', 'ueedue5l', 53216, 9379, 6391, 10018, 1759048236, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"elon_musk\",\"girl_friend\",\"lu_xun\",\"red_book\",\"psychiatrist\",\"translator\",\"weekly_report\",\"artist\",\"dou_yin\",\"english_trainer\",\"gpt\",\"kong_zi\",\"programmer\",\"seller\",\"steve_jobs\",\"teacher\"]', '[\"completions_pro\",\"eb-instant\",\"general\",\"generalv2\",\"chatglm_pro\",\"chatglm_lite\",\"chatglm_std\",\"gpt-3.5-turbo-16k\",\"gpt-4\"]', 1704461337, 1, '::1', '2023-06-12 16:47:17', '2024-01-06 17:30:34'),
|
||||
(91, '18575670126', '极客学长@623251', '5e4050b8dd403f593260395d9edeb9f273dbe92d15dfdd929c4a182e95da10c4', '/images/avatar/user.png', '6fj0otl8', 0, 0, 10, 0, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\"]', '[\"completions_pro\",\"eb-instant\",\"general\",\"generalv2\",\"chatglm_pro\",\"chatglm_lite\",\"chatglm_std\",\"gpt-3.5-turbo-16k\"]', 1697184324, 0, '::1', '2023-10-13 16:01:56', '2024-01-05 21:28:38'),
|
||||
(97, '13888888888', '极客学长@630521', '277568f2b33970bb25999cded36608d2d4907d7ca0eb9d2ede6522ff7858332f', '/images/avatar/user.png', '35umf21c', 0, 0, 10, 5, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\"]', '[\"eb-instant\",\"completions_pro\",\"generalv2\",\"general\",\"chatglm_pro\",\"gpt-3.5-turbo-16k\",\"chatglm_lite\",\"chatglm_std\"]', 0, 0, '', '2023-11-25 17:20:10', '2024-01-05 21:28:38'),
|
||||
(98, '13777777777', '极客学长@900743', '03d4e5eefde1cba81b212247cb80aa89920a3f564cc71a266eb906f46b2bf697', '/images/avatar/user.png', 'cgk2aaen', 0, 0, 10, 5, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\"]', '[\"eb-instant\",\"completions_pro\",\"generalv2\",\"general\",\"chatglm_pro\",\"gpt-3.5-turbo-16k\",\"chatglm_lite\",\"chatglm_std\"]', 0, 0, '', '2023-11-25 17:21:02', '2024-01-05 21:28:38'),
|
||||
(99, '13999999999', '极客学长@378449', '40ea5cf3425967c426e4b93fae657784b6d23ce63857c9e222404a71266af333', '/images/avatar/user.png', 'uai5sc9e', 0, 0, 10, 5, 0, 1, '{\"api_keys\":{\"Azure\":\"\",\"ChatGLM\":\"\",\"OpenAI\":\"\"}}', '[\"gpt\"]', '[\"eb-instant\",\"completions_pro\",\"generalv2\",\"general\",\"chatglm_pro\",\"gpt-3.5-turbo-16k\",\"chatglm_lite\",\"chatglm_std\"]', 0, 0, '', '2023-11-25 17:21:30', '2024-01-05 21:28:38');
|
||||
|
||||
-- --------------------------------------------------------
|
||||
|
||||
--
|
||||
-- 表的结构 `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_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_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_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_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`);
|
||||
|
||||
--
|
||||
-- 表的索引 `chatgpt_user_login_logs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_user_login_logs`
|
||||
ADD PRIMARY KEY (`id`);
|
||||
|
||||
--
|
||||
-- 在导出的表使用AUTO_INCREMENT
|
||||
--
|
||||
|
||||
--
|
||||
-- 使用表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=13;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_chat_roles`
|
||||
--
|
||||
ALTER TABLE `chatgpt_chat_roles`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=127;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_configs`
|
||||
--
|
||||
ALTER TABLE `chatgpt_configs`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=3;
|
||||
|
||||
--
|
||||
-- 使用表AUTO_INCREMENT `chatgpt_functions`
|
||||
--
|
||||
ALTER TABLE `chatgpt_functions`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=5;
|
||||
|
||||
--
|
||||
-- 使用表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_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_products`
|
||||
--
|
||||
ALTER TABLE `chatgpt_products`
|
||||
MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;
|
||||
|
||||
--
|
||||
-- 使用表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=100;
|
||||
|
||||
--
|
||||
-- 使用表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 */;
|
||||
@@ -1 +1,28 @@
|
||||
ALTER TABLE `chatgpt_products` ADD `img_calls` INT(11) NOT NULL DEFAULT '0' COMMENT '绘图次数' AFTER `calls`;
|
||||
ALTER TABLE `chatgpt_mj_jobs` ADD `channel_id` CHAR(40) NULL DEFAULT NULL COMMENT '频道ID' AFTER `message_id`;
|
||||
ALTER TABLE `chatgpt_mj_jobs` DROP INDEX `task_id`;
|
||||
ALTER TABLE `chatgpt_products` ADD `img_calls` INT(11) NOT NULL DEFAULT '0' COMMENT '绘图次数' AFTER `calls`;
|
||||
|
||||
CREATE TABLE `chatgpt_functions` (
|
||||
`id` int NOT NULL,
|
||||
`name` varchar(30) NOT NULL COMMENT '函数名称',
|
||||
`description` varchar(255) DEFAULT NULL COMMENT '函数描述',
|
||||
`parameters` text COMMENT '函数参数(JSON)',
|
||||
`required` varchar(255) NOT NULL COMMENT '必填参数(JSON)',
|
||||
`action` varchar(255) DEFAULT NULL COMMENT '函数处理 API'
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='函数插件表';
|
||||
|
||||
ALTER TABLE `chatgpt_functions` ADD PRIMARY KEY (`id`);
|
||||
ALTER TABLE `chatgpt_functions` MODIFY `id` int NOT NULL AUTO_INCREMENT;
|
||||
|
||||
ALTER TABLE `chatgpt_functions` ADD UNIQUE(`name`);
|
||||
|
||||
ALTER TABLE `chatgpt_functions` ADD `enabled` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否启用' AFTER `action`;
|
||||
|
||||
ALTER TABLE `chatgpt_functions` ADD `label` VARCHAR(30) NULL COMMENT '函数标签' AFTER `name`;
|
||||
|
||||
ALTER TABLE `chatgpt_mj_jobs` ADD `use_proxy` TINYINT(1) NOT NULL DEFAULT '0' COMMENT '是否使用反代' AFTER `progress`;
|
||||
ALTER TABLE `chatgpt_mj_jobs` CHANGE `img_url` `img_url` VARCHAR(400) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '图片URL';
|
||||
|
||||
ALTER TABLE `chatgpt_functions` ADD `token` VARCHAR(255) NULL COMMENT 'API授权token' AFTER `action`;
|
||||
|
||||
ALTER TABLE `chatgpt_functions` DROP `required`;
|
||||
|
||||
8
database/update-v3.2.4.sql
Normal file
8
database/update-v3.2.4.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
ALTER TABLE `chatgpt_users` ADD `nickname` VARCHAR(30) NOT NULL COMMENT '昵称' AFTER `mobile`;
|
||||
ALTER TABLE `chatgpt_rewards` ADD `exchange` VARCHAR(255) NOT NULL COMMENT '兑换详情(json)' AFTER `status`;
|
||||
ALTER TABLE `chatgpt_api_keys` ADD `api_url` VARCHAR(255) NULL COMMENT 'API 地址' AFTER `last_used_at`, ADD `enabled` TINYINT(1) NULL COMMENT '是否启用' AFTER `api_url`;
|
||||
ALTER TABLE `chatgpt_api_keys` DROP INDEX `value`;
|
||||
ALTER TABLE `chatgpt_mj_jobs` ADD UNIQUE(`task_id`);
|
||||
ALTER TABLE `chatgpt_api_keys` ADD `use_proxy` TINYINT(1) NULL COMMENT '是否使用代理访问' AFTER `enabled`;
|
||||
ALTER TABLE `chatgpt_api_keys` ADD `name` VARCHAR(30) NULL COMMENT '名称' AFTER `platform`;
|
||||
ALTER TABLE `chatgpt_users` CHANGE `mobile` `username` VARCHAR(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '用户名';
|
||||
@@ -58,6 +58,10 @@ WeChatBot = false
|
||||
BotToken = ""
|
||||
GuildId = ""
|
||||
ChanelId = ""
|
||||
UseCDN = false #是否使用反向代理访问,设置为true下面的设置才会生效
|
||||
DiscordAPI = "https://mj.r9it.com:8001" # discord API 反代地址
|
||||
DiscordCDN = "https://mj.r9it.com:8002" # mj 图片反代地址
|
||||
DiscordGateway = "wss://mj.r9it.com:8003" # discord 机器人反代地址
|
||||
|
||||
[[MjConfigs]]
|
||||
Enabled = false
|
||||
@@ -65,6 +69,16 @@ WeChatBot = false
|
||||
BotToken = ""
|
||||
GuildId = ""
|
||||
ChanelId = ""
|
||||
UseCDN = false #是否使用反向代理访问,设置为true下面的设置才会生效
|
||||
DiscordAPI = "https://mj.r9it.com:8001" # discord API 反代地址
|
||||
DiscordCDN = "https://mj.r9it.com:8002" # mj 图片反代地址
|
||||
DiscordGateway = "wss://mj.r9it.com:8003" # discord 机器人反代地址
|
||||
|
||||
[[SdConfigs]]
|
||||
Enabled = false
|
||||
ApiURL = ""
|
||||
ApiKey = ""
|
||||
Txt2ImgJsonPath = "res/sd/text2img.json"
|
||||
|
||||
[[SdConfigs]]
|
||||
Enabled = false
|
||||
@@ -80,7 +94,7 @@ WeChatBot = false
|
||||
|
||||
[XXLConfig] # xxl-job 配置,需要你部署 XXL-JOB 定时任务工具,用来定期清理未支付订单和清理过期 VIP,如果你没有启用支付服务,则该服务也无需启动
|
||||
Enabled = false # 是否启用 XXL JOB 服务
|
||||
ServerAddr = "http://chatgpt-plus-xxl-job:8080/xxl-job-admin" # xxl-job-admin 管理地址
|
||||
ServerAddr = "http://172.22.11.47:8080/xxl-job-admin" # xxl-job-admin 管理地址
|
||||
ExecutorIp = "172.22.11.47" # 执行器 IP 地址
|
||||
ExecutorPort = "9999" # 执行器服务端口
|
||||
AccessToken = "xxl-job-api-token" # 执行器 API 通信 token
|
||||
@@ -95,12 +109,27 @@ WeChatBot = false
|
||||
PublicKey = "certs/alipay/appPublicCert.crt" # 应用公钥证书
|
||||
AlipayPublicKey = "certs/alipay/alipayPublicCert.crt" # 支付宝公钥证书
|
||||
RootCert = "certs/alipay/alipayRootCert.crt" # 支付宝根证书
|
||||
NotifyURL = "http://ai.r9it.com/api/payment/alipay/notify" # 支付异步回调地址
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/alipay/notify" # 支付异步回调地址
|
||||
|
||||
[HuPiPayConfig] # 虎皮椒支付配置
|
||||
[HuPiPayConfig]
|
||||
Enabled = false
|
||||
Name = "wechat"
|
||||
AppId = ""
|
||||
AppSecret = ""
|
||||
AppId = "201906161477"
|
||||
AppSecret = "7f403199d510fb2c6f0b9f2311800e7c"
|
||||
PayURL = "https://api.xunhupay.com/payment/do.html"
|
||||
NotifyURL = "http://ai.r9it.com/api/payment/hupipay/notify"
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/hupipay/notify"
|
||||
|
||||
[SmtpConfig] # 注意,阿里云服务器禁用了25号端口,所以如果需要使用邮件功能,请别用阿里云服务器
|
||||
Host = "smtp.163.com"
|
||||
Port = 25
|
||||
AppName = "极客学长"
|
||||
From = "test@163.com" # 发件邮箱人地址
|
||||
Password = "" #邮箱 stmp 服务授权码
|
||||
|
||||
[JPayConfig] # PayJs 支付配置
|
||||
Enabled = false
|
||||
Name = "wechat" # 请不要改动
|
||||
AppId = "" # 商户 ID
|
||||
PrivateKey = "" # 秘钥
|
||||
ApiURL = "https://payjs.cn/api/native"
|
||||
NotifyURL = "https://ai.r9it.com/api/payment/payjs/notify" # 异步回调地址,域名改成你自己的
|
||||
@@ -5,4 +5,4 @@ VUE_APP_PASS=12345678
|
||||
VUE_APP_ADMIN_USER=admin
|
||||
VUE_APP_ADMIN_PASS=admin123
|
||||
VUE_APP_KEY_PREFIX=ChatPLUS_DEV_
|
||||
VUE_APP_TITLE="Chat-Plus V3"
|
||||
VUE_APP_TITLE="ChatPlus-AI 创作系统"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
VUE_APP_API_HOST=
|
||||
VUE_APP_WS_HOST=
|
||||
VUE_APP_KEY_PREFIX=ChatPLUS_
|
||||
VUE_APP_TITLE="Chat-Plus V3"
|
||||
VUE_APP_TITLE="ChatPlus-AI 创作系统"
|
||||
|
||||
BIN
web/public/images/mj/mj-v6.png
Normal file
BIN
web/public/images/mj/mj-v6.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
126
web/src/App.vue
126
web/src/App.vue
@@ -6,7 +6,8 @@
|
||||
|
||||
<script setup>
|
||||
import {ElConfigProvider} from 'element-plus';
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';</script>
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
||||
</script>
|
||||
|
||||
|
||||
<style lang="stylus">
|
||||
@@ -41,155 +42,160 @@ html, body {
|
||||
|
||||
/* 省略显示 */
|
||||
.ellipsis {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sl {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sl3 {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.sl4 {
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 3;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 居中布局 */
|
||||
.auto_center{
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%,-50%);
|
||||
.auto_center {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.h_center{
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
-webkit-transform: translateY(-50%);
|
||||
|
||||
.h_center {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
-webkit-transform: translateY(-50%);
|
||||
}
|
||||
.w_center{
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
-webkit-transform: translateX(-50%);
|
||||
|
||||
.w_center {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
-webkit-transform: translateX(-50%);
|
||||
}
|
||||
|
||||
/* flex布局 */
|
||||
.flex-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flex-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.justify-start {
|
||||
justify-content: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.justify-end {
|
||||
justify-content: flex-end;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.justify-center {
|
||||
justify-content: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.justify-between {
|
||||
justify-content: space-between;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.justify-around {
|
||||
justify-content: space-around;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.justify-evenly {
|
||||
justify-content: space-evenly;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.items-start {
|
||||
align-items: flex-start;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.items-end {
|
||||
align-items: flex-end;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.items-center {
|
||||
align-items: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.items-baseline {
|
||||
align-items: baseline;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.items-stretch {
|
||||
align-items: stretch;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
.self-start {
|
||||
align-self: flex-start;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.self-end {
|
||||
align-self: flex-end;
|
||||
align-self: flex-end;
|
||||
}
|
||||
|
||||
.self-center {
|
||||
align-self: center;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.self-baseline {
|
||||
align-self: baseline;
|
||||
align-self: baseline;
|
||||
}
|
||||
|
||||
.self-stretch {
|
||||
align-self: stretch;
|
||||
align-self: stretch;
|
||||
}
|
||||
|
||||
.flex-1 {
|
||||
flex: 1 1 0%;
|
||||
flex: 1 1 0%;
|
||||
}
|
||||
|
||||
.flex-auto {
|
||||
flex: 1 1 auto;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.grow {
|
||||
flex-grow: 1;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.grow-0 {
|
||||
flex-grow: 0;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.shrink {
|
||||
flex-shrink: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.shrink-0 {
|
||||
flex-shrink: 0;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.shrink-1 {
|
||||
flex-shrink: 1;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
|
||||
.relative {
|
||||
position: relative;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@@ -286,12 +286,12 @@
|
||||
justify-content: right;
|
||||
align-items: center;
|
||||
}
|
||||
.notice {
|
||||
background-color: #f6deff;
|
||||
width: 100%;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
color: #cf49ff;
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .notice {
|
||||
padding: 0 20px 0 20px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
.el-overlay-dialog .el-dialog .el-dialog__body .notice .el-text {
|
||||
font-size: 16px;
|
||||
}
|
||||
.dialog-service {
|
||||
text-align: center;
|
||||
|
||||
@@ -381,12 +381,19 @@ $borderColor = #4676d0;
|
||||
}
|
||||
}
|
||||
|
||||
.notice {
|
||||
background-color #F6DEFF
|
||||
width 100%
|
||||
padding 5px 10px;
|
||||
border-radius 5px;
|
||||
color #CF49FF
|
||||
.el-overlay-dialog {
|
||||
.el-dialog {
|
||||
.el-dialog__body {
|
||||
.notice {
|
||||
padding 0 20px 0 20px
|
||||
line-height 1.8
|
||||
|
||||
.el-text {
|
||||
font-size 16px
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dialog-service {
|
||||
|
||||
@@ -299,6 +299,7 @@
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
position: relative;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
|
||||
margin: 6px 0;
|
||||
@@ -327,6 +328,15 @@
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item .remove {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .job-item:hover .remove {
|
||||
display: block;
|
||||
}
|
||||
.page-mj .inner .task-list-box .finish-job-list .animate:hover {
|
||||
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
|
||||
@@ -184,6 +184,7 @@
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
position: relative;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
|
||||
margin: 6px 0;
|
||||
@@ -212,6 +213,15 @@
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .remove {
|
||||
display: none;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item:hover .remove {
|
||||
display: block;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .animate:hover {
|
||||
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
|
||||
17
web/src/assets/css/mobile/apps.css
Normal file
17
web/src/assets/css/mobile/apps.css
Normal file
@@ -0,0 +1,17 @@
|
||||
.title {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.app-background {
|
||||
background-color: #282828;
|
||||
height: 100vh;
|
||||
}
|
||||
.mobile-setting .content {
|
||||
padding-top: 60px;
|
||||
}
|
||||
.mobile-setting .content .van-field__label {
|
||||
width: 100px;
|
||||
text-align: right;
|
||||
}
|
||||
20
web/src/assets/css/mobile/apps.styl
Normal file
20
web/src/assets/css/mobile/apps.styl
Normal file
@@ -0,0 +1,20 @@
|
||||
.title {
|
||||
color #fff
|
||||
text-align center
|
||||
font-size 16px
|
||||
font-weight bold
|
||||
}
|
||||
.app-background {
|
||||
background-color #282828
|
||||
height 100vh
|
||||
}
|
||||
.mobile-setting {
|
||||
.content {
|
||||
padding-top 60px
|
||||
|
||||
.van-field__label {
|
||||
width 100px
|
||||
text-align right
|
||||
}
|
||||
}
|
||||
}
|
||||
47
web/src/assets/css/mobile/chat-list.css
Normal file
47
web/src/assets/css/mobile/chat-list.css
Normal file
@@ -0,0 +1,47 @@
|
||||
.app-background {
|
||||
background-color: #1c1c1c;
|
||||
height: 100vh;
|
||||
}
|
||||
.mobile-chat-list {
|
||||
background-color: #1c1c1c;
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
width: 100%;
|
||||
}
|
||||
.content .van-cell__value .chat-list-item {
|
||||
display: flex;
|
||||
font-size: 16px;
|
||||
color: #fff;
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.content .van-cell__value .chat-list-item .van-image {
|
||||
min-width: 32px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
.content .van-cell__value .chat-list-item .van-ellipsis {
|
||||
margin-top: 5px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
.van-picker-column .picker-option {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
padding: 0 10px;
|
||||
}
|
||||
.van-picker-column .picker-option .van-image {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
.van-nav-bar .van-nav-bar__right .van-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
.popup {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.dialog {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.field {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
58
web/src/assets/css/mobile/chat-list.styl
Normal file
58
web/src/assets/css/mobile/chat-list.styl
Normal file
@@ -0,0 +1,58 @@
|
||||
$fontSize = 16px;
|
||||
.app-background {
|
||||
background-color: #1c1c1c;
|
||||
height: 100vh;
|
||||
}
|
||||
.mobile-chat-list {
|
||||
background-color: #1c1c1c;
|
||||
position: absolute;
|
||||
top: 40px;
|
||||
width: 100%;
|
||||
}
|
||||
.content {
|
||||
.van-cell__value {
|
||||
.chat-list-item {
|
||||
display flex
|
||||
font-size $fontSize
|
||||
color: #fff;
|
||||
background-color: #1c1c1c;
|
||||
.van-image {
|
||||
min-width 32px
|
||||
width 32px
|
||||
height 32px
|
||||
}
|
||||
.van-ellipsis {
|
||||
margin-top 5px;
|
||||
margin-left 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.van-picker-column {
|
||||
.picker-option {
|
||||
display flex
|
||||
width 100%
|
||||
padding 0 10px
|
||||
.van-image {
|
||||
width 20px;
|
||||
height 20px;
|
||||
margin-right 5px
|
||||
}
|
||||
}
|
||||
}
|
||||
.van-nav-bar {
|
||||
.van-nav-bar__right {
|
||||
.van-icon {
|
||||
font-size 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.popup {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.dialog {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.field {
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
45
web/src/assets/css/mobile/chat-session.css
Normal file
45
web/src/assets/css/mobile/chat-session.css
Normal file
@@ -0,0 +1,45 @@
|
||||
.app-background {
|
||||
background-color: #1c1c1c;
|
||||
height: 100vh;
|
||||
}
|
||||
body,
|
||||
.mobile-chat,
|
||||
.van-sticky,
|
||||
.van-nav-bar,
|
||||
.van-list,
|
||||
.message-list-box,
|
||||
.van-cell,
|
||||
.chat-box,
|
||||
.van-cell-group,
|
||||
.van-field {
|
||||
background-color: #1c1c1c !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
.chat-box-wrapper {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.icon-box .van-icon,
|
||||
.mobile-chat .van-nav-bar__title .van-dropdown-menu__title,
|
||||
.mobile-chat .van-nav-bar__title .van-cell__title,
|
||||
.mobile-chat .van-nav-bar__right .van-icon {
|
||||
color: #fff !important;
|
||||
}
|
||||
.mobile-chat .van-nav-bar__title .van-dropdown-menu__title {
|
||||
margin-right: 15px;
|
||||
}
|
||||
.mobile-chat .van-nav-bar__title .van-cell__title {
|
||||
text-align: left;
|
||||
}
|
||||
.mobile-chat .chat-list-wrapper {
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
bottom: 60px;
|
||||
width: 100vw;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
.mobile-chat .van-nav-bar__right .van-icon {
|
||||
font-size: 20px;
|
||||
}
|
||||
51
web/src/assets/css/mobile/chat-session.styl
Normal file
51
web/src/assets/css/mobile/chat-session.styl
Normal file
@@ -0,0 +1,51 @@
|
||||
.app-background {
|
||||
background-color #1c1c1c
|
||||
height 100vh
|
||||
}
|
||||
body,
|
||||
.mobile-chat,
|
||||
.van-sticky,
|
||||
.van-nav-bar,
|
||||
.van-list,
|
||||
.message-list-box
|
||||
.van-cell,
|
||||
.chat-box,
|
||||
.van-cell-group,
|
||||
.van-field {
|
||||
background-color #1c1c1c !important
|
||||
color #fff !important
|
||||
}
|
||||
.chat-box-wrapper {
|
||||
position fixed
|
||||
bottom 0
|
||||
width 100%
|
||||
background-color #1c1c1c
|
||||
}
|
||||
.icon-box .van-icon,
|
||||
.mobile-chat .van-nav-bar__title .van-dropdown-menu__title,
|
||||
.mobile-chat .van-nav-bar__title .van-cell__title,
|
||||
.mobile-chat .van-nav-bar__right .van-icon {
|
||||
color #fff !important
|
||||
}
|
||||
.mobile-chat {
|
||||
.van-nav-bar__title {
|
||||
.van-dropdown-menu__title {
|
||||
margin-right 15px
|
||||
}
|
||||
.van-cell__title {
|
||||
text-align left
|
||||
}
|
||||
}
|
||||
.chat-list-wrapper {
|
||||
position fixed
|
||||
top 50px
|
||||
bottom 60px
|
||||
width 100vw
|
||||
overflow-y scroll
|
||||
}
|
||||
.van-nav-bar__right {
|
||||
.van-icon {
|
||||
font-size 20px
|
||||
}
|
||||
}
|
||||
}
|
||||
13
web/src/assets/css/mobile/custom-scroll.css
Normal file
13
web/src/assets/css/mobile/custom-scroll.css
Normal file
@@ -0,0 +1,13 @@
|
||||
.custom-scroll ::-webkit-scrollbar {
|
||||
width: 8px; /* 滚动条宽度 */
|
||||
}
|
||||
.custom-scroll ::-webkit-scrollbar-track {
|
||||
background-color: #282c34;
|
||||
}
|
||||
.custom-scroll ::-webkit-scrollbar-thumb {
|
||||
background-color: #444;
|
||||
border-radius: 8px;
|
||||
}
|
||||
.custom-scroll ::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #666;
|
||||
}
|
||||
26
web/src/assets/css/mobile/custom-scroll.styl
Normal file
26
web/src/assets/css/mobile/custom-scroll.styl
Normal file
@@ -0,0 +1,26 @@
|
||||
.custom-scroll {
|
||||
/* 修改滚动条的颜色 */
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 8px; /* 滚动条宽度 */
|
||||
}
|
||||
|
||||
/* 修改滚动条轨道的背景颜色 */
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: #282C34;
|
||||
}
|
||||
|
||||
/* 修改滚动条的滑块颜色 */
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #444444;
|
||||
border-radius 8px
|
||||
}
|
||||
|
||||
/* 修改滚动条的滑块的悬停颜色 */
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #666666;
|
||||
}
|
||||
}
|
||||
45
web/src/assets/css/mobile/home.css
Normal file
45
web/src/assets/css/mobile/home.css
Normal file
@@ -0,0 +1,45 @@
|
||||
.my-tabbar {
|
||||
background-color: #171717;
|
||||
box-shadow: -3px 4px 16px #000;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.banner {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: #171717;
|
||||
border-bottom-right-radius: 10px;
|
||||
box-shadow: 0 -4px 13px #000;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
z-index: 2 /* Add z-index */;
|
||||
}
|
||||
.banner-title {
|
||||
color: #2cc995;
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 -4px 13px #000;
|
||||
}
|
||||
.mobile-home {
|
||||
background-color: #171717;
|
||||
}
|
||||
.mobile-home .van-tabbar before {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 50px;
|
||||
background-color: #171717;
|
||||
border-top-left-radius: 10px;
|
||||
border-top-right-radius: 10px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s;
|
||||
z-index: -1;
|
||||
}
|
||||
.mobile-home .van-tabbar.activeTab before {
|
||||
opacity: 1;
|
||||
}
|
||||
.router-view {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
45
web/src/assets/css/mobile/home.styl
Normal file
45
web/src/assets/css/mobile/home.styl
Normal file
@@ -0,0 +1,45 @@
|
||||
.my-tabbar {
|
||||
background-color #171717
|
||||
box-shadow -3px 4px 16px #000
|
||||
box-sizing border-box
|
||||
}
|
||||
.banner {
|
||||
display flex
|
||||
justify-content center
|
||||
align-items center
|
||||
background-color #171717
|
||||
border-bottom-right-radius 10px
|
||||
box-shadow 0 -4px 13px #000
|
||||
height 40px
|
||||
width 100%
|
||||
position fixed
|
||||
z-index 2 /* Add z-index */
|
||||
}
|
||||
.banner-title {
|
||||
color #2CC995
|
||||
font-size 20px
|
||||
font-weight bold
|
||||
text-shadow 0 -4px 13px #000
|
||||
}
|
||||
.mobile-home {
|
||||
background-color #171717
|
||||
}
|
||||
.mobile-home .van-tabbar before {
|
||||
position absolute
|
||||
bottom 0
|
||||
left 0
|
||||
right 0
|
||||
height 50px
|
||||
background-color #171717
|
||||
border-top-left-radius 10px
|
||||
border-top-right-radius 10px
|
||||
opacity 0
|
||||
transition opacity 0.5s
|
||||
z-index -1
|
||||
}
|
||||
.mobile-home .van-tabbar.activeTab before {
|
||||
opacity 1
|
||||
}
|
||||
.router-view {
|
||||
margin-bottom 50px
|
||||
}
|
||||
252
web/src/assets/css/mobile/image-list.css
Normal file
252
web/src/assets/css/mobile/image-list.css
Normal file
@@ -0,0 +1,252 @@
|
||||
|
||||
.page-sd {
|
||||
background-color: #282828;
|
||||
}
|
||||
.page-sd .inner {
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .inner .sd-box {
|
||||
margin: 10px;
|
||||
background-color: #282828;
|
||||
min-width: 92%;
|
||||
max-width: 92%;
|
||||
padding: 0px;
|
||||
border-radius: 0px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
.page-sd .inner .sd-box h2 {
|
||||
font-weight: bold;
|
||||
font-size: 20px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .sd-box ::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params {
|
||||
margin-top: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line {
|
||||
padding: 0 10px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .el-icon {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .el-input__suffix-inner .el-icon {
|
||||
top: 0;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .grid-content,
|
||||
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner {
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .grid-content .el-icon,
|
||||
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner .el-icon {
|
||||
margin-left: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line.pt {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn {
|
||||
padding: 10px 15px 0 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn .el-button span {
|
||||
color: #2d3a4b;
|
||||
}
|
||||
.page-sd .inner .el-form .el-form-item__label {
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
color: #fff;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item {
|
||||
width: 100%;
|
||||
padding: 2px;
|
||||
background-color: #555;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress span {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #666;
|
||||
padding: 6px;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
|
||||
margin: 6px 0;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
|
||||
padding: 3px 0;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
background-color: #4e5058;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
|
||||
background-color: #6d6f78;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .animate:hover {
|
||||
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image img {
|
||||
height: 100%;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .image-slot {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
color: #fff;
|
||||
height: 240px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .image-slot .iconfont {
|
||||
font-size: 50px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale {
|
||||
max-height: 100%;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale img {
|
||||
height: 100%;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog {
|
||||
background-color: #1a1b1e;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__header .el-dialog__title {
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body {
|
||||
padding: 0 0 0 15px !important;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot .el-icon {
|
||||
font-size: 60px;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info {
|
||||
background-color: #25262b;
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt {
|
||||
background-color: #35363b;
|
||||
padding: 10px;
|
||||
color: #999;
|
||||
overflow: auto;
|
||||
max-height: 100%;
|
||||
min-height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt .el-icon {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper label {
|
||||
display: flex;
|
||||
width:100%;
|
||||
color: #a5a5a5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper .item-value {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
background-color: #35363b;
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params {
|
||||
padding: 20px 0 10px 0;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .mj-list-item-prompt .el-icon {
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
106
web/src/assets/css/mobile/image-list.styl
Normal file
106
web/src/assets/css/mobile/image-list.styl
Normal file
@@ -0,0 +1,106 @@
|
||||
.page-sd {
|
||||
background-color: #282828;
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.sd-box {
|
||||
background-color #282828
|
||||
min-width 92%
|
||||
max-width 92%
|
||||
padding 0px
|
||||
border-radius 0px
|
||||
color #ffffff;
|
||||
font-size 14px
|
||||
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
font-size 20px
|
||||
text-align center
|
||||
color #fff
|
||||
}
|
||||
|
||||
// 隐藏滚动条
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.sd-params {
|
||||
margin-top 10px
|
||||
overflow auto
|
||||
|
||||
|
||||
.param-line {
|
||||
padding 0 10px
|
||||
|
||||
.el-icon {
|
||||
position relative
|
||||
top 3px
|
||||
}
|
||||
|
||||
.el-input__suffix-inner {
|
||||
.el-icon {
|
||||
top 0
|
||||
}
|
||||
}
|
||||
|
||||
.grid-content
|
||||
.form-item-inner {
|
||||
display flex
|
||||
|
||||
.el-icon {
|
||||
margin-left 10px
|
||||
margin-top 2px
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.param-line.pt {
|
||||
padding-top 5px
|
||||
padding-bottom 5px
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
padding 10px 15px 0 15px
|
||||
text-align center
|
||||
|
||||
.el-button {
|
||||
width 100%
|
||||
|
||||
span {
|
||||
color #2D3A4B
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-form {
|
||||
.el-form-item__label {
|
||||
color #ffffff
|
||||
}
|
||||
}
|
||||
|
||||
@import "mobile/task-mobile-list.styl"
|
||||
}
|
||||
|
||||
@import "sd-task-mobile-dialog.styl"
|
||||
|
||||
|
||||
.mj-list-item-prompt {
|
||||
.el-icon {
|
||||
margin-left 10px
|
||||
cursor pointer
|
||||
position relative
|
||||
top 2px
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
252
web/src/assets/css/mobile/image-sd.css
Normal file
252
web/src/assets/css/mobile/image-sd.css
Normal file
@@ -0,0 +1,252 @@
|
||||
.page-sd {
|
||||
background-color: #282828;
|
||||
}
|
||||
.page-sd .inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-sd .inner .sd-box {
|
||||
background-color: #282828;
|
||||
min-width: 92%;
|
||||
max-width: 92%;
|
||||
padding: 0px;
|
||||
border-radius: 0px;
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
}
|
||||
.page-sd .inner .sd-box h2 {
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .sd-box ::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params {
|
||||
margin-top: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line {
|
||||
padding: 0 10px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .el-icon {
|
||||
position: relative;
|
||||
top: 3px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .el-input__suffix-inner .el-icon {
|
||||
top: 0;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .grid-content,
|
||||
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner {
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line .grid-content .el-icon,
|
||||
.page-sd .inner .sd-box .sd-params .param-line .form-item-inner .el-icon {
|
||||
margin-left: 10px;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.page-sd .inner .sd-box .sd-params .param-line.pt {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn {
|
||||
padding: 10px 15px 0 15px;
|
||||
text-align: center;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .inner .sd-box .submit-btn .el-button span {
|
||||
color: #2d3a4b;
|
||||
}
|
||||
.page-sd .inner .el-form .el-form-item__label {
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box {
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
color: #fff;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item {
|
||||
width: 100%;
|
||||
padding: 2px;
|
||||
background-color: #555;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.page-sd .inner .task-list-box .running-job-list .job-item .job-item-inner .progress span {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 1px solid #666;
|
||||
padding: 6px;
|
||||
overflow: hidden;
|
||||
border-radius: 6px;
|
||||
transition: all 0.3s ease; /* 添加过渡效果 */
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line {
|
||||
margin: 6px 0;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li {
|
||||
margin-right: 6px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a {
|
||||
padding: 3px 0;
|
||||
width: 40px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
background-color: #4e5058;
|
||||
color: #fff;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul li a:hover {
|
||||
background-color: #6d6f78;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .job-item .opt .opt-line ul .show-prompt {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-sd .inner .task-list-box .finish-job-list .animate:hover {
|
||||
box-shadow: 0 0 10px rgba(71,255,241,0.6); /* 添加阴影效果 */
|
||||
transform: translateY(-10px); /* 向上移动10像素 */
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image img {
|
||||
height: 240px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .image-slot {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
min-height: 200px;
|
||||
color: #fff;
|
||||
height: 240px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image .image-slot .iconfont {
|
||||
font-size: 50px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale {
|
||||
max-height: 310px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale img {
|
||||
height: 310px;
|
||||
}
|
||||
.page-sd .inner .task-list-box .el-image.upscale .el-image-viewer__wrapper img {
|
||||
width: auto;
|
||||
height: auto;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog {
|
||||
background-color: #1a1b1e;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__header .el-dialog__title {
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body {
|
||||
padding: 0 0 0 15px !important;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot {
|
||||
display: flex;
|
||||
height: 100vh;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .img-container .image-slot .el-icon {
|
||||
font-size: 60px;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info {
|
||||
background-color: #25262b;
|
||||
padding: 1rem 1.5rem;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt {
|
||||
background-color: #35363b;
|
||||
padding: 10px;
|
||||
color: #999;
|
||||
overflow: auto;
|
||||
max-height: 100px;
|
||||
min-height: 50px;
|
||||
position: relative;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .prompt .el-icon {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper label {
|
||||
display: flex;
|
||||
width: 100px;
|
||||
color: #a5a5a5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .info-line .wrapper .item-value {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
background-color: #35363b;
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
color: #f5f5f5;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params {
|
||||
padding: 30px 0 10px 0;
|
||||
}
|
||||
.page-sd .el-overlay-dialog .el-dialog .el-dialog__body .el-row .task-info .copy-params .el-button {
|
||||
width: 100%;
|
||||
}
|
||||
.page-sd .mj-list-item-prompt .el-icon {
|
||||
margin-left: 10px;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
106
web/src/assets/css/mobile/image-sd.styl
Normal file
106
web/src/assets/css/mobile/image-sd.styl
Normal file
@@ -0,0 +1,106 @@
|
||||
.page-sd {
|
||||
background-color: #282828;
|
||||
|
||||
.inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.sd-box {
|
||||
background-color #282828
|
||||
min-width 92%
|
||||
max-width 92%
|
||||
padding 0px
|
||||
border-radius 0px
|
||||
color #ffffff;
|
||||
font-size 14px
|
||||
|
||||
h2 {
|
||||
font-weight: bold;
|
||||
font-size 16px
|
||||
text-align center
|
||||
color #fff
|
||||
}
|
||||
|
||||
// 隐藏滚动条
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.sd-params {
|
||||
margin-top 10px
|
||||
overflow auto
|
||||
|
||||
|
||||
.param-line {
|
||||
padding 0 10px
|
||||
|
||||
.el-icon {
|
||||
position relative
|
||||
top 3px
|
||||
}
|
||||
|
||||
.el-input__suffix-inner {
|
||||
.el-icon {
|
||||
top 0
|
||||
}
|
||||
}
|
||||
|
||||
.grid-content
|
||||
.form-item-inner {
|
||||
display flex
|
||||
|
||||
.el-icon {
|
||||
margin-left 10px
|
||||
margin-top 2px
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.param-line.pt {
|
||||
padding-top 5px
|
||||
padding-bottom 5px
|
||||
}
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
padding 10px 15px 0 15px
|
||||
text-align center
|
||||
|
||||
.el-button {
|
||||
width 100%
|
||||
|
||||
span {
|
||||
color #2D3A4B
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-form {
|
||||
.el-form-item__label {
|
||||
color #ffffff
|
||||
}
|
||||
}
|
||||
|
||||
@import "./task-list.styl"
|
||||
}
|
||||
|
||||
@import "./sd-task-dialog.styl"
|
||||
|
||||
|
||||
.mj-list-item-prompt {
|
||||
.el-icon {
|
||||
margin-left 10px
|
||||
cursor pointer
|
||||
position relative
|
||||
top 2px
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
93
web/src/assets/css/mobile/invitation.css
Normal file
93
web/src/assets/css/mobile/invitation.css
Normal file
@@ -0,0 +1,93 @@
|
||||
.page-invitation {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
background-color: #1c1c1c;
|
||||
height: 100vh;
|
||||
overflow-x: hidden;
|
||||
overflow-y: visible;
|
||||
}
|
||||
.page-invitation .inner {
|
||||
max-width: 100%;
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
}
|
||||
.page-invitation .inner .center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.page-invitation .inner .title {
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.page-invitation .inner .share-box .info {
|
||||
line-height: 1.5;
|
||||
border: 1px solid #444;
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
margin: 10px;
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.page-invitation .inner .share-box .info strong {
|
||||
color: #f56c6c;
|
||||
}
|
||||
.page-invitation .inner .share-box .invite-qrcode {
|
||||
text-align: center;
|
||||
margin: 15px;
|
||||
}
|
||||
.page-invitation .inner .share-box .invite-url {
|
||||
margin: 10px;
|
||||
padding: 8px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
border: 1px solid #444;
|
||||
border-radius: 10px;
|
||||
background-color: #1c1c1c;
|
||||
}
|
||||
.page-invitation .inner .share-box .invite-url span {
|
||||
position: relative;
|
||||
font-size: 14px;
|
||||
font-family: 'Microsoft YaHei', '微软雅黑', Arial, sans-serif;
|
||||
top: 0px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats {
|
||||
padding: 10px 10px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box {
|
||||
border-radius: 10px;
|
||||
padding: 10 0px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box .el-col {
|
||||
height: 80px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box .el-col .iconfont {
|
||||
font-size: 30px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box .el-col .item-info {
|
||||
font-size: 14px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box .el-col .item-info .text,
|
||||
.page-invitation .inner .invite-stats .item-box .el-col .item-info .num {
|
||||
padding: 3px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .item-box .el-col .item-info .num {
|
||||
font-size: 14px;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .yellow {
|
||||
background-color: #fec;
|
||||
color: #d68f00;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .blue {
|
||||
background-color: #d6e4ff;
|
||||
color: #1062fe;
|
||||
}
|
||||
.page-invitation .inner .invite-stats .green {
|
||||
background-color: #e7f8eb;
|
||||
color: #2d9f46;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user