146 lines
6.0 KiB
PHP
146 lines
6.0 KiB
PHP
<?php
|
|
|
|
namespace Modules\EcommerceFinance\Repositories;
|
|
|
|
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQuery;
|
|
use Modules\EcommerceFinance\Services\UpstreamReadOnlyQueryFactory;
|
|
|
|
/**
|
|
* Completed product price-limit approvals from the operations replica.
|
|
*/
|
|
class ProductPriceRepository
|
|
{
|
|
private const SOURCE = 'operations_replica';
|
|
|
|
private const TABLE = '[rpt].[Bulk_Price_Limit]';
|
|
|
|
private readonly UpstreamReadOnlyQuery $query;
|
|
|
|
public function __construct(UpstreamReadOnlyQueryFactory $queries)
|
|
{
|
|
$this->query = $queries->forSource(self::SOURCE);
|
|
}
|
|
|
|
/** @return array{rows:array<int,array<string,mixed>>} */
|
|
public function paginate(array $filters, int $page, int $limit): array
|
|
{
|
|
$page = max($page, 1);
|
|
$limit = min(max($limit, 1), 100);
|
|
$where = [];
|
|
$bindings = [];
|
|
|
|
if (($filters['barcode'] ?? '') !== '') {
|
|
$where[] = "[product_code] LIKE ? ESCAPE '\\'";
|
|
$bindings[] = $this->contains((string) $filters['barcode']);
|
|
}
|
|
|
|
if (($filters['completed_from'] ?? '') !== '') {
|
|
$where[] = '[process_end_date] >= CAST(? AS date)';
|
|
$bindings[] = (string) $filters['completed_from'];
|
|
}
|
|
|
|
if (($filters['completed_to'] ?? '') !== '') {
|
|
$where[] = '[process_end_date] < DATEADD(day, 1, CAST(? AS date))';
|
|
$bindings[] = (string) $filters['completed_to'];
|
|
}
|
|
|
|
$whereSql = $where === [] ? '' : ' WHERE ' . implode(' AND ', $where);
|
|
$offset = ($page - 1) * $limit;
|
|
$fetch = $limit + 1;
|
|
$sql = 'SELECT '
|
|
. '[product_code] AS barcode, '
|
|
. '[spu] AS spu, '
|
|
. '[product_name] AS product_name, '
|
|
. '[category] AS category, '
|
|
. '[production_status] AS production_status, '
|
|
. '[min_price] AS minimum_price, '
|
|
. '[price_limit] AS price_limit, '
|
|
. '[purchase_price] AS purchase_price, '
|
|
. '[shipping_fee] AS shipping_fee, '
|
|
. '[jingxi_limit_price] AS jingxi_limit_price, '
|
|
. "CASE [status] WHEN 'COMPLETED' THEN N'已完成' ELSE NULLIF([status], '') END AS approval_status, "
|
|
. "CASE [result] WHEN 'agree' THEN N'已同意' WHEN 'refuse' THEN N'已拒绝' ELSE NULLIF([result], '') END AS approval_result, "
|
|
. '[title] AS workflow_title, '
|
|
. 'CONVERT(varchar(19), [process_end_date], 120) AS completed_at '
|
|
. 'FROM ' . self::TABLE
|
|
. $whereSql
|
|
. ' ORDER BY [process_end_date] DESC, [businessId] DESC, [product_code] ASC'
|
|
. " OFFSET {$offset} ROWS FETCH NEXT {$fetch} ROWS ONLY";
|
|
|
|
return ['rows' => array_map([$this, 'normalize'], $this->query->select($sql, $bindings))];
|
|
}
|
|
|
|
/**
|
|
* 按稳定游标分批读取已完成的上游记录,只供后台镜像同步使用。
|
|
*
|
|
* @param array{completed_at:string,business_id:string,barcode:string}|null $cursor
|
|
* @return array<int,array<string,mixed>>
|
|
*/
|
|
public function mirrorBatch(?array $cursor, string $since, string $until, int $limit): array
|
|
{
|
|
$limit = min(max($limit, 1), 2000);
|
|
$bindings = [$since, $until];
|
|
$cursorSql = '';
|
|
|
|
if ($cursor !== null) {
|
|
$cursorSql = ' AND ('
|
|
. '[process_end_date] > CAST(? AS datetime2(7))'
|
|
. " OR ([process_end_date] = CAST(? AS datetime2(7)) AND COALESCE([businessId], '') > ?)"
|
|
. " OR ([process_end_date] = CAST(? AS datetime2(7)) AND COALESCE([businessId], '') = ?"
|
|
. " AND COALESCE([product_code], '') > ?)"
|
|
. ')';
|
|
$bindings = array_merge($bindings, [
|
|
$cursor['completed_at'],
|
|
$cursor['completed_at'],
|
|
$cursor['business_id'],
|
|
$cursor['completed_at'],
|
|
$cursor['business_id'],
|
|
$cursor['barcode'],
|
|
]);
|
|
}
|
|
|
|
$sql = "SELECT TOP {$limit} "
|
|
. "COALESCE([businessId], '') AS business_id, "
|
|
. "COALESCE([product_code], '') AS barcode, "
|
|
. '[spu] AS spu, '
|
|
. '[product_name] AS product_name, '
|
|
. '[category] AS category, '
|
|
. '[production_status] AS production_status, '
|
|
. '[min_price] AS minimum_price, '
|
|
. '[price_limit] AS price_limit, '
|
|
. '[purchase_price] AS purchase_price, '
|
|
. '[shipping_fee] AS shipping_fee, '
|
|
. '[jingxi_limit_price] AS jingxi_limit_price, '
|
|
. "CASE [status] WHEN 'COMPLETED' THEN N'已完成' ELSE NULLIF([status], '') END AS approval_status, "
|
|
. "CASE [result] WHEN 'agree' THEN N'已同意' WHEN 'refuse' THEN N'已拒绝' ELSE NULLIF([result], '') END AS approval_result, "
|
|
. '[title] AS workflow_title, '
|
|
. 'CONVERT(varchar(33), [process_end_date], 126) AS cursor_completed_at, '
|
|
. 'CONVERT(varchar(26), [process_end_date], 121) AS completed_at '
|
|
. 'FROM ' . self::TABLE
|
|
. ' WHERE [process_end_date] IS NOT NULL'
|
|
. ' AND [process_end_date] >= CAST(? AS datetime2(7))'
|
|
. ' AND [process_end_date] < CAST(? AS datetime2(7))'
|
|
. $cursorSql
|
|
. " ORDER BY [process_end_date] ASC, COALESCE([businessId], '') ASC, COALESCE([product_code], '') ASC";
|
|
|
|
return array_map([$this, 'normalize'], $this->query->select($sql, $bindings));
|
|
}
|
|
|
|
private function contains(string $value): string
|
|
{
|
|
return '%' . str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], trim($value)) . '%';
|
|
}
|
|
|
|
/** @param array<string,mixed> $row */
|
|
private function normalize(array $row): array
|
|
{
|
|
foreach (['minimum_price', 'price_limit', 'purchase_price', 'shipping_fee', 'jingxi_limit_price'] as $field) {
|
|
$row[$field] = $row[$field] === null || $row[$field] === ''
|
|
? null
|
|
: (float) $row[$field];
|
|
}
|
|
|
|
return $row;
|
|
}
|
|
}
|