81 lines
2.9 KiB
Go
81 lines
2.9 KiB
Go
package repository
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"strings"
|
|
"time"
|
|
|
|
"infinite-canvas/backend/internal/model"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type PaymentOrderFilter struct {
|
|
Status, Keyword, ProviderID, TimeField string
|
|
From, Until time.Time
|
|
}
|
|
|
|
type PaymentReconciliationFilter struct {
|
|
ProviderID, Status, From, To string
|
|
}
|
|
|
|
var ErrPaymentReconciliationRunning = errors.New("payment reconciliation is running")
|
|
|
|
func (r *Repository) paymentReconciliationItemsQuery(runID, result string) *gorm.DB {
|
|
query := r.db.Model(&model.PaymentReconciliationItem{}).Where("run_id = ?", strings.TrimSpace(runID))
|
|
if normalized := strings.TrimSpace(result); normalized == "abnormal" {
|
|
query = query.Where("result NOT IN ?", []string{"matched", "recovered"})
|
|
} else if normalized != "" && normalized != "all" {
|
|
query = query.Where("result = ?", normalized)
|
|
}
|
|
return query
|
|
}
|
|
|
|
func (r *Repository) ExportPaymentOrders(filter PaymentOrderFilter, limit int) (orders []model.PaymentOrder, users map[string]model.User, err error) {
|
|
users = make(map[string]model.User)
|
|
err = r.db.Transaction(func(tx *gorm.DB) error {
|
|
snapshot := New(tx)
|
|
if err := snapshot.paymentOrderQuery(filter).Order("created_at desc, id desc").Limit(limit).Find(&orders).Error; err != nil {
|
|
return err
|
|
}
|
|
// Bound IN parameters for SQLite as well as PostgreSQL.
|
|
for start := 0; start < len(orders); start += 500 {
|
|
ids := make([]string, 0, 500)
|
|
for _, order := range orders[start:min(start+500, len(orders))] {
|
|
ids = append(ids, order.UserID)
|
|
}
|
|
batch, err := snapshot.UsersByIDs(ids)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for id, user := range batch {
|
|
users[id] = user
|
|
}
|
|
}
|
|
return nil
|
|
}, &sql.TxOptions{Isolation: sql.LevelRepeatableRead, ReadOnly: true})
|
|
return
|
|
}
|
|
|
|
func (r *Repository) ExportPaymentReconciliations(filter PaymentReconciliationFilter, limit int) ([]model.PaymentReconciliationRun, error) {
|
|
var runs []model.PaymentReconciliationRun
|
|
err := r.paymentReconciliationQuery(filter).Order("bill_date desc, started_at desc, id desc").Limit(limit).Find(&runs).Error
|
|
return runs, err
|
|
}
|
|
|
|
func (r *Repository) ExportPaymentReconciliationItems(runID, result string, limit int) (run model.PaymentReconciliationRun, items []model.PaymentReconciliationItem, err error) {
|
|
// A rerun deletes old items and reuses the run ID. Both reads must observe
|
|
// the same snapshot, including on PostgreSQL (whose default is read committed).
|
|
err = r.db.Transaction(func(tx *gorm.DB) error {
|
|
if err := tx.First(&run, "id = ?", strings.TrimSpace(runID)).Error; err != nil {
|
|
return err
|
|
}
|
|
if run.Status == model.PaymentReconciliationRunning {
|
|
return ErrPaymentReconciliationRunning
|
|
}
|
|
return New(tx).paymentReconciliationItemsQuery(run.ID, result).Order("resolved asc, created_at asc, id asc").Limit(limit).Find(&items).Error
|
|
}, &sql.TxOptions{Isolation: sql.LevelRepeatableRead, ReadOnly: true})
|
|
return
|
|
}
|