db = DB::connection((string) config('jushuitan.connection', 'jushuitan_local')); } /** * @param array $filters * @return array{data:array,total:int} */ public function paginate(array $filters, int $page, int $perPage): array { $page = max($page, 1); $perPage = min(max($perPage, 1), 100); $q = $this->filteredQuery($filters); $total = (clone $q)->count(); $rows = (clone $q) ->orderByDesc('ship_time') ->orderByDesc('id') ->forPage($page, $perPage) ->get() ->map(fn ($r) => (array) $r) ->all(); return ['data' => $rows, 'total' => $total]; } /** * @param array $filters * @return \Generator> */ public function exportRows(array $filters, int $batchSize = 1000): \Generator { $batchSize = min(max($batchSize, 1), 5000); $lastId = null; while (true) { $query = $this->filteredQuery($filters); if ($lastId !== null) { $query->where('id', '>', $lastId); } $rows = $query->orderBy('id')->limit($batchSize)->get()->all(); if ($rows === []) { return; } foreach ($rows as $row) { yield (array) $row; } $lastId = (int) $rows[array_key_last($rows)]->id; if (count($rows) < $batchSize) { return; } } } /** @param array $filters */ private function filteredQuery(array $filters): Builder { $query = $this->db->table('jst_oms_shipping'); if (($filters['express_no'] ?? '') !== '') { $query->where('express_no', '=', trim((string) $filters['express_no'])); } if (($filters['sourceno'] ?? '') !== '') { $query->where('trade_no', '=', trim((string) $filters['sourceno'])); } foreach (['barcode', 'supplier_name', 'shop_name', 'warehouse_name'] as $column) { if (($filters[$column] ?? '') !== '') { $query->whereRaw($column . " LIKE ? ESCAPE '!'", [$this->contains((string) $filters[$column])]); } } if (($filters['express_company'] ?? '') !== '') { $value = $this->contains((string) $filters['express_company']); $query->where(static function (Builder $courier) use ($value): void { $courier->whereRaw("express_name1 LIKE ? ESCAPE '!'", [$value]) ->orWhereRaw("express_name2 LIKE ? ESCAPE '!'", [$value]); }); } if (($filters['ship_from'] ?? '') !== '') { $query->where('ship_time', '>=', $filters['ship_from'] . ' 00:00:00'); } if (($filters['ship_to'] ?? '') !== '') { $query->where('ship_time', '<', date('Y-m-d 00:00:00', strtotime($filters['ship_to'] . ' +1 day'))); } return $query; } private function contains(string $value): string { return '%' . str_replace(['!', '%', '_'], ['!!', '!%', '!_'], trim($value)) . '%'; } /** * 取某字段去重候选值(供筛选框自动补全)。 * @return array */ public function distinctValues(string $field, string $keyword, int $limit = 20): array { $map = [ 'supplier_name' => ['supplier_name'], 'shop_name' => ['shop_name'], 'warehouse_name' => ['warehouse_name'], 'express_company' => ['express_name1', 'express_name2'], ]; if (! isset($map[$field])) { return []; } $limit = min(max($limit, 1), 50); $like = $this->contains($keyword); $values = []; foreach ($map[$field] as $col) { $rows = $this->db->table('jst_oms_shipping') ->selectRaw($col . ' as v') ->whereRaw($col . " LIKE ? ESCAPE '!'", [$like]) ->whereNotNull($col) ->where($col, '<>', '') ->groupBy($col) ->orderBy($col) ->limit($limit) ->pluck('v'); foreach ($rows as $v) { $v = trim((string) $v); if ($v !== '') { $values[$v] = true; } } } $values = array_keys($values); sort($values, SORT_FLAG_CASE | SORT_STRING); return array_slice($values, 0, $limit); } /** 同步状态(供页面显示同步中/最近完成)。 */ public function syncStatus(): array { try { $row = $this->db->table('jst_sync_state')->where('dataset_key', 'jst.sctrade')->first(); if (! $row) { return ['code' => 'unavailable', 'last_succeeded_at' => null]; } return [ 'code' => (string) ($row->status ?? 'unavailable'), 'last_succeeded_at' => $row->last_succeeded_at ?? null, ]; } catch (\Throwable) { return ['code' => 'unavailable', 'last_succeeded_at' => null]; } } }