统一金额分制重构

This commit is contained in:
yml
2026-06-09 19:04:11 +08:00
parent 87673288ef
commit 5cd3ead4bd
69 changed files with 886 additions and 1277 deletions
+54 -54
View File
@@ -26,35 +26,35 @@ type DashboardDTO struct {
}
type FinanceSummaryDTO struct {
TotalFlowAmountCent int64 `json:"total_flow_amount_cent"`
TotalRefundAmountCent int64 `json:"total_refund_amount_cent"`
PendingRefundAmountCent int64 `json:"pending_refund_amount_cent"`
ChannelNetAmountCent int64 `json:"channel_net_amount_cent"`
PlatformIncomeAmount float64 `json:"platform_income_amount"`
OwnerShouldIncomeAmount float64 `json:"owner_should_income_amount"`
OwnerWalletIncomeAmount float64 `json:"owner_wallet_income_amount"`
SettlementDiffAmount float64 `json:"settlement_diff_amount"`
SuccessfulPayCount int64 `json:"successful_pay_count"`
SuccessfulRefundCount int64 `json:"successful_refund_count"`
PendingRefundCount int64 `json:"pending_refund_count"`
SettledOrderCount int64 `json:"settled_order_count"`
FinancialExceptionCount int64 `json:"financial_exception_count"`
TotalFlowAmountCent int64 `json:"total_flow_amount_cent"`
TotalRefundAmountCent int64 `json:"total_refund_amount_cent"`
PendingRefundAmountCent int64 `json:"pending_refund_amount_cent"`
ChannelNetAmountCent int64 `json:"channel_net_amount_cent"`
PlatformIncomeAmountCent int64 `json:"platform_income_amount_cent"`
OwnerShouldIncomeAmountCent int64 `json:"owner_should_income_amount_cent"`
OwnerWalletIncomeAmountCent int64 `json:"owner_wallet_income_amount_cent"`
SettlementDiffAmountCent int64 `json:"settlement_diff_amount_cent"`
SuccessfulPayCount int64 `json:"successful_pay_count"`
SuccessfulRefundCount int64 `json:"successful_refund_count"`
PendingRefundCount int64 `json:"pending_refund_count"`
SettledOrderCount int64 `json:"settled_order_count"`
FinancialExceptionCount int64 `json:"financial_exception_count"`
}
type FinanceDailyDTO struct {
Date string `json:"date"`
TotalFlowAmountCent int64 `json:"total_flow_amount_cent"`
TotalRefundAmountCent int64 `json:"total_refund_amount_cent"`
PendingRefundAmountCent int64 `json:"pending_refund_amount_cent"`
ChannelNetAmountCent int64 `json:"channel_net_amount_cent"`
PlatformIncomeAmount float64 `json:"platform_income_amount"`
OwnerShouldIncomeAmount float64 `json:"owner_should_income_amount"`
OwnerWalletIncomeAmount float64 `json:"owner_wallet_income_amount"`
SettlementDiffAmount float64 `json:"settlement_diff_amount"`
SuccessfulPayCount int64 `json:"successful_pay_count"`
SuccessfulRefundCount int64 `json:"successful_refund_count"`
PendingRefundCount int64 `json:"pending_refund_count"`
SettledOrderCount int64 `json:"settled_order_count"`
Date string `json:"date"`
TotalFlowAmountCent int64 `json:"total_flow_amount_cent"`
TotalRefundAmountCent int64 `json:"total_refund_amount_cent"`
PendingRefundAmountCent int64 `json:"pending_refund_amount_cent"`
ChannelNetAmountCent int64 `json:"channel_net_amount_cent"`
PlatformIncomeAmountCent int64 `json:"platform_income_amount_cent"`
OwnerShouldIncomeAmountCent int64 `json:"owner_should_income_amount_cent"`
OwnerWalletIncomeAmountCent int64 `json:"owner_wallet_income_amount_cent"`
SettlementDiffAmountCent int64 `json:"settlement_diff_amount_cent"`
SuccessfulPayCount int64 `json:"successful_pay_count"`
SuccessfulRefundCount int64 `json:"successful_refund_count"`
PendingRefundCount int64 `json:"pending_refund_count"`
SettledOrderCount int64 `json:"settled_order_count"`
}
type PaginatedResult struct {
@@ -65,32 +65,32 @@ type PaginatedResult struct {
}
type FinanceDetailDTO struct {
OrderID uint64 `json:"order_id"`
OrderNo string `json:"order_no"`
OrderStatus string `json:"order_status"`
SettlementStatus string `json:"settlement_status"`
RefundStatus string `json:"refund_status"`
RenterID uint64 `json:"renter_id"`
RenterPhone string `json:"renter_phone"`
RenterNickname string `json:"renter_nickname"`
OwnerID uint64 `json:"owner_id"`
OwnerPhone string `json:"owner_phone"`
OwnerNickname string `json:"owner_nickname"`
OrderRentAmount float64 `json:"order_rent_amount"`
OrderDepositAmount float64 `json:"order_deposit_amount"`
CheckoutRentAmount float64 `json:"checkout_rent_amount"`
CheckoutRenterRefund float64 `json:"checkout_renter_refund"`
CheckoutOwnerIncome float64 `json:"checkout_owner_income"`
CheckoutPlatformFee float64 `json:"checkout_platform_fee"`
OwnerWalletIncomeAmount float64 `json:"owner_wallet_income_amount"`
PaidAmountCent int64 `json:"paid_amount_cent"`
RefundedAmountCent int64 `json:"refunded_amount_cent"`
RefundingAmountCent int64 `json:"refunding_amount_cent"`
FailedRefundAmountCent int64 `json:"failed_refund_amount_cent"`
ChannelNetAmountCent int64 `json:"channel_net_amount_cent"`
PlatformNetAmount float64 `json:"platform_net_amount"`
SettlementDiffAmount float64 `json:"settlement_diff_amount"`
FinanceStatus string `json:"finance_status"`
CreatedAt time.Time `json:"created_at"`
SettledAt *time.Time `json:"settled_at,omitempty"`
OrderID uint64 `json:"order_id"`
OrderNo string `json:"order_no"`
OrderStatus string `json:"order_status"`
SettlementStatus string `json:"settlement_status"`
RefundStatus string `json:"refund_status"`
RenterID uint64 `json:"renter_id"`
RenterPhone string `json:"renter_phone"`
RenterNickname string `json:"renter_nickname"`
OwnerID uint64 `json:"owner_id"`
OwnerPhone string `json:"owner_phone"`
OwnerNickname string `json:"owner_nickname"`
OrderRentAmountCent int64 `json:"order_rent_amount_cent"`
OrderDepositAmountCent int64 `json:"order_deposit_amount_cent"`
CheckoutRentAmountCent int64 `json:"checkout_rent_amount_cent"`
CheckoutRenterRefundCent int64 `json:"checkout_renter_refund_cent"`
CheckoutOwnerIncomeCent int64 `json:"checkout_owner_income_cent"`
CheckoutPlatformFeeCent int64 `json:"checkout_platform_fee_cent"`
OwnerWalletIncomeAmountCent int64 `json:"owner_wallet_income_amount_cent"`
PaidAmountCent int64 `json:"paid_amount_cent"`
RefundedAmountCent int64 `json:"refunded_amount_cent"`
RefundingAmountCent int64 `json:"refunding_amount_cent"`
FailedRefundAmountCent int64 `json:"failed_refund_amount_cent"`
ChannelNetAmountCent int64 `json:"channel_net_amount_cent"`
PlatformNetAmountCent int64 `json:"platform_net_amount_cent"`
SettlementDiffAmountCent int64 `json:"settlement_diff_amount_cent"`
FinanceStatus string `json:"finance_status"`
CreatedAt time.Time `json:"created_at"`
SettledAt *time.Time `json:"settled_at,omitempty"`
}
@@ -1,11 +1,9 @@
package adminfinance
import (
"math"
"time"
"hfb_sys/backend/internal/timeutil"
"hfb_sys/backend/pkg/money"
"gorm.io/gorm"
)
@@ -71,10 +69,10 @@ func (r *Repository) summary(query DashboardQuery) (*FinanceSummaryDTO, error) {
var settlement settlementSummaryRow
if err := r.db.Table("rental_orders AS ro").
Select(`COALESCE(SUM(oc.platform_fee), 0) AS platform_income_amount,
COALESCE(SUM(oc.owner_income_amount), 0) AS owner_should_income_amount,
COALESCE(SUM(COALESCE(w.owner_wallet_income_amount, 0)), 0) AS owner_wallet_income_amount,
COUNT(ro.id) AS settled_order_count`).
Select(`COALESCE(SUM(oc.platform_fee_cent), 0) AS platform_income_amount_cent,
COALESCE(SUM(oc.owner_income_amount_cent), 0) AS owner_should_income_amount_cent,
COALESCE(SUM(COALESCE(w.owner_wallet_income_amount_cent, 0)), 0) AS owner_wallet_income_amount_cent,
COUNT(ro.id) AS settled_order_count`).
Joins("JOIN order_checkouts AS oc ON oc.order_id = ro.id AND oc.status = 'accepted'").
Joins("LEFT JOIN (?) AS w ON w.order_id = ro.id", ownerWalletIncomeSubquery(r.db)).
Where("ro.settled_at >= ? AND ro.settled_at <= ?", query.StartDate, query.EndDate).
@@ -94,19 +92,19 @@ func (r *Repository) summary(query DashboardQuery) (*FinanceSummaryDTO, error) {
}
return &FinanceSummaryDTO{
TotalFlowAmountCent: payment.TotalFlowAmountCent,
TotalRefundAmountCent: payment.TotalRefundAmountCent,
PendingRefundAmountCent: payment.PendingRefundAmountCent,
ChannelNetAmountCent: payment.TotalFlowAmountCent - payment.TotalRefundAmountCent,
PlatformIncomeAmount: money.Round(settlement.PlatformIncomeAmount),
OwnerShouldIncomeAmount: money.Round(settlement.OwnerShouldIncomeAmount),
OwnerWalletIncomeAmount: money.Round(settlement.OwnerWalletIncomeAmount),
SettlementDiffAmount: money.Round(settlement.OwnerShouldIncomeAmount - settlement.OwnerWalletIncomeAmount),
SuccessfulPayCount: payment.SuccessfulPayCount,
SuccessfulRefundCount: payment.SuccessfulRefundCount,
PendingRefundCount: payment.PendingRefundCount,
SettledOrderCount: settlement.SettledOrderCount,
FinancialExceptionCount: exceptionCount,
TotalFlowAmountCent: payment.TotalFlowAmountCent,
TotalRefundAmountCent: payment.TotalRefundAmountCent,
PendingRefundAmountCent: payment.PendingRefundAmountCent,
ChannelNetAmountCent: payment.TotalFlowAmountCent - payment.TotalRefundAmountCent,
PlatformIncomeAmountCent: settlement.PlatformIncomeAmountCent,
OwnerShouldIncomeAmountCent: settlement.OwnerShouldIncomeAmountCent,
OwnerWalletIncomeAmountCent: settlement.OwnerWalletIncomeAmountCent,
SettlementDiffAmountCent: settlement.OwnerShouldIncomeAmountCent - settlement.OwnerWalletIncomeAmountCent,
SuccessfulPayCount: payment.SuccessfulPayCount,
SuccessfulRefundCount: payment.SuccessfulRefundCount,
PendingRefundCount: payment.PendingRefundCount,
SettledOrderCount: settlement.SettledOrderCount,
FinancialExceptionCount: exceptionCount,
}, nil
}
@@ -130,10 +128,10 @@ func (r *Repository) dailyItems(query DashboardQuery) ([]FinanceDailyDTO, error)
settlements := make([]dailySettlementRow, 0)
if err := r.db.Table("rental_orders AS ro").
Select(`DATE(ro.settled_at) AS date,
COALESCE(SUM(oc.platform_fee), 0) AS platform_income_amount,
COALESCE(SUM(oc.owner_income_amount), 0) AS owner_should_income_amount,
COALESCE(SUM(COALESCE(w.owner_wallet_income_amount, 0)), 0) AS owner_wallet_income_amount,
COUNT(ro.id) AS settled_order_count`).
COALESCE(SUM(oc.platform_fee_cent), 0) AS platform_income_amount_cent,
COALESCE(SUM(oc.owner_income_amount_cent), 0) AS owner_should_income_amount_cent,
COALESCE(SUM(COALESCE(w.owner_wallet_income_amount_cent, 0)), 0) AS owner_wallet_income_amount_cent,
COUNT(ro.id) AS settled_order_count`).
Joins("JOIN order_checkouts AS oc ON oc.order_id = ro.id AND oc.status = 'accepted'").
Joins("LEFT JOIN (?) AS w ON w.order_id = ro.id", ownerWalletIncomeSubquery(r.db)).
Where("ro.settled_at >= ? AND ro.settled_at <= ?", query.StartDate, query.EndDate).
@@ -162,10 +160,10 @@ func (r *Repository) dailyItems(query DashboardQuery) ([]FinanceDailyDTO, error)
for _, row := range settlements {
item := itemsByDate[row.Date]
item.Date = row.Date
item.PlatformIncomeAmount = money.Round(row.PlatformIncomeAmount)
item.OwnerShouldIncomeAmount = money.Round(row.OwnerShouldIncomeAmount)
item.OwnerWalletIncomeAmount = money.Round(row.OwnerWalletIncomeAmount)
item.SettlementDiffAmount = money.Round(row.OwnerShouldIncomeAmount - row.OwnerWalletIncomeAmount)
item.PlatformIncomeAmountCent = row.PlatformIncomeAmountCent
item.OwnerShouldIncomeAmountCent = row.OwnerShouldIncomeAmountCent
item.OwnerWalletIncomeAmountCent = row.OwnerWalletIncomeAmountCent
item.SettlementDiffAmountCent = row.OwnerShouldIncomeAmountCent - row.OwnerWalletIncomeAmountCent
item.SettledOrderCount = row.SettledOrderCount
itemsByDate[row.Date] = item
}
@@ -182,23 +180,23 @@ func (r *Repository) financeDetailBaseQuery(query DetailQuery) *gorm.DB {
Select(`ro.id AS order_id, ro.order_no, ro.status AS order_status, ro.settlement_status, ro.refund_status,
ro.renter_id, COALESCE(ru.phone, '') AS renter_phone, COALESCE(ru.nickname, '') AS renter_nickname,
ro.owner_id, COALESCE(ou.phone, '') AS owner_phone, COALESCE(ou.nickname, '') AS owner_nickname,
ro.rent_amount AS order_rent_amount, ro.deposit_amount AS order_deposit_amount,
COALESCE(oc.rent_amount, 0) AS checkout_rent_amount,
COALESCE(oc.renter_refund_amount, 0) AS checkout_renter_refund,
COALESCE(oc.owner_income_amount, 0) AS checkout_owner_income,
COALESCE(oc.platform_fee, 0) AS checkout_platform_fee,
COALESCE(w.owner_wallet_income_amount, 0) AS owner_wallet_income_amount,
COALESCE(p.paid_amount_cent, 0) AS paid_amount_cent,
COALESCE(p.refunded_amount_cent, 0) AS refunded_amount_cent,
COALESCE(p.refunding_amount_cent, 0) AS refunding_amount_cent,
COALESCE(p.failed_refund_amount_cent, 0) AS failed_refund_amount_cent,
COALESCE(p.paid_amount_cent, 0) - COALESCE(p.refunded_amount_cent, 0) AS channel_net_amount_cent,
COALESCE(oc.platform_fee, 0) + ((COALESCE(oc.owner_income_amount, 0) - COALESCE(w.owner_wallet_income_amount, 0))) AS platform_net_amount,
COALESCE(oc.owner_income_amount, 0) - COALESCE(w.owner_wallet_income_amount, 0) AS settlement_diff_amount,
CASE
WHEN COALESCE(p.failed_refund_amount_cent, 0) > 0 THEN 'refund_failed'
WHEN COALESCE(p.refunding_amount_cent, 0) > 0 OR ro.refund_status = 'refunding' THEN 'refund_pending'
WHEN ABS(COALESCE(oc.owner_income_amount, 0) - COALESCE(w.owner_wallet_income_amount, 0)) >= 0.05 THEN 'settlement_diff'
ro.rent_amount_cent AS order_rent_amount_cent, ro.deposit_amount_cent AS order_deposit_amount_cent,
COALESCE(oc.rent_amount_cent, 0) AS checkout_rent_amount_cent,
COALESCE(oc.renter_refund_amount_cent, 0) AS checkout_renter_refund_cent,
COALESCE(oc.owner_income_amount_cent, 0) AS checkout_owner_income_cent,
COALESCE(oc.platform_fee_cent, 0) AS checkout_platform_fee_cent,
COALESCE(w.owner_wallet_income_amount_cent, 0) AS owner_wallet_income_amount_cent,
COALESCE(p.paid_amount_cent, 0) AS paid_amount_cent,
COALESCE(p.refunded_amount_cent, 0) AS refunded_amount_cent,
COALESCE(p.refunding_amount_cent, 0) AS refunding_amount_cent,
COALESCE(p.failed_refund_amount_cent, 0) AS failed_refund_amount_cent,
COALESCE(p.paid_amount_cent, 0) - COALESCE(p.refunded_amount_cent, 0) AS channel_net_amount_cent,
COALESCE(oc.platform_fee_cent, 0) + ((COALESCE(oc.owner_income_amount_cent, 0) - COALESCE(w.owner_wallet_income_amount_cent, 0))) AS platform_net_amount_cent,
COALESCE(oc.owner_income_amount_cent, 0) - COALESCE(w.owner_wallet_income_amount_cent, 0) AS settlement_diff_amount_cent,
CASE
WHEN COALESCE(p.failed_refund_amount_cent, 0) > 0 THEN 'refund_failed'
WHEN COALESCE(p.refunding_amount_cent, 0) > 0 OR ro.refund_status = 'refunding' THEN 'refund_pending'
WHEN ABS(COALESCE(oc.owner_income_amount_cent, 0) - COALESCE(w.owner_wallet_income_amount_cent, 0)) >= 5 THEN 'settlement_diff'
ELSE 'normal'
END AS finance_status,
ro.created_at, ro.settled_at`).
@@ -250,7 +248,7 @@ func orderPaymentSubquery(db *gorm.DB) *gorm.DB {
func ownerWalletIncomeSubquery(db *gorm.DB) *gorm.DB {
return db.Table("wallet_ledger").
Select("order_id, COALESCE(SUM(amount), 0) AS owner_wallet_income_amount").
Select("order_id, COALESCE(SUM(amount_cent), 0) AS owner_wallet_income_amount_cent").
Where("direction = ? AND biz_type IN ? AND order_id IS NOT NULL", "in", []string{"owner_income", "deposit_compensation"}).
Group("order_id")
}
@@ -283,10 +281,10 @@ type paymentSummaryRow struct {
}
type settlementSummaryRow struct {
PlatformIncomeAmount float64
OwnerShouldIncomeAmount float64
OwnerWalletIncomeAmount float64
SettledOrderCount int64
PlatformIncomeAmountCent int64
OwnerShouldIncomeAmountCent int64
OwnerWalletIncomeAmountCent int64
SettledOrderCount int64
}
type dailyPaymentRow struct {
@@ -300,81 +298,88 @@ type dailyPaymentRow struct {
}
type dailySettlementRow struct {
Date string
PlatformIncomeAmount float64
OwnerShouldIncomeAmount float64
OwnerWalletIncomeAmount float64
SettledOrderCount int64
Date string
PlatformIncomeAmountCent int64
OwnerShouldIncomeAmountCent int64
OwnerWalletIncomeAmountCent int64
SettledOrderCount int64
}
type financeDetailRow struct {
OrderID uint64
OrderNo string
OrderStatus string
SettlementStatus string
RefundStatus string
RenterID uint64
RenterPhone string
RenterNickname string
OwnerID uint64
OwnerPhone string
OwnerNickname string
OrderRentAmount float64
OrderDepositAmount float64
CheckoutRentAmount float64
CheckoutRenterRefund float64
CheckoutOwnerIncome float64
CheckoutPlatformFee float64
OwnerWalletIncomeAmount float64
PaidAmountCent int64
RefundedAmountCent int64
RefundingAmountCent int64
FailedRefundAmountCent int64
ChannelNetAmountCent int64
PlatformNetAmount float64
SettlementDiffAmount float64
FinanceStatus string
CreatedAt time.Time
SettledAt *time.Time
OrderID uint64
OrderNo string
OrderStatus string
SettlementStatus string
RefundStatus string
RenterID uint64
RenterPhone string
RenterNickname string
OwnerID uint64
OwnerPhone string
OwnerNickname string
OrderRentAmountCent int64
OrderDepositAmountCent int64
CheckoutRentAmountCent int64
CheckoutRenterRefundCent int64
CheckoutOwnerIncomeCent int64
CheckoutPlatformFeeCent int64
OwnerWalletIncomeAmountCent int64
PaidAmountCent int64
RefundedAmountCent int64
RefundingAmountCent int64
FailedRefundAmountCent int64
ChannelNetAmountCent int64
PlatformNetAmountCent int64
SettlementDiffAmountCent int64
FinanceStatus string
CreatedAt time.Time
SettledAt *time.Time
}
func (r financeDetailRow) toDTO() FinanceDetailDTO {
diff := money.Round(r.SettlementDiffAmount)
diff := r.SettlementDiffAmountCent
status := r.FinanceStatus
if status == "" {
status = "normal"
}
if math.Abs(diff) < 0.05 && status == "settlement_diff" {
if absCent(diff) < 5 && status == "settlement_diff" {
status = "normal"
}
return FinanceDetailDTO{
OrderID: r.OrderID,
OrderNo: r.OrderNo,
OrderStatus: r.OrderStatus,
SettlementStatus: r.SettlementStatus,
RefundStatus: r.RefundStatus,
RenterID: r.RenterID,
RenterPhone: r.RenterPhone,
RenterNickname: r.RenterNickname,
OwnerID: r.OwnerID,
OwnerPhone: r.OwnerPhone,
OwnerNickname: r.OwnerNickname,
OrderRentAmount: money.Round(r.OrderRentAmount),
OrderDepositAmount: money.Round(r.OrderDepositAmount),
CheckoutRentAmount: money.Round(r.CheckoutRentAmount),
CheckoutRenterRefund: money.Round(r.CheckoutRenterRefund),
CheckoutOwnerIncome: money.Round(r.CheckoutOwnerIncome),
CheckoutPlatformFee: money.Round(r.CheckoutPlatformFee),
OwnerWalletIncomeAmount: money.Round(r.OwnerWalletIncomeAmount),
PaidAmountCent: r.PaidAmountCent,
RefundedAmountCent: r.RefundedAmountCent,
RefundingAmountCent: r.RefundingAmountCent,
FailedRefundAmountCent: r.FailedRefundAmountCent,
ChannelNetAmountCent: r.ChannelNetAmountCent,
PlatformNetAmount: money.Round(r.PlatformNetAmount),
SettlementDiffAmount: diff,
FinanceStatus: status,
CreatedAt: r.CreatedAt,
SettledAt: r.SettledAt,
OrderID: r.OrderID,
OrderNo: r.OrderNo,
OrderStatus: r.OrderStatus,
SettlementStatus: r.SettlementStatus,
RefundStatus: r.RefundStatus,
RenterID: r.RenterID,
RenterPhone: r.RenterPhone,
RenterNickname: r.RenterNickname,
OwnerID: r.OwnerID,
OwnerPhone: r.OwnerPhone,
OwnerNickname: r.OwnerNickname,
OrderRentAmountCent: r.OrderRentAmountCent,
OrderDepositAmountCent: r.OrderDepositAmountCent,
CheckoutRentAmountCent: r.CheckoutRentAmountCent,
CheckoutRenterRefundCent: r.CheckoutRenterRefundCent,
CheckoutOwnerIncomeCent: r.CheckoutOwnerIncomeCent,
CheckoutPlatformFeeCent: r.CheckoutPlatformFeeCent,
OwnerWalletIncomeAmountCent: r.OwnerWalletIncomeAmountCent,
PaidAmountCent: r.PaidAmountCent,
RefundedAmountCent: r.RefundedAmountCent,
RefundingAmountCent: r.RefundingAmountCent,
FailedRefundAmountCent: r.FailedRefundAmountCent,
ChannelNetAmountCent: r.ChannelNetAmountCent,
PlatformNetAmountCent: r.PlatformNetAmountCent,
SettlementDiffAmountCent: diff,
FinanceStatus: status,
CreatedAt: r.CreatedAt,
SettledAt: r.SettledAt,
}
}
func absCent(value int64) int64 {
if value < 0 {
return -value
}
return value
}