214 lines
6.0 KiB
Go
214 lines
6.0 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"errors"
|
||
"fmt"
|
||
"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/weChat"
|
||
"hepa-calc-admin-api/global"
|
||
"hepa-calc-admin-api/utils"
|
||
"strconv"
|
||
"time"
|
||
)
|
||
|
||
type OrderMemberService struct {
|
||
}
|
||
|
||
// CancelOrderMember 取消会员订单
|
||
// cancelReason:订单取消原因(1:主动取消 2:后台取消 3:支付超时取消)
|
||
func (r *OrderMemberService) CancelOrderMember(tx *gorm.DB, orderMember *model.OrderMember, refundAmount *float64, cancelReason int) (bool, error) {
|
||
// 检测多次请求
|
||
redisKey := "CancelOrderMember" + fmt.Sprintf("%d", orderMember.UserId) + fmt.Sprintf("%d", orderMember.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 orderMember.OrderStatus == 3 {
|
||
return false, errors.New("订单已取消,请勿重复操作")
|
||
}
|
||
|
||
// 取消状态(0:否 1:是)
|
||
if orderMember.CancelStatus == 1 {
|
||
return false, errors.New("订单已取消")
|
||
}
|
||
|
||
// 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款)
|
||
if orderMember.RefundStatus == 1 {
|
||
return false, errors.New("订单已申请退款")
|
||
}
|
||
|
||
if orderMember.RefundStatus == 2 {
|
||
return false, errors.New("订单退款中")
|
||
}
|
||
|
||
if orderMember.RefundStatus == 3 {
|
||
return false, errors.New("订单已退款成功")
|
||
}
|
||
|
||
if orderMember.RefundStatus == 6 {
|
||
return false, errors.New("订单退款异常")
|
||
}
|
||
|
||
// 修改订单为取消
|
||
orderMemberDao := dao.OrderMemberDao{}
|
||
|
||
orderMemberData := make(map[string]interface{})
|
||
orderMemberData["order_status"] = 3
|
||
if cancelReason == 3 {
|
||
// 支付超时取消
|
||
orderMemberData["pay_status"] = 5
|
||
}
|
||
orderMemberData["cancel_status"] = 1
|
||
orderMemberData["cancel_time"] = time.Now().Format("2006-01-02 15:04:05")
|
||
orderMemberData["cancel_remarks"] = utils.OrderCancelReasonToString(cancelReason)
|
||
err = orderMemberDao.EditOrderMemberById(tx, orderMember.OrderId, orderMemberData)
|
||
if err != nil {
|
||
return false, errors.New("订单取消失败")
|
||
}
|
||
|
||
// 订单退款
|
||
_, err = r.OrderRefund(tx, orderMember, refundAmount, utils.OrderCancelReasonToString(cancelReason))
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
|
||
// 处理支付金额为0时退还优惠卷问题
|
||
if orderMember.PaymentAmountTotal == 0 {
|
||
// 退还订单优惠卷
|
||
err = r.OrderCouponRefund(tx, orderMember)
|
||
if err != nil {
|
||
return false, err
|
||
}
|
||
}
|
||
|
||
return true, nil
|
||
}
|
||
|
||
// OrderRefund 订单退款
|
||
func (r *OrderMemberService) OrderRefund(tx *gorm.DB, order *model.OrderMember, refundAmount *float64, refundReason string) (*model.OrderMemberRefund, error) {
|
||
var refundStatus int
|
||
var successTime *time.Time
|
||
var refundId string
|
||
|
||
// 判断订单支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款)
|
||
if order.PayStatus != 2 {
|
||
// 非已支付,无需退款
|
||
return nil, nil
|
||
}
|
||
|
||
// 检测退款金额
|
||
if *refundAmount > order.PaymentAmountTotal {
|
||
return nil, errors.New("退款金额不可超过实付金额")
|
||
}
|
||
|
||
// 退款编号
|
||
refundNo := strconv.FormatInt(global.Snowflake.Generate().Int64(), 10)
|
||
|
||
// 退款金额为0
|
||
if *refundAmount <= 0 {
|
||
// 支付金额为0,模拟退款
|
||
refundId = "模拟退款:" + strconv.FormatInt(global.Snowflake.Generate().Int64(), 10)
|
||
refundStatus = 3
|
||
|
||
now := time.Now()
|
||
successTime = &now
|
||
}
|
||
|
||
// 退款金额大于0
|
||
if *refundAmount > 0 {
|
||
refundRequest := weChat.RefundRequest{
|
||
TransactionId: order.EscrowTradeNo,
|
||
OutTradeNo: order.OrderNo,
|
||
OutRefundNo: refundNo,
|
||
Reason: refundReason,
|
||
RefundAmount: int64(*refundAmount * 100),
|
||
PaymentAmountTotal: int64(order.PaymentAmountTotal * 100),
|
||
NotifyUrl: config.C.Wechat.NotifyDomain + config.C.Wechat.MemberRefundNotifyUrl,
|
||
}
|
||
|
||
refund, err := refundRequest.Refund()
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 处理退款状态
|
||
wxPayRefundResult, err := weChat.HandlePayRefundStatus(refund)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
refundStatus = wxPayRefundResult.RefundStatus
|
||
successTime = wxPayRefundResult.SuccessTime
|
||
|
||
if refund.RefundId == nil {
|
||
return nil, errors.New("缺少退款订单编号")
|
||
}
|
||
|
||
// 退款编号
|
||
refundId = *refund.RefundId
|
||
}
|
||
|
||
// 新增退款表
|
||
orderRefund := &model.OrderMemberRefund{
|
||
UserId: order.UserId,
|
||
OrderId: order.OrderId,
|
||
OrderNo: order.OrderNo,
|
||
RefundNo: refundNo,
|
||
RefundId: refundId,
|
||
RefundStatus: refundStatus,
|
||
RefundTotal: *refundAmount,
|
||
RefundReason: refundReason,
|
||
}
|
||
|
||
if refundStatus == 3 && successTime != nil {
|
||
t := model.LocalTime(*successTime)
|
||
orderRefund.SuccessTime = &t
|
||
}
|
||
|
||
orderMemberRefundDao := dao.OrderMemberRefundDao{}
|
||
orderMemberRefund, err := orderMemberRefundDao.AddOrderMemberRefund(tx, orderRefund)
|
||
if err != nil || orderMemberRefund == nil {
|
||
return nil, errors.New(err.Error())
|
||
}
|
||
|
||
return orderMemberRefund, nil
|
||
}
|
||
|
||
// OrderCouponRefund 订单优惠卷退还
|
||
func (r *OrderMemberService) OrderCouponRefund(tx *gorm.DB, order *model.OrderMember) error {
|
||
// 订单优惠金额为0,无需退还优惠卷
|
||
if order.CouponAmountTotal == 0 {
|
||
return nil
|
||
}
|
||
|
||
// 获取订单优惠卷数据
|
||
orderMemberCouponDao := dao.OrderMemberCouponDao{}
|
||
orderMemberCoupons, err := orderMemberCouponDao.GetOrderMemberCouponListByOrderId(order.OrderId)
|
||
if err != nil {
|
||
return errors.New("优惠卷数据错误")
|
||
}
|
||
|
||
userCouponService := &UserCouponService{}
|
||
for _, coupon := range orderMemberCoupons {
|
||
_ = userCouponService.ReturnUserCoupon(tx, coupon.UserCouponId)
|
||
}
|
||
|
||
return nil
|
||
}
|