Merge branch 'main' of github.com:yangjian102621/chatgpt-plus

This commit is contained in:
RockYang 2023-11-13 10:11:45 +08:00
commit c61d32816a
11 changed files with 117 additions and 100 deletions

View File

@ -1,11 +1,18 @@
# 更新日志 # 更新日志
## v3.1.8 ## v3.1.8
1. 功能新增:新增会员套餐充值,点卡充值,订单系统,集成支付宝支付通道 1. 功能新增:新增会员套餐充值,点卡充值,订单系统,集成支付宝支付通道
2. Bug修复修复 MidJourney API 参数版本更新导致调用失败的 Bug 2. Bug修复修复 MidJourney API 参数版本更新导致调用失败问题
3. 功能优化:将聊天报错信息定义为统一常量,方便修改 3. Bug修复修复 Stable Diffusion 调用后没有更新绘图调用次数问题
4. Bug修复修复七牛云上传报错 expired token
5. Bug修复修复高权重模型导致的对话次数为负数的漏洞
6. 功能优化:将聊天报错信息定义为统一常量,方便修改
7. 功能优化:优化 markdown 表格显示样式,覆写 Element-Plus 表格样式
8. 功能优化:增加倒数计时组件,定期自动清理未支付的订单
## v3.1.7 ## v3.1.7
1. 功能新增支持文心4.0 AI 模型 1. 功能新增支持文心4.0 AI 模型
2. 功能新增:可以在管理后台为用户绑定指定的 AI 模型,如只给某个用户使用 GPT-4 模型 2. 功能新增:可以在管理后台为用户绑定指定的 AI 模型,如只给某个用户使用 GPT-4 模型
3. 功能新增模型新增权重字段不同的模型每次调用耗费的点数可以设置不同比如GPT4是GPT3.5的10倍 3. 功能新增模型新增权重字段不同的模型每次调用耗费的点数可以设置不同比如GPT4是GPT3.5的10倍
@ -13,6 +20,7 @@
5. 功能优化:优化 MidJourney 专业绘画页面图片预览样式 5. 功能优化:优化 MidJourney 专业绘画页面图片预览样式
## v3.1.6 ## v3.1.6
1. 功能新增新增AI 绘画照片墙功能页面,供用户查看所有的 AI 绘画作品 1. 功能新增新增AI 绘画照片墙功能页面,供用户查看所有的 AI 绘画作品
2. 功能新增:新增 AI 角色应用功能页面,用户可以添加自己感兴趣的应用 2. 功能新增:新增 AI 角色应用功能页面,用户可以添加自己感兴趣的应用
3. 功能优化:优化瀑布流组件的页面布局 3. 功能优化:优化瀑布流组件的页面布局

View File

@ -10,7 +10,8 @@ ChatGLM,讯飞星火,文心一言等多个平台的大语言模型。集成了
* 支持 MidJourney / Stable Diffusion AI 绘画集成,开箱即用。 * 支持 MidJourney / Stable Diffusion AI 绘画集成,开箱即用。
* 支持使用个人微信二维码作为充值收费的支付渠道,无需企业支付通道。 * 支持使用个人微信二维码作为充值收费的支付渠道,无需企业支付通道。
* 已集成支付宝支付功能,支持多种会员套餐和点卡购买功能。 * 已集成支付宝支付功能,支持多种会员套餐和点卡购买功能。
* 集成插件 API 功能,可结合大语言模型的 function 功能开发各种强大的插件,已内置实现了微博热搜,今日头条,今日早报和 AI 绘画函数插件。 * 集成插件 API 功能,可结合大语言模型的 function 功能开发各种强大的插件,已内置实现了微博热搜,今日头条,今日早报和 AI
绘画函数插件。
## 功能截图 ## 功能截图
@ -23,17 +24,28 @@ ChatGLM,讯飞星火,文心一言等多个平台的大语言模型。集成了
![ChatGPT new Chat Page](/docs/imgs/chat-new.png) ![ChatGPT new Chat Page](/docs/imgs/chat-new.png)
### MidJourney 专业绘画界面 ### MidJourney 专业绘画界面
![mid-journey](/docs/imgs/mj_image.jpg) ![mid-journey](/docs/imgs/mj_image.jpg)
### Stable-Diffusion 专业绘画页面 ### Stable-Diffusion 专业绘画页面
![Stable-Diffusion](/docs/imgs/sd_image.jpg) ![Stable-Diffusion](/docs/imgs/sd_image.jpg)
![Stable-Diffusion](/docs/imgs/sd_image_detail.jpg) ![Stable-Diffusion](/docs/imgs/sd_image_detail.jpg)
### 绘图作品展 ### 绘图作品展
![ChatGPT image_list](/docs/imgs/image-list.png) ![ChatGPT image_list](/docs/imgs/image-list.png)
### AI应用列表 ### AI应用列表
![ChatGPT-app-list](/docs/imgs/app-list.jpg) ![ChatGPT-app-list](/docs/imgs/app-list.jpg)
### 会员充值
![会员充值](/docs/imgs/alipay.png)
### 自动调用函数插件 ### 自动调用函数插件
![ChatGPT function plugin](/docs/imgs/plugin.png) ![ChatGPT function plugin](/docs/imgs/plugin.png)
![ChatGPT function plugin](/docs/imgs/mj.jpg) ![ChatGPT function plugin](/docs/imgs/mj.jpg)

View File

@ -287,7 +287,8 @@ func (s *Service) callback(data CBReq) {
if data.Progress < 100 && data.ImageData != "" { if data.Progress < 100 && data.ImageData != "" {
jobVo.ImgURL = data.ImageData jobVo.ImgURL = data.ImageData
} }
// 扣减绘图次数
s.db.Where("id = ?", jobVo.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls - ?", 1))
// 推送任务到前端 // 推送任务到前端
if client != nil { if client != nil {
utils.ReplyChunkMessage(client, jobVo) utils.ReplyChunkMessage(client, jobVo)

View File

@ -38,13 +38,13 @@ func NewXXLJobExecutor(config *types.AppConfig, db *gorm.DB) *XXLJobExecutor {
} }
func (e *XXLJobExecutor) Run() error { func (e *XXLJobExecutor) Run() error {
e.executor.RegTask("ClearOrder", e.ClearOrder) e.executor.RegTask("ClearOrders", e.ClearOrders)
e.executor.RegTask("ResetVipCalls", e.ResetVipCalls) e.executor.RegTask("ResetVipCalls", e.ResetVipCalls)
return e.executor.Run() return e.executor.Run()
} }
// ClearOrder 清理未支付的订单,如果没有抛出异常则表示执行成功 // ClearOrders 清理未支付的订单,如果没有抛出异常则表示执行成功
func (e *XXLJobExecutor) ClearOrder(cxt context.Context, param *xxl.RunReq) (msg string) { func (e *XXLJobExecutor) ClearOrders(cxt context.Context, param *xxl.RunReq) (msg string) {
logger.Debug("执行清理未支付订单...") logger.Debug("执行清理未支付订单...")
var sysConfig model.Config var sysConfig model.Config
res := e.db.Where("marker", "system").First(&sysConfig) res := e.db.Where("marker", "system").First(&sysConfig)

View File

@ -17,20 +17,6 @@ CREATE TABLE `chatgpt_orders` (
`deleted_at` datetime DEFAULT NULL `deleted_at` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='充值订单表'; ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='充值订单表';
--
-- 转存表中的数据 `chatgpt_orders`
--
INSERT INTO `chatgpt_orders` (`id`, `user_id`, `product_id`, `mobile`, `order_no`, `subject`, `amount`, `status`, `remark`, `pay_time`, `created_at`, `updated_at`, `deleted_at`) VALUES
(4, 4, 1, '18575670125', '202308317102915300416290816', '会员1个月', '0.01', 2, '{\"days\":30,\"calls\":500,\"name\":\"会员1个月\",\"discount\":10.99}', 1693466990, '2023-08-31 15:29:33', '2023-08-31 15:29:51', NULL),
(5, 4, 5, '18575670125', '202308317102946758199607296', '100次点卡', '0.30', 2, '{\"days\":0,\"calls\":100,\"name\":\"100次点卡\"}', 1693466990, '2023-08-31 17:34:34', '2023-08-31 17:34:34', NULL),
(6, 4, 5, '18575670125', '202308317102946843595636736', '100次点卡', '0.03', 2, '{\"days\":0,\"calls\":100,\"name\":\"100次点卡\"}', 1693474722, '2023-08-31 17:34:54', '2023-08-31 17:38:43', NULL),
(7, 4, 1, '18575670125', '202309017103252664456052736', '会员1个月', '0.01', 2, '{\"days\":30,\"calls\":0,\"name\":\"会员1个月\"}', 1693466990, '2023-09-01 13:50:07', '2023-09-01 13:50:07', NULL),
(8, 4, 1, '18575670125', '202309017103252894391992320', '会员1个月', '0.01', 2, '{\"days\":30,\"calls\":0,\"name\":\"会员1个月\"}', 1693466990, '2023-09-01 13:51:02', '2023-09-01 13:51:02', NULL),
(9, 4, 5, '18575670125', '202309017103254657538981888', '100次点卡', '0.03', 2, '{\"days\":0,\"calls\":100,\"name\":\"100次点卡\"}', 1693474722, '2023-09-01 13:58:02', '2023-09-01 13:58:02', NULL),
(10, 4, 1, '18575670125', '202309017103259375405367296', '会员1个月', '0.01', 2, '{\"days\":30,\"calls\":0,\"name\":\"会员1个月\"}', 1693474722, '2023-09-01 14:16:47', '2023-09-01 14:16:47', NULL),
(11, 4, 3, '18575670125', '202309017103290730432430080', '会员6个月', '190.00', 2, '{\"days\":180,\"calls\":0,\"name\":\"会员6个月\",\"price\":290,\"discount\":100}', 1693474722, '2023-09-01 16:21:23', '2023-09-01 16:21:23', NULL),
(12, 4, 4, '18575670125', '202309017103291707520712704', '会员12个月', '380.00', 2, '{\"days\":365,\"calls\":0,\"name\":\"会员12个月\",\"price\":580,\"discount\":200}', 1693466990, '2023-09-01 16:25:16', '2023-09-01 16:25:16', NULL);
-- 创建索引 -- 创建索引
ALTER TABLE `chatgpt_orders` ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `order_no` (`order_no`); ALTER TABLE `chatgpt_orders` ADD PRIMARY KEY (`id`), ADD UNIQUE KEY `order_no` (`order_no`);
ALTER TABLE `chatgpt_orders` MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=13; ALTER TABLE `chatgpt_orders` MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=13;
@ -58,3 +44,5 @@ INSERT INTO `chatgpt_products` (`id`, `name`, `price`, `discount`, `days`, `call
ALTER TABLE `chatgpt_products` ADD PRIMARY KEY (`id`); ALTER TABLE `chatgpt_products` ADD PRIMARY KEY (`id`);
ALTER TABLE `chatgpt_products` MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6; ALTER TABLE `chatgpt_products` MODIFY `id` int NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=6;
ALTER TABLE `chatgpt_orders` ADD `pay_way` VARCHAR(20) DEFAULT '0' NOT NULL COMMENT '支付方式' AFTER `pay_time`;

View File

@ -11,6 +11,7 @@ services:
- CONFIG_FILE=config.toml - CONFIG_FILE=config.toml
ports: ports:
- "5678:5678" - "5678:5678"
- "9999:9999"
volumes: volumes:
- /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime - /usr/share/zoneinfo/Asia/Shanghai:/etc/localtime
- ./conf/config.toml:/var/www/app/config.toml - ./conf/config.toml:/var/www/app/config.toml

View File

@ -1,5 +1,5 @@
# 前端 Vue 项目构建 # 前端 Vue 项目构建
FROM nginx:1.20 FROM nginx:1.20.2
MAINTAINER yangjian<yangjian102621@163.com> MAINTAINER yangjian<yangjian102621@163.com>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 95 KiB

View File

@ -8,6 +8,7 @@
import {onMounted, ref, watch} from "vue"; import {onMounted, ref, watch} from "vue";
// eslint-disable-next-line no-undef
const props = defineProps({ const props = defineProps({
second: Number, second: Number,
type: { type: {
@ -16,6 +17,7 @@ const props = defineProps({
} }
}); });
// eslint-disable-next-line no-undef
const emits = defineEmits(['timeout']); const emits = defineEmits(['timeout']);
const counter = ref(props.second) const counter = ref(props.second)
const timerStr = ref("") const timerStr = ref("")
@ -80,6 +82,7 @@ const formatTimer = (secs) => {
timerStr.value = timer.join("") timerStr.value = timer.join("")
} }
// eslint-disable-next-line no-undef
defineExpose({resetTimer}) defineExpose({resetTimer})
</script> </script>

View File

@ -47,7 +47,7 @@ onMounted(() => {
const computeSize = () => { const computeSize = () => {
const w = container.value.offsetWidth - 8 // const w = container.value.offsetWidth - 8 //
let cols = Math.floor(w / props.width) let cols = Math.floor(w / props.width)
itemWidth.value = Math.floor(w / cols) itemWidth.value = Math.ceil(w / cols)
} }
window.onresize = () => { window.onresize = () => {

View File

@ -1,10 +1,11 @@
<template> <template>
<div class="member"> <div class="member custom-scroll">
<div class="title"> <div class="title">
会员充值中心 会员充值中心
</div> </div>
<div class="inner custom-scroll"> <div class="inner" :style="{height: listBoxHeight + 'px'}">
<el-row :gutter="20">
<el-col :span="7">
<div class="user-profile"> <div class="user-profile">
<user-profile/> <user-profile/>
@ -19,7 +20,8 @@
<el-button type="primary" v-if="enableReward" @click="showRewardDialog = true">加入众筹</el-button> <el-button type="primary" v-if="enableReward" @click="showRewardDialog = true">加入众筹</el-button>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
<el-button type="primary" v-if="enableReward" @click="showRewardVerifyDialog = true">众筹核销</el-button> <el-button type="primary" v-if="enableReward" @click="showRewardVerifyDialog = true">众筹核销
</el-button>
</el-col> </el-col>
<el-col :span="24" style="padding-top: 30px"> <el-col :span="24" style="padding-top: 30px">
@ -27,8 +29,10 @@
</el-col> </el-col>
</el-row> </el-row>
</div> </div>
</el-col>
<div class="product-box" :style="{height: listBoxHeight + 'px'}"> <el-col :span="17">
<div class="product-box">
<div class="info"> <div class="info">
<el-alert type="info" show-icon :closable="false" effect="dark"> <el-alert type="info" show-icon :closable="false" effect="dark">
<strong>说明:</strong> 成为本站会员后每月有500次对话额度50 AI 绘画额度限制下月1号解除若在期间超过次数后可单独购买点卡 <strong>说明:</strong> 成为本站会员后每月有500次对话额度50 AI 绘画额度限制下月1号解除若在期间超过次数后可单独购买点卡
@ -70,6 +74,9 @@
<user-order v-if="isLogin"/> <user-order v-if="isLogin"/>
</div> </div>
</div> </div>
</el-col>
</el-row>
</div> </div>
<login-dialog :show="showLoginDialog" @hide="showLoginDialog = false"/> <login-dialog :show="showLoginDialog" @hide="showLoginDialog = false"/>
@ -145,14 +152,13 @@ import BindMobile from "@/components/BindMobile.vue";
import RewardVerify from "@/components/RewardVerify.vue"; import RewardVerify from "@/components/RewardVerify.vue";
import {useRouter} from "vue-router"; import {useRouter} from "vue-router";
import {removeUserToken} from "@/store/session"; import {removeUserToken} from "@/store/session";
import CountDown from "@/components/CountDown.vue";
import UserOrder from "@/components/UserOrder.vue"; import UserOrder from "@/components/UserOrder.vue";
import CountDown from "@/components/CountDown.vue";
const listBoxHeight = window.innerHeight - 97 const listBoxHeight = window.innerHeight - 97
const list = ref([]) const list = ref([])
const showLoginDialog = ref(false) const showLoginDialog = ref(false)
const showPayDialog = ref(false) const showPayDialog = ref(false)
const elements = ref(null)
const vipImg = ref("/images/vip.png") const vipImg = ref("/images/vip.png")
const enableReward = ref(false) // const enableReward = ref(false) //
const rewardImg = ref('/images/reward.png') const rewardImg = ref('/images/reward.png')
@ -205,6 +211,7 @@ const orderPay = (row) => {
curPayProduct.value = row curPayProduct.value = row
} }
loading.value = true loading.value = true
text.value = ""
httpPost("/api/payment/alipay/qrcode", {product_id: curPayProduct.value.id, user_id: user.value.id}).then(res => { httpPost("/api/payment/alipay/qrcode", {product_id: curPayProduct.value.id, user_id: user.value.id}).then(res => {
showPayDialog.value = true showPayDialog.value = true
qrcode.value = res.data['image'] qrcode.value = res.data['image']
@ -310,13 +317,15 @@ const logout = function () {
display flex display flex
color #ffffff color #ffffff
padding 15px 0 15px 15px; padding 15px 0 15px 15px;
overflow hidden overflow-x hidden
overflow-y visible
.user-profile { .user-profile {
padding 10px 20px padding 10px 20px 20px 20px
background-color #393F4A background-color #393F4A
color #ffffff color #ffffff
border-radius 10px border-radius 10px
height 100vh
.el-form-item__label { .el-form-item__label {
color #ffffff color #ffffff
@ -336,17 +345,12 @@ const logout = function () {
.product-box { .product-box {
overflow-x hidden
overflow-y visible
width 100%
padding-left 20px
.info { .info {
.el-alert__description { .el-alert__description {
font-size 14px !important font-size 14px !important
margin 0 margin 0
} }
padding 10px 20px padding 10px 20px 20px 0
} }
.list-box { .list-box {
@ -423,11 +427,11 @@ const logout = function () {
} }
.headline { .headline {
padding 0 20px padding 0 20px 20px 0
} }
.user-order { .user-order {
padding 0 20px 20px 20px padding 0 20px 20px 0
} }
} }
} }