package adminfinance import "context" type dailyOperatingExpenseRow struct { Date string AmountCent int64 Count int64 } func (r *Repository) dailyOperatingExpenses(ctx context.Context, query DashboardQuery) ([]dailyOperatingExpenseRow, error) { rows := make([]dailyOperatingExpenseRow, 0) err := r.db.WithContext(ctx).Table("operating_expenses"). Select(`DATE(occurred_at) AS date, COALESCE(SUM(amount_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("status = ?", "paid"). Where("occurred_at >= ? AND occurred_at <= ?", query.StartDate, query.EndDate). Group("DATE(occurred_at)"). Scan(&rows).Error return rows, err } // disbursementSummary 使用实际确认打款时间统计区间出款,同时返回不受日期限制的当前待办。 func (r *Repository) disbursementSummary(ctx context.Context, query DashboardQuery) (*DisbursementSummaryDTO, error) { db := r.db.WithContext(ctx) var offlinePaid struct { AmountCent int64 Count int64 } if err := db.Table("rental_orders"). Select(`COALESCE(SUM(offline_settlement_amount_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("settlement_mode = ?", "platform_managed"). Where("offline_settlement_status = ?", "settled"). Where("offline_settled_at >= ? AND offline_settled_at <= ?", query.StartDate, query.EndDate). Scan(&offlinePaid).Error; err != nil { return nil, err } var pickupPaid struct { AmountCent int64 Count int64 } if err := db.Table("admin_pickups"). Select(`COALESCE(SUM(settle_amount_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("settlement_mode = ?", "platform_managed"). Where("offline_settlement_status = ?", "settled"). Where("offline_settled_at >= ? AND offline_settled_at <= ?", query.StartDate, query.EndDate). Scan(&pickupPaid).Error; err != nil { return nil, err } var pickupAdjustmentPaid struct { AmountCent int64 Count int64 } if err := db.Table("admin_pickup_financial_adjustments"). Select(`COALESCE(SUM(settle_delta_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("settlement_mode = ?", "platform_managed"). Where("status = ?", "settled"). Where("settle_delta_cent > 0"). Where("settled_at >= ? AND settled_at <= ?", query.StartDate, query.EndDate). Scan(&pickupAdjustmentPaid).Error; err != nil { return nil, err } var offlinePending struct { AmountCent int64 Count int64 } if err := db.Table("rental_orders"). Select(`COALESCE(SUM(offline_settlement_amount_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("settlement_mode = ?", "platform_managed"). Where("offline_settlement_status = ?", "pending"). Where("offline_settlement_amount_cent > 0"). Scan(&offlinePending).Error; err != nil { return nil, err } var pickupPending struct { AmountCent int64 Count int64 } if err := db.Table("admin_pickups"). Select(`COALESCE(SUM(settle_amount_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("settlement_mode = ?", "platform_managed"). Where("offline_settlement_status = ?", "pending"). Where("settle_amount_cent > 0"). Scan(&pickupPending).Error; err != nil { return nil, err } var pickupAdjustmentPending struct { AmountCent int64 Count int64 } if err := db.Table("admin_pickup_financial_adjustments"). Select(`COALESCE(SUM(settle_delta_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("settlement_mode = ?", "platform_managed"). Where("status = ?", "pending"). Where("settle_delta_cent > 0"). Scan(&pickupAdjustmentPending).Error; err != nil { return nil, err } // 付款、处理中、待审核三类提现在一次条件聚合中完成,避免同一张表重复扫描。 var withdrawal struct { PaidAmountCent int64 PaidFeeAmountCent int64 PaidActualAmountCent int64 PaidCount int64 PendingAmountCent int64 PendingCount int64 ReviewAmountCent int64 ReviewCount int64 } if err := db.Table("withdrawal_requests").Select(` COALESCE(SUM(CASE WHEN status = 'completed' AND paid_at >= ? AND paid_at <= ? THEN amount_cent ELSE 0 END), 0) AS paid_amount_cent, COALESCE(SUM(CASE WHEN status = 'completed' AND paid_at >= ? AND paid_at <= ? THEN fee_cent ELSE 0 END), 0) AS paid_fee_amount_cent, COALESCE(SUM(CASE WHEN status = 'completed' AND paid_at >= ? AND paid_at <= ? THEN actual_amount_cent ELSE 0 END), 0) AS paid_actual_amount_cent, COALESCE(SUM(CASE WHEN status = 'completed' AND paid_at >= ? AND paid_at <= ? THEN 1 ELSE 0 END), 0) AS paid_count, COALESCE(SUM(CASE WHEN status = 'processing' THEN actual_amount_cent ELSE 0 END), 0) AS pending_amount_cent, COALESCE(SUM(CASE WHEN status = 'processing' THEN 1 ELSE 0 END), 0) AS pending_count, COALESCE(SUM(CASE WHEN status = 'pending' THEN actual_amount_cent ELSE 0 END), 0) AS review_amount_cent, COALESCE(SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END), 0) AS review_count`, query.StartDate, query.EndDate, query.StartDate, query.EndDate, query.StartDate, query.EndDate, query.StartDate, query.EndDate). Scan(&withdrawal).Error; err != nil { return nil, err } var manualPaid struct { AmountCent int64 Count int64 } if err := db.Table("manual_disbursements"). Select(`COALESCE(SUM(amount_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("status = ?", "paid"). Where("paid_at >= ? AND paid_at <= ?", query.StartDate, query.EndDate). Scan(&manualPaid).Error; err != nil { return nil, err } return &DisbursementSummaryDTO{ PaidAmountCent: offlinePaid.AmountCent + pickupPaid.AmountCent + pickupAdjustmentPaid.AmountCent + withdrawal.PaidActualAmountCent + manualPaid.AmountCent, PaidCount: offlinePaid.Count + pickupPaid.Count + pickupAdjustmentPaid.Count + withdrawal.PaidCount + manualPaid.Count, PendingPaymentAmountCent: offlinePending.AmountCent + pickupPending.AmountCent + pickupAdjustmentPending.AmountCent + withdrawal.PendingAmountCent, PendingPaymentCount: offlinePending.Count + pickupPending.Count + pickupAdjustmentPending.Count + withdrawal.PendingCount, OfflineSettlementPaidAmountCent: offlinePaid.AmountCent + pickupPaid.AmountCent + pickupAdjustmentPaid.AmountCent, OfflineSettlementPaidCount: offlinePaid.Count + pickupPaid.Count + pickupAdjustmentPaid.Count, OfflineSettlementPendingAmountCent: offlinePending.AmountCent, OfflineSettlementPendingCount: offlinePending.Count, WithdrawalAmountCent: withdrawal.PaidAmountCent, WithdrawalFeeAmountCent: withdrawal.PaidFeeAmountCent, WithdrawalPaidAmountCent: withdrawal.PaidActualAmountCent, WithdrawalPaidCount: withdrawal.PaidCount, WithdrawalPendingAmountCent: withdrawal.PendingAmountCent, WithdrawalPendingCount: withdrawal.PendingCount, WithdrawalReviewAmountCent: withdrawal.ReviewAmountCent, WithdrawalReviewCount: withdrawal.ReviewCount, ManualPaidAmountCent: manualPaid.AmountCent, ManualPaidCount: manualPaid.Count, }, nil } func (r *Repository) dailyDisbursements(ctx context.Context, query DashboardQuery) ([]dailyDisbursementRow, error) { db := r.db.WithContext(ctx) offlineRows := make([]dailyDisbursementSourceRow, 0) if err := db.Table("rental_orders"). Select(`DATE(offline_settled_at) AS date, COALESCE(SUM(offline_settlement_amount_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("settlement_mode = ?", "platform_managed"). Where("offline_settlement_status = ?", "settled"). Where("offline_settled_at >= ? AND offline_settled_at <= ?", query.StartDate, query.EndDate). Group("DATE(offline_settled_at)"). Scan(&offlineRows).Error; err != nil { return nil, err } pickupOfflineRows := make([]dailyDisbursementSourceRow, 0) if err := db.Table("admin_pickups"). Select(`DATE(offline_settled_at) AS date, COALESCE(SUM(settle_amount_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("settlement_mode = ?", "platform_managed"). Where("offline_settlement_status = ?", "settled"). Where("offline_settled_at >= ? AND offline_settled_at <= ?", query.StartDate, query.EndDate). Group("DATE(offline_settled_at)"). Scan(&pickupOfflineRows).Error; err != nil { return nil, err } pickupAdjustmentRows := make([]dailyDisbursementSourceRow, 0) if err := db.Table("admin_pickup_financial_adjustments"). Select(`DATE(settled_at) AS date, COALESCE(SUM(settle_delta_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("settlement_mode = ?", "platform_managed"). Where("status = ?", "settled"). Where("settle_delta_cent > 0"). Where("settled_at >= ? AND settled_at <= ?", query.StartDate, query.EndDate). Group("DATE(settled_at)"). Scan(&pickupAdjustmentRows).Error; err != nil { return nil, err } offlineRows = append(offlineRows, pickupOfflineRows...) offlineRows = append(offlineRows, pickupAdjustmentRows...) withdrawalRows := make([]dailyDisbursementSourceRow, 0) if err := db.Table("withdrawal_requests"). Select(`DATE(paid_at) AS date, COALESCE(SUM(actual_amount_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("status = ?", "completed"). Where("paid_at >= ? AND paid_at <= ?", query.StartDate, query.EndDate). Group("DATE(paid_at)"). Scan(&withdrawalRows).Error; err != nil { return nil, err } manualRows := make([]dailyDisbursementSourceRow, 0) if err := db.Table("manual_disbursements"). Select(`DATE(paid_at) AS date, COALESCE(SUM(amount_cent), 0) AS amount_cent, COUNT(id) AS count`). Where("status = ?", "paid"). Where("paid_at >= ? AND paid_at <= ?", query.StartDate, query.EndDate). Group("DATE(paid_at)"). Scan(&manualRows).Error; err != nil { return nil, err } rowsByDate := make(map[string]dailyDisbursementRow, len(offlineRows)+len(withdrawalRows)+len(manualRows)) for _, row := range offlineRows { date := dailyDateKey(row.Date) item := rowsByDate[date] item.Date = date item.OfflineSettlementPaidAmountCent += row.AmountCent item.OfflineSettlementPaidCount += row.Count rowsByDate[date] = item } for _, row := range withdrawalRows { date := dailyDateKey(row.Date) item := rowsByDate[date] item.Date = date item.WithdrawalPaidAmountCent = row.AmountCent item.WithdrawalPaidCount = row.Count rowsByDate[date] = item } for _, row := range manualRows { date := dailyDateKey(row.Date) item := rowsByDate[date] item.Date = date item.ManualPaidAmountCent = row.AmountCent item.ManualPaidCount = row.Count rowsByDate[date] = item } rows := make([]dailyDisbursementRow, 0, len(rowsByDate)) for day := dayStart(query.StartDate); !day.After(query.EndDate); day = day.AddDate(0, 0, 1) { if row, ok := rowsByDate[day.Format("2006-01-02")]; ok { rows = append(rows, row) } } return rows, nil } type dailyDisbursementSourceRow struct { Date string AmountCent int64 Count int64 } type dailyDisbursementRow struct { Date string OfflineSettlementPaidAmountCent int64 OfflineSettlementPaidCount int64 WithdrawalPaidAmountCent int64 WithdrawalPaidCount int64 ManualPaidAmountCent int64 ManualPaidCount int64 }