hepa-calc-api/api/service/OrderSingle.go

482 lines
15 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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-api/api/dao"
"hepa-calc-api/api/model"
"hepa-calc-api/config"
"hepa-calc-api/extend/rabbitMq"
"hepa-calc-api/extend/weChat"
"hepa-calc-api/global"
"hepa-calc-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
}
// 获取用户数据
userDao := dao.UserDao{}
user, err := userDao.GetUserById(UserId)
if err != nil || user == nil {
return nil, errors.New("用户错误")
}
// 检测是否会员
if payChannel == 3 && user.IsMember == 0 {
return nil, errors.New("用户非会员,不可使用会员支付")
}
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 *model.LocalTime // 支付时间
var escrowTradeNo string // 第三方支付流水号
var validDate *model.LocalTime // 算一算有效期
// 获取问题最终价格
amountTotal, err = questionService.GetUserBuyPrice(UserId, question)
if err != nil {
return nil, err
}
// 检测用户优惠卷
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 := model.LocalTime(time.Now())
payTime = &now // 支付时间
escrowTradeNo = "GD" + global.Snowflake.Generate().String() // 第三方支付流水号
// 处理单项有效时间
systemSingleDao := dao.SystemSingleDao{}
maps := make(map[string]interface{})
systemSingle, err := systemSingleDao.GetSystemSingle(maps)
if err != nil {
return nil, errors.New("订单创建失败")
}
singleValidDate := model.LocalTime(time.Now().Add(time.Duration(systemSingle.ValidDays) * 24 * time.Hour))
validDate = &singleValidDate
} else {
if orderPrice == nil {
return nil, errors.New("价格异常")
}
// 实际付款金额
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 := "S" + 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: "",
ValidDate: validDate,
}
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 = 1 * 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("订单创建失败")
}
fmt.Println("添加队列成功")
}
// 会员支付
if payChannel == 3 {
// 增加单项支付次数
questionService := QuestionService{}
res, err := questionService.AddQuestionPayCount(tx, orderSingle.QuestionId)
if err != nil || res == false {
return nil, errors.New("内部错误")
}
// 增加用户单项支付次数
userService := UserService{}
res, err = userService.AddUserSingleSubmitCount(tx, orderSingle.UserId)
if err != nil || res == false {
return nil, errors.New("内部错误")
}
}
return orderSingle, nil
}
// CancelOrderSingle 取消单项订单
// cancelReason:订单取消原因1:主动取消 2:后台取消 3:支付超时取消)
func (r *OrderSingleService) CancelOrderSingle(tx *gorm.DB, orderSingle *model.OrderSingle, cancelReason int) (bool, error) {
// 检测多次请求
redisKey := "CancelOrderSingle" + fmt.Sprintf("%d", orderSingle.UserId) + fmt.Sprintf("%d", orderSingle.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("取消订单失败")
}
// 订单状态1:待支付 2:已完成 3:已取消)
if orderSingle.OrderStatus == 2 {
return true, errors.New("订单已完成,无法取消")
}
if orderSingle.OrderStatus == 3 {
return true, errors.New("订单已取消,请勿重复操作")
}
// 取消状态0:否 1:是)
if orderSingle.CancelStatus == 1 {
return true, errors.New("订单已取消")
}
// 支付状态1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款)
if orderSingle.PayStatus == 2 {
return true, errors.New("订单已支付,无法取消")
}
// 订单退款状态0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款)
if orderSingle.RefundStatus == 1 {
return true, errors.New("订单已申请退款")
}
if orderSingle.RefundStatus == 2 {
return true, errors.New("订单退款中")
}
if orderSingle.RefundStatus == 3 {
return true, errors.New("订单已退款成功")
}
if orderSingle.RefundStatus == 6 {
return true, errors.New("订单退款异常")
}
// 修改订单为取消
orderSingleDao := dao.OrderSingleDao{}
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)
err = orderSingleDao.EditOrderSingleById(tx, orderSingle.OrderId, orderSingleData)
if err != nil {
return false, errors.New("订单取消失败")
}
// 处理支付金额为0时退还优惠卷问题
if orderSingle.PaymentAmountTotal == 0 {
// 退还订单优惠卷
err = r.OrderCouponRefund(tx, orderSingle)
if err != nil {
tx.Rollback()
return false, err
}
}
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.TestPayAppId,
MchId: config.C.Wechat.Pay1281030301.MchId,
Description: "肝病算一算",
OutTradeNo: m.OrderNo,
NotifyUrl: config.C.Wechat.NotifyDomain + config.C.Wechat.SinglePayNotifyUrl,
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.TestPayAppId,
MchId: config.C.Wechat.Pay1281030301.MchId,
Description: "肝病算一算",
OutTradeNo: m.OrderNo,
NotifyUrl: config.C.Wechat.NotifyDomain + config.C.Wechat.SingleRefundNotifyUrl,
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) {
questionService := QuestionService{}
userService := UserService{}
// 获取所有未支付单项订单
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
}
systemSingleDao := dao.SystemSingleDao{}
maps = make(map[string]interface{})
systemSingle, err := systemSingleDao.GetSystemSingle(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")
orderSingleData["valid_date"] = time.Now().Add(time.Duration(systemSingle.ValidDays) * 24 * time.Hour) //有效时间
err = orderSingleDao.EditOrderSingleById(tx, single.OrderId, orderSingleData)
if err != nil {
return false, err
}
// 增加单项支付次数
res, err := questionService.AddQuestionPayCount(tx, single.QuestionId)
if err != nil || res == false {
return false, err
}
// 增加用户单项支付次数
res, err = userService.AddUserSingleSubmitCount(tx, userId)
if err != nil || res == false {
return false, err
}
}
return true, nil
}
// OrderCouponRefund 订单优惠卷退还
func (r *OrderSingleService) OrderCouponRefund(tx *gorm.DB, order *model.OrderSingle) error {
// 订单优惠金额为0无需退还优惠卷
if order.CouponAmountTotal == 0 {
return nil
}
// 获取订单优惠卷数据
orderSingleCouponDao := dao.OrderSingleCouponDao{}
orderSingleCoupons, err := orderSingleCouponDao.GetOrderSingleCouponListByOrderId(order.OrderId)
if err != nil {
return errors.New("优惠卷数据错误")
}
userCouponService := &UserCouponService{}
for _, coupon := range orderSingleCoupons {
_ = userCouponService.ReturnUserCoupon(tx, coupon.UserCouponId)
}
return nil
}