package app import ( "errors" "git.echol.cn/loser/lckt/global" "git.echol.cn/loser/lckt/model/app" request2 "git.echol.cn/loser/lckt/model/app/request" "git.echol.cn/loser/lckt/model/article" "git.echol.cn/loser/lckt/model/common/request" "git.echol.cn/loser/lckt/model/user" "git.echol.cn/loser/lckt/model/vip" "git.echol.cn/loser/lckt/utils" "git.echol.cn/loser/lckt/utils/wechat" "github.com/gin-gonic/gin" "go.uber.org/zap" "gorm.io/gorm" "time" ) type RedeemCodeService struct{} // Create 创建兑换码库 func (s RedeemCodeService) Create(p app.RedeemCode) (err error) { return global.GVA_DB.Create(&p).Error } // Delete 删除兑换码库 func (s RedeemCodeService) Delete(p app.RedeemCode) (err error) { return global.GVA_DB.Delete(&p).Error } // Update 更新兑换码库 func (s RedeemCodeService) Update(p app.RedeemCode) (err error) { return global.GVA_DB.Save(&p).Error } // GetList 分页获取兑换码库列表 func (s RedeemCodeService) GetList(p request.PageInfo) (list []app.RedeemCode, total int64, err error) { limit := p.PageSize offset := p.PageSize * (p.Page - 1) db := global.GVA_DB.Model(&app.RedeemCode{}) if p.Keyword != "" { db = db.Where("code_name LIKE ?", "%"+p.Keyword+"%") } err = db.Count(&total).Error if err != nil { return } err = db.Limit(limit).Offset(offset).Find(&list).Error return } // GetById 根据ID获取兑换码库 func (s RedeemCodeService) GetById(id int) (redeem app.RedeemCode, err error) { err = global.GVA_DB.Where("id = ?", id).First(&redeem).Error return } // ========================CDK相关======================== // CreateCDK 生成兑换码 func (s RedeemCodeService) CreateCDK(p request2.GenerateCDK) ([]app.CDK, error) { codes := utils.GenerateRedeemCodes(p.Number) var cdks []app.CDK for _, code := range codes { var cdk app.CDK cdk.Code = code cdk.RedeemId = p.Eid cdk.ValidDay = p.Expirer cdk.Status = 1 if p.Expirer == 0 { cdk.ExpireAt = "永久有效" } else { cdk.ExpireAt = time.Now().AddDate(0, 0, p.Expirer).Format("2006-01-02") } cdks = append(cdks, cdk) } err := global.GVA_DB.Create(&cdks).Error if err != nil { global.GVA_LOG.Error("生成兑换码失败", zap.Error(err)) return nil, err } // 更新兑换码库的数量 err = global.GVA_DB.Model(&app.RedeemCode{}).Where("id = ?", p.Eid).Update("num", gorm.Expr("num + ?", p.Number)).Error if err != nil { global.GVA_LOG.Error("更新兑换码库数量失败", zap.Error(err)) } return cdks, nil } // GetCDKList 获取兑换码列表 func (s RedeemCodeService) GetCDKList(p request2.GetCDKList) (list []app.CDK, total int64, err error) { limit := p.PageSize offset := p.PageSize * (p.Page - 1) db := global.GVA_DB.Model(&app.CDK{}).Where("redeem_id = ?", p.Eid) if p.Code != "" { db = db.Where("code LIKE ?", "%"+p.Code+"%") } if p.UseName != "" { db = db.Where("use_name LIKE ?", "%"+p.UseName+"%") } if p.Status != 0 { db = db.Where("status = ?", p.Status) } err = db.Count(&total).Error if err != nil { return } err = db.Limit(limit).Offset(offset).Find(&list).Error return } // DeleteCDK 删除兑换码 func (s RedeemCodeService) DeleteCDK(p app.CDK) (err error) { return global.GVA_DB.Delete(&p).Error } // Redeem 用户兑换码 func (s RedeemCodeService) Redeem(p request2.RedeemCDK, ctx *gin.Context) (err error) { var cdk app.CDK err = global.GVA_DB.Where("code = ?", p.Code).First(&cdk).Error if err != nil { return err } // 判断状态 if cdk.Status != 1 { return errors.New("兑换码不可用") } // 判断是否过期 if cdk.ValidDay != 0 { expireAt, _ := time.Parse("2006-01-02", cdk.ExpireAt) if time.Now().After(expireAt) { // 更新状态为已过期 cdk.Status = 3 _ = global.GVA_DB.Save(&cdk).Error return errors.New("兑换码已过期") } } // 获取兑换码库信息 var redeem app.RedeemCode err = global.GVA_DB.Where("id = ?", cdk.RedeemId).First(&redeem).Error if err != nil { return err } // 获取用户信息 userId := utils.GetUserID(ctx) var userInfo user.User err = global.GVA_DB.Where("id = ?", userId).First(&userInfo).Error if err != nil { global.GVA_LOG.Error("获取用户信息出错", zap.Error(err)) } // 更新兑换码状态 cdk.Status = 2 cdk.UseId = userInfo.ID cdk.UseName = userInfo.NickName cdk.UseAt = time.Now().Format("2006-01-02 15:04:05") err = global.GVA_DB.Save(&cdk).Error if err != nil { global.GVA_LOG.Error("更新兑换码状态失败", zap.Error(err)) return err } // 更新兑换码库已使用数量 err = global.GVA_DB.Model(&app.RedeemCode{}).Where("id = ?", cdk.RedeemId).Update("no", gorm.Expr("no + ?", 1)).Error if err != nil { global.GVA_LOG.Error("更新兑换码库已使用数量失败", zap.Error(err)) } // 发放对应的权益 switch redeem.Type { case 1: // VIP var vipInfo vip.Vip err = global.GVA_DB.Where("id = ?", redeem.Item).First(&vipInfo).Error if err != nil { return err } vipLevel := 1 if vipInfo.Level == 1 { vipLevel = 2 } else if vipInfo.Level == 2 { vipLevel = 3 } // 判断用户是否已经是VIP if userInfo.IsVip == 1 { // 是VIP,判断会员等级 if userInfo.UserLabel == int64(vipLevel) { // 等级相同,延长会员时间 if userInfo.VipExpireTime != "" { expireTime, _ := time.Parse("2006-01-02", userInfo.VipExpireTime) if expireTime.After(time.Now()) { // 如果会员未过期,则在原有的基础上增加时间 userInfo.VipExpireTime = expireTime.AddDate(0, 0, int(vipInfo.Expiration)).Format("2006-01-02") } else { // 如果会员已过期,则从当前时间开始计算 userInfo.VipExpireTime = time.Now().AddDate(0, 0, int(vipInfo.Expiration)).Format("2006-01-02") } } else { // 如果没有会员时间,则从当前时间开始计算 userInfo.VipExpireTime = time.Now().AddDate(0, 0, int(vipInfo.Expiration)).Format("2006-01-02") } err = global.GVA_DB.Save(&userInfo).Error if err != nil { global.GVA_LOG.Error("更新用户VIP信息失败", zap.Error(err)) return err } } if userInfo.UserLabel < int64(vipLevel) { // 等级更高,直接覆盖 userInfo.UserLabel = int64(vipLevel) userInfo.VipExpireTime = time.Now().AddDate(0, 0, int(vipInfo.Expiration)).Format("2006-01-02") err = global.GVA_DB.Save(&userInfo).Error if err != nil { global.GVA_LOG.Error("更新用户VIP信息失败", zap.Error(err)) return err } } } else { // 不是VIP,直接设置为VIP userInfo.IsVip = 1 userInfo.UserLabel = int64(vipLevel) userInfo.VipExpireTime = time.Now().AddDate(0, 0, int(vipInfo.Expiration)).Format("2006-01-02") err = global.GVA_DB.Save(&userInfo).Error if err != nil { return err } } case 2: // 讲师VIP var teacherVip app.TeacherVip err = global.GVA_DB.Where("id = ?", redeem.Item).First(&teacherVip).Error if err != nil { return err } // 判断是否已经拥有该讲师VIP var existingTeacherVip app.UserTeacherVip err = global.GVA_DB.Where("user_id = ? AND teacher_vip_id = ?", userInfo.ID, teacherVip.ID).First(&existingTeacherVip).Error if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { global.GVA_LOG.Error("获取用户讲师VIP信息出错", zap.Error(err)) return err } // 判断是否为空 if errors.Is(err, gorm.ErrRecordNotFound) { userTeacherVip := app.UserTeacherVip{ UserId: userInfo.ID, TeacherVipId: teacherVip.ID, TeacherId: teacherVip.TeacherId, IsExpire: 1, ExpireAt: time.Now().AddDate(0, 0, 30).Format("2006-01-02"), } err = global.GVA_DB.Create(&userTeacherVip).Error if err != nil { global.GVA_LOG.Error("兑换讲师包月服务失败", zap.Error(err)) return err } return } // 默认未过期,获取过期时间+30天 // 将 existingTeacherVip.ExpireAt转为time.Time类型 parse, err := time.Parse("2006-01-02", existingTeacherVip.ExpireAt) if err != nil { global.GVA_LOG.Error("转换过期时间出错", zap.Error(err)) return err } // 已过期 当前时间开始计算+30天 if existingTeacherVip.IsExpire == 2 && time.Now().After(parse) { existingTeacherVip.IsExpire = 1 existingTeacherVip.ExpireAt = time.Now().AddDate(0, 0, 30).Format("2006-01-02") } else { // 未过期 在原有时间上+30天 existingTeacherVip.ExpireAt = parse.AddDate(0, 0, 30).Format("2006-01-02") } err = global.GVA_DB.Save(&existingTeacherVip).Error if err != nil { global.GVA_LOG.Error("兑换讲师包月服务失败", zap.Error(err)) return err } case 3: // 课程 var course article.Article err = global.GVA_DB.Where("id = ?", redeem.Item).First(&course).Error if err != nil { return err } // 判断用户是否购买过该课程 var count int64 err = global.GVA_DB.Model(&app.Order{}).Where("article_id = ? AND user_id = ? AND status = 2", course.ID, userInfo.ID).Count(&count).Error if err != nil { global.GVA_LOG.Error("查询用户购买记录失败", zap.Error(err)) return err } if count == 0 { // 未购买,新增购买记录 order := app.Order{ UserId: uint64(userInfo.ID), Name: userInfo.NickName, OpenId: userInfo.OpenId, Phone: userInfo.Phone, TeacherId: uint64(course.TeacherId), ArticleId: course.ID, Price: 0, Status: 2, OrderNo: wechat.GenerateOrderNum(), OrderType: 1, Title: course.Title, Desc: "兑换码兑换" + course.Title, PayType: 4, } err = global.GVA_DB.Create(&order).Error if err != nil { global.GVA_LOG.Error("兑换课程失败", zap.Error(err)) return err } } else { // 已购买,直接返回 return errors.New("您已购买过该课程,无需重复兑换") } return } return }