package models import ( "errors" "fmt" orm "go-admin/common/global" "go-admin/logger" "gorm.io/gorm" "time" ) const ( VmEventBuyGoods = "buy_goods" // 购买商品积分抵扣 VmEventBuyGoodsReject = "buy_goods_reject" // 购买商品积分抵扣取消 VmEventOpenMember = "open_member" // 开通会员奖励 VmEventInvite1Member = "invite_1_member" // 邀请会员奖励 VmEventInvite2Member = "invite_2_member" // 邀请会员奖励 VmEventUserShareCard = "user_share_card" // 用户共享卡收益 VmEventAttendance = "attendance" // 连续签到获取积分 VmEventErpOrderSale = "erp_order_sale" // 零售销售获得积分 VmEventErpOrderReject = "erp_order_reject" // 零售退货扣除积分 VmEventExpired = "vm_expired" // 积分过期扣减 VmEventCancelMember = "cancel_member" // 取消租卡会员 VmEventCancelPrivilegeMember = "cancel_privilege_member" // 取消尊享会员 ) // 用户积分 // gen:qs // //go:generate goqueryset -in user_vm.go type UserVm struct { Model Uid uint32 `json:"uid" gorm:"column:uid;unique_index"` Vm uint32 `json:"vm"` //UserVm uint32 `json:"user_vm"` } // 用户积分-变动记录 // gen:qs type UserVmRecord struct { Model Uid uint32 `json:"uid" gorm:"column:uid;unique_index"` // 用户ID BeforeVm uint32 `json:"before_vm"` // 变动前 AfterVm uint32 `json:"after_vm"` // 变动后 Alter int `json:"alter"` // 数值 Event string `json:"event" gorm:"type:varchar(100)"` // 事件 Describe string `json:"describe" gorm:"type:text"` // 描述 ErpOrderId uint32 `json:"erp_order_id" gorm:"index"` // 零售订单id BillSn string `json:"bill_sn"` // 零售订单编号 ExpiryDate time.Time `json:"expiry_date"` // 积分过期时间 UsedVm int `json:"used_vm"` // 已使用积分 ExpiryVm int `json:"expiry_vm" gorm:"-"` // 即将过期的积分 User *UserInfo `json:"user,omitempty" gorm:"-"` // 用户信息 } func UserVmUpdate(gdb *gorm.DB, billSn string, uid uint32, amount int, event, describe string) error { var userVm UserVm err := orm.Eloquent.Table("user_vm"). Where("uid=?", uid).Find(&userVm).Error if err != nil { logger.Error("share card retrieve cards err:", logger.Field("err", err)) return err } // 变动前的积分 nBeforeVm := userVm.Vm flag := false begin := gdb if gdb == nil { flag = true begin = orm.Eloquent.Begin() } if userVm.Uid == 0 { // 没有积分记录 if amount < 0 { begin.Rollback() logger.Error("amount lt 0") return errors.New("amount lt 0") } userVm = UserVm{ Uid: uid, Vm: uint32(amount), } err = begin.Create(&userVm).Error if err != nil { begin.Rollback() logger.Error("err:", logger.Field("err", err)) return err } } else { // 如果用户积分不够抵扣,则扣到0为止 if int(userVm.Vm)+amount <= 0 { sql := fmt.Sprintf("UPDATE user_vm SET vm = ? WHERE uid=?") err = begin.Exec(sql, 0, uid).Error } else { sql := fmt.Sprintf("UPDATE user_vm SET vm = vm+? WHERE uid=?") err = begin.Exec(sql, amount, uid).Error } if err != nil { begin.Rollback() logger.Error("err:", logger.Field("err", err)) return err } } vmRecord := &UserVmRecord{ Uid: uid, BeforeVm: uint32(nBeforeVm), AfterVm: 0, // 默认值为 0 Alter: amount, Event: event, Describe: describe, BillSn: billSn, } if newValue := int(userVm.Vm) + amount; newValue > 0 { vmRecord.AfterVm = uint32(newValue) } // 如果是正积分,则记录该订单对应积分的有效时间 if amount > 0 { vmRecord.ExpiryDate = time.Now().AddDate(1, 0, 0) if event == VmEventBuyGoodsReject { // 使用积分购买商品后取消 err = RefundPoints(gdb, uid, amount) if err != nil { begin.Rollback() logger.Error("err:", logger.Field("err", err)) return err } } } else { // 如果是负积分,则记录积分使用情况;优先扣除最早获得的积分 if event == VmEventBuyGoods || event == VmEventErpOrderReject { // 使用积分购买商品,或者零售退货扣除积分 err = UsePoints(gdb, uid, -amount) if err != nil { begin.Rollback() logger.Error("err:", logger.Field("err", err)) return err } } } err = begin.Create(vmRecord).Error if err != nil { begin.Rollback() logger.Error("err:", logger.Field("err", err)) return err } if flag { err = begin.Commit().Error if err != nil { begin.Rollback() logger.Error("err:", logger.Field("err", err)) return err } } return nil } // GetUserAvailablePointsRecords 查找用户的所有可用积分记录,按时间排序 func GetUserAvailablePointsRecords(uid uint32) []UserVmRecord { var userVmRecord []UserVmRecord err := orm.Eloquent.Table("user_vm_record").Where("uid = ? and event in (?)", uid, []string{VmEventOpenMember, VmEventInvite1Member, VmEventInvite2Member, VmEventUserShareCard, VmEventAttendance, VmEventErpOrderSale}). Order("created_at DESC"). Find(&userVmRecord).Error if err != nil { logger.Error("get user_vm_record err:", logger.Field("err", err)) return []UserVmRecord{} } return userVmRecord } func UsePoints(gdb *gorm.DB, uid uint32, points int) error { // 1. 查找用户的所有可用积分记录,按时间排序 records := GetUserAvailablePointsRecords(uid) remaining := points for _, record := range records { available := record.Alter - record.UsedVm // 计算该订单剩余可用积分 if available >= remaining { // 该订单的剩余积分足够 record.UsedVm += remaining // 更新积分记录 err := gdb.Omit("created_at").Save(record).Error if err != nil { logger.Error("update user_vm_record err:", logger.Field("err", err)) return err } break } else { // 该订单的剩余积分不足,全部使用,继续扣减下一条记录 record.UsedVm = record.Alter remaining -= available // 更新积分记录 err := gdb.Omit("created_at").Save(record).Error if err != nil { logger.Error("update user_vm_record err:", logger.Field("err", err)) return err } } } return nil } // GetUserUsedPointsRecords 查找用户的已使用积分记录,按使用时间倒序排序 func GetUserUsedPointsRecords(uid uint32) []UserVmRecord { var userVmRecord []UserVmRecord err := orm.Eloquent.Table("user_vm_record").Where("uid = ? and event in (?)", uid, []string{VmEventOpenMember, VmEventInvite1Member, VmEventInvite2Member, VmEventUserShareCard, VmEventAttendance, VmEventErpOrderSale}). Where("used_vm != 0"). Order("created_at DESC"). Find(&userVmRecord).Error if err != nil { logger.Error("get user_vm_record err:", logger.Field("err", err)) return []UserVmRecord{} } return userVmRecord } // RefundPoints 查找用户的已使用积分记录,按使用时间倒序排序 func RefundPoints(gdb *gorm.DB, uid uint32, points int) error { // 1. 查找用户的已使用积分记录,按使用时间倒序排序 usedRecords := GetUserUsedPointsRecords(uid) remaining := points for _, record := range usedRecords { used := record.UsedVm // 获取该订单已使用的积分 if used >= remaining { // 该订单已使用的积分足够返还 record.UsedVm -= remaining // 更新积分记录 err := gdb.Omit("created_at").Save(record).Error if err != nil { logger.Error("RefundPoints update user_vm_record err:", logger.Field("err", err)) return err } break } else { // 该订单已使用的积分不足以完全返还,全部返还后继续处理下一条记录 record.UsedVm = 0 remaining -= used // 更新积分记录 err := gdb.Omit("created_at").Save(record).Error if err != nil { logger.Error("RefundPoints update user_vm_record err:", logger.Field("err", err)) return err } } } return nil }