diff --git a/backend/internal/modules/adminfinance/dashboard.go b/backend/internal/modules/adminfinance/dashboard.go index 5794eec..2a46026 100644 --- a/backend/internal/modules/adminfinance/dashboard.go +++ b/backend/internal/modules/adminfinance/dashboard.go @@ -222,6 +222,10 @@ func (r *Repository) dailyItems(ctx context.Context, query DashboardQuery) ([]Fi Scan(&payments).Error; err != nil { return nil, err } + paidOrders, err := r.dailyPaidOrders(ctx, query) + if err != nil { + return nil, err + } settlements := make([]dailySettlementRow, 0) if err := db.Table("rental_orders AS ro"). @@ -307,6 +311,13 @@ func (r *Repository) dailyItems(ctx context.Context, query DashboardQuery) ([]Fi item.PendingRefundCount = row.PendingRefundCount itemsByDate[date] = item } + for _, row := range paidOrders { + date := dailyDateKey(row.Date) + item := itemsByDate[date] + item.Date = date + item.PaidOrderCount = row.PaidOrderCount + itemsByDate[date] = item + } for _, row := range settlements { date := dailyDateKey(row.Date) item := itemsByDate[date] @@ -361,6 +372,19 @@ func (r *Repository) dailyItems(ctx context.Context, query DashboardQuery) ([]Fi return items, nil } +// dailyPaidOrders 仅统计普通租赁订单的成功支付,按实际支付完成时间归集。 +func (r *Repository) dailyPaidOrders(ctx context.Context, query DashboardQuery) ([]dailyPaidOrderRow, error) { + items := make([]dailyPaidOrderRow, 0) + err := r.db.WithContext(ctx).Table("payment_orders AS po"). + Select(`DATE(po.paid_at) AS date, + COUNT(DISTINCT po.order_id) AS paid_order_count`). + Where("po.biz_type = ? AND po.status = ?", "order_pay", "paid"). + Where("po.paid_at >= ? AND po.paid_at <= ?", query.StartDate, query.EndDate). + Group("DATE(po.paid_at)"). + Scan(&items).Error + return items, err +} + type paymentSummaryRow struct { TotalFlowAmountCent int64 TotalRefundAmountCent int64 @@ -398,6 +422,11 @@ type dailyPaymentRow struct { PendingRefundCount int64 } +type dailyPaidOrderRow struct { + Date string + PaidOrderCount int64 +} + type dailySettlementRow struct { Date string PlatformIncomeAmountCent int64 diff --git a/backend/internal/modules/adminfinance/dashboard_test.go b/backend/internal/modules/adminfinance/dashboard_test.go new file mode 100644 index 0000000..a965334 --- /dev/null +++ b/backend/internal/modules/adminfinance/dashboard_test.go @@ -0,0 +1,65 @@ +package adminfinance + +import ( + "testing" + "time" + + "hfb_sys/backend/internal/timeutil" + + "gorm.io/driver/sqlite" + "gorm.io/gorm" + "gorm.io/gorm/logger" +) + +func TestDailyPaidOrdersUsePaidAtAndExcludeMohong(t *testing.T) { + db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{Logger: logger.Default.LogMode(logger.Silent)}) + if err != nil { + t.Fatalf("打开测试数据库失败: %v", err) + } + if err := db.Exec(`CREATE TABLE payment_orders ( + id INTEGER PRIMARY KEY, + order_id INTEGER NOT NULL, + biz_type TEXT NOT NULL, + status TEXT NOT NULL, + paid_at DATETIME NULL + )`).Error; err != nil { + t.Fatalf("创建支付单表失败: %v", err) + } + + loc := timeutil.ShanghaiLocation() + day := func(value int) time.Time { return time.Date(2026, 8, value, 10, 0, 0, 0, loc) } + rows := [][]any{ + {1, 101, "order_pay", "paid", day(2)}, + {2, 101, "order_pay", "paid", day(2)}, + {3, 102, "order_pay", "paid", day(3)}, + {4, 201, "mohong_pay", "paid", day(2)}, + {5, 103, "order_pay", "created", nil}, + {6, 104, "order_pay", "paid", day(1)}, + } + for _, row := range rows { + if err := db.Exec(`INSERT INTO payment_orders (id, order_id, biz_type, status, paid_at) + VALUES (?, ?, ?, ?, ?)`, row...).Error; err != nil { + t.Fatalf("写入支付单失败: %v", err) + } + } + + repo := NewRepository(db) + query := DashboardQuery{ + StartDate: time.Date(2026, 8, 2, 0, 0, 0, 0, loc), + EndDate: time.Date(2026, 8, 3, 23, 59, 59, 0, loc), + } + result, err := repo.dailyPaidOrders(t.Context(), query) + if err != nil { + t.Fatalf("读取每日已付款订单失败: %v", err) + } + if len(result) != 2 { + t.Fatalf("每日已付款记录数 = %d, want 2", len(result)) + } + counts := make(map[string]int64, len(result)) + for _, row := range result { + counts[row.Date] = row.PaidOrderCount + } + if counts["2026-08-02"] != 1 || counts["2026-08-03"] != 1 { + t.Fatalf("每日已付款订单 = %#v, want 8月2日和8月3日均为1", counts) + } +} diff --git a/backend/internal/modules/adminfinance/dto.go b/backend/internal/modules/adminfinance/dto.go index ce74291..ade46f5 100644 --- a/backend/internal/modules/adminfinance/dto.go +++ b/backend/internal/modules/adminfinance/dto.go @@ -216,6 +216,7 @@ type FinanceDailyDTO struct { SettledOrderCount int64 `json:"settled_order_count"` PickupCompletedCount int64 `json:"pickup_completed_count"` SalesCount int64 `json:"sales_count"` + PaidOrderCount int64 `json:"paid_order_count"` OfflineSettlementPendingCount int64 `json:"offline_settlement_pending_count"` OfflineSettlementPaidAmountCent int64 `json:"offline_settlement_paid_amount_cent"` OfflineSettlementPaidCount int64 `json:"offline_settlement_paid_count"` diff --git a/backend/migrations/000047_finance_daily_paid_order_stats.sql b/backend/migrations/000047_finance_daily_paid_order_stats.sql new file mode 100644 index 0000000..1b38e1f --- /dev/null +++ b/backend/migrations/000047_finance_daily_paid_order_stats.sql @@ -0,0 +1,9 @@ +-- +goose Up + +ALTER TABLE payment_orders + ADD KEY idx_payment_orders_biz_status_paid_at_order (biz_type, status, paid_at, order_id); + +-- +goose Down + +ALTER TABLE payment_orders + DROP KEY idx_payment_orders_biz_status_paid_at_order; diff --git a/frontend/src/features/admin/api/adminFinance.ts b/frontend/src/features/admin/api/adminFinance.ts index d23f0e7..4a8581f 100644 --- a/frontend/src/features/admin/api/adminFinance.ts +++ b/frontend/src/features/admin/api/adminFinance.ts @@ -53,6 +53,7 @@ export interface FinanceDailyItem { settled_order_count: number pickup_completed_count: number sales_count: number + paid_order_count: number offline_settlement_pending_count: number offline_settlement_paid_amount_cent: number offline_settlement_paid_count: number diff --git a/frontend/src/features/admin/views/AdminFinanceDashboardView.vue b/frontend/src/features/admin/views/AdminFinanceDashboardView.vue index 8b1fbd3..ddc5385 100644 --- a/frontend/src/features/admin/views/AdminFinanceDashboardView.vue +++ b/frontend/src/features/admin/views/AdminFinanceDashboardView.vue @@ -255,9 +255,12 @@ function diffType(value: number) { - + + + +