diff --git a/modules/EcommerceFinance/Repositories/SmallPaymentWorkOrderMirrorRepository.php b/modules/EcommerceFinance/Repositories/SmallPaymentWorkOrderMirrorRepository.php index a949e337..fcc80bb6 100644 --- a/modules/EcommerceFinance/Repositories/SmallPaymentWorkOrderMirrorRepository.php +++ b/modules/EcommerceFinance/Repositories/SmallPaymentWorkOrderMirrorRepository.php @@ -40,6 +40,9 @@ class SmallPaymentWorkOrderMirrorRepository private const COMPLETED_WORK_ORDER_STATUSES = ['CONFIRMED', '已完成', '已确认']; + /** 打款金额在 businessFieldList 中的候选字段名(按优先级) */ + private const PAYMENT_AMOUNT_FIELDS = ['打款金额', '付款金额', '小额打款金额', 'amountField']; + private readonly ConnectionInterface $connection; public function __construct( @@ -177,18 +180,48 @@ class SmallPaymentWorkOrderMirrorRepository } /** + * 统计当前筛选条件下全部记录的打款金额合计。 + * + * 只取金额所需字段,按 Id 做键集分块批量读取:不使用逐行游标(本地/远端库时每次取行都是一次网络往返), + * 也不复用导出用的分页排序查询(会为大结果集重复执行连接与排序)。 + * * @param array{from:string,to:string,work_order_no:string,shop_name:string,transaction_no:string,online_order_no:string,status:string,payment_reason:string} $filters */ - public function filteredPaymentAmount(array $filters): string + public function filteredPaymentAmount(array $filters, int $chunkSize = 1000): string { $this->ensureReady(); - $amounts = []; - foreach ($this->exportRows($filters, 100) as $row) { - $amounts[] = $row['payment_amount'] ?? null; - } + $query = $this->baseQuery(); + $this->applyFilters($query, $filters); - return $this->sumMoney($amounts); + $chunkSize = min(max($chunkSize, 100), 5000); + $cents = 0; + $lastId = null; + + do { + $chunk = (clone $query) + ->select(['w.Id AS source_id', 'w.businessFieldList']) + ->when($lastId !== null, static fn (Builder $q): Builder => $q->where('w.Id', '>', $lastId)) + ->orderBy('w.Id') + ->limit($chunkSize) + ->get(); + + foreach ($chunk as $row) { + $cents += $this->centsFromMoney($this->paymentAmountOf($row->businessFieldList ?? null)); + } + + $lastId = $chunk->isEmpty() ? null : $chunk->last()->source_id; + } while ($chunk->count() === $chunkSize); + + return $this->formatCents($cents); + } + + private function paymentAmountOf(mixed $businessFieldList): ?string + { + return $this->money($this->field( + $this->businessFields($businessFieldList), + self::PAYMENT_AMOUNT_FIELDS, + )); } /** @@ -390,9 +423,7 @@ class SmallPaymentWorkOrderMirrorRepository $row['alipay_batch_no'] = $this->field($fields, [ '支付宝批次号', '支付宝打款批次号', '打款批次号', '批次号', 'alipayBatchNo', 'batchNo', ]); - $row['payment_amount'] = $this->money($this->field($fields, [ - '打款金额', '付款金额', '小额打款金额', 'amountField', - ])); + $row['payment_amount'] = $this->money($this->field($fields, self::PAYMENT_AMOUNT_FIELDS)); $row['online_order_no'] = $this->firstFilled( $this->field($fields, ['线上订单号', '线上单号', '订单号', 'orderField']), $row['mapped_online_order_no'] ?? null, @@ -535,14 +566,26 @@ class SmallPaymentWorkOrderMirrorRepository { $cents = 0; foreach ($amounts as $amount) { - if ($amount === null || ! preg_match('/^(-?)(\d+)\.(\d{2})$/', (string) $amount, $matches)) { - continue; - } - - $value = ((int) $matches[2] * 100) + (int) $matches[3]; - $cents += $matches[1] === '-' ? -$value : $value; + $cents += $this->centsFromMoney($amount); } + return $this->formatCents($cents); + } + + /** 将 '123.45' / '-123.45' 形式的金额字符串转成分(无效值计 0)。 */ + private function centsFromMoney(mixed $amount): int + { + if ($amount === null || ! preg_match('/^(-?)(\d+)\.(\d{2})$/', (string) $amount, $matches)) { + return 0; + } + + $value = ((int) $matches[2] * 100) + (int) $matches[3]; + + return $matches[1] === '-' ? -$value : $value; + } + + private function formatCents(int $cents): string + { $sign = $cents < 0 ? '-' : ''; $absolute = abs($cents);