fix(admin): correct Google paid sync count
This commit is contained in:
parent
b5f85ec5c5
commit
14604861ea
@ -37,9 +37,12 @@ type LegacyGooglePaidStats struct {
|
|||||||
EstNetUSDMinor int64
|
EstNetUSDMinor int64
|
||||||
}
|
}
|
||||||
|
|
||||||
// LegacyGooglePaidStatsByBillTime 按账单创建时间段聚合已同步实付:税/费/净收按实付占比折算到账单美金口径。
|
// LegacyGooglePaidStatsByBillTime 按账单创建时间段聚合实付同步覆盖度。
|
||||||
|
// synced_at_ms 是 Orders API 请求已成功落库的事实;Google 作为 Merchant of Record 时可能不返回
|
||||||
|
// developerRevenueInBuyerCurrency,此时 paid_net_micro=0 只表示缺少费用明细,不能把已同步订单重新算成未同步。
|
||||||
|
// 税/费/净收仍只对实付和净收都有效的订单按比例折算,避免用缺失字段伪造费用数据。
|
||||||
func (s *Store) LegacyGooglePaidStatsByBillTime(appCode string, startAtMS int64, endAtMS int64) (LegacyGooglePaidStats, error) {
|
func (s *Store) LegacyGooglePaidStatsByBillTime(appCode string, startAtMS int64, endAtMS int64) (LegacyGooglePaidStats, error) {
|
||||||
where := "app_code = ? AND paid_amount_micro > 0 AND paid_net_micro > 0"
|
where := "app_code = ?"
|
||||||
args := []any{strings.ToLower(strings.TrimSpace(appCode))}
|
args := []any{strings.ToLower(strings.TrimSpace(appCode))}
|
||||||
if startAtMS > 0 {
|
if startAtMS > 0 {
|
||||||
where += " AND bill_created_at_ms >= ?"
|
where += " AND bill_created_at_ms >= ?"
|
||||||
@ -51,11 +54,14 @@ func (s *Store) LegacyGooglePaidStatsByBillTime(appCode string, startAtMS int64,
|
|||||||
}
|
}
|
||||||
var stats LegacyGooglePaidStats
|
var stats LegacyGooglePaidStats
|
||||||
row := s.db.Raw(`
|
row := s.db.Raw(`
|
||||||
SELECT COUNT(*),
|
SELECT COALESCE(SUM(CASE WHEN synced_at_ms > 0 THEN 1 ELSE 0 END), 0),
|
||||||
COALESCE(SUM(bill_usd_minor), 0),
|
COALESCE(SUM(CASE WHEN paid_amount_micro > 0 AND paid_net_micro > 0 THEN bill_usd_minor ELSE 0 END), 0),
|
||||||
COALESCE(SUM(ROUND(bill_usd_minor * GREATEST(paid_amount_micro - paid_net_micro - paid_tax_micro, 0) / paid_amount_micro)), 0),
|
COALESCE(SUM(CASE WHEN paid_amount_micro > 0 AND paid_net_micro > 0
|
||||||
COALESCE(SUM(ROUND(bill_usd_minor * paid_tax_micro / paid_amount_micro)), 0),
|
THEN ROUND(bill_usd_minor * GREATEST(paid_amount_micro - paid_net_micro - paid_tax_micro, 0) / paid_amount_micro) ELSE 0 END), 0),
|
||||||
COALESCE(SUM(ROUND(bill_usd_minor * paid_net_micro / paid_amount_micro)), 0)
|
COALESCE(SUM(CASE WHEN paid_amount_micro > 0 AND paid_net_micro > 0
|
||||||
|
THEN ROUND(bill_usd_minor * paid_tax_micro / paid_amount_micro) ELSE 0 END), 0),
|
||||||
|
COALESCE(SUM(CASE WHEN paid_amount_micro > 0 AND paid_net_micro > 0
|
||||||
|
THEN ROUND(bill_usd_minor * paid_net_micro / paid_amount_micro) ELSE 0 END), 0)
|
||||||
FROM admin_legacy_google_paid_details
|
FROM admin_legacy_google_paid_details
|
||||||
WHERE `+where, args...).Row()
|
WHERE `+where, args...).Row()
|
||||||
if err := row.Scan(&stats.SyncedCount, &stats.CoveredUSDMinor, &stats.EstFeeUSDMinor, &stats.EstTaxUSDMinor, &stats.EstNetUSDMinor); err != nil {
|
if err := row.Scan(&stats.SyncedCount, &stats.CoveredUSDMinor, &stats.EstFeeUSDMinor, &stats.EstTaxUSDMinor, &stats.EstNetUSDMinor); err != nil {
|
||||||
|
|||||||
@ -0,0 +1,33 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/DATA-DOG/go-sqlmock"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestLegacyGooglePaidStatsCountsSyncedRowsWithoutNetRevenue(t *testing.T) {
|
||||||
|
store, mock, closeStore := newRepositorySQLMock(t)
|
||||||
|
defer closeStore()
|
||||||
|
|
||||||
|
// 108 笔 Orders API 结果都已落库,但只有 2 笔带开发者净收。同步覆盖度必须仍为 108,
|
||||||
|
// 金额估算则继续只覆盖净收有效的 2 笔,这正是 Google Merchant of Record 订单的线上边界。
|
||||||
|
mock.ExpectQuery(`(?s)SELECT.*SUM\(CASE WHEN synced_at_ms > 0 THEN 1 ELSE 0 END\).*CASE WHEN paid_amount_micro > 0 AND paid_net_micro > 0.*WHERE app_code = \? AND bill_created_at_ms >= \? AND bill_created_at_ms < \?`).
|
||||||
|
WithArgs("yumi", int64(1783872000000), int64(1783958400000)).
|
||||||
|
WillReturnRows(sqlmock.NewRows([]string{"synced_count", "covered_usd_minor", "fee_usd_minor", "tax_usd_minor", "net_usd_minor"}).
|
||||||
|
AddRow(108, 4200, 630, 210, 3360))
|
||||||
|
|
||||||
|
stats, err := store.LegacyGooglePaidStatsByBillTime(" YUMI ", 1783872000000, 1783958400000)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("load legacy google paid stats failed: %v", err)
|
||||||
|
}
|
||||||
|
if stats.SyncedCount != 108 {
|
||||||
|
t.Fatalf("synced count must use synced_at_ms instead of net revenue: %+v", stats)
|
||||||
|
}
|
||||||
|
if stats.CoveredUSDMinor != 4200 || stats.EstFeeUSDMinor != 630 || stats.EstTaxUSDMinor != 210 || stats.EstNetUSDMinor != 3360 {
|
||||||
|
t.Fatalf("settlement estimates mismatch: %+v", stats)
|
||||||
|
}
|
||||||
|
if err := mock.ExpectationsWereMet(); err != nil {
|
||||||
|
t.Fatalf("sql expectations mismatch: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user