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.
293 lines
6.9 KiB
Go
293 lines
6.9 KiB
Go
package mall
|
|
|
|
import (
|
|
"joybuywork/internal/app"
|
|
"joybuywork/internal/consts/ckey"
|
|
"joybuywork/internal/plugins/hashid"
|
|
"joybuywork/internal/plugins/jsonm"
|
|
"joybuywork/internal/repository/cache"
|
|
"joybuywork/internal/repository/dbcheck"
|
|
"joybuywork/internal/repository/model"
|
|
"joybuywork/internal/repository/mtype"
|
|
"joybuywork/internal/service/provider"
|
|
"joybuywork/internal/service/provider/malls"
|
|
"joybuywork/internal/service/provider/projectuser"
|
|
stos "joybuywork/internal/service/provider/s2s"
|
|
"joybuywork/internal/service/provider/stocks"
|
|
"joybuywork/internal/utils/logic"
|
|
"joybuywork/internal/utils/verify"
|
|
pb "joybuywork/proto/go_proto/w_mall"
|
|
"time"
|
|
|
|
"gorm.io/gen"
|
|
)
|
|
|
|
const (
|
|
exchangeStatusOk = iota // 成功
|
|
exchangeStatusLack // 库存不足
|
|
exchangeStatusLimit // 兑换上限
|
|
exchangeStatusDown // 商品已下架
|
|
exchangeStatusPointLack // 积分不足
|
|
exchangeStatusExchanging // 兑换中
|
|
exchangeStatusFail // 兑换失败
|
|
)
|
|
|
|
type ExchangeItem struct {
|
|
logic.Logic
|
|
|
|
Req *pb.ExchangeItemReq
|
|
Res *pb.ExchangeItemRes
|
|
}
|
|
|
|
func (s *ExchangeItem) Run() (err error) {
|
|
return logic.Run(s)
|
|
}
|
|
|
|
func (s *ExchangeItem) Data() (data jsonm.JsonM) {
|
|
data, _ = jsonm.Marshal(s.Req)
|
|
return
|
|
}
|
|
|
|
func (s *ExchangeItem) Exec() (err error) {
|
|
item, err := s.item()
|
|
if err != nil || item == nil {
|
|
return
|
|
}
|
|
|
|
pu, err := s.pu()
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
exchanging := app.NewRedisLock(s.Ctx, ckey.MallItemExchanging(pu.GetProjectUser().ID))
|
|
if !exchanging.SoftLock() {
|
|
s.Res.Status = exchangeStatusExchanging
|
|
return
|
|
}
|
|
defer exchanging.Release()
|
|
|
|
err = s.checkItem(item)
|
|
if err != nil || s.Res.GetStatus() != exchangeStatusOk {
|
|
return
|
|
}
|
|
|
|
if s.Req.GetPointSource() == 0 {
|
|
return s.appExchange(pu, item)
|
|
}
|
|
|
|
return s.exchange(pu, item)
|
|
}
|
|
|
|
func (s *ExchangeItem) item() (item *model.RedeemMallItem, err error) {
|
|
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 *ExchangeItem) pu() (pu *projectuser.ProjectUser, err error) {
|
|
return projectuser.NewProjectUser(s.Ctx, projectuser.WithInit(s.Req.GetProjectId(), s.Req.GetUserId()), projectuser.WithDao(s.GetDao()))
|
|
}
|
|
|
|
func (s *ExchangeItem) 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 *ExchangeItem) exchange(pu *projectuser.ProjectUser, item *model.RedeemMallItem) (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.ProjectUserCoinUseSourceCenter, mtype.ProjectUserCoinDerMall, item.MallID, item.CoinNum)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if !ok {
|
|
s.Rollback("项目用户积分不足")
|
|
s.Res.Status = exchangeStatusPointLack
|
|
return
|
|
}
|
|
|
|
return s._pushReward(item)
|
|
}
|
|
|
|
func (s *ExchangeItem) appExchange(pu *projectuser.ProjectUser, item *model.RedeemMallItem) (err error) {
|
|
lackKey := ckey.ProjectUserCoinLack(pu.GetProjectUser().ProjectID, pu.GetProjectUser().UserID)
|
|
// 看看是不是有
|
|
if app.Redis.Cache.Exists(s.Ctx, lackKey).Val() != 0 {
|
|
s.Res.Status = exchangeStatusLack
|
|
return
|
|
}
|
|
|
|
deductM := s.GetDao().ProjectUserCoinDeduct
|
|
|
|
var cond = []gen.Condition{
|
|
deductM.UserID.Eq(pu.GetProjectUser().ID),
|
|
deductM.RelateID.Eq(item.ID),
|
|
deductM.Num.Eq(item.CoinNum),
|
|
deductM.Source.Eq(mtype.ProjectUserCoinDeductSourceApp),
|
|
}
|
|
|
|
deduct, err := deductM.WithContext(s.Ctx).Where(cond...).Attrs(deductM.ID.Value(hashid.IdBigint())).FirstOrCreate()
|
|
if err = dbcheck.DbError(err); err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
return
|
|
}
|
|
|
|
defer func() {
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
_, err = deductM.WithContext(s.Ctx).Delete(deduct)
|
|
if err != nil {
|
|
err = s.BE().MysqlError(err)
|
|
}
|
|
}()
|
|
|
|
s2s, err := stos.NewUserCoinChange(s.Ctx, pu.GetProjectUser(), s.Req.GetUrlId(), stos.SetDao(s.GetDao()))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
res, err := s2s.CenterExchange(
|
|
item.CoinNum,
|
|
deduct.ID,
|
|
item.ID,
|
|
s.Req.GetSourceData().GetCenterId(),
|
|
s.Req.GetSourceData().GetCenterTitle(),
|
|
s.Req.GetSourceData().GetCenterHashId(),
|
|
s.Req.GetUserId(),
|
|
)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
if res.State == stos.PointsChangeLack {
|
|
app.Redis.Cache.Set(s.Ctx, lackKey, 1, 10*time.Second)
|
|
s.Res.Status = exchangeStatusPointLack
|
|
return
|
|
}
|
|
|
|
return s._pushReward(item)
|
|
}
|
|
|
|
func (s *ExchangeItem) _pushReward(item *model.RedeemMallItem) (err error) {
|
|
mall, err := malls.NewMall(s.Ctx, item.MallID)
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
var record *model.RedeemMallRecord
|
|
var info = provider.RewardAutoLang{}
|
|
switch s.Req.GetSource() {
|
|
case 1:
|
|
record, info, err = mall.Exchange(
|
|
item,
|
|
mtype.RewardPushSourceCenter,
|
|
s.Req.GetSourceData().GetCenterId(),
|
|
s.Req.GetSourceData().GetModuleId(),
|
|
s.Req.GetSourceData().GetRelateId(),
|
|
mtype.RewardPushParam{
|
|
SourceHashId: s.Req.GetSourceData().GetCenterTitle(),
|
|
UserId: s.Req.GetUserId(),
|
|
UserPlatform: s.Req.GetSourceData().GetUserPlatform(),
|
|
ParentUserId: s.Req.GetSourceData().GetParentUserId(),
|
|
ParentUserPlatform: s.Req.GetSourceData().GetParentUserPlatform(),
|
|
Num: item.CoinNum,
|
|
Lang: s.Req.GetUserId(),
|
|
Title: s.Req.GetSourceData().GetCenterTitle(),
|
|
RewardType: mtype.FissionCampaignRewardTypeExchange,
|
|
},
|
|
)
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if record == nil {
|
|
return s.BE().VerifyError("not found reward")
|
|
}
|
|
|
|
default:
|
|
return s.BE().VerifyError("not found source")
|
|
}
|
|
|
|
s.Res.Status = exchangeStatusOk
|
|
if record.Status == mtype.RedeemMallRecordStatusFail {
|
|
s.Res.Status = exchangeStatusFail
|
|
}
|
|
|
|
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.ExchangeItemRes_KV, 0, len(info.KvData))
|
|
|
|
for _, datum := range info.KvData {
|
|
s.Res.KvData = append(s.Res.KvData, &pb.ExchangeItemRes_KV{
|
|
Key: datum.Key,
|
|
Value: datum.Value,
|
|
IsShow: datum.IsShow,
|
|
})
|
|
}
|
|
|
|
return
|
|
}
|