feat: add invitation and promotion functions

This commit is contained in:
RockYang
2023-11-23 16:30:15 +08:00
parent d35164506a
commit a4a1eec30b
17 changed files with 627 additions and 70 deletions

View File

@@ -124,6 +124,11 @@ type UserChatConfig struct {
ApiKeys map[Platform]string `json:"api_keys"`
}
type InviteReward struct {
ChatCalls int `json:"chat_calls"`
ImgCalls int `json:"img_calls"`
}
type ModelAPIConfig struct {
ApiURL string `json:"api_url,omitempty"`
Temperature float32 `json:"temperature"`
@@ -135,8 +140,8 @@ type SystemConfig struct {
Title string `json:"title"`
AdminTitle string `json:"admin_title"`
Models []string `json:"models"`
UserInitCalls int `json:"user_init_calls"` // 新用户注册默认总送多少次调用
InitImgCalls int `json:"init_img_calls"`
InitChatCalls int `json:"init_chat_calls"` // 新用户注册赠送对话次数
InitImgCalls int `json:"init_img_calls"` // 新用户注册赠送绘图次数
VipMonthCalls int `json:"vip_month_calls"` // 会员每个赠送的调用次数
EnabledRegister bool `json:"enabled_register"`
EnabledMsg bool `json:"enabled_msg"` // 启用短信验证码服务
@@ -148,4 +153,6 @@ type SystemConfig struct {
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"` // 邀请用户注册奖励绘图次数
}

View File

@@ -49,3 +49,11 @@ func (h *BaseHandler) GetUserKey(c *gin.Context) string {
}
return fmt.Sprintf("users/%v", userId)
}
func (h *BaseHandler) GetLoginUserId(c *gin.Context) uint {
userId, ok := c.Get(types.LoginUserID)
if !ok {
return 0
}
return uint(utils.IntValue(utils.InterfaceToString(userId), 0))
}

View File

@@ -0,0 +1,66 @@
package handler
import (
"chatplus/core"
"chatplus/store/model"
"chatplus/store/vo"
"chatplus/utils"
"chatplus/utils/resp"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"strings"
)
// InviteHandler 用户邀请
type InviteHandler struct {
BaseHandler
db *gorm.DB
}
func NewInviteHandler(app *core.AppServer, db *gorm.DB) *InviteHandler {
h := InviteHandler{db: db}
h.App = app
return &h
}
// Code 获取当前用户邀请码
func (h *InviteHandler) Code(c *gin.Context) {
userId := h.GetLoginUserId(c)
var inviteCode model.InviteCode
res := h.db.Where("user_id = ?", userId).First(&inviteCode)
// 如果邀请码不存在,则创建一个
if res.Error != nil {
code := strings.ToUpper(utils.RandString(8))
for {
res = h.db.Where("code = ?", code).First(&inviteCode)
if res.Error != nil { // 不存在相同的邀请码则退出
break
}
}
inviteCode.UserId = userId
inviteCode.Code = code
h.db.Create(&inviteCode)
}
var codeVo vo.InviteCode
err := utils.CopyObject(inviteCode, &codeVo)
if err != nil {
resp.ERROR(c, "拷贝对象失败")
return
}
resp.SUCCESS(c, codeVo)
}
// List Log 用户邀请记录
func (h *InviteHandler) List(c *gin.Context) {
resp.SUCCESS(c)
}
// Hits 访问邀请码
func (h *InviteHandler) Hits(c *gin.Context) {
code := c.Query("code")
h.db.Model(&model.InviteCode{}).Where("code = ?", code).UpdateColumn("hits", gorm.Expr("hits + ?", 1))
resp.SUCCESS(c)
}

View File

@@ -39,9 +39,10 @@ func NewUserHandler(
func (h *UserHandler) Register(c *gin.Context) {
// parameters process
var data struct {
Mobile string `json:"mobile"`
Password string `json:"password"`
Code string `json:"code"`
Mobile string `json:"mobile"`
Password string `json:"password"`
Code string `json:"code"`
InviteCode string `json:"invite_code"`
}
if err := c.ShouldBindJSON(&data); err != nil {
resp.ERROR(c, types.InvalidArgs)
@@ -68,6 +69,16 @@ func (h *UserHandler) Register(c *gin.Context) {
}
}
// 验证邀请码
inviteCode := model.InviteCode{}
if data.InviteCode != "" {
res := h.db.Where("code = ?", data.InviteCode).First(&inviteCode)
if res.Error != nil {
resp.ERROR(c, "无效的邀请码")
return
}
}
// check if the username is exists
var item model.User
res := h.db.Where("mobile = ?", data.Mobile).First(&item)
@@ -92,7 +103,7 @@ func (h *UserHandler) Register(c *gin.Context) {
types.ChatGLM: "",
},
}),
Calls: h.App.SysConfig.UserInitCalls,
Calls: h.App.SysConfig.InitChatCalls,
ImgCalls: h.App.SysConfig.InitImgCalls,
}
res = h.db.Create(&user)
@@ -102,6 +113,26 @@ func (h *UserHandler) Register(c *gin.Context) {
return
}
// 记录邀请关系
if data.InviteCode != "" {
// 增加邀请数量
h.db.Model(&model.InviteCode{}).Where("code = ?", data.InviteCode).UpdateColumn("reg_num", gorm.Expr("reg_num + ?", 1))
if h.App.SysConfig.InviteChatCalls > 0 {
h.db.Model(&model.User{}).Where("id = ?", inviteCode.UserId).UpdateColumn("calls", gorm.Expr("calls + ?", h.App.SysConfig.InviteChatCalls))
}
if h.App.SysConfig.InviteImgCalls > 0 {
h.db.Model(&model.User{}).Where("id = ?", inviteCode.UserId).UpdateColumn("img_calls", gorm.Expr("img_calls + ?", h.App.SysConfig.InviteImgCalls))
}
// 添加邀请记录
h.db.Create(&model.InviteLog{
InviterId: inviteCode.UserId,
UserId: user.Id,
Username: user.Mobile,
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) // 注册成功,删除短信验证码
}

View File

@@ -346,6 +346,14 @@ func main() {
group.GET("list", h.List)
}),
fx.Provide(handler.NewInviteHandler),
fx.Invoke(func(s *core.AppServer, h *handler.InviteHandler) {
group := s.Engine.Group("/api/invite/")
group.GET("code", h.Code)
group.GET("list", h.List)
group.GET("hits", h.Hits)
}),
fx.Invoke(func(s *core.AppServer, db *gorm.DB) {
err := s.Run(db)
if err != nil {

View File

@@ -0,0 +1,12 @@
package model
import "time"
type InviteCode struct {
Id uint `gorm:"primarykey;column:id"`
UserId uint
Code string
Hits int // 点击次数
RegNum int // 注册人数
CreatedAt time.Time
}

View File

@@ -0,0 +1,15 @@
package model
import (
"time"
)
type InviteLog struct {
Id uint `gorm:"primarykey;column:id"`
InviterId uint
UserId uint
Username string
InviteCode string
Reward string `gorm:"column:reward_json"` // 邀请奖励
CreatedAt time.Time
}

View File

@@ -0,0 +1,10 @@
package vo
type InviteCode struct {
Id uint `json:"id"`
UserId uint `json:"user_id"`
Code string `json:"code"`
Hits int `json:"hits"`
RegNum int `json:"reg_num"`
CreatedAt int64 `json:"created_at"`
}

View File

@@ -0,0 +1,15 @@
package vo
import (
"chatplus/core/types"
)
type InviteLog struct {
Id uint `json:"id"`
InviterId uint `json:"inviter_id"`
UserId uint `json:"user_id"`
Username string `json:"username"`
InviteCode string `json:"invite_code"`
Reward types.InviteReward `json:"reward"`
CreatedAt int64 `json:"created_at"`
}

View File

@@ -2,13 +2,12 @@ package main
import (
"fmt"
"strconv"
"reflect"
"time"
)
func main() {
value, err := strconv.Atoi("012345")
if err != nil {
panic(err)
}
fmt.Println(value)
r := time.Now()
f := reflect.ValueOf(r)
fmt.Println(f.Type().Kind())
}

View File

@@ -4,6 +4,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
"github.com/nfnt/resize"
"github.com/skip2/go-qrcode"
"image"
@@ -14,8 +15,6 @@ import (
"reflect"
"strconv"
"strings"
"github.com/lionsoul2014/ip2region/binding/golang/xdb"
)
// CopyObject 拷贝对象
@@ -60,6 +59,8 @@ func CopyObject(src interface{}, dst interface{}) error {
value.Set(reflect.ValueOf(""))
}
}
} else if field.Type.Kind() != value.Type().Kind() { // 不同类型的字段过滤掉
continue
} else { // 简单数据类型的强制类型转换
switch value.Kind() {
case reflect.Int: