perf: 复用支付宝店铺期初缓存 (#1042)

This commit is contained in:
qiuxiang 2026-11-10 14:34:55 +08:00
parent 906b9ea197
commit 4ee396151d
3 changed files with 74 additions and 76 deletions

View File

@ -98,6 +98,33 @@ class TaobaoAlipaySettlementBillRepository
return $this->summaryForQuery($query, $filters);
}
/**
* 复用总览期初查询已经缓存的店铺余额,避免店铺明细再次扫描全历史。
*
* @return array<string,float>
*/
public function storeOpeningBalances(?string $periodStart): array
{
$start = $this->date($periodStart);
if ($start === null) {
return [];
}
$monthStart = $start->modify('first day of this month');
$cutoff = $monthStart->format('Y-m-d') . ' 00:00:00';
$importVersion = (string) ($this->connection->table(self::IMPORT_TABLE)
->where('status', 'succeeded')
->max('completed_at') ?? '');
$cacheKey = $this->openingBalanceCacheKey($cutoff, '', $importVersion);
$opening = Cache::get($cacheKey);
if (! is_array($opening) || ! is_array($opening['stores'] ?? null)) {
$this->openingBalance(['start' => $periodStart]);
$opening = Cache::get($cacheKey);
}
return is_array($opening['stores'] ?? null) ? $opening['stores'] : [];
}
/**
* @return array{income:float,expense:float,opening_balance:float,opening_balance_date:?string,opening_balance_source:string,balance:float,count:int}
*/
@ -223,11 +250,7 @@ class TaobaoAlipaySettlementBillRepository
$importVersion = (string) ($this->connection->table(self::IMPORT_TABLE)
->where('status', 'succeeded')
->max('completed_at') ?? '');
$cacheKey = 'taobao-alipay-opening-balance:v1:' . hash('sha256', implode('|', [
$cutoff,
$store,
$importVersion,
]));
$cacheKey = $this->openingBalanceCacheKey($cutoff, $store, $importVersion);
$opening = Cache::remember(
$cacheKey,
@ -259,9 +282,20 @@ class TaobaoAlipaySettlementBillRepository
->get();
if ($latestRows->isNotEmpty()) {
$stores = [];
foreach ($latestRows as $row) {
$storeName = trim((string) ($row->store_name ?? ''));
$storeName = $storeName !== '' ? $storeName : '未标注店铺';
$stores[$storeName] = round(
(float) ($stores[$storeName] ?? 0) + (float) $row->balance_amount,
2,
);
}
return [
'balance' => round((float) $latestRows->sum('balance_amount'), 2),
'source' => 'previous_period',
'stores' => $stores,
];
}
@ -291,6 +325,18 @@ class TaobaoAlipaySettlementBillRepository
->fromSub($firstRows, 'first_balances')
->where('row_rank', 1)
->get();
$stores = [];
foreach ($first as $row) {
$storeName = trim((string) ($row->store_name ?? ''));
$storeName = $storeName !== '' ? $storeName : '未标注店铺';
$stores[$storeName] = round(
(float) ($stores[$storeName] ?? 0)
+ (float) $row->balance_amount
- (float) $row->income_amount
+ (float) $row->expense_amount,
2,
);
}
return [
'balance' => round((float) $first->sum(
@ -299,6 +345,7 @@ class TaobaoAlipaySettlementBillRepository
+ (float) $row->expense_amount,
), 2),
'source' => 'first_row_estimate',
'stores' => $stores,
];
},
);
@ -356,4 +403,13 @@ class TaobaoAlipaySettlementBillRepository
{
return str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $value);
}
private function openingBalanceCacheKey(string $cutoff, string $store, string $importVersion): string
{
return 'taobao-alipay-opening-balance:v2:' . hash('sha256', implode('|', [
$cutoff,
$store,
$importVersion,
]));
}
}

View File

@ -20,6 +20,7 @@ use Modules\EcommerceFinance\Repositories\PinduoduoSettlementBillRepository;
use Modules\EcommerceFinance\Repositories\StoreReceivingAlipayBillMirrorRepository;
use Modules\EcommerceFinance\Repositories\StoreReceivingDouyinBillMirrorRepository;
use Modules\EcommerceFinance\Repositories\StoreReceivingJdWalletBillRepository;
use Modules\EcommerceFinance\Repositories\TaobaoAlipaySettlementBillRepository;
use Modules\EcommerceFinance\Repositories\TaobaoWechatOpeningBalanceRepository;
use Modules\EcommerceFinance\Repositories\TaobaoWechatSettlementBillRepository;
use Modules\EcommerceFinance\Repositories\VipshopJxOpeningBalanceRepository;
@ -623,77 +624,8 @@ class StoreReceivingBalanceOverviewService
->where('occurred_at', '>=', $start . ' 00:00:00')
->where('occurred_at', '<', $this->dayAfter($end) . ' 00:00:00'),
);
$cutoff = $start . ' 00:00:00';
$periodEnd = $this->dayAfter($end) . ' 00:00:00';
$ranked = $connection->table(self::TAOBAO_ALIPAY_ROW_TABLE)
->where('occurred_at', '<', $cutoff)
->whereNotNull('alipay_account')
->where('alipay_account', '<>', '')
->select([
'store_name',
'alipay_account',
'occurred_at',
'source_row_number',
'income_amount',
'expense_amount',
'balance_amount',
])
->selectRaw(
'ROW_NUMBER() OVER (' .
'PARTITION BY alipay_account ' .
'ORDER BY occurred_at DESC, source_row_number DESC' .
') AS row_rank',
);
$latestRows = $connection->query()
->fromSub($ranked, 'latest_store_balances')
->where('row_rank', 1)
->get();
$opening = [];
if ($latestRows->isNotEmpty()) {
foreach ($latestRows as $row) {
$storeName = $this->storeName($row->store_name);
$opening[$storeName] = round(
(float) ($opening[$storeName] ?? 0) + (float) $row->balance_amount,
2,
);
}
} else {
$firstRanked = $connection->table(self::TAOBAO_ALIPAY_ROW_TABLE)
->where('occurred_at', '>=', $cutoff)
->where('occurred_at', '<', $periodEnd)
->whereNotNull('alipay_account')
->where('alipay_account', '<>', '')
->select([
'store_name',
'alipay_account',
'occurred_at',
'source_row_number',
'income_amount',
'expense_amount',
'balance_amount',
])
->selectRaw(
'ROW_NUMBER() OVER (' .
'PARTITION BY alipay_account ' .
'ORDER BY occurred_at ASC, source_row_number ASC' .
') AS row_rank',
);
$firstRows = $connection->query()
->fromSub($firstRanked, 'first_store_balances')
->where('row_rank', 1)
->get();
foreach ($firstRows as $row) {
$storeName = $this->storeName($row->store_name);
$opening[$storeName] = round(
(float) ($opening[$storeName] ?? 0)
+ (float) $row->balance_amount
- (float) $row->income_amount
+ (float) $row->expense_amount,
2,
);
}
}
$opening = app(TaobaoAlipaySettlementBillRepository::class)
->storeOpeningBalances($start);
return $this->mergeStoreBalances($opening, $activity);
}

View File

@ -65,6 +65,11 @@ class TaobaoAlipaySettlementBillRepositoryTest extends TestCase
'end' => '2026-10-31',
]),
);
$this->assertSame(
['天猫-A店' => 180.0, '淘宝-B店' => 50.0],
app(TaobaoAlipaySettlementBillRepository::class)
->storeOpeningBalances('2026-10-01'),
);
}
public function test_month_balance_respects_store_filter(): void
@ -116,6 +121,11 @@ class TaobaoAlipaySettlementBillRepositoryTest extends TestCase
$this->assertSame(35.0, $result['summary']['income']);
$this->assertSame(15.0, $result['summary']['expense']);
$this->assertSame(320.0, $result['summary']['balance']);
$this->assertSame(
['天猫-A店' => 200.0, '淘宝-B店' => 100.0],
app(TaobaoAlipaySettlementBillRepository::class)
->storeOpeningBalances('2026-07-01'),
);
}
private function createTables(): void