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.
295 lines
7.0 KiB
Go
295 lines
7.0 KiB
Go
package campaignuser
|
|
|
|
import (
|
|
"joybuywork/internal/app"
|
|
"joybuywork/internal/consts"
|
|
"joybuywork/internal/consts/ckey"
|
|
"joybuywork/internal/plugins/jsonm"
|
|
"joybuywork/internal/repository/cache"
|
|
"joybuywork/internal/repository/finder"
|
|
"joybuywork/internal/repository/model"
|
|
"joybuywork/internal/repository/mtype"
|
|
"joybuywork/internal/repository/withdefault"
|
|
"joybuywork/internal/service/provider/fissionaction"
|
|
"joybuywork/internal/service/provider/malls"
|
|
"joybuywork/internal/service/provider/players"
|
|
"joybuywork/internal/service/provider/projectuser"
|
|
"joybuywork/internal/service/provider/stocks"
|
|
"joybuywork/internal/service/queue/producer"
|
|
"joybuywork/internal/service/queue/types"
|
|
"joybuywork/internal/utils/logic"
|
|
"joybuywork/internal/utils/verify"
|
|
pb "joybuywork/proto/go_proto/w_campaign_user"
|
|
"joybuywork/proto/go_proto/workerr"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
exchangeStatusOk = iota // 成功
|
|
exchangeStatusLack // 库存不足
|
|
exchangeStatusLimit // 兑换上限
|
|
exchangeStatusDown // 商品已下架
|
|
exchangeStatusPointLack // 积分不足
|
|
exchangeStatusExchanging // 兑换中
|
|
exchangeStatusFail // 兑换失败
|
|
)
|
|
|
|
type Exchange struct {
|
|
logic.Logic
|
|
|
|
Req *pb.ExchangeReq
|
|
Res *pb.ExchangeRes
|
|
}
|
|
|
|
func (s *Exchange) Run() (err error) {
|
|
return logic.Run(s)
|
|
}
|
|
|
|
func (s *Exchange) Data() (data jsonm.JsonM) {
|
|
data, _ = jsonm.Marshal(s.Req)
|
|
return
|
|
}
|
|
|
|
func (s *Exchange) Exec() (err error) {
|
|
cpn, user, err := s.user()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
pu, item, err := s.pu(cpn, user)
|
|
if err != nil || item == nil {
|
|
return
|
|
}
|
|
|
|
err = s.checkItem(item)
|
|
if err != nil || s.Res.GetStatus() != exchangeStatusOk {
|
|
return
|
|
}
|
|
|
|
exchanging := app.NewRedisLock(s.Ctx, ckey.MallItemExchanging(pu.GetProjectUser().ID))
|
|
if !exchanging.SoftLock() {
|
|
s.Res.Status = exchangeStatusExchanging
|
|
return
|
|
}
|
|
defer exchanging.Release()
|
|
|
|
return s.exchange(pu, item, user, cpn)
|
|
}
|
|
|
|
func (s *Exchange) user() (cpn *model.FissionCampaign, user *model.FissionUser, err error) {
|
|
cpn, err = finder.Campaign(s.Ctx, s.Req.GetCampaignId())
|
|
if err != nil {
|
|
err = s.BE().CacheError(err)
|
|
return
|
|
}
|
|
|
|
if cpn == nil || cpn.IsDel == mtype.Yes {
|
|
err = workerr.NotFound("not found cpn")
|
|
return
|
|
}
|
|
|
|
user, err = finder.FissionUser(s.Ctx, cpn.ID, s.Req.GetUserId())
|
|
if err != nil {
|
|
err = s.BE().CacheError(err)
|
|
return
|
|
}
|
|
|
|
if user == nil {
|
|
err = workerr.UnauthorizedError
|
|
return
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Exchange) pu(cpn *model.FissionCampaign, user *model.FissionUser) (pu *projectuser.ProjectUser, item *model.RedeemMallItem, err error) {
|
|
pu, err = projectuser.NewProjectUser(s.Ctx, projectuser.WithInit(cpn.ProjectID, user.SubUserID), projectuser.WithDao(s.GetDao()))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
item, err = cache.NewRedeemMallItem(s.Ctx).FindById(s.Req.GetMallItemId())
|
|
if err != nil {
|
|
err = s.BE().CacheError(err)
|
|
return
|
|
}
|
|
|
|
if item == nil {
|
|
s.Res.Status = exchangeStatusDown
|
|
return
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
func (s *Exchange) checkItem(item *model.RedeemMallItem) (err error) {
|
|
if verify.InArray[int32](item.GetStatus(), mtype.RedeemMallItemStatusDel, mtype.RedeemMallItemStatusDown) {
|
|
s.Res.Status = exchangeStatusDown
|
|
return
|
|
}
|
|
|
|
stock, err := stocks.NewMallItem(s.Ctx, item.ID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
ok, err := stock.DecrPoolAndAdd(item.ID, 1)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if !ok {
|
|
s.Res.Status = exchangeStatusLack
|
|
return
|
|
}
|
|
|
|
s.AddErrRun(func() (err error) {
|
|
_, _ = stock.DecrPoolAndAdd(item.ID, -1)
|
|
return
|
|
})
|
|
|
|
s.AddErrRun(func() (err error) {
|
|
if s.Res.Status != exchangeStatusOk {
|
|
_, _ = stock.DecrPoolAndAdd(item.ID, -1)
|
|
}
|
|
return
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Exchange) exchange(pu *projectuser.ProjectUser, item *model.RedeemMallItem, user *model.FissionUser, cpn *model.FissionCampaign) (err error) {
|
|
coin, err := pu.GetCoin()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if coin.Num < int64(item.CoinNum) {
|
|
s.Res.Status = exchangeStatusPointLack
|
|
return
|
|
}
|
|
|
|
ok, err := pu.Deduct(mtype.ProjectUserCoinUseSourceCampaign, mtype.ProjectUserCoinDerMall, user.CampaignID, item.CoinNum)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if !ok {
|
|
s.Rollback("项目用户积分币不足")
|
|
s.Res.Status = exchangeStatusPointLack
|
|
return
|
|
}
|
|
|
|
mall, err := malls.NewMall(s.Ctx, item.MallID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
reward, mallRecord, info, err := mall.ExchangeRecord(item, user.LangMark)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
record := withdefault.FissionRewardRecord(&model.FissionRewardRecord{
|
|
CampaignID: user.CampaignID,
|
|
UserID: user.ID,
|
|
RewardID: reward.ID,
|
|
Title: info.Title,
|
|
ImgURL: info.ImgUrl,
|
|
RewardType: mtype.FissionCampaignRewardTypeExchange,
|
|
RewardNum: 1,
|
|
BaseRewardID: reward.ID,
|
|
PropMark: mtype.FissionCampaignRewardPropMarkReward,
|
|
MallRecordID: mallRecord.ID,
|
|
ExchangePoints: int32(mallRecord.CoinNum),
|
|
})
|
|
|
|
err = s.GetDao().FissionRewardRecord.WithContext(s.Ctx).Create(record)
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
err = s.GetDao().FissionRewardRecord.SubTable(user.CampaignID).WithContext(s.Ctx).Create(record)
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
err = s.GetDao().FissionClientReward.WithContext(s.Ctx).Create(withdefault.FissionClientReward(&model.FissionClientReward{
|
|
UserID: user.SubUserID,
|
|
ProjectID: pu.GetProjectUser().ProjectID,
|
|
CampaignID: user.CampaignID,
|
|
RecordID: record.ID,
|
|
CreatedAt: record.CreatedAt,
|
|
}))
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
err = s._fissionCallback(cpn, user, item)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
s.AddAfterRun(func() (err error) {
|
|
bp := fissionaction.NewMarkPoint(s.Ctx).InApp(user)
|
|
if item.CoinNum > 0 {
|
|
bp.PointsUse(time.Now().Unix(), item.CoinNum)
|
|
}
|
|
bp.MallRedeem(time.Now().Unix(), item.MallID, item.ID, item.CoinNum)
|
|
|
|
producer.NewCampaignNotice(s.Ctx).CampaignRewardNotify(types.CampaignRewardNotify{
|
|
CampaignId: user.CampaignID,
|
|
UserId: user.ID,
|
|
UserRewardId: record.ID,
|
|
})
|
|
|
|
return
|
|
})
|
|
|
|
s.Res.Status = exchangeStatusOk
|
|
s.Res.Id = record.ID
|
|
s.Res.RewardId = info.Id
|
|
s.Res.Title = info.Title
|
|
s.Res.ImgUrl = info.ImgUrl
|
|
s.Res.ReceiveType = info.ReceiveType
|
|
s.Res.Contact = info.Contact
|
|
s.Res.OpenToUse = info.OpenToUse
|
|
s.Res.CustomRewardVal = record.CustomRewardVal
|
|
s.Res.WebUrl = info.WebUrl
|
|
s.Res.AppParam = info.AppParam
|
|
s.Res.PcParam = info.PcParam
|
|
s.Res.FailType = record.FailType
|
|
s.Res.FailMsg = record.FailMsg
|
|
s.Res.KvData = make([]*pb.ExchangeRes_KV, 0, len(info.KvData))
|
|
|
|
for _, datum := range info.KvData {
|
|
s.Res.KvData = append(s.Res.KvData, &pb.ExchangeRes_KV{
|
|
Key: datum.Key,
|
|
Value: datum.Value,
|
|
IsShow: datum.IsShow,
|
|
})
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
func (s *Exchange) _fissionCallback(cpn *model.FissionCampaign, user *model.FissionUser, item *model.RedeemMallItem) (err error) {
|
|
switch cpn.FissionID {
|
|
case consts.IdVs:
|
|
player, err1 := players.NewVs(s.Ctx, user, players.SetCpn(cpn), players.SetDao(s.GetDao()))
|
|
if err1 != nil {
|
|
return err1
|
|
}
|
|
err = player.Exchange(item)
|
|
if err != nil {
|
|
return
|
|
}
|
|
}
|
|
|
|
return
|
|
}
|