157 lines
6.4 KiB
PHP
157 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\Repositories;
|
|
|
|
use Illuminate\Database\Query\Builder;
|
|
use Modules\EcommerceFinance\Services\BusinessMirrorDatasetRegistry;
|
|
use RuntimeException;
|
|
|
|
/** 店铺收款账单·淘系支付宝账户只读共享业务镜像。 */
|
|
class StoreReceivingAlipayBillMirrorRepository
|
|
{
|
|
public const DATASET = 'jiansun.dbo.alipay_Bill_Detail';
|
|
|
|
public function __construct(
|
|
private readonly BusinessMirrorDatasetRegistry $datasets,
|
|
private readonly GenericBusinessMirrorRepository $mirror,
|
|
) {}
|
|
|
|
/** @return array{data:array<int,array<string,mixed>>,total:int,current_page:int,last_page:int,per_page:int,summary:array{income:float,expense:float,balance:float,count:int},sync:array<string,mixed>} */
|
|
public function paginate(array $filters, string $sort, string $direction, int $page, int $perPage): array
|
|
{
|
|
$dataset = $this->datasets->get(self::DATASET);
|
|
if (! $this->mirror->ready($dataset)) {
|
|
throw new RuntimeException('store_receiving_alipay_bill_mirror_not_ready');
|
|
}
|
|
|
|
$page = max($page, 1);
|
|
$perPage = min(max($perPage, 1), 100);
|
|
$direction = $direction === 'asc' ? 'asc' : 'desc';
|
|
$sortColumn = [
|
|
'record_code' => 'Id',
|
|
'occurred_at' => 'happenTime',
|
|
'income' => 'inAmount',
|
|
'expense' => 'outAmount',
|
|
'balance' => 'accountAmount',
|
|
][$sort] ?? 'happenTime';
|
|
|
|
$query = $this->mirror->connection()->table($dataset->targetTable());
|
|
$this->applyFilters($query, $filters);
|
|
$total = (clone $query)->count();
|
|
$summaryRow = (clone $query)->selectRaw(
|
|
'COALESCE(SUM(CASE WHEN inAmount > 0 THEN inAmount ELSE 0 END), 0) AS income, '
|
|
. 'COALESCE(SUM(ABS(COALESCE(outAmount, 0))), 0) AS expense, COUNT(*) AS aggregate_count'
|
|
)->first();
|
|
$latestBalance = (clone $query)
|
|
->orderByDesc('happenTime')
|
|
->orderByDesc('Id')
|
|
->value('accountAmount');
|
|
|
|
$rows = $query
|
|
->orderBy($sortColumn, $direction)
|
|
->orderBy('Id', $direction)
|
|
->forPage($page, $perPage)
|
|
->get()
|
|
->map(fn (object $row): array => $this->mapRow($row))
|
|
->all();
|
|
$state = $this->mirror->state($dataset) ?? [];
|
|
|
|
return [
|
|
'data' => $rows,
|
|
'total' => $total,
|
|
'current_page' => $page,
|
|
'last_page' => max((int) ceil($total / $perPage), 1),
|
|
'per_page' => $perPage,
|
|
'summary' => [
|
|
'income' => (float) ($summaryRow->income ?? 0),
|
|
'expense' => (float) ($summaryRow->expense ?? 0),
|
|
'balance' => (float) ($latestBalance ?? 0),
|
|
'count' => (int) ($summaryRow->aggregate_count ?? 0),
|
|
],
|
|
'sync' => [
|
|
'status' => $state['status'] ?? 'unknown',
|
|
'last_succeeded_at' => $state['last_succeeded_at'] ?? null,
|
|
],
|
|
];
|
|
}
|
|
|
|
private function applyFilters(Builder $query, array $filters): void
|
|
{
|
|
if (($filters['store_name'] ?? '') !== '') {
|
|
$query->where('shopName', $filters['store_name']);
|
|
}
|
|
if (($filters['alipay_account'] ?? '') !== '') {
|
|
$value = '%' . $this->escapeLike((string) $filters['alipay_account']) . '%';
|
|
$query->where(function (Builder $query) use ($value): void {
|
|
$query->where('appId', 'like', $value)
|
|
->orWhere('account', 'like', $value);
|
|
});
|
|
}
|
|
if (($filters['bill_item'] ?? '') !== '') {
|
|
$query->where('tradeChannel', $filters['bill_item']);
|
|
}
|
|
if (($filters['business_type'] ?? '') !== '') {
|
|
$query->where('businessType', $filters['business_type']);
|
|
}
|
|
if (($filters['start'] ?? '') !== '') {
|
|
$query->where('happenTime', '>=', $filters['start'] . ' 00:00:00');
|
|
}
|
|
if (($filters['end'] ?? '') !== '') {
|
|
$query->where('happenTime', '<', date('Y-m-d', strtotime($filters['end'] . ' +1 day')) . ' 00:00:00');
|
|
}
|
|
if (($filters['keyword'] ?? '') !== '') {
|
|
$value = '%' . $this->escapeLike((string) $filters['keyword']) . '%';
|
|
$query->where(function (Builder $query) use ($value): void {
|
|
$query->where('commodityName', 'like', $value)
|
|
->orWhere('accountNum', 'like', $value)
|
|
->orWhere('businessNum', 'like', $value)
|
|
->orWhere('merchantOrder', 'like', $value)
|
|
->orWhere('storeNum', 'like', $value)
|
|
->orWhere('remark', 'like', $value);
|
|
});
|
|
}
|
|
}
|
|
|
|
private function mapRow(object $row): array
|
|
{
|
|
return [
|
|
'id' => (string) $row->Id,
|
|
'record_code' => (string) $row->Id,
|
|
'store_name' => $row->shopName,
|
|
'custom_remark' => $row->buyerRemark,
|
|
'bill_item' => $row->tradeChannel,
|
|
'alipay_account' => $row->appId ?: $row->account,
|
|
'account_order_no' => $row->accountNum,
|
|
'business_serial_no' => $row->businessNum,
|
|
'merchant_order_no' => $row->merchantOrder,
|
|
'business_base_order_no' => $row->busiBasicNum,
|
|
'goods_name' => $row->commodityName,
|
|
'counterparty_account' => $row->oppAccount,
|
|
'occurred_at' => $this->dateTime($row->happenTime),
|
|
'income' => max((float) ($row->inAmount ?? 0), 0),
|
|
'expense' => abs((float) ($row->outAmount ?? 0)),
|
|
'balance' => (float) ($row->accountAmount ?? 0),
|
|
'business_type' => $row->businessType,
|
|
'remark' => $row->remark,
|
|
'online_order_no' => $row->storeNum,
|
|
'online_real_store' => $row->shopName,
|
|
'business_description' => $row->busiDescription,
|
|
'business_bill_source' => $row->busiBillSource,
|
|
'business_order_no' => $row->busiNum,
|
|
'created_at' => '',
|
|
'updated_at' => '',
|
|
'modifier' => '',
|
|
];
|
|
}
|
|
|
|
private function dateTime(mixed $value): string
|
|
{
|
|
return $value === null || $value === '' ? '' : date('Y-m-d H:i:s', strtotime((string) $value));
|
|
}
|
|
|
|
private function escapeLike(string $value): string
|
|
{
|
|
return str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $value);
|
|
}
|
|
}
|