263 lines
7.7 KiB
Go
263 lines
7.7 KiB
Go
package snapshot
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"time"
|
|
|
|
"bindbox-game/internal/repository/mysql"
|
|
"bindbox-game/internal/repository/mysql/model"
|
|
)
|
|
|
|
// UserStateSnapshot 用户状态快照
|
|
type UserStateSnapshot struct {
|
|
SnapshotTime time.Time `json:"snapshot_time"`
|
|
User *UserInfo `json:"user"`
|
|
Points *PointsInfo `json:"points"`
|
|
Coupons []*CouponInfo `json:"coupons"`
|
|
ItemCards []*ItemCardInfo `json:"item_cards"`
|
|
InventoryCount int64 `json:"inventory_count"`
|
|
}
|
|
|
|
// UserInfo 用户基本信息
|
|
type UserInfo struct {
|
|
ID int64 `json:"id"`
|
|
Nickname string `json:"nickname"`
|
|
Mobile string `json:"mobile"`
|
|
}
|
|
|
|
// PointsInfo 积分信息
|
|
type PointsInfo struct {
|
|
Balance int64 `json:"balance"`
|
|
Version int32 `json:"version"`
|
|
}
|
|
|
|
// CouponInfo 优惠券信息
|
|
type CouponInfo struct {
|
|
UserCouponID int64 `json:"user_coupon_id"`
|
|
CouponID int64 `json:"coupon_id"`
|
|
CouponName string `json:"coupon_name"`
|
|
Status int32 `json:"status"`
|
|
BalanceAmount int64 `json:"balance_amount"`
|
|
ValidEnd time.Time `json:"valid_end"`
|
|
}
|
|
|
|
// ItemCardInfo 道具卡信息
|
|
type ItemCardInfo struct {
|
|
UserItemCardID int64 `json:"user_item_card_id"`
|
|
CardID int64 `json:"card_id"`
|
|
CardName string `json:"card_name"`
|
|
Status int32 `json:"status"`
|
|
ValidEnd time.Time `json:"valid_end"`
|
|
}
|
|
|
|
// SnapshotDiff 快照差异
|
|
type SnapshotDiff struct {
|
|
PointsChanged int64 `json:"points_changed"`
|
|
CouponsUsed []*CouponInfo `json:"coupons_used"`
|
|
ItemCardsUsed []*ItemCardInfo `json:"item_cards_used"`
|
|
InventoryAdded int64 `json:"inventory_added"`
|
|
}
|
|
|
|
// Service 快照服务
|
|
type Service interface {
|
|
// CaptureUserState 采集用户当前状态
|
|
CaptureUserState(ctx context.Context, userID int64) (*UserStateSnapshot, error)
|
|
// SaveSnapshot 保存快照
|
|
SaveSnapshot(ctx context.Context, orderID int64, orderNo string, userID int64, snapshotType int32, snapshot *UserStateSnapshot) error
|
|
// GetSnapshotsByOrderID 获取订单快照
|
|
GetSnapshotsByOrderID(ctx context.Context, orderID int64) (*model.OrderSnapshots, *model.OrderSnapshots, error)
|
|
// CompareSnapshots 对比快照差异
|
|
CompareSnapshots(before, after *UserStateSnapshot) *SnapshotDiff
|
|
}
|
|
|
|
type service struct {
|
|
db mysql.Repo
|
|
}
|
|
|
|
// NewService 创建快照服务
|
|
func NewService(db mysql.Repo) Service {
|
|
return &service{db: db}
|
|
}
|
|
|
|
// CaptureUserState 采集用户当前状态
|
|
func (s *service) CaptureUserState(ctx context.Context, userID int64) (*UserStateSnapshot, error) {
|
|
snapshot := &UserStateSnapshot{
|
|
SnapshotTime: time.Now(),
|
|
}
|
|
|
|
// 1. 查询用户基本信息
|
|
var user model.Users
|
|
if err := s.db.GetDbR().WithContext(ctx).Where("id = ?", userID).First(&user).Error; err == nil {
|
|
snapshot.User = &UserInfo{
|
|
ID: user.ID,
|
|
Nickname: user.Nickname,
|
|
Mobile: user.Mobile,
|
|
}
|
|
}
|
|
|
|
// 2. 查询用户积分
|
|
var points model.UserPoints
|
|
if err := s.db.GetDbR().WithContext(ctx).Where("user_id = ?", userID).First(&points).Error; err == nil {
|
|
snapshot.Points = &PointsInfo{
|
|
Balance: points.Points,
|
|
Version: points.Version,
|
|
}
|
|
} else {
|
|
snapshot.Points = &PointsInfo{Balance: 0, Version: 0}
|
|
}
|
|
|
|
// 3. 查询未使用的优惠券
|
|
var userCoupons []model.UserCoupons
|
|
s.db.GetDbR().WithContext(ctx).Where("user_id = ? AND status = 1", userID).Find(&userCoupons)
|
|
|
|
if len(userCoupons) > 0 {
|
|
couponIDs := make([]int64, 0, len(userCoupons))
|
|
for _, uc := range userCoupons {
|
|
couponIDs = append(couponIDs, uc.CouponID)
|
|
}
|
|
|
|
var systemCoupons []model.SystemCoupons
|
|
s.db.GetDbR().WithContext(ctx).Where("id IN ?", couponIDs).Find(&systemCoupons)
|
|
|
|
couponNameMap := make(map[int64]string)
|
|
for _, sc := range systemCoupons {
|
|
couponNameMap[sc.ID] = sc.Name
|
|
}
|
|
|
|
for _, uc := range userCoupons {
|
|
snapshot.Coupons = append(snapshot.Coupons, &CouponInfo{
|
|
UserCouponID: uc.ID,
|
|
CouponID: uc.CouponID,
|
|
CouponName: couponNameMap[uc.CouponID],
|
|
Status: uc.Status,
|
|
BalanceAmount: uc.BalanceAmount,
|
|
ValidEnd: uc.ValidEnd,
|
|
})
|
|
}
|
|
}
|
|
|
|
// 4. 查询未使用的道具卡
|
|
var userItemCards []model.UserItemCards
|
|
s.db.GetDbR().WithContext(ctx).Where("user_id = ? AND status = 1", userID).Find(&userItemCards)
|
|
|
|
if len(userItemCards) > 0 {
|
|
cardIDs := make([]int64, 0, len(userItemCards))
|
|
for _, uc := range userItemCards {
|
|
cardIDs = append(cardIDs, uc.CardID)
|
|
}
|
|
|
|
var systemCards []model.SystemItemCards
|
|
s.db.GetDbR().WithContext(ctx).Where("id IN ?", cardIDs).Find(&systemCards)
|
|
|
|
cardNameMap := make(map[int64]string)
|
|
for _, sc := range systemCards {
|
|
cardNameMap[sc.ID] = sc.Name
|
|
}
|
|
|
|
for _, uc := range userItemCards {
|
|
snapshot.ItemCards = append(snapshot.ItemCards, &ItemCardInfo{
|
|
UserItemCardID: uc.ID,
|
|
CardID: uc.CardID,
|
|
CardName: cardNameMap[uc.CardID],
|
|
Status: uc.Status,
|
|
ValidEnd: uc.ValidEnd,
|
|
})
|
|
}
|
|
}
|
|
|
|
// 5. 查询资产数量
|
|
var inventoryCount int64
|
|
s.db.GetDbR().WithContext(ctx).Model(&model.UserInventory{}).Where("user_id = ? AND status = 1", userID).Count(&inventoryCount)
|
|
snapshot.InventoryCount = inventoryCount
|
|
|
|
return snapshot, nil
|
|
}
|
|
|
|
// SaveSnapshot 保存快照
|
|
func (s *service) SaveSnapshot(ctx context.Context, orderID int64, orderNo string, userID int64, snapshotType int32, snapshot *UserStateSnapshot) error {
|
|
data, err := json.Marshal(snapshot)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 计算hash
|
|
hash := sha256.Sum256(data)
|
|
hashStr := hex.EncodeToString(hash[:])
|
|
|
|
record := &model.OrderSnapshots{
|
|
OrderID: orderID,
|
|
OrderNo: orderNo,
|
|
UserID: userID,
|
|
SnapshotType: snapshotType,
|
|
SnapshotData: string(data),
|
|
SnapshotHash: hashStr,
|
|
}
|
|
|
|
return s.db.GetDbW().WithContext(ctx).Create(record).Error
|
|
}
|
|
|
|
// GetSnapshotsByOrderID 获取订单快照
|
|
func (s *service) GetSnapshotsByOrderID(ctx context.Context, orderID int64) (*model.OrderSnapshots, *model.OrderSnapshots, error) {
|
|
var snapshots []model.OrderSnapshots
|
|
if err := s.db.GetDbR().WithContext(ctx).Where("order_id = ?", orderID).Order("snapshot_type ASC").Find(&snapshots).Error; err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
var before, after *model.OrderSnapshots
|
|
for i := range snapshots {
|
|
if snapshots[i].SnapshotType == 1 {
|
|
before = &snapshots[i]
|
|
} else if snapshots[i].SnapshotType == 2 {
|
|
after = &snapshots[i]
|
|
}
|
|
}
|
|
|
|
return before, after, nil
|
|
}
|
|
|
|
// CompareSnapshots 对比快照差异
|
|
func (s *service) CompareSnapshots(before, after *UserStateSnapshot) *SnapshotDiff {
|
|
diff := &SnapshotDiff{}
|
|
|
|
// 积分差异
|
|
if before.Points != nil && after.Points != nil {
|
|
diff.PointsChanged = after.Points.Balance - before.Points.Balance
|
|
}
|
|
|
|
// 优惠券使用情况(消费前有,消费后没有或状态变化)
|
|
afterCouponMap := make(map[int64]*CouponInfo)
|
|
for _, c := range after.Coupons {
|
|
afterCouponMap[c.UserCouponID] = c
|
|
}
|
|
for _, c := range before.Coupons {
|
|
if _, ok := afterCouponMap[c.UserCouponID]; !ok {
|
|
// 消费后不存在,说明被使用了
|
|
diff.CouponsUsed = append(diff.CouponsUsed, c)
|
|
} else if afterCouponMap[c.UserCouponID].Status != c.Status {
|
|
// 状态变化,说明被使用了
|
|
diff.CouponsUsed = append(diff.CouponsUsed, c)
|
|
}
|
|
}
|
|
|
|
// 道具卡使用情况
|
|
afterCardMap := make(map[int64]*ItemCardInfo)
|
|
for _, c := range after.ItemCards {
|
|
afterCardMap[c.UserItemCardID] = c
|
|
}
|
|
for _, c := range before.ItemCards {
|
|
if _, ok := afterCardMap[c.UserItemCardID]; !ok {
|
|
diff.ItemCardsUsed = append(diff.ItemCardsUsed, c)
|
|
} else if afterCardMap[c.UserItemCardID].Status != c.Status {
|
|
diff.ItemCardsUsed = append(diff.ItemCardsUsed, c)
|
|
}
|
|
}
|
|
|
|
// 资产增加
|
|
diff.InventoryAdded = after.InventoryCount - before.InventoryCount
|
|
|
|
return diff
|
|
}
|