package model import ( "errors" "fmt" "github.com/codinl/go-logger" "github.com/jinzhu/gorm" "time" ) const ( //VmEventExchangeGoods = "exchange_goods" VmEventBuyGoods = "buy_goods" // 购买商品积分抵扣 VmEventBuyGoodsReject = "buy_goods_reject" // 购买商品积分抵扣取消 VmEventOpenMember = "open_member" // 开通租卡会员奖励 VmEventRenewMember = "renew_member" // 续费租卡会员奖励 VmEventUpgradeMember = "upgrade_member" // 升级租卡会员奖励 VmEventInvite1Member = "invite_1_member" // 邀请会员奖励 VmEventInvite2Member = "invite_2_member" // 邀请会员奖励 VmEventAttendance = "attendance" // 签到获取积分 VmEventErpOrderSale = "erp_order_sale" // 零售销售获得积分 VmEventErpOrderReject = "erp_order_reject" // 零售退货扣除积分 VmEventOpenPrivilegeMember = "open_privilege_member" // 开通尊享会员奖励 VmEventRenewPrivilegeMember = "renew_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"` } // 用户积分-变动记录 // gen:qs type UserVmRecord struct { Model Uid uint32 `json:"uid" gorm:"column:uid;index"` 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:"-"` // 即将过期的积分 } func UserVmUpdate(uid uint32, amount int, event, describe string) error { var userVm UserVm err := NewUserVmQuerySet(DB).UidEq(uid).One(&userVm) if err != nil && err != RecordNotFound { logger.Error("err:", err) return err } begin := DB.Begin() if err == RecordNotFound { 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:", 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:", err) return err } } vmRecord := &UserVmRecord{ Uid: uid, BeforeVm: uint32(userVm.Vm), AfterVm: uint32(int(userVm.Vm) + amount), Alter: amount, Event: event, Describe: describe, } if newValue := int(userVm.Vm) + amount; newValue > 0 { vmRecord.AfterVm = uint32(newValue) } // 如果是正积分,则记录该订单对应积分的有效时间 if amount > 0 { expireTime := time.Now().AddDate(1, 0, 0) vmRecord.ExpiryDate = &expireTime if event == VmEventBuyGoodsReject { // 使用积分购买商品后取消 err = RefundPoints(begin, uid, amount) if err != nil { begin.Rollback() logger.Error("err:", err) return err } } } else { // 如果是负积分,则记录积分使用情况;优先扣除最早获得的积分 if event == VmEventBuyGoods || event == VmEventErpOrderReject { // 使用积分购买商品,或者零售退货扣除积分 err = UsePoints(begin, uid, -amount) if err != nil { begin.Rollback() logger.Error("err:", err) return err } } } err = begin.Create(vmRecord).Error if err != nil { begin.Rollback() logger.Error("err:", err) return err } err = begin.Commit().Error if err != nil { begin.Rollback() logger.Error("err:", err) return err } return nil } // GetUserAvailablePointsRecords 查找用户的所有可用积分记录,按时间排序 func GetUserAvailablePointsRecords(uid uint32) []UserVmRecord { var userVmRecord []UserVmRecord err := NewUserVmRecordQuerySet(DB).UidEq(uid).OrderDescByCreatedAt(). EventIn(VmEventOpenMember, VmEventInvite1Member, VmEventInvite2Member, VmEventRenewMember, VmEventUpgradeMember, VmEventAttendance, VmEventErpOrderSale).All(&userVmRecord) if err != nil { logger.Error("get user_vm_record 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 { if record.ExpiryDate != nil && record.ExpiryDate.IsZero() { record.ExpiryDate = nil } available := record.Alter - record.UsedVm // 计算该订单剩余可用积分 if available >= remaining { // 该订单的剩余积分足够 record.UsedVm += remaining // 更新积分记录 err := gdb.Model(&record).Where("id = ?", record.ID).Updates(record).Error if err != nil { logger.Error("update user_vm_record err:", err) return err } break } else { // 该订单的剩余积分不足,全部使用,继续扣减下一条记录 record.UsedVm = record.Alter remaining -= available // 更新积分记录 err := gdb.Model(&record).Where("id = ?", record.ID).Updates(record).Error if err != nil { logger.Error("update user_vm_record err:", err) return err } } } return nil } // GetUserUsedPointsRecords 查找用户的已使用积分记录,按使用时间倒序排序 func GetUserUsedPointsRecords(uid uint32) []UserVmRecord { var userVmRecord []UserVmRecord err := NewUserVmRecordQuerySet(DB).UidEq(uid).OrderDescByCreatedAt(). EventIn(VmEventOpenMember, VmEventInvite1Member, VmEventInvite2Member, VmEventRenewMember, VmEventUpgradeMember, VmEventAttendance, VmEventErpOrderSale).UsedVmNe(0).All(&userVmRecord) if err != nil { logger.Error("get user_vm_record 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.Model(&record).Where("id = ?", record.ID).Updates(record).Error if err != nil { logger.Error("RefundPoints update user_vm_record err:", err) return err } break } else { // 该订单已使用的积分不足以完全返还,全部返还后继续处理下一条记录 record.UsedVm = 0 remaining -= used // 更新积分记录 err := gdb.Model(&record).Where("id = ?", record.ID).Updates(record).Error //err := gdb.Omit("created_at").Save(record).Error if err != nil { logger.Error("RefundPoints update user_vm_record err:", err) return err } } } return nil }