You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
360 lines
7.5 KiB
Go
360 lines
7.5 KiB
Go
package projectuser
|
|
|
|
import (
|
|
"context"
|
|
"joybuywork/internal/app"
|
|
"joybuywork/internal/consts/ckey"
|
|
"joybuywork/internal/repository/cache"
|
|
"joybuywork/internal/repository/model"
|
|
"joybuywork/internal/repository/mtype"
|
|
"joybuywork/internal/repository/withdefault"
|
|
"joybuywork/internal/service/queue/producer"
|
|
"joybuywork/internal/service/queue/types"
|
|
"joybuywork/internal/utils"
|
|
"joybuywork/internal/utils/lockdo"
|
|
"joybuywork/internal/utils/logic"
|
|
"joybuywork/internal/utils/merr"
|
|
"time"
|
|
)
|
|
|
|
type initData struct {
|
|
ProjectID int64
|
|
UserID string
|
|
}
|
|
|
|
type ProjectUser struct {
|
|
logic.Context
|
|
|
|
user *model.ProjectUser
|
|
|
|
coinKey string
|
|
|
|
withInit *initData
|
|
}
|
|
|
|
func NewProjectUser(ctx context.Context, opts ...Option) (pu *ProjectUser, err error) {
|
|
pu = new(ProjectUser)
|
|
{
|
|
pu.BaseInit(ctx, merr.NewBE(ctx))
|
|
}
|
|
|
|
for _, opt := range opts {
|
|
opt(pu)
|
|
}
|
|
|
|
if pu.withInit != nil {
|
|
err = pu.init(pu.withInit.ProjectID, pu.withInit.UserID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
pu.coinKey = ckey.ProjectUserCoinNum(pu.user.ID)
|
|
|
|
return
|
|
}
|
|
|
|
func (s *ProjectUser) GetProjectUser() *model.ProjectUser {
|
|
return s.user
|
|
}
|
|
|
|
func (s *ProjectUser) init(projectId int64, userId string) (err error) {
|
|
s.user, err = cache.NewProjectUser(s.Ctx).FindByUniqId(projectId, userId)
|
|
if err != nil {
|
|
err = s.BE().CacheError(err)
|
|
return
|
|
}
|
|
|
|
if s.user != nil {
|
|
return
|
|
}
|
|
|
|
lc := app.NewFollower(s.Ctx, ckey.ProjectUserCreate(projectId, userId)).Lock()
|
|
defer lc.Release()
|
|
if lc.Error() != nil {
|
|
return lc.Error()
|
|
}
|
|
|
|
s.user, err = cache.NewProjectUser(s.Ctx).FindByUniqId(projectId, userId)
|
|
if err != nil {
|
|
err = s.BE().CacheError(err)
|
|
return
|
|
}
|
|
|
|
if s.user != nil {
|
|
return
|
|
}
|
|
|
|
user := withdefault.ProjectUser(&model.ProjectUser{
|
|
ProjectID: projectId,
|
|
UserID: userId,
|
|
})
|
|
|
|
if err = s.GetDao().ProjectUser.WithContext(s.Ctx).Create(user); err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
s.user = user
|
|
|
|
return
|
|
}
|
|
|
|
type Coin struct {
|
|
Num int64 `json:"num"`
|
|
}
|
|
|
|
func (s *ProjectUser) GetCoin() (coin Coin, err error) {
|
|
|
|
coinNum, err := s.coinNum()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
coin.Num = coinNum
|
|
|
|
return
|
|
}
|
|
|
|
func (s *ProjectUser) IncrCoin(num int64) (coinNum int64, err error) {
|
|
coinNum, err = s.coinNum()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
coinNum, err = app.Redis.Cache.IncrBy(s.Ctx, s.coinKey, num).Result()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
app.Redis.Cache.Expire(s.Ctx, s.coinKey, lockdo.Rand2HourExp())
|
|
|
|
return
|
|
}
|
|
|
|
func (s *ProjectUser) Expired(num int64) (coinNum int64, err error) {
|
|
if app.Redis.Cache.Exists(s.Ctx, s.coinKey).Val() == 0 {
|
|
return
|
|
}
|
|
|
|
return s.IncrCoin(-num)
|
|
}
|
|
|
|
type coin struct {
|
|
Num int64 `json:"num"`
|
|
}
|
|
|
|
func (s *ProjectUser) coinNum() (num int64, err error) {
|
|
var coinNum coin
|
|
m := s.GetDao().ProjectUserCoin
|
|
err = m.WithContext(s.Ctx).
|
|
Select(m.CoinUse.Sum().As("num")).
|
|
Where(
|
|
m.ProjectID.Eq(s.user.ProjectID),
|
|
m.UserID.Eq(s.user.ID),
|
|
m.ExpiredAt.Gt(time.Now().Unix()),
|
|
m.IsUsed.Eq(mtype.No),
|
|
).
|
|
Scan(&coinNum)
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
return coinNum.Num, nil
|
|
}
|
|
|
|
type EarnCoinParam struct {
|
|
CoinNum int32
|
|
IsExpired int8
|
|
ExpiredAt int64
|
|
Source string
|
|
SourceID int64
|
|
SourceData map[string]string
|
|
Operate string
|
|
}
|
|
|
|
func (s *ProjectUser) EarnCoin(earns ...*EarnCoinParam) (err error) {
|
|
var incrNum int64
|
|
var coins []*model.ProjectUserCoin
|
|
var expired []*model.ProjectUserCoinExpired
|
|
var records []*model.ProjectUserCoinRecord
|
|
for _, earn := range earns {
|
|
one := withdefault.ProjectUserCoin(&model.ProjectUserCoin{
|
|
ProjectID: s.user.ProjectID,
|
|
UserID: s.user.ID,
|
|
CoinNum: earn.CoinNum,
|
|
CoinUse: earn.CoinNum,
|
|
ExpiredAt: earn.ExpiredAt,
|
|
IsExpired: earn.IsExpired,
|
|
IsUsed: 0,
|
|
Source: earn.Source,
|
|
SourceID: earn.SourceID,
|
|
SourceData: "",
|
|
}, earn.SourceData)
|
|
|
|
coins = append(coins, one)
|
|
|
|
if earn.IsExpired == mtype.Yes {
|
|
expired = append(expired, withdefault.ProjectUserCoinExpired(&model.ProjectUserCoinExpired{
|
|
UserID: s.user.ID,
|
|
CoinID: one.ID,
|
|
DoneAt: earn.ExpiredAt,
|
|
}))
|
|
}
|
|
|
|
incrNum += int64(earn.CoinNum)
|
|
|
|
records = append(records, withdefault.ProjectUserCoinRecord(&model.ProjectUserCoinRecord{
|
|
ProjectID: s.user.ProjectID,
|
|
UserID: s.user.ID,
|
|
Source: earn.Source,
|
|
SourceID: earn.SourceID,
|
|
Type: mtype.ProjectUserCoinRecordTypeAdd,
|
|
Mark: earn.Operate,
|
|
Num: earn.CoinNum,
|
|
ExpiredAt: utils.Ternary[int64](earn.IsExpired == mtype.Yes, earn.ExpiredAt, 0),
|
|
}))
|
|
}
|
|
|
|
if err = s.GetDao().ProjectUserCoin.WithContext(s.Ctx).Create(coins...); err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
if err = s.GetDao().ProjectUserCoinRecord.WithContext(s.Ctx).Create(records...); err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
if len(expired) > 0 {
|
|
if err = s.GetDao().ProjectUserCoinExpired.WithContext(s.Ctx).Create(expired...); err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
}
|
|
|
|
//_, err = s.IncrCoin(incrNum)
|
|
//if err != nil {
|
|
// return
|
|
//}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *ProjectUser) Deduct(source, operate string, sourceId int64, num int32) (ok bool, err error) {
|
|
m := s.GetDao().ProjectUserCoin
|
|
var coinIds []int64
|
|
var deductNum = num
|
|
var lastId int64
|
|
|
|
for {
|
|
var list []*model.ProjectUserCoin
|
|
list, err = m.WithContext(s.Ctx).Where(
|
|
m.UserID.Eq(s.user.ID),
|
|
m.ExpiredAt.Gte(time.Now().Unix()),
|
|
m.IsUsed.Eq(mtype.No),
|
|
m.ID.Gt(lastId),
|
|
).Order(m.ExpiredAt, m.ID).Limit(100).Find()
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
if len(list) == 0 {
|
|
break
|
|
}
|
|
|
|
var partIds []int64
|
|
for _, item := range list {
|
|
deductNum -= item.CoinUse
|
|
if deductNum >= 0 {
|
|
partIds = append(partIds, item.ID)
|
|
}
|
|
|
|
if deductNum < 0 {
|
|
_, err = m.WithContext(s.Ctx).Where(m.ID.Eq(item.ID)).UpdateSimple(m.CoinUse.Value(-deductNum))
|
|
if err != nil {
|
|
return
|
|
}
|
|
coinIds = append(coinIds, item.ID)
|
|
break
|
|
}
|
|
}
|
|
|
|
if len(partIds) > 0 {
|
|
_, err = m.WithContext(s.Ctx).Where(m.ID.In(partIds...)).UpdateSimple(m.CoinUse.Value(0), m.IsUsed.Value(mtype.Yes))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
coinIds = append(coinIds, partIds...)
|
|
}
|
|
|
|
if deductNum <= 0 {
|
|
break
|
|
}
|
|
|
|
lastId = list[len(list)-1].ID
|
|
}
|
|
|
|
if deductNum > 0 {
|
|
return false, nil
|
|
}
|
|
|
|
use := withdefault.ProjectUserCoinUse(&model.ProjectUserCoinUse{
|
|
UserID: s.user.ID,
|
|
Num: num,
|
|
Source: source,
|
|
SourceID: sourceId,
|
|
Operate: operate,
|
|
}, coinIds)
|
|
|
|
err = s.GetDao().ProjectUserCoinUse.WithContext(s.Ctx).Create(use)
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
err = s.GetDao().ProjectUserCoinRecord.WithContext(s.Ctx).Create(withdefault.ProjectUserCoinRecord(&model.ProjectUserCoinRecord{
|
|
ProjectID: s.user.ProjectID,
|
|
UserID: s.user.ID,
|
|
Source: source,
|
|
SourceID: sourceId,
|
|
Type: mtype.ProjectUserCoinRecordTypeDer,
|
|
Mark: operate,
|
|
Num: num,
|
|
}))
|
|
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
return true, nil
|
|
}
|
|
|
|
func (s *ProjectUser) DeductOk(source, relateMark string, relateID int64, operate string, num int32) (err error) {
|
|
deduct := withdefault.ProjectUserCoinDeduct(&model.ProjectUserCoinDeduct{
|
|
UserID: s.user.ID,
|
|
Source: source,
|
|
RelateMark: relateMark,
|
|
RelateID: relateID,
|
|
Operate: operate,
|
|
Num: num,
|
|
})
|
|
|
|
if err = s.GetDao().ProjectUserCoinDeduct.WithContext(s.Ctx).Create(deduct); err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
producer.NewCampaignNotice(s.Ctx).ProjectUserCoinDeduct(types.ProjectUserCoinDeduct{
|
|
Type: types.ProjectUserCoinDeductTypeDeduct,
|
|
UserId: s.user.ID,
|
|
DeductId: deduct.ID,
|
|
})
|
|
|
|
return
|
|
}
|