package service import ( "context" "errors" "fmt" "github.com/wechatpay-apiv3/wechatpay-go/services/payments/app" "github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi" "gorm.io/gorm" "hepa-calc-admin-api/api/dao" "hepa-calc-admin-api/api/model" "hepa-calc-admin-api/config" "hepa-calc-admin-api/extend/rabbitMq" "hepa-calc-admin-api/extend/weChat" "hepa-calc-admin-api/global" "hepa-calc-admin-api/utils" "time" ) type OrderSingleService struct { } // AddOrderSingle 创建单项订单 // payChannel:支付渠道(1:h5支付 2:app支付 3:会员支付) func (r *OrderSingleService) AddOrderSingle(tx *gorm.DB, UserId, QuestionId int64, UserCouponId *int64, payChannel int, orderPrice float64) (orderSingle *model.OrderSingle, err error) { // 检测并发请求 redisKey := "AddOrderSingle" + fmt.Sprintf("%d", UserId) + fmt.Sprintf("%d", QuestionId) res, _ := global.Redis.Get(context.Background(), redisKey).Result() if res != "" { return nil, errors.New("请勿重复操作") } defer func(redisKey string) { global.Redis.Del(context.Background(), redisKey) }(redisKey) // 添加缓存 _, err = global.Redis.Set(context.Background(), redisKey, "1", (10)*time.Second).Result() if err != nil { return nil, errors.New("生成订单失败") } // 获取题目数据 questionDao := dao.QuestionDao{} question, err := questionDao.GetQuestionById(QuestionId) if err != nil { return nil, errors.New("题目异常") } // 检测题目 questionService := &QuestionService{} isNormal, err := questionService.CheckQuestion(question) if err != nil || isNormal == false { return nil, err } var amountTotal *float64 // 总金额 var couponAmountTotal float64 // 优惠卷总金额 var paymentAmountTotal float64 // 实际付款金额 var orderStatus int // 订单状态(1:待支付 2:已完成 3:已取消) var payStatus int // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) var payTime *time.Time // 支付时间 var escrowTradeNo string // 第三方支付流水号 // 获取问题最终价格 amountTotal, err = questionService.GetUserBuyPrice(UserId, QuestionId) if err != nil { return nil, err } if amountTotal == nil { return nil, errors.New("价格错误") } // 检测用户优惠卷 var userCoupon *model.UserCoupon if UserCouponId != nil { // 获取优惠卷数据 UserCouponDao := dao.UserCouponDao{} userCoupon, err = UserCouponDao.GetUserCouponPreloadById(*UserCouponId) if err != nil { return nil, errors.New("优惠券异常") } userCouponService := &UserCouponService{} isCanUse, err := userCouponService.CheckUserCoupon(userCoupon, QuestionId, 1, *amountTotal) if err != nil || isCanUse == false { return nil, errors.New("价格异常") } // 优惠卷总金额 couponAmountTotal = userCoupon.Coupon.CouponPrice } // 会员支付 if payChannel == 3 { paymentAmountTotal = 0 // 实际付款金额 orderStatus = 2 // 订单状态(1:待支付 2:已完成 3:已取消) payStatus = 2 // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) now := time.Now() payTime = &now // 支付时间 escrowTradeNo = "GD" + global.Snowflake.Generate().String() // 第三方支付流水号 } else { // 实际付款金额 paymentAmountTotal = *amountTotal - couponAmountTotal if orderPrice != paymentAmountTotal { return nil, errors.New("价格异常") } orderStatus = 1 // 订单状态(1:待支付 2:已完成 3:已取消) payStatus = 1 // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) payTime = nil // 支付时间 } // 生成订单号 orderNo := global.Snowflake.Generate().String() // 创建订单 orderSingle = &model.OrderSingle{ UserId: UserId, QuestionId: QuestionId, OrderStatus: orderStatus, IsDelete: 0, PayChannel: payChannel, PayStatus: payStatus, PayTime: payTime, RefundStatus: 0, OrderNo: orderNo, EscrowTradeNo: escrowTradeNo, AmountTotal: *amountTotal, CouponAmountTotal: couponAmountTotal, PaymentAmountTotal: paymentAmountTotal, CancelStatus: 0, CancelTime: nil, CancelRemarks: "", OrderRemarks: "", } orderSingleDao := dao.OrderSingleDao{} orderSingle, err = orderSingleDao.AddOrderSingle(tx, orderSingle) if err != nil { return nil, errors.New("订单创建失败") } // 创建优惠卷表 if userCoupon != nil { orderSingleCoupon := &model.OrderSingleCoupon{ OrderId: orderSingle.OrderId, UserCouponId: *UserCouponId, CouponName: userCoupon.Coupon.CouponName, CouponUsePrice: userCoupon.Coupon.CouponPrice, } orderSingleCouponDao := dao.OrderSingleCouponDao{} orderSingleCoupon, err = orderSingleCouponDao.AddOrderSingleCoupon(tx, orderSingleCoupon) if err != nil { tx.Rollback() return nil, errors.New("订单创建失败") } // 修改优惠卷使用状态 userCouponDao := dao.UserCouponDao{} userCouponData := make(map[string]interface{}) userCouponData["user_coupon_status"] = 1 userCouponData["coupon_use_date"] = time.Now().Format("2006-01-02 15:04:05") err := userCouponDao.EditUserCouponById(tx, userCoupon.UserCouponId, userCouponData) if err != nil { return nil, errors.New("订单创建失败") } } // 增加未支付取消订单延迟队列 if payChannel == 1 || payChannel == 2 { delay := 30 * time.Minute if config.C.Env == "dev" { delay = 3 * time.Minute } data := make(map[string]interface{}) data["order_id"] = fmt.Sprintf("%d", orderSingle.OrderId) data["order_no"] = orderSingle.OrderNo data["user_id"] = fmt.Sprintf("%d", orderSingle.UserId) data["order_type"] = 1 data["pay_channel"] = orderSingle.PayChannel p := rabbitMq.PublishS{ QueueName: "cancel.unpay.order.delay.queue", ExchangeName: "amqp.delay.direct", RoutingKey: "CancelUnPayOrder", Message: data, Delay: delay, } err := p.PublishWithDelay() if err != nil { utils.LogJsonErr("添加处理取消未支付订单队列失败:", err.Error()) return nil, errors.New("订单创建失败") } } return orderSingle, nil } // CancelOrderSingle 取消单项订单 // cancelReason:订单取消原因(1:主动取消 2:后台取消 3:支付超时取消) func (r *OrderSingleService) CancelOrderSingle(tx *gorm.DB, userId, orderId int64, cancelReason int) (bool, error) { // 检测多次请求 redisKey := "CancelOrderSingle" + fmt.Sprintf("%d", userId) + fmt.Sprintf("%d", orderId) res, _ := global.Redis.Get(context.Background(), redisKey).Result() if res != "" { return false, errors.New("请勿重复操作") } defer func(redisKey string) { global.Redis.Del(context.Background(), redisKey) }(redisKey) // 添加缓存 _, err := global.Redis.Set(context.Background(), redisKey, "1", (10)*time.Second).Result() if err != nil { return false, errors.New("取消订单失败") } // 获取订单数据 orderSingleDao := dao.OrderSingleDao{} maps := make(map[string]interface{}) maps["order_id"] = orderId orderSingle, err := orderSingleDao.GetOrderSingle(maps) if err != nil { return false, errors.New("订单异常") } // 订单状态(1:待支付 2:已完成 3:已取消) if orderSingle.OrderStatus == 2 { return false, errors.New("订单已完成,无法取消") } if orderSingle.OrderStatus == 3 { return false, errors.New("订单已取消,请勿重复操作") } // 取消状态(0:否 1:是) if orderSingle.CancelStatus == 1 { return false, errors.New("订单已取消") } // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) if orderSingle.PayStatus == 2 { return false, errors.New("订单已支付,无法取消") } // 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款) if orderSingle.RefundStatus == 1 { return false, errors.New("订单已申请退款") } if orderSingle.RefundStatus == 2 { return false, errors.New("订单退款中") } if orderSingle.RefundStatus == 3 { return false, errors.New("订单已退款成功") } if orderSingle.RefundStatus == 6 { return false, errors.New("订单退款异常") } // 修改订单为取消 orderSingleData := make(map[string]interface{}) orderSingleData["order_status"] = 3 if cancelReason == 3 { // 支付超时取消 orderSingleData["pay_status"] = 5 } orderSingleData["cancel_status"] = 1 orderSingleData["cancel_time"] = time.Now().Format("2006-01-02 15:04:05") orderSingleData["cancel_remarks"] = utils.OrderCancelReasonToString(cancelReason) orderSingleData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") err = orderSingleDao.EditOrderSingleById(tx, orderId, orderSingleData) if err != nil { return false, errors.New("订单取消失败") } // 退还订单优惠卷 if orderSingle.CouponAmountTotal != 0 { // 获取订单优惠卷数据 orderSingleCouponDao := dao.OrderSingleCouponDao{} orderSingleCoupon, err := orderSingleCouponDao.GetOrderSingleCouponByOrderId(orderId) if err != nil { tx.Rollback() return false, errors.New("订单取消失败") } userCouponService := &UserCouponService{} userCouponService.ReturnUserCoupon(tx, orderSingleCoupon.UserCouponId) } return true, nil } // GetJsapiPrepay 获取jsapi预支付交易会话标识 func (r *OrderSingleService) GetJsapiPrepay(m *model.OrderSingle) (prepay *jsapi.PrepayWithRequestPaymentResponse, err error) { // 获取用户数据 userDao := dao.UserDao{} user, err := userDao.GetUserById(m.UserId) if err != nil || user == nil { return nil, errors.New("用户错误") } if user.OpenId == "" { return nil, errors.New("发起支付失败") } jsapiRequest := weChat.JsapiRequest{ AppId: config.C.Wechat.AppId, MchId: config.C.Wechat.Pay1281030301.MchId, Description: "肝病算一算", OutTradeNo: m.OrderNo, NotifyUrl: config.C.Wechat.RefundNotifyDomain + config.C.Wechat.RefundNotifyUrl, Amount: weChat.JsapiRequestAmountRequest{ Total: int64(m.PaymentAmountTotal * 100), Currency: "CNY", }, Payer: weChat.JsapiRequestPayerRequest{OpenId: user.OpenId}, } prepay, err = jsapiRequest.GetJsapiPrepay() if err != nil { return nil, err } return prepay, nil } // GetAppPrepay 获取app预支付交易会话标识 func (r *OrderSingleService) GetAppPrepay(m *model.OrderSingle) (prepay *app.PrepayWithRequestPaymentResponse, err error) { // 获取用户数据 userDao := dao.UserDao{} user, err := userDao.GetUserById(m.UserId) if err != nil || user == nil { return nil, errors.New("用户错误") } if user.OpenId == "" { return nil, errors.New("发起支付失败") } appRequest := weChat.AppRequest{ AppId: config.C.Wechat.AppId, MchId: config.C.Wechat.Pay1281030301.MchId, Description: "肝病算一算", OutTradeNo: m.OrderNo, NotifyUrl: config.C.Wechat.RefundNotifyDomain + config.C.Wechat.RefundNotifyUrl, Amount: weChat.AppRequestAmountRequest{ Total: int64(m.PaymentAmountTotal * 100), Currency: "CNY", }, } prepay, err = appRequest.GetAppPrepay() if err != nil { return nil, err } return prepay, nil } // CompleteUnPayOrderSingle 完成未支付单项订单-开通会员成功时使用 func (r *OrderSingleService) CompleteUnPayOrderSingle(tx *gorm.DB, userId int64) (bool, error) { // 获取所有未支付单项订单 orderSingleDao := dao.OrderSingleDao{} maps := make(map[string]interface{}) maps["user_id"] = userId maps["order_status"] = 1 maps["pay_status"] = 1 maps["cancel_status"] = 0 orderSingles, err := orderSingleDao.GetOrderSingleList(maps) if err != nil { return false, err } for _, single := range orderSingles { // 生成第三方支付流水号 escrowTradeNo := "GD" + global.Snowflake.Generate().String() orderSingleData := make(map[string]interface{}) orderSingleData["order_status"] = 2 orderSingleData["pay_status"] = 2 orderSingleData["pay_time"] = time.Now().Format("2006-01-02 15:04:05") orderSingleData["escrow_trade_no"] = escrowTradeNo orderSingleData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") err = orderSingleDao.EditOrderSingleById(tx, single.OrderId, orderSingleData) if err != nil { return false, err } // 增加题目支付次数 questionDao := dao.QuestionDao{} err = questionDao.Inc(tx, single.QuestionId, "pay_count", 1) if err != nil { return false, err } } return true, nil }