'0', 'repricing' => '1', ]; private const COST_REDUCTION = [ 'yes' => '1', 'no' => '0', ]; private readonly UpstreamReadOnlyQuery $query; public function __construct(UpstreamReadOnlyQueryFactory $queries) { $this->query = $queries->forSource(self::SOURCE); } /** @return array{rows:array>} */ 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[] = "[skucode] LIKE ? ESCAPE '\\'"; $bindings[] = $this->contains((string) $filters['barcode']); } $priceType = self::PRICE_TYPES[(string) ($filters['price_type'] ?? '')] ?? null; if ($priceType !== null) { $where[] = '[priceType] = ?'; $bindings[] = $priceType; } $costReduction = self::COST_REDUCTION[(string) ($filters['cost_reduction'] ?? '')] ?? null; if ($costReduction !== null) { $where[] = '[isCostReduction] = ?'; $bindings[] = $costReduction; } if (($filters['effective_from'] ?? '') !== '') { $where[] = '[effectStartTime] >= CAST(? AS date)'; $bindings[] = (string) $filters['effective_from']; } if (($filters['effective_to'] ?? '') !== '') { $where[] = '[effectStartTime] < DATEADD(day, 1, CAST(? AS date))'; $bindings[] = (string) $filters['effective_to']; } $whereSql = $where === [] ? '' : ' WHERE ' . implode(' AND ', $where); $offset = ($page - 1) * $limit; $fetch = $limit + 1; $sql = 'SELECT ' . '[skucode] AS barcode, ' . "CASE [priceType] WHEN '0' THEN N'定价' WHEN '1' THEN N'变价' ELSE NULLIF([priceType], '') END AS price_type, " . "CASE [priceChangeType] WHEN '0' THEN N'普通变价' WHEN '1' THEN N'清货变价' " . "WHEN '2' THEN N'阶段变价' WHEN '4' THEN N'打折入库' ELSE NULLIF([priceChangeType], '') END AS change_type, " . "CASE [isCostReduction] WHEN '1' THEN N'是' WHEN '0' THEN N'否' ELSE NULL END AS cost_reduction, " . '[originalPrice] AS price_before, ' . '[afterPrice] AS price_after, ' . '[diffPrice] AS price_diff, ' . '[applyUser] AS applicant, ' . '[remark] AS remark, ' . 'CONVERT(varchar(19), [effectStartTime], 120) AS effective_start, ' . 'CONVERT(varchar(19), [effectEndTime], 120) AS effective_end, ' . 'CONVERT(varchar(19), COALESCE([updateTime], [createTime]), 120) AS completed_at ' . 'FROM ' . self::TABLE . $whereSql . ' ORDER BY COALESCE([updateTime], [createTime]) DESC, [Id] DESC, [skucode] ASC' . " OFFSET {$offset} ROWS FETCH NEXT {$fetch} ROWS ONLY"; return ['rows' => array_map([$this, 'normalize'], $this->query->select($sql, $bindings))]; } private function contains(string $value): string { return '%' . str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], trim($value)) . '%'; } /** @param array $row */ private function normalize(array $row): array { foreach (['price_before', 'price_after', 'price_diff'] as $field) { $row[$field] = $row[$field] === null || $row[$field] === '' ? null : (float) $row[$field]; } return $row; } }