diff --git a/api/controller/OrderMember.go b/api/controller/OrderMember.go index e8ff405..bbca26f 100644 --- a/api/controller/OrderMember.go +++ b/api/controller/OrderMember.go @@ -96,6 +96,19 @@ func (b *OrderMember) GetOrderMember(c *gin.Context) { // PutCancelOrderMember 取消会员订单 func (b *OrderMember) PutCancelOrderMember(c *gin.Context) { + orderMemberRequest := requests.OrderMemberRequest{} + req := orderMemberRequest.CancelOrderMember + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + id := c.Param("order_id") if id == "" { responses.FailWithMessage("缺少参数", c) @@ -127,7 +140,7 @@ func (b *OrderMember) PutCancelOrderMember(c *gin.Context) { // 取消单项订单 orderMemberService := service.OrderMemberService{} - res, err := orderMemberService.CancelOrderMember(tx, orderMember.UserId, orderId, 2) + res, err := orderMemberService.CancelOrderMember(tx, orderMember, req.RefundAmount, 2) if err != nil { tx.Rollback() responses.FailWithMessage(err.Error(), c) diff --git a/api/controller/OrderSingle.go b/api/controller/OrderSingle.go index 47a8c02..ec2e25c 100644 --- a/api/controller/OrderSingle.go +++ b/api/controller/OrderSingle.go @@ -68,6 +68,19 @@ func (b *OrderSingle) GetOrderSinglePage(c *gin.Context) { // PutCancelOrderSingle 取消单项订单 func (b *OrderSingle) PutCancelOrderSingle(c *gin.Context) { + orderSingleRequest := requests.OrderSingleRequest{} + req := orderSingleRequest.CancelOrderSingle + if err := c.ShouldBind(&req); err != nil { + responses.FailWithMessage(err.Error(), c) + return + } + + // 参数验证 + if err := global.Validate.Struct(req); err != nil { + responses.FailWithMessage(utils.Translate(err), c) + return + } + id := c.Param("order_id") if id == "" { responses.FailWithMessage("缺少参数", c) @@ -83,9 +96,7 @@ func (b *OrderSingle) PutCancelOrderSingle(c *gin.Context) { // 获取订单数据 orderSingleDao := dao.OrderSingleDao{} - maps := make(map[string]interface{}) - maps["order_id"] = orderId - orderSingle, err := orderSingleDao.GetOrderSingle(maps) + orderSingle, err := orderSingleDao.GetOrderSingleById(orderId) if err != nil { responses.FailWithMessage("订单异常", c) return @@ -101,7 +112,7 @@ func (b *OrderSingle) PutCancelOrderSingle(c *gin.Context) { // 取消单项订单 orderSingleService := service.OrderSingleService{} - res, err := orderSingleService.CancelOrderSingle(tx, orderSingle.UserId, orderId, 2) + res, err := orderSingleService.CancelOrderSingle(tx, orderSingle, req.RefundAmount, 2) if err != nil { tx.Rollback() responses.FailWithMessage(err.Error(), c) diff --git a/api/dao/OrderMemberCoupon.go b/api/dao/OrderMemberCoupon.go index ae13d7a..1452c6b 100644 --- a/api/dao/OrderMemberCoupon.go +++ b/api/dao/OrderMemberCoupon.go @@ -45,6 +45,15 @@ func (r *OrderMemberCouponDao) DeleteOrderMemberCouponById(tx *gorm.DB, OrderMem return nil } +// GetOrderMemberCouponListByOrderId 获取列表-订单id +func (r *OrderMemberCouponDao) GetOrderMemberCouponListByOrderId(orderId int64) (m []*model.OrderMemberCoupon, err error) { + err = global.Db.Where("order_id = ?", orderId).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + // EditOrderMemberCoupon 修改 func (r *OrderMemberCouponDao) EditOrderMemberCoupon(tx *gorm.DB, maps interface{}, data interface{}) error { err := tx.Model(&model.OrderMemberCoupon{}).Where(maps).Updates(data).Error diff --git a/api/dao/OrderSingleCoupon.go b/api/dao/OrderSingleCoupon.go index 457c262..a57c1f8 100644 --- a/api/dao/OrderSingleCoupon.go +++ b/api/dao/OrderSingleCoupon.go @@ -37,6 +37,15 @@ func (r *OrderSingleCouponDao) GetOrderSingleCouponByOrderId(orderId int64) (m * return m, nil } +// GetOrderSingleCouponListByOrderId 获取列表-订单id +func (r *OrderSingleCouponDao) GetOrderSingleCouponListByOrderId(orderId int64) (m []*model.OrderSingleCoupon, err error) { + err = global.Db.Where("order_id = ?", orderId).Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + // DeleteOrderSingleCoupon 删除 func (r *OrderSingleCouponDao) DeleteOrderSingleCoupon(tx *gorm.DB, maps interface{}) error { err := tx.Where(maps).Delete(&model.OrderSingleCoupon{}).Error diff --git a/api/dto/OrderSingle.go b/api/dto/OrderSingle.go index 8433de2..8c96d29 100644 --- a/api/dto/OrderSingle.go +++ b/api/dto/OrderSingle.go @@ -37,6 +37,13 @@ type OrderSingleDto struct { MemberExpireDate *model.LocalTime `json:"member_expire_date"` // 用户会员到期时间(非会员时为null) } +// OrderRefundDto 订单退款 +type OrderRefundDto struct { + RefundStatus int `json:"refund_status"` // 订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款) + SuccessTime string `json:"success_time"` // 退款成功时间 + RefundId string `json:"refund_id"` // 系统退款编号 +} + // GetOrderSingleListDto 列表 func GetOrderSingleListDto(m []*model.OrderSingle) []*OrderSingleDto { // 处理返回值 diff --git a/api/model/OrderMemberRefund.go b/api/model/OrderMemberRefund.go index 1f4aee8..bc97e1d 100644 --- a/api/model/OrderMemberRefund.go +++ b/api/model/OrderMemberRefund.go @@ -8,16 +8,16 @@ import ( // OrderMemberRefund 订单-会员-退款表 type OrderMemberRefund struct { - OrderRefundId int64 `gorm:"column:order_refund_id;type:bigint(19);primary_key;comment:主键id" json:"order_refund_id"` - UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id;NOT NULL" json:"user_id"` - OrderId int64 `gorm:"column:order_id;type:bigint(19);comment:订单id;NOT NULL" json:"order_id"` - OrderNo string `gorm:"column:order_no;type:varchar(40);comment:系统订单编号" json:"order_no"` - RefundNo string `gorm:"column:refund_no;type:varchar(50);comment:系统退款编号" json:"refund_no"` - RefundId string `gorm:"column:refund_id;type:varchar(50);comment:第三方退款单号" json:"refund_id"` - RefundStatus int `gorm:"column:refund_status;type:tinyint(1);default:0;comment:订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款)" json:"refund_status"` - RefundTotal float64 `gorm:"column:refund_total;type:decimal(10,2) unsigned;default:0.00;comment:退款金额" json:"refund_total"` - RefundReason string `gorm:"column:refund_reason;type:varchar(255);comment:退款原因" json:"refund_reason"` - SuccessTime LocalTime `gorm:"column:success_time;type:datetime;comment:退款成功时间" json:"success_time"` + OrderRefundId int64 `gorm:"column:order_refund_id;type:bigint(19);primary_key;comment:主键id" json:"order_refund_id"` + UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id;NOT NULL" json:"user_id"` + OrderId int64 `gorm:"column:order_id;type:bigint(19);comment:订单id;NOT NULL" json:"order_id"` + OrderNo string `gorm:"column:order_no;type:varchar(40);comment:系统订单编号" json:"order_no"` + RefundNo string `gorm:"column:refund_no;type:varchar(50);comment:系统退款编号" json:"refund_no"` + RefundId string `gorm:"column:refund_id;type:varchar(50);comment:第三方退款单号" json:"refund_id"` + RefundStatus int `gorm:"column:refund_status;type:tinyint(1);default:0;comment:订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款)" json:"refund_status"` + RefundTotal float64 `gorm:"column:refund_total;type:decimal(10,2) unsigned;default:0.00;comment:退款金额" json:"refund_total"` + RefundReason string `gorm:"column:refund_reason;type:varchar(255);comment:退款原因" json:"refund_reason"` + SuccessTime *LocalTime `gorm:"column:success_time;type:datetime;comment:退款成功时间" json:"success_time"` Model } diff --git a/api/model/OrderSingleRefund.go b/api/model/OrderSingleRefund.go index 29f95cf..215a3bd 100644 --- a/api/model/OrderSingleRefund.go +++ b/api/model/OrderSingleRefund.go @@ -8,16 +8,16 @@ import ( // OrderSingleRefund 订单-单项-退款表 type OrderSingleRefund struct { - OrderRefundId int64 `gorm:"column:order_refund_id;type:bigint(19);primary_key;comment:主键id" json:"order_refund_id"` - UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id;NOT NULL" json:"user_id"` - OrderId int64 `gorm:"column:order_id;type:bigint(19);comment:订单id;NOT NULL" json:"order_id"` - OrderNo string `gorm:"column:order_no;type:varchar(40);comment:系统订单编号" json:"order_no"` - RefundNo string `gorm:"column:refund_no;type:varchar(50);comment:系统退款编号" json:"refund_no"` - RefundId string `gorm:"column:refund_id;type:varchar(50);comment:第三方退款单号" json:"refund_id"` - RefundStatus int `gorm:"column:refund_status;type:tinyint(1);default:0;comment:订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款)" json:"refund_status"` - RefundTotal float64 `gorm:"column:refund_total;type:decimal(10,2) unsigned;default:0.00;comment:退款金额" json:"refund_total"` - RefundReason string `gorm:"column:refund_reason;type:varchar(255);comment:退款原因" json:"refund_reason"` - SuccessTime LocalTime `gorm:"column:success_time;type:datetime;comment:退款成功时间" json:"success_time"` + OrderRefundId int64 `gorm:"column:order_refund_id;type:bigint(19);primary_key;comment:主键id" json:"order_refund_id"` + UserId int64 `gorm:"column:user_id;type:bigint(19);comment:用户id;NOT NULL" json:"user_id"` + OrderId int64 `gorm:"column:order_id;type:bigint(19);comment:订单id;NOT NULL" json:"order_id"` + OrderNo string `gorm:"column:order_no;type:varchar(40);comment:系统订单编号" json:"order_no"` + RefundNo string `gorm:"column:refund_no;type:varchar(50);comment:系统退款编号" json:"refund_no"` + RefundId string `gorm:"column:refund_id;type:varchar(50);comment:第三方退款单号" json:"refund_id"` + RefundStatus int `gorm:"column:refund_status;type:tinyint(1);default:0;comment:订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常 7:部分退款)" json:"refund_status"` + RefundTotal float64 `gorm:"column:refund_total;type:decimal(10,2) unsigned;default:0.00;comment:退款金额" json:"refund_total"` + RefundReason string `gorm:"column:refund_reason;type:varchar(255);comment:退款原因" json:"refund_reason"` + SuccessTime *LocalTime `gorm:"column:success_time;type:datetime;comment:退款成功时间" json:"success_time"` Model } diff --git a/api/requests/OrderMember.go b/api/requests/OrderMember.go index a1e4683..baeee7d 100644 --- a/api/requests/OrderMember.go +++ b/api/requests/OrderMember.go @@ -3,6 +3,7 @@ package requests type OrderMemberRequest struct { GetOrderMemberPage // 获取会员订单列表-分页 PutOrderMemberDeleteStatus // 操作会员订单删除状态 + CancelOrderMember // 取消会员订单 } // GetOrderMemberPage 获取会员订单列表-分页 @@ -26,3 +27,8 @@ type GetOrderMemberPage struct { type PutOrderMemberDeleteStatus struct { IsDelete int `json:"is_delete" form:"is_delete" label:"删除状态"` // 用户删除状态(0:否 1:是) } + +// CancelOrderMember 取消会员订单 +type CancelOrderMember struct { + RefundAmount *float64 `json:"refund_amount" form:"refund_amount" label:"退款金额"` +} diff --git a/api/requests/OrderSingle.go b/api/requests/OrderSingle.go index 32e9d67..193e4c6 100644 --- a/api/requests/OrderSingle.go +++ b/api/requests/OrderSingle.go @@ -3,6 +3,7 @@ package requests type OrderSingleRequest struct { GetOrderSinglePage // 获取单项订单列表-分页 PutOrderSingleDeleteStatus // 操作单项订单删除状态 + CancelOrderSingle // 取消单项订单 } // GetOrderSinglePage 获取单项订单列表-分页 @@ -26,3 +27,8 @@ type GetOrderSinglePage struct { type PutOrderSingleDeleteStatus struct { IsDelete int `json:"is_delete" form:"is_delete" label:"删除状态"` // 用户删除状态(0:否 1:是) } + +// CancelOrderSingle 取消单项订单 +type CancelOrderSingle struct { + RefundAmount *float64 `json:"refund_amount" form:"refund_amount" label:"退款金额"` +} diff --git a/api/service/OrderMember.go b/api/service/OrderMember.go index 09ddd5c..bf0ccfe 100644 --- a/api/service/OrderMember.go +++ b/api/service/OrderMember.go @@ -13,6 +13,7 @@ import ( "hepa-calc-admin-api/extend/weChat" "hepa-calc-admin-api/global" "hepa-calc-admin-api/utils" + "strconv" "time" ) @@ -21,9 +22,9 @@ type OrderMemberService struct { // CancelOrderMember 取消会员订单 // cancelReason:订单取消原因(1:主动取消 2:后台取消 3:支付超时取消) -func (r *OrderMemberService) CancelOrderMember(tx *gorm.DB, userId, orderId int64, cancelReason int) (bool, error) { +func (r *OrderMemberService) CancelOrderMember(tx *gorm.DB, orderMember *model.OrderMember, refundAmount *float64, cancelReason int) (bool, error) { // 检测多次请求 - redisKey := "CancelOrderMember" + fmt.Sprintf("%d", userId) + fmt.Sprintf("%d", orderId) + 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("请勿重复操作") @@ -39,15 +40,6 @@ func (r *OrderMemberService) CancelOrderMember(tx *gorm.DB, userId, orderId int6 return false, errors.New("取消订单失败") } - // 获取订单数据 - orderMemberDao := dao.OrderMemberDao{} - maps := make(map[string]interface{}) - maps["order_id"] = orderId - orderMember, err := orderMemberDao.GetOrderMember(maps) - if err != nil { - return false, errors.New("订单异常") - } - // 订单状态(1:待支付 2:已完成 3:已取消) if orderMember.OrderStatus == 2 { return false, errors.New("订单已完成,无法取消") @@ -95,7 +87,9 @@ func (r *OrderMemberService) CancelOrderMember(tx *gorm.DB, userId, orderId int6 orderMemberData["cancel_time"] = time.Now().Format("2006-01-02 15:04:05") orderMemberData["cancel_remarks"] = utils.OrderCancelReasonToString(cancelReason) orderMemberData["updated_at"] = time.Now().Format("2006-01-02 15:04:05") - err = orderMemberDao.EditOrderMemberById(tx, orderId, orderMemberData) + + orderMemberDao := dao.OrderMemberDao{} + err = orderMemberDao.EditOrderMemberById(tx, orderMember.OrderId, orderMemberData) if err != nil { return false, errors.New("订单取消失败") } @@ -104,7 +98,7 @@ func (r *OrderMemberService) CancelOrderMember(tx *gorm.DB, userId, orderId int6 if orderMember.CouponAmountTotal != 0 { // 获取订单优惠卷数据 orderMemberCouponDao := dao.OrderMemberCouponDao{} - orderMemberCoupon, err := orderMemberCouponDao.GetOrderMemberCouponByOrderId(orderId) + orderMemberCoupon, err := orderMemberCouponDao.GetOrderMemberCouponByOrderId(orderMember.OrderId) if err != nil { tx.Rollback() return false, errors.New("订单取消失败") @@ -114,6 +108,20 @@ func (r *OrderMemberService) CancelOrderMember(tx *gorm.DB, userId, orderId int6 userCouponService.ReturnUserCoupon(tx, orderMemberCoupon.UserCouponId) } + // 退还订单优惠卷 + err = r.OrderCouponRefund(tx, orderMember) + if err != nil { + tx.Rollback() + return false, err + } + + // 订单退款 + _, err = r.OrderRefund(tx, orderMember, refundAmount, utils.OrderCancelReasonToString(cancelReason)) + if err != nil { + tx.Rollback() + return false, err + } + return true, nil } @@ -183,3 +191,126 @@ func (r *OrderMemberService) GetAppPrepay(m *model.OrderMember) (prepay *app.Pre return prepay, 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 + successTime = time.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.RefundNotifyDomain + config.C.Wechat.RefundNotifyUrl, + } + + refund, err := refundRequest.Refund() + if err != nil { + return nil, err + } + + if refund.Status == nil { + return nil, errors.New("退款状态错误") + } + + // 处理订单状态 + switch *refund.Status { + case "SUCCESS": // 退款成功 + refundStatus = 3 + if refund.SuccessTime != nil { + successTime = *refund.SuccessTime + } + case "CLOSED": // 退款关闭 + + refundStatus = 5 + case "PROCESSING": // 退款处理中 + refundStatus = 2 + case "ABNORMAL": // 退款异常 + return nil, errors.New("退款状态错误") + default: + return nil, errors.New("退款状态错误") + } + + 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.IsZero() { + 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 +} diff --git a/api/service/OrderSingle.go b/api/service/OrderSingle.go index 4887a39..c0f9b01 100644 --- a/api/service/OrderSingle.go +++ b/api/service/OrderSingle.go @@ -13,6 +13,7 @@ import ( "hepa-calc-admin-api/extend/weChat" "hepa-calc-admin-api/global" "hepa-calc-admin-api/utils" + "strconv" "time" ) @@ -21,9 +22,9 @@ type OrderSingleService struct { // CancelOrderSingle 取消单项订单 // cancelReason:订单取消原因(1:主动取消 2:后台取消 3:支付超时取消) -func (r *OrderSingleService) CancelOrderSingle(tx *gorm.DB, userId, orderId int64, cancelReason int) (bool, error) { +func (r *OrderSingleService) CancelOrderSingle(tx *gorm.DB, orderSingle *model.OrderSingle, refundAmount *float64, cancelReason int) (bool, error) { // 检测多次请求 - redisKey := "CancelOrderSingle" + fmt.Sprintf("%d", userId) + fmt.Sprintf("%d", orderId) + 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("请勿重复操作") @@ -39,15 +40,6 @@ func (r *OrderSingleService) CancelOrderSingle(tx *gorm.DB, userId, orderId int6 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("订单已完成,无法取消") @@ -95,23 +87,25 @@ func (r *OrderSingleService) CancelOrderSingle(tx *gorm.DB, userId, orderId int6 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) + + orderSingleDao := dao.OrderSingleDao{} + err = orderSingleDao.EditOrderSingleById(tx, orderSingle.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("订单取消失败") - } + err = r.OrderCouponRefund(tx, orderSingle) + if err != nil { + tx.Rollback() + return false, err + } - userCouponService := &UserCouponService{} - userCouponService.ReturnUserCoupon(tx, orderSingleCoupon.UserCouponId) + // 订单退款 + _, err = r.OrderRefund(tx, orderSingle, refundAmount, utils.OrderCancelReasonToString(cancelReason)) + if err != nil { + tx.Rollback() + return false, err } return true, nil @@ -223,3 +217,126 @@ func (r *OrderSingleService) CompleteUnPayOrderSingle(tx *gorm.DB, userId int64) return true, nil } + +// OrderRefund 订单退款 +func (r *OrderSingleService) OrderRefund(tx *gorm.DB, order *model.OrderSingle, refundAmount *float64, refundReason string) (*model.OrderSingleRefund, 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 + successTime = time.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.RefundNotifyDomain + config.C.Wechat.RefundNotifyUrl, + } + + refund, err := refundRequest.Refund() + if err != nil { + return nil, err + } + + if refund.Status == nil { + return nil, errors.New("退款状态错误") + } + + // 处理订单状态 + switch *refund.Status { + case "SUCCESS": // 退款成功 + refundStatus = 3 + if refund.SuccessTime != nil { + successTime = *refund.SuccessTime + } + case "CLOSED": // 退款关闭 + + refundStatus = 5 + case "PROCESSING": // 退款处理中 + refundStatus = 2 + case "ABNORMAL": // 退款异常 + return nil, errors.New("退款状态错误") + default: + return nil, errors.New("退款状态错误") + } + + if refund.RefundId == nil { + return nil, errors.New("缺少退款订单编号") + } + + // 退款编号 + refundId = *refund.RefundId + } + + // 新增退款表 + orderRefund := &model.OrderSingleRefund{ + UserId: order.UserId, + OrderId: order.OrderId, + OrderNo: order.OrderNo, + RefundNo: refundNo, + RefundId: refundId, + RefundStatus: refundStatus, + RefundTotal: *refundAmount, + RefundReason: refundReason, + } + + if refundStatus == 3 && !successTime.IsZero() { + t := model.LocalTime(successTime) + orderRefund.SuccessTime = &t + } + + orderSingleRefundDao := dao.OrderSingleRefundDao{} + orderSingleRefund, err := orderSingleRefundDao.AddOrderSingleRefund(tx, orderRefund) + if err != nil || orderSingleRefund == nil { + return nil, errors.New(err.Error()) + } + + return orderSingleRefund, 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 +}