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[] = "[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> */ 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 $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; } }