fix(admin): correct Google paid sync count

This commit is contained in:
zhx 2026-07-14 11:07:06 +08:00
parent b5f85ec5c5
commit 14604861ea
2 changed files with 46 additions and 7 deletions

View File

@ -37,9 +37,12 @@ type LegacyGooglePaidStats struct {
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) {
where := "app_code = ? AND paid_amount_micro > 0 AND paid_net_micro > 0"
where := "app_code = ?"
args := []any{strings.ToLower(strings.TrimSpace(appCode))}
if startAtMS > 0 {
where += " AND bill_created_at_ms >= ?"
@ -51,11 +54,14 @@ func (s *Store) LegacyGooglePaidStatsByBillTime(appCode string, startAtMS int64,
}
var stats LegacyGooglePaidStats
row := s.db.Raw(`
SELECT COUNT(*),
COALESCE(SUM(bill_usd_minor), 0),
COALESCE(SUM(ROUND(bill_usd_minor * GREATEST(paid_amount_micro - paid_net_micro - paid_tax_micro, 0) / paid_amount_micro)), 0),
COALESCE(SUM(ROUND(bill_usd_minor * paid_tax_micro / paid_amount_micro)), 0),
COALESCE(SUM(ROUND(bill_usd_minor * paid_net_micro / paid_amount_micro)), 0)
SELECT COALESCE(SUM(CASE WHEN synced_at_ms > 0 THEN 1 ELSE 0 END), 0),
COALESCE(SUM(CASE WHEN paid_amount_micro > 0 AND paid_net_micro > 0 THEN bill_usd_minor ELSE 0 END), 0),
COALESCE(SUM(CASE WHEN paid_amount_micro > 0 AND paid_net_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(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
WHERE `+where, args...).Row()
if err := row.Scan(&stats.SyncedCount, &stats.CoveredUSDMinor, &stats.EstFeeUSDMinor, &stats.EstTaxUSDMinor, &stats.EstNetUSDMinor); err != nil {

View File

@ -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)
}
}