fix(admin): 订单金额币种符号读取 currency 字段

This commit is contained in:
wucm667
2026-06-25 16:23:45 +08:00
parent 00d68ff6df
commit 55242ffac1
14 changed files with 506 additions and 42 deletions
@@ -2,6 +2,7 @@ package admin
import (
"strconv"
"time"
dbent "github.com/Wei-Shaw/sub2api/ent"
"github.com/Wei-Shaw/sub2api/internal/pkg/response"
@@ -115,24 +116,105 @@ func (h *PaymentHandler) RetryFulfillment(c *gin.Context) {
response.Success(c, gin.H{"message": "fulfillment retried"})
}
func sanitizeAdminPaymentOrdersForResponse(orders []*dbent.PaymentOrder) []*dbent.PaymentOrder {
if len(orders) == 0 {
return orders
}
out := make([]*dbent.PaymentOrder, 0, len(orders))
type AdminPaymentOrderResult struct {
ID int64 `json:"id"`
UserID int64 `json:"user_id"`
UserEmail string `json:"user_email,omitempty"`
UserName string `json:"user_name,omitempty"`
UserNotes *string `json:"user_notes,omitempty"`
Amount float64 `json:"amount"`
PayAmount float64 `json:"pay_amount"`
FeeRate float64 `json:"fee_rate"`
Currency string `json:"currency"`
RechargeCode string `json:"recharge_code,omitempty"`
OutTradeNo string `json:"out_trade_no"`
PaymentType string `json:"payment_type"`
PaymentTradeNo string `json:"payment_trade_no,omitempty"`
PayURL *string `json:"pay_url,omitempty"`
QRCode *string `json:"qr_code,omitempty"`
QRCodeImg *string `json:"qr_code_img,omitempty"`
OrderType string `json:"order_type"`
PlanID *int64 `json:"plan_id,omitempty"`
SubscriptionGroupID *int64 `json:"subscription_group_id,omitempty"`
SubscriptionDays *int `json:"subscription_days,omitempty"`
ProviderInstanceID *string `json:"provider_instance_id,omitempty"`
ProviderKey *string `json:"provider_key,omitempty"`
Status string `json:"status"`
RefundAmount float64 `json:"refund_amount"`
RefundReason *string `json:"refund_reason,omitempty"`
RefundAt *time.Time `json:"refund_at,omitempty"`
ForceRefund bool `json:"force_refund,omitempty"`
RefundRequestedAt *time.Time `json:"refund_requested_at,omitempty"`
RefundRequestReason *string `json:"refund_request_reason,omitempty"`
RefundRequestedBy *string `json:"refund_requested_by,omitempty"`
ExpiresAt time.Time `json:"expires_at"`
PaidAt *time.Time `json:"paid_at,omitempty"`
CompletedAt *time.Time `json:"completed_at,omitempty"`
FailedAt *time.Time `json:"failed_at,omitempty"`
FailedReason *string `json:"failed_reason,omitempty"`
ClientIP string `json:"client_ip,omitempty"`
SrcHost string `json:"src_host,omitempty"`
SrcURL *string `json:"src_url,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
func sanitizeAdminPaymentOrdersForResponse(orders []*dbent.PaymentOrder) []*AdminPaymentOrderResult {
out := make([]*AdminPaymentOrderResult, 0, len(orders))
for _, order := range orders {
out = append(out, sanitizeAdminPaymentOrderForResponse(order))
if item := sanitizeAdminPaymentOrderForResponse(order); item != nil {
out = append(out, item)
}
}
return out
}
func sanitizeAdminPaymentOrderForResponse(order *dbent.PaymentOrder) *dbent.PaymentOrder {
func sanitizeAdminPaymentOrderForResponse(order *dbent.PaymentOrder) *AdminPaymentOrderResult {
if order == nil {
return nil
}
cloned := *order
cloned.ProviderSnapshot = nil
return &cloned
return &AdminPaymentOrderResult{
ID: order.ID,
UserID: order.UserID,
UserEmail: order.UserEmail,
UserName: order.UserName,
UserNotes: order.UserNotes,
Amount: order.Amount,
PayAmount: order.PayAmount,
FeeRate: order.FeeRate,
Currency: service.PaymentOrderCurrency(order),
RechargeCode: order.RechargeCode,
OutTradeNo: order.OutTradeNo,
PaymentType: order.PaymentType,
PaymentTradeNo: order.PaymentTradeNo,
PayURL: order.PayURL,
QRCode: order.QrCode,
QRCodeImg: order.QrCodeImg,
OrderType: order.OrderType,
PlanID: order.PlanID,
SubscriptionGroupID: order.SubscriptionGroupID,
SubscriptionDays: order.SubscriptionDays,
ProviderInstanceID: order.ProviderInstanceID,
ProviderKey: order.ProviderKey,
Status: order.Status,
RefundAmount: order.RefundAmount,
RefundReason: order.RefundReason,
RefundAt: order.RefundAt,
ForceRefund: order.ForceRefund,
RefundRequestedAt: order.RefundRequestedAt,
RefundRequestReason: order.RefundRequestReason,
RefundRequestedBy: order.RefundRequestedBy,
ExpiresAt: order.ExpiresAt,
PaidAt: order.PaidAt,
CompletedAt: order.CompletedAt,
FailedAt: order.FailedAt,
FailedReason: order.FailedReason,
ClientIP: order.ClientIP,
SrcHost: order.SrcHost,
SrcURL: order.SrcURL,
CreatedAt: order.CreatedAt,
UpdatedAt: order.UpdatedAt,
}
}
// AdminProcessRefundRequest is the request body for admin refund processing.
@@ -0,0 +1,48 @@
package admin
import (
"encoding/json"
"strings"
"testing"
"time"
dbent "github.com/Wei-Shaw/sub2api/ent"
)
func TestSanitizeAdminPaymentOrderForResponseAddsCurrency(t *testing.T) {
now := time.Now()
order := &dbent.PaymentOrder{
ID: 1,
UserID: 2,
Amount: 100,
PayAmount: 108,
FeeRate: 8,
OutTradeNo: "sub2_202606250001",
PaymentType: "stripe",
OrderType: "subscription",
Status: "COMPLETED",
ExpiresAt: now,
CreatedAt: now,
UpdatedAt: now,
ProviderSnapshot: map[string]any{
"schema_version": 2,
"currency": "USD",
},
}
got := sanitizeAdminPaymentOrderForResponse(order)
if got == nil {
t.Fatal("expected sanitized order")
}
if got.Currency != "USD" {
t.Fatalf("expected currency USD, got %q", got.Currency)
}
body, err := json.Marshal(got)
if err != nil {
t.Fatalf("marshal sanitized order: %v", err)
}
if strings.Contains(string(body), "provider_snapshot") {
t.Fatalf("expected provider_snapshot to be omitted, got %s", string(body))
}
}