>,total:int,ready:bool} */ public function paginate(int $page, int $perPage): array { $connection = $this->mirror->connection(); $dataset = $this->datasets->get(self::DATASET); $items = $this->datasets->get(self::ITEM_DATASET); if (! $this->ready($dataset, $items, $connection->getName())) { return ['data' => [], 'total' => 0, 'ready' => false]; } $perPage = min(max($perPage, 1), 100); // 主视图按商品行展开,同一安装单会出现多行;主表只保留每个 // SourceGuid 的最新 Id,商品行通过 details() 单独展开。 $latest = $connection->table($dataset->targetTable()) ->selectRaw('MAX(Id) AS id') ->groupBy('SourceGuid'); $query = $connection->table($dataset->targetTable() . ' AS v') ->joinSub($latest, 'latest', static fn ($join) => $join->on('latest.id', '=', 'v.Id')) ->select('v.*'); $total = (clone $query)->count(); $rows = $query->orderByDesc('v.Id')->forPage(max($page, 1), $perPage)->get(); return ['data' => $rows->map(fn (object $row): array => $this->map((array) $row))->all(), 'total' => $total, 'ready' => true]; } /** @return array{data:list>,ready:bool} */ public function details(string $guid): array { $dataset = $this->datasets->get(self::DATASET); $items = $this->datasets->get(self::ITEM_DATASET); $connection = $this->mirror->connection(); if (! $this->ready($dataset, $items, $connection->getName())) { return ['data' => [], 'ready' => false]; } $mainExists = $connection->table($dataset->targetTable()) ->where('SourceGuid', $guid) ->exists(); if (! $mainExists) { return ['data' => [], 'ready' => true]; } $rows = $connection->table($items->targetTable()) ->where('guid', $guid) ->orderBy('Id') ->limit(100) ->get(); return ['data' => $rows->map(fn (object $row): array => $this->mapItem((array) $row))->all(), 'ready' => true]; } private function ready($dataset, $items, string $connection): bool { return $this->mirror->ready($dataset) && $this->mirror->ready($items) && Schema::connection($connection)->hasTable($dataset->targetTable()) && Schema::connection($connection)->hasTable($items->targetTable()); } /** @return array{rows:int,requested:float,settled:float,paid:float} */ public function summary(): array { $dataset = $this->datasets->get(self::DATASET); $connection = $this->mirror->connection(); if (! $this->mirror->ready($dataset) || ! Schema::connection($connection->getName())->hasTable($dataset->targetTable())) { return ['rows' => 0, 'requested' => 0.0, 'settled' => 0.0, 'paid' => 0.0]; } $latest = $connection->table($dataset->targetTable()) ->selectRaw('MAX(Id) AS id') ->groupBy('SourceGuid'); $row = $connection->table($dataset->targetTable() . ' AS v') ->joinSub($latest, 'latest', static fn ($join) => $join->on('latest.id', '=', 'v.Id')) ->selectRaw('COUNT(*) AS row_count, COALESCE(SUM(v.installcost),0) AS requested, COALESCE(SUM(v.settleAmount),0) AS settled, COALESCE(SUM(v.acceptAmount),0) AS paid') ->first(); return ['rows' => (int) $row->row_count, 'requested' => (float) $row->requested, 'settled' => (float) $row->settled, 'paid' => (float) $row->paid]; } /** @param array $row */ private function map(array $row): array { $value = static function (array $row, string ...$names): mixed { foreach ($names as $name) { foreach ($row as $key => $value) if (strcasecmp((string) $key, $name) === 0) return $value; } return null; }; $mapped = [ 'id' => $value($row, 'detail_id', 'Id'), 'installation_no' => $value($row, 'ordernum_id', 'OrderNum'), 'delete_status' => $value($row, 'Delete'), 'quote_status' => $value($row, 'BidStatus'), 'quote_deadline' => $value($row, 'BidEndTime'), 'installation_company' => $value($row, 'installCompanyName'), 'submitted_at' => $value($row, 'SubmitTime'), 'shipped_at' => $value($row, 'ShipTIme'), 'buyer_nickname' => $value($row, 'BuyersNick'), 'store_name' => $value($row, 'shop_name', 'shopname'), 'installation_type' => $value($row, 'TypeDto'), 'pre_push_verification' => $value($row, 'checkMsg'), 'push_status' => $value($row, 'apistatus'), 'push_result' => $value($row, 'apiresult'), 'arranged_status' => $this->arranged($value($row, 'StatusId')), 'order_no' => $value($row, 'OrderNum'), 'item_quantity' => $value($row, 'Num'), 'buyer_name' => $value($row, 'BuyersName'), 'buyer_phone' => $value($row, 'Mobile'), 'notes' => $value($row, 'Nots'), 'province' => $value($row, 'Province'), 'city' => $value($row, 'City'), 'district' => $value($row, 'Area'), 'address' => $value($row, 'Address'), 'requested_amount' => $value($row, 'installcost'), 'settled_amount' => $value($row, 'settleAmount'), 'image_urls' => $value($row, 'Picture'), 'completed_status' => $this->yesNo($value($row, 'installComplete')), 'customer_service_review' => $this->review($value($row, 'kfsh')), 'customer_service_reviewed_at' => $value($row, 'kfshTime'), 'finance_review' => $this->review($value($row, 'cwsh')), 'finance_reviewed_at' => $value($row, 'cwshTime'), 'actual_install_quantity' => $value($row, 'installNum'), 'actual_install_fee' => $value($row, 'install_Cost'), 'actual_install_product_name' => $value($row, 'installGoodsName'), 'installer_phone' => $value($row, 'installTel'), 'installation_company_notes' => $value($row, 'installNote'), 'installation_fee_payer' => $value($row, 'costtaker'), 'payment_channel' => $value($row, 'costWay'), 'payment_transaction_no' => $value($row, 'actporderNum'), 'payment_voucher' => $value($row, 'costWayVoucher'), 'payment_amount' => $value($row, 'acceptAmount'), 'creator' => $value($row, 'Operation'), 'related_guid' => $value($row, 'detail_guid', 'SourceGuid'), 'product_id' => $value($row, 'shopproid'), 'auxiliary_spu' => $value($row, 'Spu'), 'product_store_code' => $value($row, 'shopskucode'), 'item_no' => $value($row, 'proCode'), 'barcode' => $value($row, 'barCode'), 'product_name' => $value($row, 'skuName'), 'supplier_name' => $value($row, 'scmname'), 'detail_requested_amount' => $value($row, 'applyItemAmount'), 'detail_requested_quantity' => $value($row, 'installcount'), 'detail_settled_amount' => $value($row, 'settleItemAmount'), 'detail_received_amount' => $value($row, 'acceptItemAmount'), 'change_reason' => $value($row, 'remark'), 'source_created_at' => $value($row, 'item_created_at'), 'source_modified_at' => $value($row, 'item_modified_at'), ]; // 页面按模板的完整 61 列渲染;视图暂未提供的字段必须显式为 null, // 不能让 Blade 读取未定义属性。 return array_replace(array_fill_keys(array_keys(\App\Models\Business\InstallationDetail::columnLabels()), null), $mapped); } /** @param array $row */ private function mapItem(array $row): array { $value = static function (array $row, string ...$names): mixed { foreach ($names as $name) { foreach ($row as $key => $value) { if (strcasecmp((string) $key, $name) === 0) { return $value; } } } return null; }; return [ 'transaction_no' => $value($row, 'orderNum', 'ordernum'), 'product_id' => $value($row, 'shopproid'), 'id_owner' => $value($row, 'id_owner', 'idOwner'), 'department_owner' => $value($row, 'department_owner', 'departmentOwner'), 'auxiliary_spu' => $value($row, 'Spu'), 'product_store_code' => $value($row, 'shopskucode'), 'item_no' => $value($row, 'proCode'), 'barcode' => $value($row, 'barCode'), 'product_name' => $value($row, 'skuName'), 'supplier_name' => $value($row, 'scmname'), 'detail_requested_amount' => $value($row, 'applyItemAmount'), 'detail_requested_quantity' => $value($row, 'installcount'), 'detail_settled_amount' => $value($row, 'settleItemAmount'), 'detail_received_amount' => $value($row, 'acceptItemAmount'), 'installation_company' => $value($row, 'installCompanyName'), 'available' => $value($row, 'isAvailable', 'available'), 'quote' => $value($row, 'quote'), 'quote_owner' => $value($row, 'quoteOwner'), 'quote_time' => $value($row, 'quoteTime'), ]; } private function yesNo(mixed $value): ?string { return $value === null ? null : ((int) $value === 1 ? '是' : '否'); } private function review(mixed $value): ?string { return $value === null ? null : ((int) $value === 1 ? '已审核' : '未审核'); } private function arranged(mixed $value): ?string { return $value === null ? null : ((int) $value > 0 ? '已安排' : '未安排'); } }