package app import ( "fmt" "git.echol.cn/loser/lckt/model/vip" "strconv" "strings" "time" "git.echol.cn/loser/lckt/global" "git.echol.cn/loser/lckt/model/app" "git.echol.cn/loser/lckt/model/app/request" userM "git.echol.cn/loser/lckt/model/user" "git.echol.cn/loser/lckt/utils/wechat" "github.com/gin-gonic/gin" "go.uber.org/zap" ) type OrderService struct{} // Pay 发起支付 func (s *OrderService) Pay(p request.PayReq, ctx *gin.Context) (interface{}, error) { order := app.Order{} err := global.GVA_DB.Where("id = ? AND order_no = ?", p.OrderId, p.OrderNo).First(&order).Error if err != nil { global.GVA_LOG.Error("查询订单失败", zap.Error(err)) return "", err } if order.Status == 3 { global.GVA_LOG.Error("订单已过期", zap.Int64("order_id", int64(order.ID))) return "", fmt.Errorf("订单已过期") } if p.Mode == "h5" { payConf, err := wechat.H5Pay(&order, ctx) if err != nil { global.GVA_LOG.Error("微信支付订单失败", zap.Error(err)) return "", err } return payConf, nil } else if p.Mode == "jsapi" { payConf, err := wechat.JSAPIPay(&order, ctx) if err != nil { global.GVA_LOG.Error("微信支付订单失败", zap.Error(err)) return "", err } return payConf, nil } return "", nil } func (s *OrderService) Create(o *app.Order) (*app.Order, error) { // 生成订单号 o.OrderNo = wechat.GenerateOrderNum() // 查询订单商品价格 price := 0 switch o.OrderType { case 1: err := global.GVA_DB.Table("article").Select("price").Where("id = ?", o.ArticleId).Scan(&price).Error if err != nil { global.GVA_LOG.Error("查询商品价格失败", zap.Error(err)) return nil, err } case 2: err := global.GVA_DB.Table("lckt_vip").Select("price").Where("id = ?", o.VipId).Scan(&price).Error if err != nil { global.GVA_LOG.Error("查询VIP价格失败", zap.Error(err)) return nil, err } case 3: // 讲师包月 //切割TeacherVipId字符串 ids := strings.Split(o.TeacherVipId, ",") // 查询每个服务的价格并累加 totalPrice := 0 for _, id := range ids { var p int err := global.GVA_DB.Table("app_teacher_vip").Select("price").Where("id = ?", id).Scan(&p).Error if err != nil { global.GVA_LOG.Error("查询讲师包月价格失败", zap.Error(err)) return nil, err } totalPrice += p } price = totalPrice } o.Price = int64(price) o.Status = 1 // 设置订单状态为未付款 // 设置openid openId := "" err := global.GVA_DB.Table("app_user").Where("id = ?", o.UserId).Select("open_id").Scan(&openId).Error if err != nil { global.GVA_LOG.Error("查询用户OpenID失败,请检查微信是否绑定", zap.Error(err)) return nil, fmt.Errorf("查询用户OpenID失败,请检查微信是否绑定") } o.OpenId = openId err = global.GVA_DB.Create(&o).Error if err != nil { global.GVA_LOG.Error("创建订单失败", zap.Error(err)) return nil, err } return o, nil } func (s *OrderService) GetOrderDetail(id string) (order app.Order, err error) { err = global.GVA_DB.Where("id = ?", id).First(&order).Error if err != nil { global.GVA_LOG.Error("获取订单详情失败", zap.Error(err)) return app.Order{}, err } return order, nil } func (s *OrderService) BalancePay(p request.BalancePay) error { order := app.Order{} err := global.GVA_DB.Where("id = ? AND order_no = ?", p.OrderId, p.OrderNo).First(&order).Error if err != nil { global.GVA_LOG.Error("查询订单失败", zap.Error(err)) return err } if order.Status != 1 { global.GVA_LOG.Error("订单状态错误,无法支付", zap.Int("status", order.Status)) return err } // 检查用户余额是否足够 var user userM.User err = global.GVA_DB.Where("id = ?", p.UserId).First(&user).Error if err != nil { global.GVA_LOG.Error("查询用户信息失败", zap.Error(err)) return err } // 将订单价格从分转换为元(保持精度) orderPriceInYuan := float64(order.Price) / 100.0 // 检查用户余额是否足够(使用float64进行比较,避免精度丢失) if user.Balance < orderPriceInYuan { global.GVA_LOG.Error("用户余额不足", zap.Float64("balance", user.Balance), zap.Float64("order_price_yuan", orderPriceInYuan), zap.Int64("order_price_cent", order.Price)) return fmt.Errorf("用户余额不足") } // 扣除用户余额(保持精度) newBalance := user.Balance - orderPriceInYuan err = global.GVA_DB.Model(&user).Where("id = ?", p.UserId).Update("balance", newBalance).Error if err != nil { global.GVA_LOG.Error("扣除用户余额失败", zap.Error(err)) return err } // 更新订单状态为已付款 order.Status = 2 err = global.GVA_DB.Model(&order).Where("id = ?", order.ID).Update("status", 2).Error if err != nil { global.GVA_LOG.Error("更新订单状态失败", zap.Error(err)) return err } // 全站Vip if order.OrderType == 2 { // 查询用户购买的会员信息 vipInfo := vip.Vip{} err = global.GVA_DB.Model(&vip.Vip{}).Where("id = ?", order.VipId).First(&vipInfo).Error if err != nil { global.GVA_LOG.Error("查询会员信息失败", zap.Error(err)) return err } // 计算会员的过期时间 if user.VipExpireTime != "" { expireTime, _ := time.Parse("2006-01-02 15:04:05", user.VipExpireTime) if expireTime.After(time.Now()) { // 如果会员未过期,则在原有的基础上增加时间 user.VipExpireTime = expireTime.AddDate(0, 0, int(vipInfo.Expiration)).Format("2006-01-02 15:04:05") } else { // 如果会员已过期,则从当前时间开始计算 user.VipExpireTime = time.Now().AddDate(0, 0, int(vipInfo.Expiration)).Format("2006-01-02 15:04:05") } } else { // 如果没有会员时间,则从当前时间开始计算 user.VipExpireTime = time.Now().AddDate(0, 0, int(vipInfo.Expiration)).Format("2006-01-02 15:04:05") } // 更新用户的会员状态 user.IsVip = 1 if vipInfo.Level == 1 { user.UserLabel = 2 // VIP } if vipInfo.Level == 2 { user.UserLabel = 3 // SVIP } err = global.GVA_DB.Save(&user).Error if err != nil { global.GVA_LOG.Error("更新用户会员状态失败", zap.Error(err)) return err } return nil } // 讲师包月 if order.OrderType == 3 { // 逗号分割字符串 ids := strings.Split(order.TeacherVipId, ",") for _, id := range ids { teacherVip := app.UserTeacherVip{} err = global.GVA_DB.Model(&app.UserTeacherVip{}). Where("teacher_id = ? AND user_id = ? AND teacher_vip_id = ?", order.TeacherId, order.UserId, id). Order("id desc"). // 取最新一条 First(&teacherVip).Error now := time.Now() var newExpireAt time.Time if err == nil { // 找到记录,判断是否过期 expireTime, _ := time.Parse("2006-01-02 15:04:05", teacherVip.ExpireAt) if teacherVip.IsExpire == 1 && expireTime.After(now) { // 未过期,在原有基础上加一个月 newExpireAt = expireTime.AddDate(0, 1, 0) } else { // 已过期,从当前时间加一个月 newExpireAt = now.AddDate(0, 1, 0) } teacherVip.ExpireAt = newExpireAt.Format("2006-01-02 15:04:05") teacherVip.IsExpire = 1 // 设置为未过期 err = global.GVA_DB.Save(&teacherVip).Error if err != nil { global.GVA_LOG.Error("更新用户讲师会员信息失败", zap.Error(err)) return err } } else { // 没有购买过,直接新建 teacherVip := app.UserTeacherVip{ TeacherId: uint(order.TeacherId), UserId: uint(order.UserId), TeacherVipId: func() uint { v, _ := strconv.ParseUint(id, 10, 64); return uint(v) }(), ExpireAt: now.AddDate(0, 1, 0).Format("2006-01-02 15:04:05"), IsExpire: 1, } err = global.GVA_DB.Create(&teacherVip).Error if err != nil { global.GVA_LOG.Error("购买讲师会员回调处理失败:", zap.Error(err)) return err } } } // 计算分成比例,按比例增加讲师余额 teacher := userM.User{} err = global.GVA_DB.Model(&userM.User{}).Where("id = ?", order.TeacherId).First(&teacher).Error if err != nil { global.GVA_LOG.Error("查询讲师信息失败", zap.Error(err)) return err } // 计算分成金额 amount := float64(order.Price/100) * float64(teacher.ExpectRate) / 100.0 teacher.Balance = teacher.Balance + amount err = global.GVA_DB.Save(&teacher).Error if err != nil { global.GVA_LOG.Error("更新讲师余额失败", zap.Error(err)) return err } return nil } // 计算分成比例,按比例增加讲师余额 teacher := userM.User{} err = global.GVA_DB.Model(&userM.User{}).Where("id = ?", order.TeacherId).First(&teacher).Error if err != nil { global.GVA_LOG.Error("查询讲师信息失败", zap.Error(err)) return err } // 计算分成金额 amount := float64(order.Price/100) * float64(teacher.ExpectRate) / 100.0 teacher.Balance = teacher.Balance + amount err = global.GVA_DB.Save(&teacher).Error if err != nil { global.GVA_LOG.Error("更新讲师余额失败", zap.Error(err)) return err } global.GVA_LOG.Info("余额支付成功", zap.Int64("user_id", int64(p.UserId)), zap.String("order_no", order.OrderNo)) return nil } // AppGetOrderList APP端获取订单列表 func (s *OrderService) AppGetOrderList(p request.GetOrderList, id uint) (orders []app.Order, total int64, err error) { limit := p.PageSize offset := p.PageSize * (p.Page - 1) db := global.GVA_DB.Model(&app.Order{}).Where("user_id = ?", id) if p.Keyword != "" { db = db.Where("title LIKE ? ", "%"+p.Keyword+"%") } if p.Status != 0 { db = db.Where("status = ?", p.Status) } err = db.Count(&total).Error err = db.Limit(limit).Offset(offset).Order("created_at desc").Find(&orders).Error if err != nil { global.GVA_LOG.Error("获取订单列表失败", zap.Error(err)) return nil, 0, err } return } // GetOrderList APP端获取订单列表 func (s *OrderService) GetOrderList(p request.GetOrderList) (orders []app.Order, total int64, err error) { limit := p.PageSize offset := p.PageSize * (p.Page - 1) db := global.GVA_DB.Model(&app.Order{}) if p.Title != "" { db = db.Where("title LIKE ? ", "%"+p.Title+"%") } if p.Status != 0 { db = db.Where("status = ?", p.Status) } if p.PayType != 0 { db = db.Where("pay_type = ?", p.PayType) } if p.OrderType != 0 { db = db.Where("order_type = ?", p.OrderType) } if p.StartTime != "" && p.EndTime != "" { db = db.Where("created_at BETWEEN ? AND ?", p.StartTime, p.EndTime) } if p.Name != "" { db = db.Where("name LIKE ? ", "%"+p.Name+"%") } err = db.Count(&total).Error err = db.Limit(limit).Offset(offset).Order("created_at desc").Find(&orders).Error if err != nil { global.GVA_LOG.Error("获取订单列表失败", zap.Error(err)) return nil, 0, err } return }