330 lines
8.0 KiB
Go
330 lines
8.0 KiB
Go
package model
|
|
|
|
import (
|
|
"context"
|
|
"database/sql/driver"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"github.com/go-redis/redis/v8"
|
|
"gorm.io/gorm"
|
|
"samba/pkg/log"
|
|
"samba/pkg/xtime"
|
|
"samba/proto"
|
|
"samba/util/rdbkey"
|
|
"time"
|
|
)
|
|
|
|
type Items map[int]int
|
|
|
|
func (i *Items) Scan(v any) error {
|
|
if i == nil {
|
|
*i = make(Items)
|
|
}
|
|
switch js := v.(type) {
|
|
case string:
|
|
if len(js) <= 0 {
|
|
return nil
|
|
}
|
|
return json.Unmarshal([]byte(js), i)
|
|
case []byte:
|
|
if len(js) <= 0 {
|
|
return nil
|
|
}
|
|
return json.Unmarshal(js, i)
|
|
default:
|
|
return fmt.Errorf(fmt.Sprintf("item is %T but not string", v))
|
|
}
|
|
}
|
|
|
|
func (i *Items) Value() (driver.Value, error) {
|
|
data, err := json.Marshal(i)
|
|
return string(data), err
|
|
}
|
|
|
|
type Rule proto.Rule
|
|
|
|
func (r *Rule) Scan(v any) error {
|
|
if r == nil {
|
|
*r = Rule{}
|
|
}
|
|
switch js := v.(type) {
|
|
case string:
|
|
if len(js) <= 0 {
|
|
return nil
|
|
}
|
|
return json.Unmarshal([]byte(js), &r)
|
|
case []byte:
|
|
if len(js) <= 0 {
|
|
return nil
|
|
}
|
|
return json.Unmarshal(js, &r)
|
|
default:
|
|
return fmt.Errorf(fmt.Sprintf("rule is %T but not string", v))
|
|
}
|
|
|
|
}
|
|
func (r *Rule) Value() (driver.Value, error) {
|
|
data, err := json.Marshal(r)
|
|
return string(data), err
|
|
}
|
|
|
|
// 兑换码
|
|
|
|
type RedeemCode struct {
|
|
// 兑换码本身
|
|
Code string `json:"code" gorm:"column:code"`
|
|
// 生效时间
|
|
StartTime time.Time `json:"start_time" gorm:"column:start_time"`
|
|
// 截止时间
|
|
EndTime time.Time `json:"end_time" gorm:"column:end_time"`
|
|
// 创建时间
|
|
CreateTime time.Time `json:"create_time" gorm:"column:create_time"`
|
|
// 可兑换次数
|
|
Count int `json:"count" gorm:"column:count"`
|
|
// 兑换物品
|
|
Item *Items `json:"item" gorm:"column:item"`
|
|
// 兑换规则
|
|
Rule *Rule `json:"rule" gorm:"column:rule"`
|
|
}
|
|
|
|
func (*RedeemCode) TableName() string {
|
|
return "t_redeem_code"
|
|
}
|
|
|
|
type RedeemCodeOp struct {
|
|
db *gorm.DB
|
|
rdb *redis.Client
|
|
}
|
|
|
|
func NewRedeemCodeOp() *RedeemCodeOp {
|
|
return &RedeemCodeOp{
|
|
rdb: rdbBaseInfo, db: userDB,
|
|
}
|
|
}
|
|
|
|
// IsExist 判断兑换码是否存在
|
|
func (op *RedeemCodeOp) IsExist(code string) (exist bool) {
|
|
res := op.rdb.HGet(context.TODO(), rdbkey.RedeemCodeKey(), code)
|
|
if res.Err() == nil && res.Val() != "" {
|
|
return true
|
|
}
|
|
var r RedeemCode
|
|
if err := op.db.Where("code=?", code).First(&r).Error; err != nil {
|
|
if !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
// 非字段不存在错误
|
|
log.Error(fmt.Sprintf("RedeemCodeOp code=%s err:%s", code, err.Error()))
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 回写redis
|
|
op.insertRedis(&r)
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
// Insert 插入一条兑换码
|
|
func (op *RedeemCodeOp) Insert(code *RedeemCode) error {
|
|
err := op.db.Create(code).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
op.insertRedis(code)
|
|
return nil
|
|
}
|
|
|
|
// insertRedis 插入redis
|
|
func (op *RedeemCodeOp) insertRedis(code *RedeemCode) {
|
|
data, err := json.Marshal(code)
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("RedeemCodeOp InsertRedis code=%s err:%s", code.Code, err.Error()))
|
|
return
|
|
}
|
|
err = op.rdb.HSet(context.TODO(), rdbkey.RedeemCodeKey(), code.Code, string(data)).Err()
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("RedeemCodeOp InsertRedis code=%s err:%s", code.Code, err.Error()))
|
|
}
|
|
}
|
|
|
|
// Get 获取兑换码
|
|
func (op *RedeemCodeOp) Get(code string) (r *RedeemCode, err error) {
|
|
res := op.rdb.HGet(context.TODO(), rdbkey.RedeemCodeKey(), code)
|
|
r = &RedeemCode{}
|
|
if res.Err() == nil && res.Val() != "" {
|
|
return r, json.Unmarshal([]byte(res.Val()), r)
|
|
}
|
|
err = op.db.Where("code=?", code).First(r).Error
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// 回写redis
|
|
op.insertRedis(r)
|
|
return r, nil
|
|
|
|
}
|
|
|
|
// GetList 获取兑换码列表
|
|
func (op *RedeemCodeOp) GetList(limit, offset int) (rs []*RedeemCode, err error) {
|
|
return rs, op.db.Limit(limit).Offset(offset).Order("create_time desc").Find(&rs).Error
|
|
}
|
|
|
|
// Count 获取兑换码总数
|
|
func (op *RedeemCodeOp) Count() (count int64, err error) {
|
|
return count, op.db.Model(&RedeemCode{}).Count(&count).Error
|
|
}
|
|
|
|
// 兑换码使用记录
|
|
|
|
type RedeemCodeUseRecord struct {
|
|
// 兑换码本身
|
|
Code string `json:"code" gorm:"column:code"`
|
|
// 使用者ID
|
|
Uid int64 `json:"uid" gorm:"column:uid"`
|
|
// 使用时间
|
|
UseTime time.Time `json:"use_time" gorm:"column:use_time"`
|
|
// 用户昵称
|
|
MNick string `json:"mnick" gorm:"column:mnick"`
|
|
// 用户注册时间
|
|
MTime int64 `json:"mtime" gorm:"column:mtime"`
|
|
|
|
// 用户注册渠道
|
|
API int `json:"api" gorm:"column:api"`
|
|
}
|
|
|
|
func (*RedeemCodeUseRecord) TableName() string {
|
|
return "t_redeem_code_record"
|
|
}
|
|
|
|
type RedeemCodeUseRecordOp struct {
|
|
db *gorm.DB
|
|
rdb *redis.Client
|
|
}
|
|
|
|
func NewRedeemCodeUseRecordOp() *RedeemCodeUseRecordOp {
|
|
return &RedeemCodeUseRecordOp{
|
|
rdb: rdbBaseInfo, db: userDB,
|
|
}
|
|
}
|
|
|
|
func (op *RedeemCodeUseRecordOp) GetByCode(code string, limit, offset int) ([]*RedeemCodeUseRecord, error) {
|
|
var res []*RedeemCodeUseRecord
|
|
return res, op.db.Where("code=?", code).Limit(limit).Offset(offset).Order("use_time desc").Find(&res).Error
|
|
}
|
|
|
|
func (op *RedeemCodeUseRecordOp) CountByCode(code string) (count int64, err error) {
|
|
return count, op.db.Model(&RedeemCodeUseRecord{}).Where("code=?", code).Count(&count).Error
|
|
}
|
|
|
|
func (op *RedeemCodeUseRecordOp) GetByUid(uid int64, limit, offset int) ([]*RedeemCodeUseRecord, error) {
|
|
var res []*RedeemCodeUseRecord
|
|
return res, op.db.Where("uid=?", uid).Limit(limit).Offset(offset).Order("use_time desc").Find(&res).Error
|
|
}
|
|
func (op *RedeemCodeUseRecordOp) GetByUidAndCode(uid int64, code string) (*RedeemCodeUseRecord, error) {
|
|
res := &RedeemCodeUseRecord{}
|
|
return res, op.db.Where("uid=? and code=?", uid, code).First(&res).Error
|
|
}
|
|
|
|
// CodeUsedCount 查询兑换码的使用次数
|
|
func (op *RedeemCodeUseRecordOp) CodeUsedCount(code string) (int64, error) {
|
|
count, err := op.rdb.HGet(context.TODO(), rdbkey.RedeemCodeUsedKey(), code).Int64()
|
|
if err != nil {
|
|
return op.updateUseCount(code)
|
|
}
|
|
return count, nil
|
|
}
|
|
|
|
// UserIsUsedCode 判断用户是否使用过兑换码
|
|
func (op *RedeemCodeUseRecordOp) UserIsUsedCode(uid int64, code string) (bool, error) {
|
|
r, err := op.GetByUidAndCode(uid, code)
|
|
if err != nil {
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
return false, nil
|
|
}
|
|
return false, err
|
|
}
|
|
return r != nil, nil
|
|
}
|
|
|
|
// Insert 插入一条兑换码使用记录
|
|
func (op *RedeemCodeUseRecordOp) Insert(uid int64, code string) error {
|
|
uInfo, err := NewUserInfoOp().Load(uid)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
record := &RedeemCodeUseRecord{
|
|
Code: code,
|
|
Uid: uInfo.UID,
|
|
UseTime: xtime.Now().StdTime(),
|
|
MNick: uInfo.MNick,
|
|
MTime: uInfo.MTime,
|
|
API: uInfo.API,
|
|
}
|
|
|
|
err = op.db.Create(record).Error
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, _ = op.updateUseCount(code)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (op *RedeemCodeUseRecordOp) GetCodeUseList() {
|
|
|
|
}
|
|
|
|
// updateUseCount 更新兑换码使用次数
|
|
func (op *RedeemCodeUseRecordOp) updateUseCount(code string) (count int64, err error) {
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("RedeemCodeUseRecordOp updateUseCount code=%s, err:%s", code, err.Error()))
|
|
}
|
|
}()
|
|
err = op.db.Model(&RedeemCodeUseRecord{}).Where("code=?", code).Count(&count).Error
|
|
if err != nil {
|
|
return
|
|
}
|
|
err = op.rdb.HSet(context.TODO(), rdbkey.RedeemCodeUsedKey(), code, count).Err()
|
|
return
|
|
}
|
|
|
|
// insertRedis 插入到redis缓存
|
|
//
|
|
// 暂时先不用,双写有一致性问题
|
|
func (op *RedeemCodeUseRecordOp) insertRedis(rs ...*RedeemCodeUseRecord) {
|
|
|
|
userMp := make(map[int64][]any)
|
|
codeMp := make(map[string][]any)
|
|
|
|
for _, r := range rs {
|
|
data, err := json.Marshal(r)
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("RedeemCodeUseRecordOp InsertRedis code=%s uid=%d err:%s", r.Code, r.Uid, err.Error()))
|
|
return
|
|
}
|
|
strData := string(data)
|
|
userMp[r.Uid] = append(userMp[r.Uid], r.Code, strData)
|
|
codeMp[r.Code] = append(codeMp[r.Code], r.Uid, strData)
|
|
}
|
|
|
|
for uid, kvs := range userMp {
|
|
err := op.rdb.HSet(context.TODO(), rdbkey.RedeemCodeUserUseKey(uid), kvs...).Err()
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("RedeemCodeUseRecordOp InsertRedis uid=%d, err:%s", uid, err.Error()))
|
|
}
|
|
}
|
|
|
|
for code, kvs := range codeMp {
|
|
err := op.rdb.HSet(context.TODO(), rdbkey.RedeemCodeUseKey(code), kvs...).Err()
|
|
if err != nil {
|
|
log.Error(fmt.Sprintf("RedeemCodeUseRecordOp InsertRedis code=%s, err:%s", code, err.Error()))
|
|
}
|
|
}
|
|
|
|
}
|