按实际纯币金额计算租客优惠与积分
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"time"
|
||||
|
||||
"hfb_sys/backend/internal/model"
|
||||
"hfb_sys/backend/internal/modules/rentergrowth"
|
||||
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/datatypes"
|
||||
@@ -273,7 +274,7 @@ func (r *Repository) prepareRefundOrder(ctx context.Context, originalPayment mod
|
||||
}
|
||||
func (r *Repository) syncRefundPayment(ctx context.Context, payment *model.PaymentOrder, queryTerminal bool) (*model.PaymentOrder, error) {
|
||||
if payment.Status == "refunded" && !queryTerminal {
|
||||
if err := r.updateOrderRefundStatus(ctx, payment.OrderID, payment.AmountCent); err != nil {
|
||||
if err := r.updateOrderRefundStatus(ctx, payment.OrderID, payment.AmountCent, payment.BizType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return payment, nil
|
||||
@@ -290,7 +291,7 @@ func (r *Repository) syncRefundPayment(ctx context.Context, payment *model.Payme
|
||||
}
|
||||
if runtimeConfig.isMockMode() {
|
||||
if payment.Status == "refunded" {
|
||||
if err := r.updateOrderRefundStatus(ctx, payment.OrderID, payment.AmountCent); err != nil {
|
||||
if err := r.updateOrderRefundStatus(ctx, payment.OrderID, payment.AmountCent, payment.BizType); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
@@ -371,7 +372,7 @@ func (r *Repository) applyRefundChannelStatus(ctx context.Context, payment *mode
|
||||
}
|
||||
switch status {
|
||||
case "refunded":
|
||||
return r.updateOrderRefundStatus(ctx, payment.OrderID, payment.AmountCent)
|
||||
return r.updateOrderRefundStatus(ctx, payment.OrderID, payment.AmountCent, payment.BizType)
|
||||
case "failed":
|
||||
return r.markOrderRefundFailed(ctx, payment.OrderID, payment.AmountCent)
|
||||
default:
|
||||
@@ -394,13 +395,23 @@ func refundQueryRequest(payment model.PaymentOrder, originalPayment model.Paymen
|
||||
ProviderRefundID: payment.ProviderOrderID,
|
||||
}
|
||||
}
|
||||
func (r *Repository) updateOrderRefundStatus(ctx context.Context, orderID uint64, refundAmountCent int64) error {
|
||||
now := time.Now()
|
||||
return r.db.WithContext(ctx).Model(&model.RentalOrder{}).Where("id = ?", orderID).Updates(map[string]any{
|
||||
"refund_status": "refunded",
|
||||
"refund_amount_cent": refundAmountCent,
|
||||
"refunded_at": now,
|
||||
}).Error
|
||||
func (r *Repository) updateOrderRefundStatus(ctx context.Context, orderID uint64, refundAmountCent int64, bizType string) error {
|
||||
return r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
var order model.RentalOrder
|
||||
if err := tx.Clauses(clause.Locking{Strength: "UPDATE"}).First(&order, orderID).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
now := time.Now()
|
||||
order.RefundStatus = "refunded"
|
||||
order.RefundAmountCent = refundAmountCent
|
||||
order.RefundedAt = &now
|
||||
if bizType == "admin_refund" && order.Status == "completed" && refundAmountCent >= order.RentAmountCent+order.DepositAmountCent {
|
||||
if err := rentergrowth.RevokeOrderCompleted(tx, &order); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return tx.Save(&order).Error
|
||||
})
|
||||
}
|
||||
func (r *Repository) markOrderRefunding(ctx context.Context, orderID uint64, refundAmountCent int64) error {
|
||||
return r.db.WithContext(ctx).Model(&model.RentalOrder{}).Where("id = ?", orderID).Updates(map[string]any{
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"hfb_sys/backend/internal/database"
|
||||
"hfb_sys/backend/internal/model"
|
||||
ordermodule "hfb_sys/backend/internal/modules/order"
|
||||
"hfb_sys/backend/internal/modules/rentergrowth"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -388,6 +389,55 @@ func TestRefundAmountMustNotExceedOriginal(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSuccessfulAdminFullRefundRevokesCompletedOrderGrowthPoints(t *testing.T) {
|
||||
db := setupPaymentTestDB(t)
|
||||
repo := NewRepository(db, nil, nil)
|
||||
renter := model.User{Phone: "13900001991", RenterGrowthLevel: "normal"}
|
||||
if err := db.Create(&renter).Error; err != nil {
|
||||
t.Fatalf("create renter failed: %v", err)
|
||||
}
|
||||
order := model.RentalOrder{
|
||||
OrderNo: "ORD202607260991",
|
||||
RenterID: renter.ID,
|
||||
RentAmountCent: 1000,
|
||||
Status: "completed",
|
||||
GrowthPointsBasisCent: 1000,
|
||||
GrowthPointsPerYuan: 1,
|
||||
}
|
||||
if err := db.Create(&order).Error; err != nil {
|
||||
t.Fatalf("create order failed: %v", err)
|
||||
}
|
||||
if err := rentergrowth.AwardOrderCompleted(db, &order, 1000, rentergrowth.SourceOrderCompleted); err != nil {
|
||||
t.Fatalf("award growth points failed: %v", err)
|
||||
}
|
||||
if err := db.Save(&order).Error; err != nil {
|
||||
t.Fatalf("save awarded order failed: %v", err)
|
||||
}
|
||||
|
||||
if err := repo.updateOrderRefundStatus(t.Context(), order.ID, 1000, "admin_refund"); err != nil {
|
||||
t.Fatalf("update admin refund status failed: %v", err)
|
||||
}
|
||||
if err := repo.updateOrderRefundStatus(t.Context(), order.ID, 1000, "admin_refund"); err != nil {
|
||||
t.Fatalf("repeat admin refund status failed: %v", err)
|
||||
}
|
||||
var savedUser model.User
|
||||
if err := db.First(&savedUser, renter.ID).Error; err != nil {
|
||||
t.Fatalf("find renter failed: %v", err)
|
||||
}
|
||||
if savedUser.RenterGrowthPoints != 0 || savedUser.RenterGrowthLevel != rentergrowth.DefaultLevelCode {
|
||||
t.Fatalf("growth after refund = %d/%s, want 0/%s", savedUser.RenterGrowthPoints, savedUser.RenterGrowthLevel, rentergrowth.DefaultLevelCode)
|
||||
}
|
||||
var revokeCount int64
|
||||
if err := db.Model(&model.RenterGrowthLedger{}).
|
||||
Where("order_id = ? AND source = ?", order.ID, rentergrowth.SourceOrderRefunded).
|
||||
Count(&revokeCount).Error; err != nil {
|
||||
t.Fatalf("count revoke ledger failed: %v", err)
|
||||
}
|
||||
if revokeCount != 1 {
|
||||
t.Fatalf("revoke ledger count = %d, want 1", revokeCount)
|
||||
}
|
||||
}
|
||||
|
||||
// TestPaymentNotifyResultStructure 测试支付回调结果结构
|
||||
func TestNotifyResultHasRequiredFields(t *testing.T) {
|
||||
result := NotifyResult{
|
||||
|
||||
Reference in New Issue
Block a user