feat: add img_calls field for recharge products

This commit is contained in:
RockYang 2023-12-15 16:56:56 +08:00
parent d974b1ff0e
commit 45cb29d9a0
13 changed files with 83 additions and 41 deletions

View File

@ -150,7 +150,8 @@ type SystemConfig struct {
Models []string `json:"models"` Models []string `json:"models"`
InitChatCalls int `json:"init_chat_calls"` // 新用户注册赠送对话次数 InitChatCalls int `json:"init_chat_calls"` // 新用户注册赠送对话次数
InitImgCalls int `json:"init_img_calls"` // 新用户注册赠送绘图次数 InitImgCalls int `json:"init_img_calls"` // 新用户注册赠送绘图次数
VipMonthCalls int `json:"vip_month_calls"` // 会员每个赠送的调用次数 VipMonthCalls int `json:"vip_month_calls"` // VIP 会员每月赠送的对话次数
VipMonthImgCalls int `json:"vip_month_img_calls"` // VIP 会员每月赠送绘图次数
EnabledRegister bool `json:"enabled_register"` // 是否启用注册功能,关闭注册功能之后将无法注册 EnabledRegister bool `json:"enabled_register"` // 是否启用注册功能,关闭注册功能之后将无法注册
EnabledMsg bool `json:"enabled_msg"` // 是否启用短信验证码服务 EnabledMsg bool `json:"enabled_msg"` // 是否启用短信验证码服务
RewardImg string `json:"reward_img"` // 众筹收款二维码地址 RewardImg string `json:"reward_img"` // 众筹收款二维码地址

View File

@ -9,9 +9,10 @@ const (
) )
type OrderRemark struct { type OrderRemark struct {
Days int `json:"days"` // 有效期 Days int `json:"days"` // 有效期
Calls int `json:"calls"` // 增加调用次数 Calls int `json:"calls"` // 增加对话次数
Name string `json:"name"` // 产品名称 ImgCalls int `json:"img_calls"` // 增加绘图次数
Name string `json:"name"` // 产品名称
Price float64 `json:"price"` Price float64 `json:"price"`
Discount float64 `json:"discount"` Discount float64 `json:"discount"`
} }

View File

@ -33,6 +33,7 @@ func (h *ProductHandler) Save(c *gin.Context) {
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
Days int `json:"days"` Days int `json:"days"`
Calls int `json:"calls"` Calls int `json:"calls"`
ImgCalls int `json:"img_calls"`
CreatedAt int64 `json:"created_at"` CreatedAt int64 `json:"created_at"`
} }
if err := c.ShouldBindJSON(&data); err != nil { if err := c.ShouldBindJSON(&data); err != nil {
@ -40,7 +41,14 @@ func (h *ProductHandler) Save(c *gin.Context) {
return return
} }
item := model.Product{Name: data.Name, Price: data.Price, Discount: data.Discount, Days: data.Days, Calls: data.Calls, Enabled: data.Enabled} item := model.Product{
Name: data.Name,
Price: data.Price,
Discount: data.Discount,
Days: data.Days,
Calls: data.Calls,
ImgCalls: data.ImgCalls,
Enabled: data.Enabled}
item.Id = data.Id item.Id = data.Id
if item.Id > 0 { if item.Id > 0 {
item.CreatedAt = time.Unix(data.CreatedAt, 0) item.CreatedAt = time.Unix(data.CreatedAt, 0)

View File

@ -196,6 +196,7 @@ func (h *PaymentHandler) PayQrcode(c *gin.Context) {
remark := types.OrderRemark{ remark := types.OrderRemark{
Days: product.Days, Days: product.Days,
Calls: product.Calls, Calls: product.Calls,
ImgCalls: product.ImgCalls,
Name: product.Name, Name: product.Name,
Price: product.Price, Price: product.Price,
Discount: product.Discount, Discount: product.Discount,
@ -330,6 +331,12 @@ func (h *PaymentHandler) notify(orderNo string) error {
user.Calls += h.App.SysConfig.VipMonthCalls user.Calls += h.App.SysConfig.VipMonthCalls
} }
if remark.ImgCalls > 0 {
user.ImgCalls += remark.ImgCalls
} else {
user.ImgCalls += h.App.SysConfig.VipMonthImgCalls
}
// 更新用户信息 // 更新用户信息
res = h.db.Updates(&user) res = h.db.Updates(&user)
if res.Error != nil { if res.Error != nil {

View File

@ -28,7 +28,7 @@ func NewServicePool(db *gorm.DB, redisCli *redis.Client, manager *oss.UploaderMa
name := fmt.Sprintf("MjService-%d", k) name := fmt.Sprintf("MjService-%d", k)
// create mj service // create mj service
service := NewService(name, queue, 4, 600, db, client, manager, appConfig) service := NewService(name, queue, 4, 600, db, client, manager, appConfig.ProxyURL)
botName := fmt.Sprintf("MjBot-%d", k) 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 { if err != nil {

View File

@ -25,7 +25,7 @@ type Service struct {
taskTimeout int64 taskTimeout int64
} }
func NewService(name string, queue *store.RedisQueue, maxTaskNum int32, timeout int64, db *gorm.DB, client *Client, manager *oss.UploaderManager, config *types.AppConfig) *Service { func NewService(name string, queue *store.RedisQueue, maxTaskNum int32, timeout int64, db *gorm.DB, client *Client, manager *oss.UploaderManager, proxy string) *Service {
return &Service{ return &Service{
name: name, name: name,
db: db, db: db,
@ -34,7 +34,7 @@ func NewService(name string, queue *store.RedisQueue, maxTaskNum int32, timeout
uploadManager: manager, uploadManager: manager,
taskTimeout: timeout, taskTimeout: timeout,
maxHandleTaskNum: maxTaskNum, maxHandleTaskNum: maxTaskNum,
proxyURL: config.ProxyURL, proxyURL: proxy,
taskStartTimes: make(map[int]time.Time, 0), taskStartTimes: make(map[int]time.Time, 0),
} }
} }

View File

@ -101,30 +101,39 @@ func (e *XXLJobExecutor) ResetVipCalls(cxt context.Context, param *xxl.RunReq) (
u.Vip = false u.Vip = false
} else { } else {
if u.Calls <= 0 { if u.Calls <= 0 {
u.Calls = config.VipMonthCalls u.Calls = 0
} else {
// 如果该用户当月有充值点卡,则将点卡中未用完的点数结余到下个月
var orders []model.Order
e.db.Debug().Where("user_id = ? AND pay_time > ?", u.Id, firstOfMonth).Find(&orders)
var calls = 0
for _, o := range orders {
var remark types.OrderRemark
err = utils.JsonDecode(o.Remark, &remark)
if err != nil {
continue
}
if remark.Days > 0 { // 会员续费
continue
}
calls += remark.Calls
}
if u.Calls > calls { // 本月套餐没有用完
u.Calls = calls + config.VipMonthCalls
} else {
u.Calls = u.Calls + config.VipMonthCalls
}
logger.Infof("%s 点卡结余:%d", u.Mobile, calls)
} }
if u.ImgCalls <= 0 {
u.ImgCalls = 0
}
// 如果该用户当月有充值点卡,则将点卡中未用完的点数结余到下个月
var orders []model.Order
e.db.Debug().Where("user_id = ? AND pay_time > ?", u.Id, firstOfMonth).Find(&orders)
var calls = 0
var imgCalls = 0
for _, o := range orders {
var remark types.OrderRemark
err = utils.JsonDecode(o.Remark, &remark)
if err != nil {
continue
}
if remark.Days > 0 { // 会员续费
continue
}
calls += remark.Calls
imgCalls += remark.ImgCalls
}
if u.Calls > calls { // 本月套餐没有用完
u.Calls = calls + config.VipMonthCalls
} else {
u.Calls = u.Calls + config.VipMonthCalls
}
if u.ImgCalls > imgCalls { // 本月套餐没有用完
u.ImgCalls = imgCalls + config.VipMonthImgCalls
} else {
u.ImgCalls = u.ImgCalls + config.VipMonthImgCalls
}
logger.Infof("%s 点卡结余:%d", u.Mobile, calls)
} }
u.Tokens = 0 u.Tokens = 0
// update user // update user

View File

@ -8,6 +8,7 @@ type Product struct {
Discount float64 Discount float64
Days int Days int
Calls int Calls int
ImgCalls int
Enabled bool Enabled bool
Sales int Sales int
SortNum int SortNum int

View File

@ -7,6 +7,7 @@ type Product struct {
Discount float64 `json:"discount"` Discount float64 `json:"discount"`
Days int `json:"days"` Days int `json:"days"`
Calls int `json:"calls"` Calls int `json:"calls"`
ImgCalls int `json:"img_calls"`
Enabled bool `json:"enabled"` Enabled bool `json:"enabled"`
Sales int `json:"sales"` Sales int `json:"sales"`
SortNum int `json:"sort_num"` SortNum int `json:"sort_num"`

View File

@ -0,0 +1 @@
ALTER TABLE `chatgpt_products` ADD `img_calls` INT(11) NOT NULL DEFAULT '0' COMMENT '绘图次数' AFTER `calls`;

View File

@ -17,11 +17,16 @@
</el-table-column> </el-table-column>
<el-table-column prop="subject" label="产品名称"/> <el-table-column prop="subject" label="产品名称"/>
<el-table-column prop="amount" label="订单金额"/> <el-table-column prop="amount" label="订单金额"/>
<el-table-column label="调用次数"> <el-table-column label="对话次数">
<template #default="scope"> <template #default="scope">
<span>{{ scope.row.remark?.calls }}</span> <span>{{ scope.row.remark?.calls }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="绘图次数">
<template #default="scope">
<span>{{ scope.row.remark?.img_calls ?? 0 }}</span>
</template>
</el-table-column>
<el-table-column label="支付时间"> <el-table-column label="支付时间">
<template #default="scope"> <template #default="scope">

View File

@ -9,7 +9,7 @@
<el-table :data="items" :row-key="row => row.id" table-layout="auto"> <el-table :data="items" :row-key="row => row.id" table-layout="auto">
<el-table-column prop="name" label="产品名称"> <el-table-column prop="name" label="产品名称">
<template #default="scope"> <template #default="scope">
<span class="sort" :data-id="scope.row.id">{{scope.row.name}}</span> <span class="sort" :data-id="scope.row.id">{{ scope.row.name }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="price" label="产品价格"/> <el-table-column prop="price" label="产品价格"/>
@ -17,10 +17,11 @@
<el-table-column prop="days" label="有效期(天)"> <el-table-column prop="days" label="有效期(天)">
<template #default="scope"> <template #default="scope">
<el-tag v-if="scope.row.days === 0">长期有效</el-tag> <el-tag v-if="scope.row.days === 0">长期有效</el-tag>
<span v-else>{{scope.row.days}}</span> <span v-else>{{ scope.row.days }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="calls" label="调用次数"/> <el-table-column prop="calls" label="对话次数"/>
<el-table-column prop="img_calls" label="绘图次数"/>
<el-table-column prop="sales" label="销量"/> <el-table-column prop="sales" label="销量"/>
<el-table-column prop="enabled" label="启用状态"> <el-table-column prop="enabled" label="启用状态">
<template #default="scope"> <template #default="scope">
@ -69,8 +70,12 @@
<el-input v-model.number="item.days" autocomplete="off" placeholder="会员有效期(天)"/> <el-input v-model.number="item.days" autocomplete="off" placeholder="会员有效期(天)"/>
</el-form-item> </el-form-item>
<el-form-item label="调用次数:" prop="days"> <el-form-item label="对话次数:" prop="calls">
<el-input v-model.number="item.calls" autocomplete="off" placeholder="增加调用次数"/> <el-input v-model.number="item.calls" autocomplete="off" placeholder="增加对话次数"/>
</el-form-item>
<el-form-item label="绘图次数:" prop="img_calls">
<el-input v-model.number="item.img_calls" autocomplete="off" placeholder="增加绘图次数"/>
</el-form-item> </el-form-item>
<el-form-item label="启用状态:" prop="enable"> <el-form-item label="启用状态:" prop="enable">
@ -140,13 +145,13 @@ onMounted(() => {
const sortedData = Array.from(from.children).map(row => row.querySelector('.sort').getAttribute('data-id')); const sortedData = Array.from(from.children).map(row => row.querySelector('.sort').getAttribute('data-id'));
const ids = [] const ids = []
const sorts = [] const sorts = []
sortedData.forEach((id,index) => { sortedData.forEach((id, index) => {
ids.push(parseInt(id)) ids.push(parseInt(id))
sorts.push(index) sorts.push(index)
}) })
httpPost("/api/admin/product/sort", {ids: ids, sorts:sorts}).catch(e => { httpPost("/api/admin/product/sort", {ids: ids, sorts: sorts}).catch(e => {
ElMessage.error("排序失败:"+e.message) ElMessage.error("排序失败:" + e.message)
}) })
} }
}) })
@ -189,7 +194,7 @@ const enable = (row) => {
httpPost('/api/admin/product/enable', {id: row.id, enabled: row.enabled}).then(() => { httpPost('/api/admin/product/enable', {id: row.id, enabled: row.enabled}).then(() => {
ElMessage.success("操作成功!") ElMessage.success("操作成功!")
}).catch(e => { }).catch(e => {
ElMessage.error("操作失败:"+e.message) ElMessage.error("操作失败:" + e.message)
}) })
} }

View File

@ -24,6 +24,9 @@
<el-form-item label="VIP每月对话次数" prop="vip_month_calls"> <el-form-item label="VIP每月对话次数" prop="vip_month_calls">
<el-input v-model.number="system['vip_month_calls']" placeholder="VIP用户每月赠送对话次数"/> <el-input v-model.number="system['vip_month_calls']" placeholder="VIP用户每月赠送对话次数"/>
</el-form-item> </el-form-item>
<el-form-item label="VIP每月绘图次数" prop="vip_month_img_calls">
<el-input v-model.number="system['vip_month_img_calls']" placeholder="VIP用户每月赠送绘图次数"/>
</el-form-item>
<el-form-item label="开放注册服务" prop="enabled_register"> <el-form-item label="开放注册服务" prop="enabled_register">
<el-switch v-model="system['enabled_register']"/> <el-switch v-model="system['enabled_register']"/>
</el-form-item> </el-form-item>