108 lines
2.5 KiB
Go
108 lines
2.5 KiB
Go
package services
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"platform/web/core"
|
|
g "platform/web/globals"
|
|
m "platform/web/models"
|
|
q "platform/web/queries"
|
|
|
|
"github.com/shopspring/decimal"
|
|
)
|
|
|
|
var User = &userService{}
|
|
|
|
type userService struct{}
|
|
|
|
func (s *userService) UpdateBalanceByTrade(uid int32, info *RechargeProductInfo, trade *m.Trade) (err error) {
|
|
err = g.Redsync.WithLock(userBalanceKey(uid), func() error {
|
|
return q.Q.Transaction(func(q *q.Query) error {
|
|
|
|
err = updateBalance(q, uid, info)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 生成账单
|
|
subject := info.GetSubject()
|
|
amount := info.GetAmount()
|
|
err = q.Bill.Create(newForRecharge(uid, Bill.GenNo(), subject, amount, trade))
|
|
if err != nil {
|
|
return core.NewServErr("生成账单失败", err)
|
|
}
|
|
|
|
return nil
|
|
})
|
|
})
|
|
if err != nil {
|
|
return core.NewServErr("更新用户余额失败")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func updateBalance(q *q.Query, uid int32, info *RechargeProductInfo) error {
|
|
user, err := q.User.
|
|
Where(q.User.ID.Eq(uid)).Take()
|
|
if err != nil {
|
|
return core.NewServErr("查询用户失败", err)
|
|
}
|
|
|
|
amount := info.GetAmount()
|
|
balance := user.Balance.Add(amount)
|
|
if balance.IsNegative() {
|
|
return core.NewServErr("用户余额不足")
|
|
}
|
|
|
|
_, err = q.User.
|
|
Where(q.User.ID.Eq(user.ID)).
|
|
UpdateSimple(q.User.Balance.Value(balance))
|
|
if err != nil {
|
|
return core.NewServErr("更新用户余额失败", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func userBalanceKey(uid int32) string {
|
|
return fmt.Sprintf("user:%d:balance", uid)
|
|
}
|
|
|
|
type RechargeProductInfo struct {
|
|
Amount int `json:"amount"`
|
|
}
|
|
|
|
func (r *RechargeProductInfo) GetType() m.TradeType {
|
|
return m.TradeTypeRecharge
|
|
}
|
|
|
|
func (r *RechargeProductInfo) GetSubject() string {
|
|
return fmt.Sprintf("账户充值 - %s元", r.GetAmount().StringFixed(2))
|
|
}
|
|
|
|
func (r *RechargeProductInfo) GetAmount() decimal.Decimal {
|
|
return decimal.NewFromInt(int64(r.Amount)).Div(decimal.NewFromInt(100))
|
|
}
|
|
|
|
func (r *RechargeProductInfo) Serialize() (string, error) {
|
|
bytes, err := json.Marshal(r)
|
|
return string(bytes), err
|
|
}
|
|
|
|
func (r *RechargeProductInfo) Deserialize(str string) error {
|
|
return json.Unmarshal([]byte(str), r)
|
|
}
|
|
|
|
type UserOnTradeComplete struct{}
|
|
|
|
func (u UserOnTradeComplete) Check(t m.TradeType) (ProductInfo, bool) {
|
|
if t == m.TradeTypeRecharge {
|
|
return &RechargeProductInfo{}, true
|
|
}
|
|
return nil, false
|
|
}
|
|
|
|
func (u UserOnTradeComplete) OnTradeComplete(info ProductInfo, trade *m.Trade) error {
|
|
return User.UpdateBalanceByTrade(trade.UserID, info.(*RechargeProductInfo), trade)
|
|
}
|