482 lines
15 KiB
Go
482 lines
15 KiB
Go
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
|
||
}
|