CaiWuXiTong/modules/EcommerceFinance/Repositories/DingProcessDetailMirrorRepository.php

223 lines
7.0 KiB
PHP

<?php
namespace Modules\EcommerceFinance\Repositories;
use Illuminate\Database\ConnectionInterface;
use Illuminate\Database\DatabaseManager;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\Schema;
use RuntimeException;
class DingProcessDetailMirrorRepository
{
public const DEFAULT_SCOPE = 'personal_permission';
public const DATASET_PREFIX = 'ding.process_details.';
private readonly ConnectionInterface $connection;
public function __construct(DatabaseManager $database)
{
$this->connection = $database->connection(
(string) config('upstream_mirrors.connection', 'business_mirror')
);
}
public function scope(): string
{
$scope = trim((string) config(
'upstream_mirrors.ding_process_details.scope',
self::DEFAULT_SCOPE,
));
return $scope !== '' ? $scope : self::DEFAULT_SCOPE;
}
public function datasetKey(): string
{
return self::DATASET_PREFIX . $this->scope();
}
public function ready(): bool
{
$schema = Schema::connection($this->connection->getName());
if (! $schema->hasTable('ding_process_detail_mirror')
|| ! $schema->hasTable('business_mirror_table_states')) {
return false;
}
$state = $this->state();
return $state !== null && ($state['initial_completed_at'] ?? null) !== null;
}
/** @return array<string,mixed>|null */
public function state(): ?array
{
if (! Schema::connection($this->connection->getName())->hasTable('business_mirror_table_states')) {
return null;
}
$row = $this->connection->table('business_mirror_table_states')
->where('dataset_key', $this->datasetKey())
->first();
return $row === null ? null : (array) $row;
}
/**
* @param array<string,string> $filters
* @return array{
* data:list<object>,
* total:int,
* summary:array{rows:int,approvals:int,amount_total:float,bank_amount_total:float},
* status:string,
* last_synced_at:?string,
* scope:string
* }
*/
public function paginate(array $filters, int $page, int $perPage): array
{
if (! $this->ready()) {
throw new RuntimeException('ding_process_detail_mirror_not_ready');
}
$page = max($page, 1);
$perPage = min(max($perPage, 1), 100);
$query = $this->filteredQuery($filters);
$total = (clone $query)->count();
$summary = [
'rows' => $total,
'approvals' => (clone $query)->distinct()->count('business_id'),
'amount_total' => (float) ((clone $query)->sum('amount') ?? 0),
'bank_amount_total' => (float) ((clone $query)->sum('bank_amount') ?? 0),
];
$data = $query
->orderByDesc('business_month')
->orderByDesc('business_id')
->orderBy('line_no')
->forPage($page, $perPage)
->get()
->all();
$state = $this->state();
return [
'data' => $data,
'total' => $total,
'summary' => $summary,
'status' => (string) ($state['status'] ?? 'unknown'),
'last_synced_at' => isset($state['last_succeeded_at'])
? (string) $state['last_succeeded_at']
: null,
'scope' => $this->scope(),
];
}
/** @return array{months:list<string>,document_types:list<string>,statuses:list<string>} */
public function filterOptions(): array
{
if (! Schema::connection($this->connection->getName())->hasTable('ding_process_detail_mirror')) {
return ['months' => [], 'document_types' => [], 'statuses' => []];
}
$base = $this->connection->table('ding_process_detail_mirror')
->where('source_scope', $this->scope());
return [
'months' => (clone $base)
->whereNotNull('business_month')
->where('business_month', '!=', '')
->distinct()
->orderByDesc('business_month')
->pluck('business_month')
->map(static function (mixed $value): string {
$month = (string) $value;
return preg_match('/^\d{6}$/', $month) === 1
? substr($month, 0, 4) . '-' . substr($month, 4, 2)
: $month;
})
->all(),
'document_types' => (clone $base)
->whereNotNull('document_type')
->where('document_type', '!=', '')
->distinct()
->orderBy('document_type')
->pluck('document_type')
->map(static fn (mixed $value): string => (string) $value)
->all(),
'statuses' => (clone $base)
->whereNotNull('status')
->where('status', '!=', '')
->distinct()
->orderBy('status')
->pluck('status')
->map(static fn (mixed $value): string => (string) $value)
->all(),
];
}
/** @param array<string,string> $filters */
public function exportCount(array $filters): int
{
if (! Schema::connection($this->connection->getName())->hasTable('ding_process_detail_mirror')) {
return 0;
}
return (int) $this->filteredQuery($filters)->count();
}
/**
* @param array<string,string> $filters
* @return \Generator<int,array<string,mixed>>
*/
public function exportRows(array $filters): \Generator
{
$query = $this->filteredQuery($filters)
->orderByDesc('business_month')
->orderByDesc('business_id')
->orderBy('line_no');
foreach ($query->cursor() as $row) {
yield (array) $row;
}
}
/** @param array<string,string> $filters */
private function filteredQuery(array $filters): Builder
{
$query = $this->connection->table('ding_process_detail_mirror')
->where('source_scope', $this->scope());
$month = trim((string) ($filters['month'] ?? ''));
if (preg_match('/^\d{4}-\d{2}$/', $month) === 1) {
$query->where('business_month', str_replace('-', '', $month));
}
foreach ([
'document_type' => 'document_type',
'status' => 'status',
] as $filter => $column) {
$value = trim((string) ($filters[$filter] ?? ''));
if ($value !== '') {
$query->where($column, $value);
}
}
foreach ([
'business_id' => 'business_id',
'originator_name' => 'originator_name',
'keyword' => 'purpose',
] as $filter => $column) {
$value = mb_substr(trim((string) ($filters[$filter] ?? '')), 0, 100);
if ($value !== '') {
$query->where($column, 'like', '%' . $value . '%');
}
}
return $query;
}
}