feat: aggregate daily payment history by hour

This commit is contained in:
2026-08-21 16:28:13 +08:00
parent 927319807b
commit 7140213857
4 changed files with 64 additions and 11 deletions

View File

@@ -43,6 +43,7 @@ type RunScope struct {
type PaymentHistoryPoint struct {
LocalDate string `json:"localDate"`
LocalHour *int `json:"localHour,omitempty"`
TotalSessions int64 `json:"totalSessions"`
BankPaidSessions int64 `json:"bankPaidSessions"`
BankPaidAmountRial int64 `json:"bankPaidAmountRial"`
@@ -209,24 +210,25 @@ func (m metricValues) overview(period Period, history []PaymentHistoryPoint) map
}
func (s *Service) paymentHistory(ctx context.Context, merchantID int64, period Period, terminalID string) ([]PaymentHistoryPoint, error) {
rows, err := s.pool.Query(ctx, `SELECT (ps.created_at AT TIME ZONE 'Asia/Tehran')::date local_date,
count(*),count(*) FILTER (WHERE ps.status IN ('Paid','Verified')),
coalesce(sum(ps.amount_rial) FILTER (WHERE ps.status IN ('Paid','Verified')),0)
FROM payment_sessions ps WHERE ps.merchant_id=$1 AND ps.created_at >= $2 AND ps.created_at < $3
AND ($4='' OR ps.terminal_id=(SELECT id FROM terminals WHERE merchant_id=$1 AND source_key=$4))
GROUP BY local_date ORDER BY local_date`, merchantID, period.StartAt, period.EndAt, terminalID)
query, capacity := paymentHistoryQuery(period.PeriodType)
rows, err := s.pool.Query(ctx, query, merchantID, period.StartAt, period.EndAt, terminalID)
if err != nil {
return nil, fmt.Errorf("querying payment history: %w", err)
}
defer rows.Close()
points := make([]PaymentHistoryPoint, 0, 93)
points := make([]PaymentHistoryPoint, 0, capacity)
for rows.Next() {
var date time.Time
var localHour *int16
var point PaymentHistoryPoint
if err := rows.Scan(&date, &point.TotalSessions, &point.BankPaidSessions, &point.BankPaidAmountRial); err != nil {
if err := rows.Scan(&date, &localHour, &point.TotalSessions, &point.BankPaidSessions, &point.BankPaidAmountRial); err != nil {
return nil, fmt.Errorf("scanning payment history: %w", err)
}
point.LocalDate = date.Format(time.DateOnly)
if localHour != nil {
hour := int(*localHour)
point.LocalHour = &hour
}
points = append(points, point)
}
if err := rows.Err(); err != nil {
@@ -234,6 +236,25 @@ func (s *Service) paymentHistory(ctx context.Context, merchantID int64, period P
}
return points, nil
}
func paymentHistoryQuery(periodType string) (string, int) {
if periodType == "DAY" {
return `SELECT (ps.created_at AT TIME ZONE 'Asia/Tehran')::date local_date,
EXTRACT(HOUR FROM ps.created_at AT TIME ZONE 'Asia/Tehran')::smallint local_hour,
count(*),count(*) FILTER (WHERE ps.status IN ('Paid','Verified')),
coalesce(sum(ps.amount_rial) FILTER (WHERE ps.status IN ('Paid','Verified')),0)
FROM payment_sessions ps WHERE ps.merchant_id=$1 AND ps.created_at >= $2 AND ps.created_at < $3
AND ($4='' OR ps.terminal_id=(SELECT id FROM terminals WHERE merchant_id=$1 AND source_key=$4))
GROUP BY local_date, local_hour ORDER BY local_date, local_hour`, 24
}
return `SELECT (ps.created_at AT TIME ZONE 'Asia/Tehran')::date local_date,
NULL::smallint local_hour,
count(*),count(*) FILTER (WHERE ps.status IN ('Paid','Verified')),
coalesce(sum(ps.amount_rial) FILTER (WHERE ps.status IN ('Paid','Verified')),0)
FROM payment_sessions ps WHERE ps.merchant_id=$1 AND ps.created_at >= $2 AND ps.created_at < $3
AND ($4='' OR ps.terminal_id=(SELECT id FROM terminals WHERE merchant_id=$1 AND source_key=$4))
GROUP BY local_date ORDER BY local_date`, 93
}
func timing(id string, p50, p95 *float64, samples, eligible int64) map[string]any {
var a, b any
if p50 != nil {