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

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
}