diff --git a/backend/app/controller/UsedCarController.php b/backend/app/controller/UsedCarController.php new file mode 100644 index 0000000..a9978b7 --- /dev/null +++ b/backend/app/controller/UsedCarController.php @@ -0,0 +1,357 @@ +service = new UsedCarService(); + $this->pdfService = new PdfService(); + } + + /** + * 获取二手车列表 + */ + public function list(Request $request): Response + { + $userId = $this->getUserId($request); + $type = $request->get('type', 'self'); + $page = (int)$request->get('page', 1); + $pageSize = (int)$request->get('page_size', 20); + $keyword = $request->get('keyword'); + $status = $request->get('status'); + + try { + $result = $this->service->getList( + $userId, + $type, + $page, + $pageSize, + $keyword, + $status !== null ? (int)$status : null + ); + + return $this->success($result); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 获取二手车详情 + */ + public function detail(Request $request): Response + { + $userId = $this->getUserId($request); + $id = (int)$request->get('id'); + + if ($id <= 0) { + return $this->error('无效的ID'); + } + + try { + $detail = $this->service->getDetail($id, $userId); + return $this->success($detail); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 创建二手车记录 + */ + public function create(Request $request): Response + { + $userId = $this->requireAuth($request); + $data = $request->post(); + + try { + $car = $this->service->create($data, $userId); + return $this->success($car->toArray(), '创建成功'); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 更新二手车记录 + */ + public function update(Request $request): Response + { + $userId = $this->requireAuth($request); + $id = (int)$request->get('id'); + $data = $request->post(); + + if ($id <= 0) { + return $this->error('无效的ID'); + } + + try { + $car = $this->service->update($id, $data, $userId); + return $this->success($car->toArray(), '更新成功'); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 删除二手车记录 + */ + public function delete(Request $request): Response + { + $userId = $this->requireAuth($request); + $id = (int)$request->get('id'); + + if ($id <= 0) { + return $this->error('无效的ID'); + } + + try { + $this->service->delete($id, $userId); + return $this->success(null, '删除成功'); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 更新状态 + */ + public function updateStatus(Request $request): Response + { + $userId = $this->requireAuth($request); + $id = (int)$request->post('id'); + $status = (int)$request->post('status'); + + if ($id <= 0) { + return $this->error('无效的ID'); + } + + try { + $this->service->updateStatus($id, $status, $userId); + return $this->success(null, '状态更新成功'); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * URL抓取车辆信息 + */ + public function crawlByUrl(Request $request): Response + { + $this->requireAuth($request); + $url = $request->post('url') ?: $request->get('url'); + + if (empty($url)) { + return $this->error('URL不能为空'); + } + + try { + $data = $this->service->crawlByUrl($url); + return $this->success($data); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 移动端链接抓取(处理短链) + */ + public function crawlMobileUrl(Request $request): Response + { + $this->requireAuth($request); + $url = $request->post('url') ?: $request->get('url'); + + if (empty($url)) { + return $this->error('URL不能为空'); + } + + try { + $data = $this->service->crawlByUrl($url); + return $this->success($data); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 生成PDF + */ + public function generatePdf(Request $request): Response + { + $userId = $this->requireAuth($request); + $id = (int)$request->post('id'); + $price = (float)$request->post('price'); + $lang = $request->post('lang', 'zh'); + $currency = $request->post('currency', 'CNY'); + $tradeTerm = $request->post('trade_term', ''); + + if ($id <= 0) { + return $this->error('无效的ID'); + } + + if ($price <= 0) { + return $this->error('请输入有效的价格'); + } + + try { + $pdfFileName = $this->pdfService->generateUsedCarPdf( + $id, + $userId, + $price, + $lang, + $currency, + $tradeTerm + ); + + return $this->success([ + 'pdf_file_name' => $pdfFileName, + ], 'PDF生成成功'); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 下载PDF + */ + public function downloadPdf(Request $request): Response + { + $userId = $this->getUserId($request); + $id = (int)$request->get('id'); + + if ($id <= 0) { + return $this->error('无效的ID'); + } + + try { + $car = \app\Model\UsedCar::findOrFail($id); + + if (empty($car->pdf_src)) { + return $this->error('PDF文件不存在,请先生成PDF'); + } + + $pdfPath = $this->pdfService->getPdfPath($car->pdf_src); + + if (!$this->pdfService->pdfExists($car->pdf_src)) { + return $this->error('PDF文件不存在'); + } + + // 设置下载的文件名 + $filename = $car->product_name . '-' . $car->code . '-' . $car->id . '.pdf'; + + // 返回文件下载响应 + return response()->file($pdfPath, $filename); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 获取PDF预览URL + */ + public function getPdfUrl(Request $request): Response + { + $userId = $this->getUserId($request); + $id = (int)$request->get('id'); + + if ($id <= 0) { + return $this->error('无效的ID'); + } + + try { + $car = \app\Model\UsedCar::findOrFail($id); + + if (empty($car->pdf_src)) { + return $this->error('PDF文件不存在,请先生成PDF'); + } + + if (!$this->pdfService->pdfExists($car->pdf_src)) { + return $this->error('PDF文件不存在'); + } + + // 将PDF复制到public目录以便访问 + $publicPdfDir = public_path('pdf'); + if (!is_dir($publicPdfDir)) { + mkdir($publicPdfDir, 0755, true); + } + + $publicPdfPath = $publicPdfDir . '/' . $car->pdf_src; + if (!file_exists($publicPdfPath)) { + copy($this->pdfService->getPdfPath($car->pdf_src), $publicPdfPath); + } + + // 构建访问URL + $baseUrl = $this->getBaseUrl($request); + $pdfUrl = $baseUrl . '/pdf/' . $car->pdf_src; + + return $this->success([ + 'pdf_url' => $pdfUrl, + 'file_name' => $car->product_name . '-' . $car->code . '-' . $car->id . '.pdf', + ]); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 预览PDF(直接返回PDF内容) + */ + public function previewPdf(Request $request): Response + { + $id = (int)$request->get('id'); + + if ($id <= 0) { + return $this->error('无效的ID'); + } + + try { + $car = \app\Model\UsedCar::findOrFail($id); + + if (empty($car->pdf_src)) { + return $this->error('PDF文件不存在,请先生成PDF'); + } + + $pdfPath = $this->pdfService->getPdfPath($car->pdf_src); + + if (!$this->pdfService->pdfExists($car->pdf_src)) { + return $this->error('PDF文件不存在'); + } + + // 返回PDF内容 + $content = file_get_contents($pdfPath); + + return response($content, 200, [ + 'Content-Type' => 'application/pdf', + 'Access-Control-Allow-Origin' => '*', + 'Access-Control-Allow-Methods' => 'GET, OPTIONS', + 'Access-Control-Allow-Headers' => '*', + ]); + } catch (\Exception $e) { + return $this->error($e->getMessage()); + } + } + + /** + * 获取基础URL + */ + private function getBaseUrl(Request $request): string + { + $scheme = $request->header('x-forwarded-proto') ?? $request->header('x-scheme') ?? 'https'; + $host = $request->host(); + return $scheme . '://' . $host; + } +} diff --git a/backend/app/model/UsedCar.php b/backend/app/model/UsedCar.php new file mode 100644 index 0000000..c3656a6 --- /dev/null +++ b/backend/app/model/UsedCar.php @@ -0,0 +1,143 @@ + 'integer', + 'mileage' => 'integer', + 'crawl_brand_id' => 'integer', + 'crawl_series_id' => 'integer', + 'crawl_msrp' => 'integer', + 'crawl_car_id' => 'integer', + 'status' => 'integer', + 'sort' => 'integer', + 'attachment' => 'array', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * 状态常量 + */ + const STATUS_NORMAL = 1; // 正常 + const STATUS_ARCHIVED = 2; // 归档 + const STATUS_DELETED = 3; // 删除 + + /** + * 关联用户 + */ + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } + + /** + * 关联PDF生成日志 + */ + public function pdfLogs() + { + return $this->hasMany(UsedCarPdfLog::class, 'used_car_id')->orderBy('created_at', 'desc'); + } + + /** + * 判断是否可以编辑 + */ + public function canEdit(): bool + { + return $this->status === self::STATUS_NORMAL; + } + + /** + * 判断是否已删除 + */ + public function isDeleted(): bool + { + return $this->status === self::STATUS_DELETED; + } + + /** + * 判断是否已归档 + */ + public function isArchived(): bool + { + return $this->status === self::STATUS_ARCHIVED; + } + + /** + * 获取图片URL数组 + */ + public function getImageUrlsAttribute(): array + { + if (empty($this->images)) { + return []; + } + return explode(',', $this->images); + } + + /** + * 作用域:正常状态 + */ + public function scopeNormal($query) + { + return $query->where('status', self::STATUS_NORMAL); + } + + /** + * 作用域:按用户筛选 + */ + public function scopeByUser($query, int $userId) + { + return $query->where('user_id', $userId); + } + + /** + * 作用域:搜索关键词 + */ + public function scopeSearch($query, string $keyword) + { + return $query->where(function ($q) use ($keyword) { + $q->where('product_name', 'like', "%{$keyword}%") + ->orWhere('code', 'like', "%{$keyword}%"); + }); + } +} diff --git a/backend/app/model/UsedCarPdfLog.php b/backend/app/model/UsedCarPdfLog.php new file mode 100644 index 0000000..9727058 --- /dev/null +++ b/backend/app/model/UsedCarPdfLog.php @@ -0,0 +1,58 @@ + 'integer', + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + ]; + + /** + * 关联用户 + */ + public function user() + { + return $this->belongsTo(User::class, 'user_id'); + } + + /** + * 关联二手车 + */ + public function usedCar() + { + return $this->belongsTo(UsedCar::class, 'used_car_id'); + } + + /** + * 作用域:按二手车筛选 + */ + public function scopeByUsedCar($query, int $usedCarId) + { + return $query->where('used_car_id', $usedCarId); + } + + /** + * 作用域:按用户筛选 + */ + public function scopeByUser($query, int $userId) + { + return $query->where('user_id', $userId); + } +} diff --git a/backend/app/process/Task.php b/backend/app/process/Task.php new file mode 100644 index 0000000..c43f9db --- /dev/null +++ b/backend/app/process/Task.php @@ -0,0 +1,53 @@ + date('Y-m-d H:i:s')]); + + // 清空数据库中的PDF路径 + Db::table('used_car')->update(['pdf_src' => '']); + + // 删除runtime目录下的PDF文件 + $pdfFiles = glob(runtime_path('pdf/*.pdf')); + if (is_array($pdfFiles)) { + array_map('unlink', $pdfFiles); + } + + // 删除public目录下的PDF文件 + $publicPdfFiles = glob(public_path('pdf/*.pdf')); + if (is_array($publicPdfFiles)) { + array_map('unlink', $publicPdfFiles); + } + + Log::info('PDF文件清理完成', ['time' => date('Y-m-d H:i:s')]); + }); + + // 每3小时自动归档15天前的二手车记录(将状态改为删除) + new Crontab('0 25 */3 * * *', function () { + Log::info('开始归档二手车记录', ['time' => date('Y-m-d H:i:s')]); + + // 将15天前创建的记录状态改为删除(状态=3) + $count = Db::table('used_car') + ->where('status', UsedCar::STATUS_NORMAL) + ->where('created_at', '<', date('Y-m-d H:i:s', time() - (86400 * 15))) + ->update(['status' => UsedCar::STATUS_DELETED]); + + Log::info('归档完成', ['affected_rows' => $count, 'time' => date('Y-m-d H:i:s')]); + }); + } +} diff --git a/backend/app/service/PdfService.php b/backend/app/service/PdfService.php new file mode 100644 index 0000000..a5c4cff --- /dev/null +++ b/backend/app/service/PdfService.php @@ -0,0 +1,518 @@ + '中文', + 'en' => '英语', + 'ru' => '俄语', + 'es' => '西班牙语', + 'fr' => '法语', + ]; + + /** + * 货币符号映射 + */ + private const CURRENCIES = [ + 'CNY' => '¥', + 'USD' => '$', + 'RUB' => '₽', + 'AED' => 'د.إ', + 'EUR' => '€', + ]; + + /** + * 生成二手车PDF + * + * @param int $usedCarId 二手车ID + * @param int $userId 用户ID + * @param float $price 价格(用于PDF显示) + * @param string $lang 语言代码 + * @param string $currency 货币代码 + * @param string $tradeTerm 贸易条款 + * @return string PDF文件名 + * @throws \Exception + */ + public function generateUsedCarPdf(int $usedCarId, int $userId, float $price, string $lang = 'zh', string $currency = 'CNY', string $tradeTerm = ''): string + { + // 验证参数 + if (!isset(self::LANGUAGES[$lang])) { + throw new \Exception('不支持的语言: ' . $lang); + } + if (!isset(self::CURRENCIES[$currency])) { + throw new \Exception('不支持的货币: ' . $currency); + } + + // 获取车辆信息 + $car = UsedCar::with('user')->findOrFail($usedCarId); + + // 检查权限 + if ($car->user_id !== $userId) { + throw new \Exception('无权生成此车辆的PDF'); + } + + // 获取用户信息 + $user = $car->user ?? User::findOrFail($userId); + + // 准备PDF数据 + $pdfData = $this->preparePdfData($car, $price, $lang, $currency, $tradeTerm, $user); + + // 生成PDF + $pdfFileName = $this->renderPdf($pdfData, $car, $user, $lang); + + // 更新车辆记录 + $car->pdf_src = $pdfFileName; + $car->save(); + + // 记录PDF生成日志 + UsedCarPdfLog::create([ + 'user_id' => $userId, + 'used_car_id' => $usedCarId, + 'price_snapshot' => (int)($price * 100), // 转换为分 + 'pdf_path' => $pdfFileName, + ]); + + // 清理临时文件 + $this->cleanupTempFiles(); + + return $pdfFileName; + } + + /** + * 准备PDF数据 + */ + private function preparePdfData(UsedCar $car, float $price, string $lang, string $currency, string $tradeTerm, User $user): array + { + // 获取字段翻译 + $field = $this->getFieldTranslations($lang); + $field['currency'] = self::CURRENCIES[$currency]; + + // 获取名片翻译 + $contactField = $this->getContactTranslations($lang); + + // 构建车辆名称 + $productName = trim(($car->crawl_brand ?? '') . ' ' . ($car->crawl_series ?? '') . ' ' . ($car->crawl_year ?? '') . '款 ' . ($car->crawl_title ?? $car->product_name)); + + // 处理图片 - 下载到本地 + $images = []; + $imageDir = runtime_path('pdf/images/'); + if (!is_dir($imageDir)) { + mkdir($imageDir, 0755, true); + } + + if (!empty($car->images)) { + $imageUrls = explode(',', $car->images); + $crawlService = new UsedCarCrawlService(); + $downloadedImages = $crawlService->downloadImages($imageUrls, $imageDir); + + foreach ($downloadedImages as $localPath) { + if ($localPath && file_exists($localPath)) { + $images[] = $localPath; + } + } + } + + // 处理附件PDF + $attachments = []; + if (!empty($car->attachment)) { + $attachmentData = json_decode($car->attachment, true); + if (is_array($attachmentData)) { + $attachmentDir = runtime_path('pdf/attachment/'); + if (!is_dir($attachmentDir)) { + mkdir($attachmentDir, 0755, true); + } + + foreach ($attachmentData as $attachment) { + $src = $attachment['src'] ?? ''; + if (str_ends_with(strtolower($src), '.pdf')) { + // 下载PDF附件 + $localPath = $this->downloadAttachment($src, $attachmentDir); + if ($localPath && file_exists($localPath)) { + $attachments[] = $localPath; + } + } + } + } + } + + // 处理社交二维码 + $socialCode = ''; + if (!empty($user->social_code)) { + $socialCodePath = $this->downloadSocialCode($user->social_code); + if ($socialCodePath && file_exists($socialCodePath)) { + $socialCode = $socialCodePath; + } + } + + return [ + 'car' => [ + 'product_name' => $productName, + 'price' => $price, + 'trade' => $tradeTerm, + 'plate_time' => $car->plate_time ?? '', + 'displacement' => $car->displacement ?? '', + 'mileage' => $car->mileage ?? 0, + 'power' => $car->power ?? '', + 'body_color' => $car->body_color ?? '', + 'condition_desc' => $car->condition_desc ?? '', + 'images' => $images, + 'attachment' => $attachments, + 'created_at' => $car->created_at ? $car->created_at->format('Y-m-d H:i:s') : '', + ], + 'user' => [ + 'name' => $user->name ?? '', + 'name_en' => $user->name_en ?? $user->name ?? '', + 'email' => $user->email ?? '', + 'phone' => $user->phone ?? '', + 'whatsapp' => $user->whatsapp ?? '', + 'social' => !empty($user->social_code), + 'social_code' => $socialCode, + ], + 'field' => $field, + 'contact_field' => $contactField, + 'lang' => $lang, + ]; + } + + /** + * 渲染PDF + */ + private function renderPdf(array $pdfData, UsedCar $car, User $user, string $lang): string + { + // 创建临时目录 + $tempDir = runtime_path('pdf/temp/'); + if (!is_dir($tempDir)) { + mkdir($tempDir, 0755, true); + } + + // 初始化mPDF + $mpdf = new Mpdf([ + 'mode' => 'utf-8', + 'format' => 'A4-L', // 横向 + 'margin_top' => 10, + 'margin_bottom' => 10, + 'margin_left' => 10, + 'margin_right' => 10, + 'tempDir' => $tempDir, + 'default_font' => 'sun-exta', + ]); + + // 设置水印 + $watermarkText = $pdfData['user']['name_en'] ?? $pdfData['user']['name']; + if (!empty($watermarkText)) { + $mpdf->SetWatermarkText($watermarkText, 0.08); + $mpdf->showWatermarkText = true; + } + + // 渲染车辆详情页面 + ob_start(); + $carTpl = runtime_path('pdf/templates/usedCar.stub'); + extract([ + 'car' => $pdfData['car'], + 'field' => $pdfData['field'], + ]); + include $carTpl; + $carHtml = ob_get_clean(); + $mpdf->WriteHTML($carHtml); + + // 追加附件PDF + foreach ($pdfData['car']['attachment'] as $attachmentPdf) { + $this->appendPdf($mpdf, $attachmentPdf); + } + + // 添加名片页面 + $mpdf->AddPage(); + ob_start(); + $cardTpl = runtime_path('pdf/templates/contactCard.stub'); + extract([ + 'user' => $pdfData['user'], + 'contactField' => $pdfData['contact_field'], + ]); + include $cardTpl; + $cardHtml = ob_get_clean(); + $mpdf->WriteHTML($cardHtml); + + // 生成PDF文件 + $pdfDir = runtime_path('pdf/'); + if (!is_dir($pdfDir)) { + mkdir($pdfDir, 0755, true); + } + + $pdfFileName = 'usedcar-' . $car->code . '-' . $car->id . '-' . $lang . '-' . substr(time(), -6) . '.pdf'; + $pdfPath = $pdfDir . '/' . $pdfFileName; + + $mpdf->Output($pdfPath, \Mpdf\Output\Destination::FILE); + + return $pdfFileName; + } + + /** + * 追加PDF到现有文档 + */ + private function appendPdf(Mpdf $mpdf, string $pdfPath): void + { + if (!file_exists($pdfPath)) { + return; + } + + try { + $pageCount = $mpdf->SetSourceFile($pdfPath); + for ($i = 1; $i <= $pageCount; $i++) { + $tplId = $mpdf->ImportPage($i); + $size = $mpdf->getTemplateSize($tplId); + + $mpdf->AddPageByArray([ + 'orientation' => $size['orientation'], + 'sheet-size' => [$size['width'], $size['height']] + ]); + + $mpdf->UseTemplate($tplId); + } + } catch (\Exception $e) { + error_log('追加PDF失败: ' . $e->getMessage()); + } + } + + /** + * 下载附件 + */ + private function downloadAttachment(string $url, string $saveDir): ?string + { + $filename = md5($url) . '.pdf'; + $savePath = $saveDir . $filename; + + if (file_exists($savePath)) { + return $savePath; + } + + try { + $content = file_get_contents($url); + if ($content !== false) { + file_put_contents($savePath, $content); + return $savePath; + } + } catch (\Exception $e) { + error_log('下载附件失败: ' . $e->getMessage()); + } + + return null; + } + + /** + * 下载社交二维码 + */ + private function downloadSocialCode(string $url): ?string + { + $saveDir = runtime_path('pdf/qrcode/'); + if (!is_dir($saveDir)) { + mkdir($saveDir, 0755, true); + } + + $filename = md5($url) . '.jpg'; + $savePath = $saveDir . $filename; + + if (file_exists($savePath)) { + return $savePath; + } + + try { + $content = file_get_contents($url); + if ($content !== false) { + file_put_contents($savePath, $content); + return $savePath; + } + } catch (\Exception $e) { + error_log('下载二维码失败: ' . $e->getMessage()); + } + + return null; + } + + /** + * 清理临时文件 + */ + private function cleanupTempFiles(): void + { + // 清理图片 + $imageDir = runtime_path('pdf/images/'); + if (is_dir($imageDir)) { + array_map('unlink', glob($imageDir . '*.jpg') ?: []); + array_map('unlink', glob($imageDir . '*.png') ?: []); + } + + // 清理附件 + $attachmentDir = runtime_path('pdf/attachment/'); + if (is_dir($attachmentDir)) { + array_map('unlink', glob($attachmentDir . '*.pdf') ?: []); + } + + // 清理二维码 + $qrcodeDir = runtime_path('pdf/qrcode/'); + if (is_dir($qrcodeDir)) { + array_map('unlink', glob($qrcodeDir . '*.jpg') ?: []); + array_map('unlink', glob($qrcodeDir . '*.png') ?: []); + } + } + + /** + * 获取字段翻译 + */ + private function getFieldTranslations(string $lang): array + { + $translations = [ + 'zh' => [ + 'price' => '车价', + 'source_place' => '车源地', + 'plate_time' => '上牌时间', + 'displacement' => '排量', + 'mileage' => '里程', + 'power' => '马力', + 'body_color' => '车身颜色', + 'created_at' => '创建时间', + 'condition_desc' => '车况介绍', + 'images' => '产品图', + 'trade' => '贸易条款', + ], + 'en' => [ + 'price' => 'Price', + 'source_place' => 'Vehicle Location', + 'plate_time' => 'Registration Date', + 'displacement' => 'Engine Displacement', + 'mileage' => 'Mileage', + 'power' => 'Horsepower', + 'body_color' => 'Body Color', + 'created_at' => 'Created At', + 'condition_desc' => 'Vehicle Condition', + 'images' => 'Product Image', + 'trade' => 'Trade Terms', + ], + 'ru' => [ + 'price' => 'Цена', + 'source_place' => 'Местонахождение автомобиля', + 'plate_time' => 'Дата регистрации', + 'displacement' => 'Объём двигателя', + 'mileage' => 'Пробег', + 'power' => 'Мощность', + 'body_color' => 'Цвет кузова', + 'created_at' => 'Дата создания', + 'condition_desc' => 'Состояние автомобиля', + 'images' => 'Фотографии', + 'trade' => 'Условия торговли', + ], + 'es' => [ + 'price' => 'Precio', + 'source_place' => 'Ubicación del vehículo', + 'plate_time' => 'Fecha de matriculación', + 'displacement' => 'Cilindrada', + 'mileage' => 'Kilometraje', + 'power' => 'Potencia', + 'body_color' => 'Color de la carrocería', + 'created_at' => 'Fecha de creación', + 'condition_desc' => 'Estado del vehículo', + 'images' => 'Imagen del producto', + 'trade' => 'Condiciones de comercio', + ], + 'fr' => [ + 'price' => 'Prix', + 'source_place' => 'Emplacement du véhicule', + 'plate_time' => 'Date d\'immatriculation', + 'displacement' => 'Cylindrée', + 'mileage' => 'Kilométrage', + 'power' => 'Puissance', + 'body_color' => 'Couleur de la carrosserie', + 'created_at' => 'Date de création', + 'condition_desc' => 'État du véhicule', + 'images' => 'Image du produit', + 'trade' => 'Conditions commerciales', + ], + ]; + + return $translations[$lang] ?? $translations['zh']; + } + + /** + * 获取名片翻译 + */ + private function getContactTranslations(string $lang): array + { + $translations = [ + 'zh' => [ + 'title' => '联系人名片', + 'name' => '姓名', + 'email' => '邮箱', + 'phone' => '电话', + 'code' => '二维码', + 'left' => '扫码即可联系本人', + 'right' => '本资料随PDF自动生成', + ], + 'en' => [ + 'title' => 'Contact Name Card', + 'name' => 'Name', + 'email' => 'Email', + 'phone' => 'Phone', + 'code' => 'QR Code', + 'left' => 'Scan the code to contact me', + 'right' => 'This information is automatically generated with the PDF', + ], + 'ru' => [ + 'title' => 'Контактная карточка', + 'name' => 'Имя', + 'email' => 'Электронная почта', + 'phone' => 'Телефон', + 'code' => 'QR-код', + 'left' => 'Код позволяет связаться с самим собой.', + 'right' => 'Эта информация автоматически генерируется с помощью PDF', + ], + 'es' => [ + 'title' => 'Tarjeta de visita de contacto', + 'name' => 'Nombre', + 'email' => 'Correo electrónico', + 'phone' => 'Teléfono', + 'code' => 'Código QR', + 'left' => 'Escanee el código para contactarme.', + 'right' => 'Esta información se genera automáticamente con PDF', + ], + 'fr' => [ + 'title' => 'Carte de visite de contact', + 'name' => 'Nom', + 'email' => 'E-mail', + 'phone' => 'Téléphone', + 'code' => 'Code QR', + 'left' => 'Scannez le code pour me contacter', + 'right' => 'Ces informations sont générées automatiquement avec le PDF', + ], + ]; + + return $translations[$lang] ?? $translations['zh']; + } + + /** + * 获取PDF文件路径 + */ + public function getPdfPath(string $pdfFileName): string + { + return runtime_path('pdf/' . $pdfFileName); + } + + /** + * 检查PDF文件是否存在 + */ + public function pdfExists(string $pdfFileName): bool + { + return file_exists($this->getPdfPath($pdfFileName)); + } +} diff --git a/backend/app/service/UsedCarCrawlService.php b/backend/app/service/UsedCarCrawlService.php new file mode 100644 index 0000000..6e0ef31 --- /dev/null +++ b/backend/app/service/UsedCarCrawlService.php @@ -0,0 +1,524 @@ + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36', + 'Accept-Language' => 'zh-CN,zh;q=0.9', + 'Referer' => 'https://www.dongchedi.com/', + ]; + + public function __construct() + { + $this->httpClient = new Client([ + 'verify' => false, + 'timeout' => 15, + ]); + } + + /** + * 选择抓取方式并执行 + * + * @param string $url 车辆详情页URL + * @return array 抓取结果 + * @throws \Exception + */ + public function crawlByUrl(string $url): array + { + // 根据URL判断来源并选择抓取方法 + if (str_contains($url, 'dongchedi.com')) { + // 懂车帝PC端 + return $this->crawlDongchedi($url); + } elseif (str_contains($url, 'dcdapp.com')) { + // 懂车帝移动端 - 需要先转换为PC端 + $pcUrl = $this->getDongchediPcUrl($url); + if (!$pcUrl) { + throw new \Exception('无法获取懂车帝PC端链接'); + } + return $this->crawlDongchedi($pcUrl); + } elseif (str_contains($url, 't.zijieimg.com')) { + // 懂车帝分享短链 + $finalUrl = $this->getFinalUrl($url); + $pcUrl = $this->getDongchediPcUrl($finalUrl); + if (!$pcUrl) { + throw new \Exception('无法获取懂车帝PC端链接'); + } + return $this->crawlDongchedi($pcUrl); + } elseif (str_contains($url, 'che168.com')) { + // 汽车之家 + return $this->crawlChe168($url); + } + + throw new \Exception('暂不支持此网站的数据抓取'); + } + + /** + * 获取短链的最终URL + */ + private function getFinalUrl(string $url): string + { + try { + $response = $this->httpClient->get($url, [ + 'allow_redirects' => ['track_redirects' => true], + ]); + + $history = $response->getHeader('X-Guzzle-Redirect-History'); + return end($history) ?: $url; + } catch (\Exception $e) { + throw new \Exception('获取短链失败: ' . $e->getMessage()); + } + } + + /** + * 从懂车帝移动端URL获取PC端URL + */ + private function getDongchediPcUrl(string $url): ?string + { + $query = parse_url($url, PHP_URL_QUERY); + if (!$query) { + return null; + } + + parse_str($query, $params); + $skuId = $params['sku_id'] ?? null; + + return $skuId ? 'https://www.dongchedi.com/usedcar/' . $skuId : null; + } + + /** + * 抓取懂车帝二手车信息 + */ + private function crawlDongchedi(string $url): array + { + try { + $html = $this->fetchHtml($url); + + if (str_contains($html, '404 Not Found')) { + throw new \Exception('车辆已下架或找不到页面'); + } + + return $this->parseDongchediHtml($html, $url); + } catch (\Exception $e) { + throw new \Exception('抓取懂车帝数据失败: ' . $e->getMessage()); + } + } + + /** + * 抓取汽车之家二手车信息 + */ + private function crawlChe168(string $url): array + { + try { + // 处理不同格式的汽车之家URL + if (str_contains($url, 'home?infoid=')) { + // PC端链接转换 + $queryString = parse_url($url, PHP_URL_QUERY); + parse_str($queryString, $params); + $carId = $params['infoid'] ?? null; + $url = 'https://www.che168.com/dealer/111/' . $carId . '.html?pvareaid=0'; + } elseif (str_contains($url, 'wap.che168.com/dealer')) { + // 移动端链接转换 + $url = $this->getChe168PcUrl($url); + } + + $html = $this->fetchHtml($url); + return $this->parseChe168Html($html, $url); + } catch (\Exception $e) { + throw new \Exception('抓取汽车之家数据失败: ' . $e->getMessage()); + } + } + + /** + * 从汽车之家移动端URL获取PC端URL + */ + private function getChe168PcUrl(string $url): string + { + $path = parse_url($url, PHP_URL_PATH); + $parts = explode('/', $path); + $filename = end($parts); + $carId = pathinfo($filename, PATHINFO_FILENAME); + + return 'https://www.che168.com/dealer/111/' . $carId . '.html?pvareaid=0'; + } + + /** + * 获取网页HTML内容 + */ + private function fetchHtml(string $url): string + { + try { + $response = $this->httpClient->get($url, [ + 'headers' => self::DEFAULT_HEADERS, + ]); + return (string)$response->getBody(); + } catch (GuzzleException $e) { + throw new \Exception('获取页面失败: ' . $e->getMessage()); + } + } + + /** + * 解析懂车帝HTML + */ + private function parseDongchediHtml(string $html, string $url): array + { + try { + $ql = QueryList::html($html); + + // 提取JSON数据 + $jsonText = $ql->find('#__NEXT_DATA__')->text(); + if (empty($jsonText)) { + throw new \Exception('无法找到页面数据'); + } + + $jsonData = json_decode($jsonText, true); + if (!$jsonData) { + throw new \Exception('解析JSON数据失败'); + } + + $skuDetail = $jsonData['props']['pageProps']['skuDetail'] ?? []; + $carInfo = $skuDetail['car_info'] ?? []; + + // 提取价格(懂车帝使用特殊字符编码) + $priceText = $ql->find('.head-info_price-wrap__Y4bxi > p')->text(); + $price = $this->decodeDongchediPrice($priceText); + + // 提取车辆信息 + $title = $carInfo['car_name'] ?? ''; + $brandName = $carInfo['brand_name'] ?? ''; + $seriesName = $carInfo['series_name'] ?? ''; + $year = $carInfo['year'] ?? ''; + $bodyColor = $carInfo['body_color'] ?? ''; + + // 提取配置信息 + $overview = $skuDetail['car_config_overview'] ?? []; + $power = $overview['power']['horsepower'] ?? ''; + $displacement = $overview['power']['capacity'] ?? ''; + + // 提取上牌时间和车源地 + $otherParams = $skuDetail['other_params'] ?? []; + $plateTime = $this->getParamValue($otherParams, '上牌时间'); + $sourcePlace = $this->getParamValue($otherParams, '车源地'); + + // 提取里程 + $importantText = $skuDetail['important_text'] ?? ''; + $mileage = 0; + if (preg_match('/(\d+\.?\d*)\s*万公里/', $importantText, $matches)) { + $mileage = (int)($matches[1] * 10000); + } + + // 提取图片 + $images = $skuDetail['head_images'] ?? []; + + // 提取车况介绍 + $conditionDesc = ''; + if (preg_match('/车况介绍\s*(.*?)\s*推荐理由/s', $html, $matches)) { + $conditionDesc = trim($matches[1]); + } + + // 提取车辆ID + $carId = $carInfo['car_id'] ?? 0; + $brandId = $carInfo['brand_id'] ?? 0; + $seriesId = $carInfo['series_id'] ?? 0; + $usedCarId = $this->extractIdFromUrl($url); + + return [ + 'title' => $title, + 'price' => $price, + 'brand_name' => $brandName, + 'brand_id' => $brandId, + 'series_name' => $seriesName, + 'series_id' => $seriesId, + 'year' => $year, + 'car_id' => $carId, + 'used_car_id' => $usedCarId, + 'power' => $power, + 'displacement' => $displacement, + 'plate_time' => $plateTime, + 'source_place' => $sourcePlace, + 'body_color' => $bodyColor, + 'mileage' => $mileage, + 'images' => $images, + 'condition_desc' => $conditionDesc, + 'source_url' => $url, + ]; + } catch (\Exception $e) { + throw new \Exception('解析懂车帝页面失败: ' . $e->getMessage()); + } + } + + /** + * 解析汽车之家HTML + */ + private function parseChe168Html(string $html, string $url): array + { + try { + // 汽车之家使用 GB2312 编码,需要转换 + $html = $this->convertToUtf8($html); + + $ql = QueryList::html($html); + + // 提取车辆名称 + $productName = $ql->find('div.car-box .car-brand-name')->text(); + + // 提取价格 + $price = $ql->find('div.goodstartmoney')->text(); + if (empty($price)) { + $price = $ql->find('span#overlayPrice')->text(); + } + $price = str_replace(['¥', '¥', '万'], '', $price); + $price = is_numeric($price) ? (int)($price * 10000) : 0; + + // 提取基本信息 + $basicInfo = []; + $ql->find('div#nav1 ul.basic-item-ul li')->each(function($li) use (&$basicInfo) { + $field = trim($li->find('.item-name')->text()); + $value = trim(str_replace($field, '', $li->text())); + + $fieldMap = [ + '上牌时间' => 'plate_time', + '表显里程' => 'mileage_text', + '排 量' => 'displacement', + '所 在 地' => 'source_place', + '车身颜色' => 'body_color', + '车况介绍' => 'condition_desc', + ]; + + if (isset($fieldMap[$field])) { + $basicInfo[$fieldMap[$field]] = $value; + } + }); + + // 处理里程 + $mileage = 0; + if (isset($basicInfo['mileage_text'])) { + $mileage = (int)str_replace('万公里', '', $basicInfo['mileage_text']) * 10000; + } + + // 提取面包屑导航获取品牌和车系 + $breadcrumbs = []; + $ql->find('.bread-crumbs a')->each(function($a) use (&$breadcrumbs) { + $breadcrumbs[] = trim($a->text()); + }); + + $brandName = $breadcrumbs[3] ?? ''; + $seriesName = $breadcrumbs[4] ?? ''; + $brandName = str_replace('二手', '', $brandName); + $seriesName = str_replace('二手', '', $seriesName); + + // 提取年份 + $year = ''; + if (preg_match('/(\d{4})款/', $productName, $matches)) { + $year = $matches[1]; + } + + // 提取图片 + $images = []; + $ql->find('.swiper-container2 .swiper-slide .jiaodianphotoclick img')->each(function($img) use (&$images) { + $src = $img->attr('src'); + if ($src && !str_contains($src, 'default-che168')) { + $images[] = $src; + } + }); + + // 提取车辆ID + $usedCarId = $this->extractIdFromUrl($url); + + return [ + 'title' => $productName, + 'product_name' => $productName, + 'price' => $price, + 'brand_name' => $brandName, + 'brand_id' => 0, + 'series_name' => $seriesName, + 'series_id' => 0, + 'year' => $year, + 'car_id' => 0, + 'used_car_id' => $usedCarId, + 'plate_time' => $basicInfo['plate_time'] ?? '', + 'displacement' => $basicInfo['displacement'] ?? '', + 'source_place' => $basicInfo['source_place'] ?? '', + 'body_color' => $basicInfo['body_color'] ?? '', + 'mileage' => $mileage, + 'images' => $images, + 'condition_desc' => $basicInfo['condition_desc'] ?? '', + 'source_url' => $url, + ]; + } catch (\Exception $e) { + throw new \Exception('解析汽车之家页面失败: ' . $e->getMessage()); + } + } + + /** + * 解码懂车帝价格(特殊字符编码) + */ + private function decodeDongchediPrice(string $str): float + { + $map = [ + "\u{E53D}" => '1', + "\u{E3F0}" => '2', + "\u{E422}" => '3', + "\u{E42C}" => '4', + "\u{E49C}" => '5', + "\u{E42B}" => '6', + "\u{E4FE}" => '7', + "\u{E548}" => '8', + "\u{E4C8}" => '9', + "\u{E453}" => '0', + "\u{E45F}" => '.', + ]; + + $priceStr = strtr($str, $map); + $priceStr = str_replace('万', '', $priceStr); + + return (float)trim($priceStr); + } + + /** + * 从URL中提取ID + */ + private function extractIdFromUrl(string $url): int + { + if (preg_match('/(\d+)\.html?$/', $url, $matches)) { + return (int)$matches[1]; + } + if (preg_match('/\/usedcar\/(\d+)/', $url, $matches)) { + return (int)$matches[1]; + } + return 0; + } + + /** + * 从参数列表中获取指定名称的值 + */ + private function getParamValue(array $params, string $name): ?string + { + foreach ($params as $item) { + if (($item['name'] ?? null) === $name) { + return $item['value'] ?? null; + } + } + return null; + } + + /** + * 将GB2312编码转换为UTF-8 + */ + private function convertToUtf8(string $html): string + { + if (!function_exists('mb_convert_encoding')) { + return $html; + } + + $isGb = stripos($html, 'charset=gb2312') !== false || stripos($html, 'charset=gbk') !== false; + if (!$isGb) { + return $html; + } + + $out = @mb_convert_encoding($html, 'UTF-8', 'GB2312'); + if ($out === false || $out === '') { + $out = @mb_convert_encoding($html, 'UTF-8', 'GBK'); + } + + if ($out !== false && $out !== '') { + $html = preg_replace('/charset\s*=\s*["\']?gb(2312|k)["\']?/i', 'charset="utf-8"', $html); + return $out; + } + + return $html; + } + + /** + * 下载图片到本地 + * + * @param string $url 图片URL + * @param string $saveDir 保存目录 + * @return string|null 保存的文件路径,失败返回null + */ + public function downloadImage(string $url, string $saveDir): ?string + { + try { + if (!is_dir($saveDir)) { + mkdir($saveDir, 0755, true); + } + + $filename = md5($url) . '.jpg'; + $savePath = rtrim($saveDir, '/') . '/' . $filename; + + // 如果文件已存在,直接返回 + if (file_exists($savePath)) { + return $savePath; + } + + // 使用 curl 下载图片 + $ch = curl_init($url); + curl_setopt_array($ch, [ + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_CONNECTTIMEOUT => 5, + CURLOPT_TIMEOUT => 15, + CURLOPT_SSL_VERIFYPEER => false, + CURLOPT_SSL_VERIFYHOST => false, + CURLOPT_HTTPHEADER => [ + 'User-Agent: ' . self::DEFAULT_HEADERS['User-Agent'], + 'Referer: ' . self::DEFAULT_HEADERS['Referer'], + ], + ]); + + $data = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); + + if ($httpCode !== 200 || $data === false) { + return null; + } + + file_put_contents($savePath, $data); + return $savePath; + } catch (\Exception $e) { + error_log('下载图片失败: ' . $e->getMessage()); + return null; + } + } + + /** + * 批量下载图片 + * + * @param array $urls 图片URL数组 + * @param string $saveDir 保存目录 + * @return array [url => localPath] + */ + public function downloadImages(array $urls, string $saveDir): array + { + $result = []; + foreach ($urls as $url) { + $url = trim($url); + if (empty($url)) { + continue; + } + + // 处理协议相对URL + if (str_starts_with($url, '//')) { + $url = 'https:' . $url; + } + + $localPath = $this->downloadImage($url, $saveDir); + $result[$url] = $localPath; + } + + return $result; + } +} diff --git a/backend/app/service/UsedCarService.php b/backend/app/service/UsedCarService.php new file mode 100644 index 0000000..de58676 --- /dev/null +++ b/backend/app/service/UsedCarService.php @@ -0,0 +1,529 @@ +crawlService = new UsedCarCrawlService(); + } + + /** + * 获取二手车列表 + * + * @param int $userId 用户ID + * @param string $type 列表类型: all-全部, self-我创建的 + * @param int $page 页码 + * @param int $pageSize 每页数量 + * @param string|null $keyword 搜索关键词 + * @param int|null $status 状态筛选 + * @return array + */ + public function getList(int $userId, string $type = 'self', int $page = 1, int $pageSize = 20, ?string $keyword = null, ?int $status = null): array + { + $query = UsedCar::query(); + + // 筛选条件 + switch ($type) { + case 'self': + $query->where('user_id', $userId); + break; + case 'all': + // 管理员可以查看所有 + break; + } + + // 状态筛选 + if ($status !== null) { + $query->where('status', $status); + } else { + // 默认不显示已删除的 + $query->where('status', '!=', UsedCar::STATUS_DELETED); + } + + // 关键词搜索 + if ($keyword) { + $query->where(function ($q) use ($keyword) { + $q->where('product_name', 'like', "%{$keyword}%") + ->orWhere('code', 'like', "%{$keyword}%"); + }); + } + + // 获取总数 + $total = $query->count(); + + // 分页查询 + $list = $query->orderBy('sort', 'desc') + ->orderBy('id', 'desc') + ->offset(($page - 1) * $pageSize) + ->limit($pageSize) + ->get(); + + // 格式化数据 + $formattedList = $list->map(function ($item) { + return $this->formatListItem($item); + })->toArray(); + + return [ + 'list' => $formattedList, + 'total' => $total, + 'page' => $page, + 'page_size' => $pageSize, + ]; + } + + /** + * 获取二手车详情 + * + * @param int $id 二手车ID + * @param int $userId 当前用户ID + * @return array + * @throws \Exception + */ + public function getDetail(int $id, int $userId): array + { + $car = UsedCar::with(['pdfLogs', 'user'])->findOrFail($id); + + // 格式化数据 + return $this->formatDetailItem($car); + } + + /** + * 创建二手车记录 + * + * @param array $data + * @param int $userId + * @return UsedCar + * @throws \Exception + */ + public function create(array $data, int $userId): UsedCar + { + // 验证数据 + $this->validateCarData($data); + + return Db::connection()->transaction(function () use ($data, $userId) { + // 获取用户信息 + $user = User::findOrFail($userId); + + // 生成唯一编号 + $code = $this->generateCode($userId, $user->name ?? ''); + + // 处理图片 + $images = $data['images'] ?? []; + if (!is_array($images)) { + $images = explode(',', $images); + } + $images = array_filter($images); + + // 处理附件 + $attachment = $data['attachment'] ?? null; + if (is_array($attachment)) { + $attachment = json_encode($attachment, JSON_UNESCAPED_UNICODE); + } + + // 检查source_url是否已存在 + if (!empty($data['source_url'])) { + $exists = UsedCar::where('source_url', $data['source_url']) + ->where('user_id', $userId) + ->exists(); + if ($exists) { + throw new \Exception('该车源已收录'); + } + } + + // 创建记录 + $car = UsedCar::create([ + 'code' => $code, + 'user_id' => $userId, + 'role' => $user->role ?? 1, + 'product_name' => $data['product_name'], + 'vin' => $data['vin'] ?? null, + 'price' => $this->parsePrice($data['price'] ?? 0), + 'source_place' => $data['source_place'] ?? null, + 'plate_place' => $data['plate_place'] ?? null, + 'plate_time' => $data['plate_time'] ?? null, + 'displacement' => $data['displacement'] ?? null, + 'mileage' => $this->parseMileage($data['mileage'] ?? 0), + 'power' => $this->parsePower($data['power'] ?? ''), + 'body_color' => $data['body_color'] ?? null, + 'images' => implode(',', $images), + 'condition_desc' => $data['condition_desc'] ?? null, + 'attachment' => $attachment, + 'source_url' => $data['source_url'] ?? null, + 'crawl_title' => $data['crawl_title'] ?? null, + 'crawl_brand' => $data['crawl_brand'] ?? null, + 'crawl_brand_id' => $data['crawl_brand_id'] ?? 0, + 'crawl_series' => $data['crawl_series'] ?? null, + 'crawl_series_id' => $data['crawl_series_id'] ?? 0, + 'crawl_year' => $data['crawl_year'] ?? null, + 'crawl_msrp' => $this->parsePrice($data['crawl_msrp'] ?? 0), + 'crawl_car_id' => $data['crawl_car_id'] ?? 0, + 'status' => UsedCar::STATUS_NORMAL, + 'sort' => $data['sort'] ?? 0, + ]); + + return $car; + }); + } + + /** + * 更新二手车记录 + * + * @param int $id + * @param array $data + * @param int $userId + * @return UsedCar + * @throws \Exception + */ + public function update(int $id, array $data, int $userId): UsedCar + { + $car = UsedCar::findOrFail($id); + + // 检查权限 + if ($car->user_id !== $userId) { + throw new \Exception('无权编辑此记录'); + } + + // 验证数据 + $this->validateCarData($data); + + // 处理图片 + if (isset($data['images'])) { + $images = is_array($data['images']) ? $data['images'] : explode(',', $data['images']); + $data['images'] = implode(',', array_filter($images)); + } + + // 处理附件 + if (isset($data['attachment']) && is_array($data['attachment'])) { + $data['attachment'] = json_encode($data['attachment'], JSON_UNESCAPED_UNICODE); + } + + // 处理价格和里程 + if (isset($data['price'])) { + $data['price'] = $this->parsePrice($data['price']); + } + if (isset($data['mileage'])) { + $data['mileage'] = $this->parseMileage($data['mileage']); + } + if (isset($data['power'])) { + $data['power'] = $this->parsePower($data['power']); + } + if (isset($data['crawl_msrp'])) { + $data['crawl_msrp'] = $this->parsePrice($data['crawl_msrp']); + } + + // 只更新允许的字段 + $fillable = [ + 'product_name', 'vin', 'price', 'source_place', 'plate_place', + 'plate_time', 'displacement', 'mileage', 'power', 'body_color', + 'images', 'condition_desc', 'attachment', + ]; + + foreach ($fillable as $field) { + if (array_key_exists($field, $data)) { + $car->$field = $data[$field]; + } + } + + $car->save(); + return $car; + } + + /** + * 删除二手车记录(软删除) + * + * @param int $id + * @param int $userId + * @return bool + * @throws \Exception + */ + public function delete(int $id, int $userId): bool + { + $car = UsedCar::findOrFail($id); + + // 检查权限 + if ($car->user_id !== $userId) { + throw new \Exception('无权删除此记录'); + } + + $car->status = UsedCar::STATUS_DELETED; + return $car->save(); + } + + /** + * 更新状态 + * + * @param int $id + * @param int $status + * @param int $userId + * @return bool + * @throws \Exception + */ + public function updateStatus(int $id, int $status, int $userId): bool + { + $car = UsedCar::findOrFail($id); + + // 检查权限 + if ($car->user_id !== $userId) { + throw new \Exception('无权修改此记录'); + } + + $car->status = $status; + return $car->save(); + } + + /** + * URL抓取车辆信息 + * + * @param string $url + * @return array + * @throws \Exception + */ + public function crawlByUrl(string $url): array + { + $data = $this->crawlService->crawlByUrl($url); + + // 下载图片到临时目录 + $tempDir = runtime_path('tmp/usedcar_images/'); + $images = $data['images'] ?? []; + + return [ + 'product_name' => $data['title'] ?? '', + 'price' => $this->parsePrice($data['price'] ?? 0), + 'source_place' => $data['source_place'] ?? '', + 'plate_time' => $data['plate_time'] ?? '', + 'displacement' => $data['displacement'] ?? '', + 'mileage' => $data['mileage'] ?? 0, + 'power' => $data['power'] ?? '', + 'body_color' => $data['body_color'] ?? '', + 'images' => $images, + 'condition_desc' => $data['condition_desc'] ?? '', + 'source_url' => $data['source_url'] ?? $url, + 'crawl_title' => $data['title'] ?? '', + 'crawl_brand' => $data['brand_name'] ?? '', + 'crawl_brand_id' => $data['brand_id'] ?? 0, + 'crawl_series' => $data['series_name'] ?? '', + 'crawl_series_id' => $data['series_id'] ?? 0, + 'crawl_year' => $data['year'] ?? '', + 'crawl_car_id' => $data['car_id'] ?? 0, + 'used_car_id' => $data['used_car_id'] ?? 0, + ]; + } + + /** + * 生成唯一编号 + * + * @param int $userId + * @param string $userName + * @return string + */ + private function generateCode(int $userId, string $userName): string + { + // 获取用户名拼音首字母 + $nameInitials = $this->getNameInitials($userName); + $userCode = str_pad($userId, 3, '0', STR_PAD_LEFT); + + // 从Redis获取今日计数 + $redisKey = 'used_car_count_' . $userId; + $todayStart = strtotime('today'); + + // 检查数据库中今日已创建的数量 + $count = UsedCar::where('user_id', $userId) + ->where('created_at', '>=', $todayStart) + ->count(); + + // 从Redis获取缓存计数 + $cachedCount = Redis::get($redisKey, 0); + $count = max($count, $cachedCount) + 1; + + // 更新Redis缓存(24小时过期) + Redis::setex($redisKey, 86400, $count); + + $countCode = str_pad($count, 3, '0', STR_PAD_LEFT); + + return $nameInitials . $userCode . date('Ymd') . $countCode; + } + + /** + * 获取中文名拼音首字母 + */ + private function getNameInitials(string $name): string + { + $initials = ''; + $len = mb_strlen($name); + + for ($i = 0; $i < $len; $i++) { + $char = mb_substr($name, $i, 1); + $initials .= $this->getFirstChar($char); + } + + return strtoupper($initials); + } + + /** + * 获取单个汉字的首字母 + */ + private function getFirstChar(string $char): string + { + // 英文字母直接返回 + if (preg_match('/^[a-zA-Z]$/', $char)) { + return strtoupper($char); + } + + // 转换为GB2312编码 + $s = @iconv('UTF-8', 'GB2312//IGNORE', $char); + if ($s === false || strlen($s) < 2) { + return ''; + } + + $code = ord($s[0]) * 256 + ord($s[1]) - 65536; + + $map = [ + [-20319, -20284, 'A'], [-20283, -19776, 'B'], [-19775, -19219, 'C'], + [-19218, -18711, 'D'], [-18710, -18527, 'E'], [-18526, -18240, 'F'], + [-18239, -17923, 'G'], [-17922, -17418, 'H'], [-17417, -16475, 'J'], + [-16474, -16213, 'K'], [-16212, -15641, 'L'], [-15640, -15166, 'M'], + [-15165, -14923, 'N'], [-14922, -14915, 'O'], [-14914, -14631, 'P'], + [-14630, -14150, 'Q'], [-14149, -14091, 'R'], [-14090, -13319, 'S'], + [-13318, -12839, 'T'], [-12838, -12557, 'W'], [-12556, -11848, 'X'], + [-11847, -11056, 'Y'], [-11055, -10247, 'Z'] + ]; + + foreach ($map as [$start, $end, $letter]) { + if ($code >= $start && $code <= $end) { + return $letter; + } + } + + return ''; + } + + /** + * 解析价格(单位统一转换为分) + */ + private function parsePrice($price): int + { + $price = floatval(str_replace(['万', ','], '', $price)); + // 如果小于200,说明是以万为单位 + if ($price < 200 && $price > 0) { + $price = $price * 10000; + } + return (int)($price * 100); // 转换为分 + } + + /** + * 解析里程(移除单位) + */ + private function parseMileage($mileage): int + { + $mileage = str_replace(['万', 'K', 'M', 'k', 'm', '公里'], '', $mileage); + return (int)floatval($mileage); + } + + /** + * 解析马力(移除单位) + */ + private function parsePower($power): string + { + return str_replace(['P', 'S', 'p', 's', '马力', 'Ps', 'PS'], '', $power); + } + + /** + * 验证车辆数据 + */ + private function validateCarData(array $data): void + { + if (empty($data['product_name'])) { + throw new \Exception('产品名称不能为空'); + } + } + + /** + * 格式化列表项 + */ + private function formatListItem(UsedCar $car): array + { + $data = $car->toArray(); + + // 格式化时间 + if ($car->created_at) { + $data['created_at'] = $car->created_at->toDateTimeString(); + } + if ($car->updated_at) { + $data['updated_at'] = $car->updated_at->toDateTimeString(); + } + + // 处理图片 + $data['images_array'] = []; + if (!empty($car->images)) { + $data['images_array'] = explode(',', $car->images); + } + + return $data; + } + + /** + * 格式化详情项 + */ + private function formatDetailItem(UsedCar $car): array + { + $data = $car->toArray(); + + // 格式化时间 + if ($car->created_at) { + $data['created_at'] = $car->created_at->toDateTimeString(); + } + if ($car->updated_at) { + $data['updated_at'] = $car->updated_at->toDateTimeString(); + } + + // 处理图片 + $data['images_array'] = []; + if (!empty($car->images)) { + $data['images_array'] = explode(',', $car->images); + } + + // 处理附件 + $data['attachment_array'] = []; + if (!empty($car->attachment)) { + $attachment = json_decode($car->attachment, true); + if (is_array($attachment)) { + $data['attachment_array'] = $attachment; + } + } + + // 添加用户信息 + if ($car->user) { + $data['user'] = [ + 'id' => $car->user->id, + 'name' => $car->user->name, + ]; + } + + // PDF生成日志 + if ($car->pdfLogs) { + $data['pdf_logs'] = $car->pdfLogs->map(function ($log) { + return [ + 'id' => $log->id, + 'price_snapshot' => $log->price_snapshot, + 'pdf_path' => $log->pdf_path, + 'created_at' => $log->created_at ? $log->created_at->toDateTimeString() : null, + ]; + })->toArray(); + } + + return $data; + } +} diff --git a/backend/composer.json b/backend/composer.json index 25689ea..699ff7a 100644 --- a/backend/composer.json +++ b/backend/composer.json @@ -14,7 +14,11 @@ "vlucas/phpdotenv": "^5.6", "webman/database": "^2.1", "symfony/var-dumper": "^8.0", - "webman/redis": "^2.1" + "webman/redis": "^2.1", + "guzzlehttp/guzzle": "^7.8", + "jaeger/querylist": "^4.4", + "mpdf/mpdf": "^8.2", + "workerman/crontab": "^1.0" }, "require-dev": { "mockery/mockery": "^1.5", diff --git a/backend/composer.lock b/backend/composer.lock index 0894d1d..b553335 100644 --- a/backend/composer.lock +++ b/backend/composer.lock @@ -4,19 +4,14 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "76cf41958d6f742bb6839fffc9775c3a", + "content-hash": "3973dc3a38a7efaf78808007d80f4cd7", "packages": [ { "name": "brick/math", "version": "0.12.3", - "source": { - "type": "git", - "url": "https://github.com/brick/math.git", - "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/brick/math/zipball/866551da34e9a618e64a819ee1e01c20d8a588ba", + "url": "https://mirrors.cloud.tencent.com/repository/composer/brick/math/0.12.3/brick-math-0.12.3.zip", "reference": "866551da34e9a618e64a819ee1e01c20d8a588ba", "shasum": "" }, @@ -34,7 +29,6 @@ "Brick\\Math\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -58,25 +52,14 @@ "issues": "https://github.com/brick/math/issues", "source": "https://github.com/brick/math/tree/0.12.3" }, - "funding": [ - { - "url": "https://github.com/BenMorel", - "type": "github" - } - ], "time": "2025-02-28T13:11:00+00:00" }, { "name": "carbonphp/carbon-doctrine-types", "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", - "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", + "url": "https://mirrors.cloud.tencent.com/repository/composer/carbonphp/carbon-doctrine-types/2.1.0/carbonphp-carbon-doctrine-types-2.1.0.zip", "reference": "99f76ffa36cce3b70a4a6abce41dba15ca2e84cb", "shasum": "" }, @@ -97,7 +80,6 @@ "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -119,33 +101,14 @@ "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/2.1.0" }, - "funding": [ - { - "url": "https://github.com/kylekatarnls", - "type": "github" - }, - { - "url": "https://opencollective.com/Carbon", - "type": "open_collective" - }, - { - "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", - "type": "tidelift" - } - ], "time": "2023-12-11T17:09:12+00:00" }, { "name": "doctrine/inflector", "version": "2.1.0", - "source": { - "type": "git", - "url": "https://github.com/doctrine/inflector.git", - "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/inflector/zipball/6d6c96277ea252fc1304627204c3d5e6e15faa3b", + "url": "https://mirrors.cloud.tencent.com/repository/composer/doctrine/inflector/2.1.0/doctrine-inflector-2.1.0.zip", "reference": "6d6c96277ea252fc1304627204c3d5e6e15faa3b", "shasum": "" }, @@ -165,7 +128,6 @@ "Doctrine\\Inflector\\": "src" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -209,33 +171,14 @@ "issues": "https://github.com/doctrine/inflector/issues", "source": "https://github.com/doctrine/inflector/tree/2.1.0" }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", - "type": "tidelift" - } - ], "time": "2025-08-10T19:31:58+00:00" }, { "name": "firebase/php-jwt", "version": "v6.11.1", - "source": { - "type": "git", - "url": "https://github.com/firebase/php-jwt.git", - "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/firebase/php-jwt/zipball/d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", + "url": "https://mirrors.cloud.tencent.com/repository/composer/firebase/php-jwt/v6.11.1/firebase-php-jwt-v6.11.1.zip", "reference": "d1e91ecf8c598d073d0995afa8cd5c75c6e19e66", "shasum": "" }, @@ -260,7 +203,6 @@ "Firebase\\JWT\\": "src" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -291,14 +233,9 @@ { "name": "fruitcake/php-cors", "version": "v1.4.0", - "source": { - "type": "git", - "url": "https://github.com/fruitcake/php-cors.git", - "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/fruitcake/php-cors/zipball/38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", + "url": "https://mirrors.cloud.tencent.com/repository/composer/fruitcake/php-cors/v1.4.0/fruitcake-php-cors-v1.4.0.zip", "reference": "38aaa6c3fd4c157ffe2a4d10aa8b9b16ba8de379", "shasum": "" }, @@ -322,7 +259,6 @@ "Fruitcake\\Cors\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -347,29 +283,14 @@ "issues": "https://github.com/fruitcake/php-cors/issues", "source": "https://github.com/fruitcake/php-cors/tree/v1.4.0" }, - "funding": [ - { - "url": "https://fruitcake.nl", - "type": "custom" - }, - { - "url": "https://github.com/barryvdh", - "type": "github" - } - ], "time": "2025-12-03T09:33:47+00:00" }, { "name": "graham-campbell/result-type", "version": "v1.1.4", - "source": { - "type": "git", - "url": "https://github.com/GrahamCampbell/Result-Type.git", - "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/GrahamCampbell/Result-Type/zipball/e01f4a821471308ba86aa202fed6698b6b695e3b", + "url": "https://mirrors.cloud.tencent.com/repository/composer/graham-campbell/result-type/v1.1.4/graham-campbell-result-type-v1.1.4.zip", "reference": "e01f4a821471308ba86aa202fed6698b6b695e3b", "shasum": "" }, @@ -386,7 +307,6 @@ "GrahamCampbell\\ResultType\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -409,29 +329,279 @@ "issues": "https://github.com/GrahamCampbell/Result-Type/issues", "source": "https://github.com/GrahamCampbell/Result-Type/tree/v1.1.4" }, - "funding": [ + "time": "2025-12-27T19:43:20+00:00" + }, + { + "name": "guzzlehttp/guzzle", + "version": "7.10.0", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/guzzlehttp/guzzle/7.10.0/guzzlehttp-guzzle-7.10.0.zip", + "reference": "b51ac707cfa420b7bfd4e4d5e510ba8008e822b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^2.3", + "guzzlehttp/psr7": "^2.8", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "ext-curl": "*", + "guzzle/client-integration-tests": "3.0.2", + "php-http/message-factory": "^1.1", + "phpunit/phpunit": "^8.5.39 || ^9.6.20", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://github.com/GrahamCampbell", - "type": "github" + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" }, { - "url": "https://tidelift.com/funding/github/packagist/graham-campbell/result-type", - "type": "tidelift" + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" } ], - "time": "2025-12-27T19:43:20+00:00" + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.10.0" + }, + "time": "2025-08-23T22:36:01+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "2.3.0", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/guzzlehttp/promises/2.3.0/guzzlehttp-promises-2.3.0.zip", + "reference": "481557b130ef3790cf82b713667b43030dc9c957", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/2.3.0" + }, + "time": "2025-08-22T14:34:08+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.8.0", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/guzzlehttp/psr7/2.8.0/guzzlehttp-psr7-2.8.0.zip", + "reference": "21dc724a0583619cd1652f673303492272778051", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.1 || ^2.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.8.2", + "http-interop/http-factory-tests": "0.9.0", + "phpunit/phpunit": "^8.5.44 || ^9.6.25" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "bamarni-bin": { + "bin-links": true, + "forward-command": false + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.8.0" + }, + "time": "2025-08-23T21:21:41+00:00" }, { "name": "guzzlehttp/uri-template", "version": "v1.0.5", - "source": { - "type": "git", - "url": "https://github.com/guzzle/uri-template.git", - "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/uri-template/zipball/4f4bbd4e7172148801e76e3decc1e559bdee34e1", + "url": "https://mirrors.cloud.tencent.com/repository/composer/guzzlehttp/uri-template/v1.0.5/guzzlehttp-uri-template-v1.0.5.zip", "reference": "4f4bbd4e7172148801e76e3decc1e559bdee34e1", "shasum": "" }, @@ -456,7 +626,6 @@ "GuzzleHttp\\UriTemplate\\": "src" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -491,33 +660,14 @@ "issues": "https://github.com/guzzle/uri-template/issues", "source": "https://github.com/guzzle/uri-template/tree/v1.0.5" }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://github.com/Nyholm", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/uri-template", - "type": "tidelift" - } - ], "time": "2025-08-22T14:27:06+00:00" }, { "name": "illuminate/bus", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/bus.git", - "reference": "053f902d546d719c3f2752f7d3805a466e317312" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/bus/zipball/053f902d546d719c3f2752f7d3805a466e317312", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/bus/v10.49.0/illuminate-bus-v10.49.0.zip", "reference": "053f902d546d719c3f2752f7d3805a466e317312", "shasum": "" }, @@ -542,7 +692,6 @@ "Illuminate\\Bus\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -563,14 +712,9 @@ { "name": "illuminate/collections", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/collections.git", - "reference": "6ae9c74fa92d4e1824d1b346cd435e8eacdc3232" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/collections/zipball/6ae9c74fa92d4e1824d1b346cd435e8eacdc3232", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/collections/v10.49.0/illuminate-collections-v10.49.0.zip", "reference": "6ae9c74fa92d4e1824d1b346cd435e8eacdc3232", "shasum": "" }, @@ -597,7 +741,6 @@ "Illuminate\\Support\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -618,14 +761,9 @@ { "name": "illuminate/conditionable", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/conditionable.git", - "reference": "47c700320b7a419f0d188d111f3bbed978fcbd3f" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/conditionable/zipball/47c700320b7a419f0d188d111f3bbed978fcbd3f", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/conditionable/v10.49.0/illuminate-conditionable-v10.49.0.zip", "reference": "47c700320b7a419f0d188d111f3bbed978fcbd3f", "shasum": "" }, @@ -643,7 +781,6 @@ "Illuminate\\Support\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -664,14 +801,9 @@ { "name": "illuminate/container", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/container.git", - "reference": "b4956de5de18524c21ef36221a8ffd7fa3b534db" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/container/zipball/b4956de5de18524c21ef36221a8ffd7fa3b534db", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/container/v10.49.0/illuminate-container-v10.49.0.zip", "reference": "b4956de5de18524c21ef36221a8ffd7fa3b534db", "shasum": "" }, @@ -694,7 +826,6 @@ "Illuminate\\Container\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -715,14 +846,9 @@ { "name": "illuminate/contracts", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/contracts.git", - "reference": "2393ef579e020d88e24283913c815c3e2c143323" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/contracts/zipball/2393ef579e020d88e24283913c815c3e2c143323", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/contracts/v10.49.0/illuminate-contracts-v10.49.0.zip", "reference": "2393ef579e020d88e24283913c815c3e2c143323", "shasum": "" }, @@ -742,7 +868,6 @@ "Illuminate\\Contracts\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -763,14 +888,9 @@ { "name": "illuminate/database", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/database.git", - "reference": "711519fa4eca9c55d4f3d6680ffca71b28317e7a" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/database/zipball/711519fa4eca9c55d4f3d6680ffca71b28317e7a", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/database/v10.49.0/illuminate-database-v10.49.0.zip", "reference": "711519fa4eca9c55d4f3d6680ffca71b28317e7a", "shasum": "" }, @@ -809,7 +929,6 @@ "Illuminate\\Database\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -836,14 +955,9 @@ { "name": "illuminate/events", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/events.git", - "reference": "4a8e4fbc95c7e46aa6152fd8c900d56e5ef538cf" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/events/zipball/4a8e4fbc95c7e46aa6152fd8c900d56e5ef538cf", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/events/v10.49.0/illuminate-events-v10.49.0.zip", "reference": "4a8e4fbc95c7e46aa6152fd8c900d56e5ef538cf", "shasum": "" }, @@ -870,7 +984,6 @@ "Illuminate\\Events\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -891,14 +1004,9 @@ { "name": "illuminate/filesystem", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/filesystem.git", - "reference": "584ff4da2218e63e7210bba1c541ce526f24f37e" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/filesystem/zipball/584ff4da2218e63e7210bba1c541ce526f24f37e", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/filesystem/v10.49.0/illuminate-filesystem-v10.49.0.zip", "reference": "584ff4da2218e63e7210bba1c541ce526f24f37e", "shasum": "" }, @@ -937,7 +1045,6 @@ "Illuminate\\Filesystem\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -958,14 +1065,9 @@ { "name": "illuminate/http", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/http.git", - "reference": "45dd7db0731bb1f28acbb5d6b59b029e83d3cb8e" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/http/zipball/45dd7db0731bb1f28acbb5d6b59b029e83d3cb8e", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/http/v10.49.0/illuminate-http-v10.49.0.zip", "reference": "45dd7db0731bb1f28acbb5d6b59b029e83d3cb8e", "shasum": "" }, @@ -997,7 +1099,6 @@ "Illuminate\\Http\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1018,14 +1119,9 @@ { "name": "illuminate/macroable", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/macroable.git", - "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/macroable/zipball/dff667a46ac37b634dcf68909d9d41e94dc97c27", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/macroable/v10.49.0/illuminate-macroable-v10.49.0.zip", "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27", "shasum": "" }, @@ -1043,7 +1139,6 @@ "Illuminate\\Support\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1064,14 +1159,9 @@ { "name": "illuminate/pagination", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/pagination.git", - "reference": "616874b9607ff35925347e1710a8b5151858cdf2" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/pagination/zipball/616874b9607ff35925347e1710a8b5151858cdf2", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/pagination/v10.49.0/illuminate-pagination-v10.49.0.zip", "reference": "616874b9607ff35925347e1710a8b5151858cdf2", "shasum": "" }, @@ -1093,7 +1183,6 @@ "Illuminate\\Pagination\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1114,14 +1203,9 @@ { "name": "illuminate/pipeline", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/pipeline.git", - "reference": "c12e4f1d8a1fbecdc1e0fa4dc9fe17b4315832e9" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/pipeline/zipball/c12e4f1d8a1fbecdc1e0fa4dc9fe17b4315832e9", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/pipeline/v10.49.0/illuminate-pipeline-v10.49.0.zip", "reference": "c12e4f1d8a1fbecdc1e0fa4dc9fe17b4315832e9", "shasum": "" }, @@ -1141,7 +1225,6 @@ "Illuminate\\Pipeline\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1162,14 +1245,9 @@ { "name": "illuminate/redis", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/redis.git", - "reference": "f13f1e03469598055e140a79e0cb31b3874164a4" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/redis/zipball/f13f1e03469598055e140a79e0cb31b3874164a4", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/redis/v10.49.0/illuminate-redis-v10.49.0.zip", "reference": "f13f1e03469598055e140a79e0cb31b3874164a4", "shasum": "" }, @@ -1195,7 +1273,6 @@ "Illuminate\\Redis\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1216,14 +1293,9 @@ { "name": "illuminate/session", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/session.git", - "reference": "ddfa808aadcfeaec6586349d013fc55776146283" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/session/zipball/ddfa808aadcfeaec6586349d013fc55776146283", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/session/v10.49.0/illuminate-session-v10.49.0.zip", "reference": "ddfa808aadcfeaec6586349d013fc55776146283", "shasum": "" }, @@ -1252,7 +1324,6 @@ "Illuminate\\Session\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1273,14 +1344,9 @@ { "name": "illuminate/support", "version": "v10.49.0", - "source": { - "type": "git", - "url": "https://github.com/illuminate/support.git", - "reference": "28b505e671dbe119e4e32a75c78f87189d046e39" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/illuminate/support/zipball/28b505e671dbe119e4e32a75c78f87189d046e39", + "url": "https://mirrors.cloud.tencent.com/repository/composer/illuminate/support/v10.49.0/illuminate-support-v10.49.0.zip", "reference": "28b505e671dbe119e4e32a75c78f87189d046e39", "shasum": "" }, @@ -1323,7 +1389,6 @@ "Illuminate\\Support\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1342,16 +1407,142 @@ "time": "2025-09-08T19:05:53+00:00" }, { - "name": "laravel/serializable-closure", - "version": "v2.0.10", - "source": { - "type": "git", - "url": "https://github.com/laravel/serializable-closure.git", - "reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669" - }, + "name": "jaeger/g-http", + "version": "V2.0.1", "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/870fc81d2f879903dfc5b60bf8a0f94a1609e669", + "url": "https://mirrors.cloud.tencent.com/repository/composer/jaeger/g-http/V2.0.1/jaeger-g-http-V2.0.1.zip", + "reference": "29cb537c57f230980ca10622de190f04831e35e6", + "shasum": "" + }, + "require": { + "guzzlehttp/guzzle": "^6.0 || ^7.0", + "php": ">=8.1", + "symfony/cache": "^6.4 || ^7.0" + }, + "require-dev": { + "predis/predis": "^2.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "Jaeger\\": "src" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaeger", + "email": "JaegerCode@gmail.com" + } + ], + "description": "Simple Http client base on GuzzleHttp", + "support": { + "issues": "https://github.com/jae-jae/GHttp/issues", + "source": "https://github.com/jae-jae/GHttp/tree/V2.0.1" + }, + "time": "2024-06-14T08:13:32+00:00" + }, + { + "name": "jaeger/phpquery-single", + "version": "1.1.1", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/jaeger/phpquery-single/1.1.1/jaeger-phpquery-single-1.1.1.zip", + "reference": "39a650ade692a6b480c22220dce0c198d6a946fb", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "phpQuery.php" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Tobiasz Cudnik", + "email": "tobiasz.cudnik@gmail.com", + "homepage": "https://github.com/TobiaszCudnik", + "role": "Developer" + }, + { + "name": "Jaeger", + "role": "Packager" + } + ], + "description": "phpQuery单文件版本,是Querylist的依赖(http://querylist.cc/),phpQuery项目主页:http://code.google.com/p/phpquery/", + "homepage": "http://code.google.com/p/phpquery/", + "support": { + "issues": "https://github.com/jae-jae/phpQuery-single/issues", + "source": "https://github.com/jae-jae/phpQuery-single/tree/1.1.1" + }, + "time": "2022-03-26T15:01:16+00:00" + }, + { + "name": "jaeger/querylist", + "version": "V4.4.7", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/jaeger/querylist/V4.4.7/jaeger-querylist-V4.4.7.zip", + "reference": "7888655eb12a0abea907954c4b6bbc817ecad3f1", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "jaeger/g-http": "^2.0", + "jaeger/phpquery-single": "^1", + "php": ">=8.1", + "symfony/var-dumper": ">3.4" + }, + "require-dev": { + "phpunit/phpunit": "^8.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/Collect/Support/helpers.php", + "src/Collect/Support/alias.php" + ], + "psr-4": { + "QL\\": "src" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaeger", + "email": "JaegerCode@gmail.com" + } + ], + "description": "Simple, elegant, extensible PHP Web Scraper (crawler/spider),Use the css3 dom selector,Based on phpQuery! 简洁、优雅、可扩展的PHP采集工具(爬虫),基于phpQuery。", + "homepage": "http://querylist.cc", + "keywords": [ + "QueryList", + "phpQuery", + "spider" + ], + "support": { + "issues": "https://github.com/jae-jae/QueryList/issues", + "source": "https://github.com/jae-jae/QueryList/tree/V4.4.7" + }, + "time": "2026-02-02T06:26:44+00:00" + }, + { + "name": "laravel/serializable-closure", + "version": "v2.0.10", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/laravel/serializable-closure/v2.0.10/laravel-serializable-closure-v2.0.10.zip", "reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669", "shasum": "" }, @@ -1376,7 +1567,6 @@ "Laravel\\SerializableClosure\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1405,14 +1595,9 @@ { "name": "monolog/monolog", "version": "2.11.0", - "source": { - "type": "git", - "url": "https://github.com/Seldaek/monolog.git", - "reference": "37308608e599f34a1a4845b16440047ec98a172a" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/37308608e599f34a1a4845b16440047ec98a172a", + "url": "https://mirrors.cloud.tencent.com/repository/composer/monolog/monolog/2.11.0/monolog-monolog-2.11.0.zip", "reference": "37308608e599f34a1a4845b16440047ec98a172a", "shasum": "" }, @@ -1470,7 +1655,6 @@ "Monolog\\": "src/Monolog" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1492,29 +1676,211 @@ "issues": "https://github.com/Seldaek/monolog/issues", "source": "https://github.com/Seldaek/monolog/tree/2.11.0" }, - "funding": [ + "time": "2026-01-01T13:05:00+00:00" + }, + { + "name": "mpdf/mpdf", + "version": "v8.2.7", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/mpdf/mpdf/v8.2.7/mpdf-mpdf-v8.2.7.zip", + "reference": "b59670a09498689c33ce639bac8f5ba26721dab3", + "shasum": "" + }, + "require": { + "ext-gd": "*", + "ext-mbstring": "*", + "mpdf/psr-http-message-shim": "^1.0 || ^2.0", + "mpdf/psr-log-aware-trait": "^2.0 || ^3.0", + "myclabs/deep-copy": "^1.7", + "paragonie/random_compat": "^1.4|^2.0|^9.99.99", + "php": "^5.6 || ^7.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0 || ~8.5.0", + "psr/http-message": "^1.0 || ^2.0", + "psr/log": "^1.0 || ^2.0 || ^3.0", + "setasign/fpdi": "^2.1" + }, + "require-dev": { + "mockery/mockery": "^1.3.0", + "mpdf/qrcode": "^1.1.0", + "squizlabs/php_codesniffer": "^3.5.0", + "tracy/tracy": "~2.5", + "yoast/phpunit-polyfills": "^1.0" + }, + "suggest": { + "ext-bcmath": "Needed for generation of some types of barcodes", + "ext-xml": "Needed mainly for SVG manipulation", + "ext-zlib": "Needed for compression of embedded resources, such as fonts" + }, + "type": "library", + "autoload": { + "files": [ + "src/functions.php" + ], + "psr-4": { + "Mpdf\\": "src/" + } + }, + "license": [ + "GPL-2.0-only" + ], + "authors": [ { - "url": "https://github.com/Seldaek", - "type": "github" + "name": "Matěj Humpál", + "role": "Developer, maintainer" }, { - "url": "https://tidelift.com/funding/github/packagist/monolog/monolog", - "type": "tidelift" + "name": "Ian Back", + "role": "Developer (retired)" } ], - "time": "2026-01-01T13:05:00+00:00" + "description": "PHP library generating PDF files from UTF-8 encoded HTML", + "homepage": "https://mpdf.github.io", + "keywords": [ + "pdf", + "php", + "utf-8" + ], + "support": { + "docs": "https://mpdf.github.io", + "issues": "https://github.com/mpdf/mpdf/issues", + "source": "https://github.com/mpdf/mpdf" + }, + "time": "2025-12-01T10:18:02+00:00" + }, + { + "name": "mpdf/psr-http-message-shim", + "version": "v2.0.1", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/mpdf/psr-http-message-shim/v2.0.1/mpdf-psr-http-message-shim-v2.0.1.zip", + "reference": "f25a0153d645e234f9db42e5433b16d9b113920f", + "shasum": "" + }, + "require": { + "psr/http-message": "^2.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Mpdf\\PsrHttpMessageShim\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Dorison", + "email": "mark@chromatichq.com" + }, + { + "name": "Kristofer Widholm", + "email": "kristofer@chromatichq.com" + }, + { + "name": "Nigel Cunningham", + "email": "nigel.cunningham@technocrat.com.au" + } + ], + "description": "Shim to allow support of different psr/message versions.", + "support": { + "issues": "https://github.com/mpdf/psr-http-message-shim/issues", + "source": "https://github.com/mpdf/psr-http-message-shim/tree/v2.0.1" + }, + "time": "2023-10-02T14:34:03+00:00" + }, + { + "name": "mpdf/psr-log-aware-trait", + "version": "v3.0.0", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/mpdf/psr-log-aware-trait/v3.0.0/mpdf-psr-log-aware-trait-v3.0.0.zip", + "reference": "a633da6065e946cc491e1c962850344bb0bf3e78", + "shasum": "" + }, + "require": { + "psr/log": "^3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Mpdf\\PsrLogAwareTrait\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mark Dorison", + "email": "mark@chromatichq.com" + }, + { + "name": "Kristofer Widholm", + "email": "kristofer@chromatichq.com" + } + ], + "description": "Trait to allow support of different psr/log versions.", + "support": { + "issues": "https://github.com/mpdf/psr-log-aware-trait/issues", + "source": "https://github.com/mpdf/psr-log-aware-trait/tree/v3.0.0" + }, + "time": "2023-05-03T06:19:36+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/myclabs/deep-copy/1.13.4/myclabs-deep-copy-1.13.4.zip", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "time": "2025-08-01T08:46:24+00:00" }, { "name": "nesbot/carbon", "version": "2.73.0", - "source": { - "type": "git", - "url": "https://github.com/CarbonPHP/carbon.git", - "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/9228ce90e1035ff2f0db84b40ec2e023ed802075", + "url": "https://mirrors.cloud.tencent.com/repository/composer/nesbot/carbon/2.73.0/nesbot-carbon-2.73.0.zip", "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075", "shasum": "" }, @@ -1568,7 +1934,6 @@ "Carbon\\": "src/Carbon/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1595,33 +1960,14 @@ "issues": "https://github.com/briannesbitt/Carbon/issues", "source": "https://github.com/briannesbitt/Carbon" }, - "funding": [ - { - "url": "https://github.com/sponsors/kylekatarnls", - "type": "github" - }, - { - "url": "https://opencollective.com/Carbon#sponsor", - "type": "opencollective" - }, - { - "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", - "type": "tidelift" - } - ], "time": "2025-01-08T20:10:23+00:00" }, { "name": "nikic/fast-route", "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/nikic/FastRoute.git", - "reference": "181d480e08d9476e61381e04a71b34dc0432e812" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "url": "https://mirrors.cloud.tencent.com/repository/composer/nikic/fast-route/v1.3.0/nikic-fast-route-v1.3.0.zip", "reference": "181d480e08d9476e61381e04a71b34dc0432e812", "shasum": "" }, @@ -1640,7 +1986,6 @@ "FastRoute\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -1662,16 +2007,55 @@ "time": "2018-02-13T20:26:39+00:00" }, { - "name": "phpoption/phpoption", - "version": "1.9.5", - "source": { - "type": "git", - "url": "https://github.com/schmittjoh/php-option.git", - "reference": "75365b91986c2405cf5e1e012c5595cd487a98be" - }, + "name": "paragonie/random_compat", + "version": "v9.99.100", "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-option/zipball/75365b91986c2405cf5e1e012c5595cd487a98be", + "url": "https://mirrors.cloud.tencent.com/repository/composer/paragonie/random_compat/v9.99.100/paragonie-random_compat-v9.99.100.zip", + "reference": "996434e5492cb4c3edcb9168db6fbb1359ef965a", + "shasum": "" + }, + "require": { + "php": ">= 7" + }, + "require-dev": { + "phpunit/phpunit": "4.*|5.*", + "vimeo/psalm": "^1" + }, + "suggest": { + "ext-libsodium": "Provides a modern crypto API that can be used to generate random bytes." + }, + "type": "library", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Paragon Initiative Enterprises", + "email": "security@paragonie.com", + "homepage": "https://paragonie.com" + } + ], + "description": "PHP 5.x polyfill for random_bytes() and random_int() from PHP 7", + "keywords": [ + "csprng", + "polyfill", + "pseudorandom", + "random" + ], + "support": { + "email": "info@paragonie.com", + "issues": "https://github.com/paragonie/random_compat/issues", + "source": "https://github.com/paragonie/random_compat" + }, + "time": "2020-10-15T08:29:30+00:00" + }, + { + "name": "phpoption/phpoption", + "version": "1.9.5", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/phpoption/phpoption/1.9.5/phpoption-phpoption-1.9.5.zip", "reference": "75365b91986c2405cf5e1e012c5595cd487a98be", "shasum": "" }, @@ -1697,7 +2081,6 @@ "PhpOption\\": "src/PhpOption/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "Apache-2.0" ], @@ -1724,29 +2107,57 @@ "issues": "https://github.com/schmittjoh/php-option/issues", "source": "https://github.com/schmittjoh/php-option/tree/1.9.5" }, - "funding": [ + "time": "2025-12-27T19:41:33+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/psr/cache/3.0.0/psr-cache-3.0.0.zip", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpoption/phpoption", - "type": "tidelift" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "time": "2025-12-27T19:41:33+00:00" + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+00:00" }, { "name": "psr/clock", "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/clock.git", - "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "url": "https://mirrors.cloud.tencent.com/repository/composer/psr/clock/1.0.0/psr-clock-1.0.0.zip", "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", "shasum": "" }, @@ -1759,7 +2170,6 @@ "Psr\\Clock\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1787,14 +2197,9 @@ { "name": "psr/container", "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://mirrors.cloud.tencent.com/repository/composer/psr/container/2.0.2/psr-container-2.0.2.zip", "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", "shasum": "" }, @@ -1812,7 +2217,6 @@ "Psr\\Container\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1840,14 +2244,9 @@ { "name": "psr/event-dispatcher", "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://mirrors.cloud.tencent.com/repository/composer/psr/event-dispatcher/1.0.0/psr-event-dispatcher-1.0.0.zip", "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "shasum": "" }, @@ -1865,7 +2264,6 @@ "Psr\\EventDispatcher\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1888,16 +2286,153 @@ "time": "2019-01-08T18:20:26+00:00" }, { - "name": "psr/log", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" - }, + "name": "psr/http-client", + "version": "1.0.3", "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "url": "https://mirrors.cloud.tencent.com/repository/composer/psr/http-client/1.0.3/psr-http-client-1.0.3.zip", + "reference": "bb5906edc1c324c9a05aa0873d40117941e5fa90", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client" + }, + "time": "2023-09-23T14:17:50+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.1.0", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/psr/http-factory/1.1.0/psr-http-factory-1.1.0.zip", + "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a", + "shasum": "" + }, + "require": { + "php": ">=7.1", + "psr/http-message": "^1.0 || ^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory" + }, + "time": "2024-04-15T12:06:14+00:00" + }, + { + "name": "psr/http-message", + "version": "2.0", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/psr/http-message/2.0/psr-http-message-2.0.zip", + "reference": "402d35bcb92c70c026d1a6a9883f06b2ead23d71", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/2.0" + }, + "time": "2023-04-04T09:54:51+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/psr/log/3.0.2/psr-log-3.0.2.zip", "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "shasum": "" }, @@ -1915,7 +2450,6 @@ "Psr\\Log\\": "src" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1940,14 +2474,9 @@ { "name": "psr/simple-cache", "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/simple-cache.git", - "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "url": "https://mirrors.cloud.tencent.com/repository/composer/psr/simple-cache/3.0.0/psr-simple-cache-3.0.0.zip", "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", "shasum": "" }, @@ -1965,7 +2494,6 @@ "Psr\\SimpleCache\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -1989,114 +2517,245 @@ "time": "2021-10-29T13:26:27+00:00" }, { - "name": "symfony/console", - "version": "v7.4.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894" - }, + "name": "ralouphie/getallheaders", + "version": "3.0.3", "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/41e38717ac1dd7a46b6bda7d6a82af2d98a78894", - "reference": "41e38717ac1dd7a46b6bda7d6a82af2d98a78894", + "url": "https://mirrors.cloud.tencent.com/repository/composer/ralouphie/getallheaders/3.0.3/ralouphie-getallheaders-3.0.3.zip", + "reference": "120b605dfeb996808c31b6477290a714d356e822", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2|^8.0" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" + "php": ">=5.6" }, "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0|^8.0", - "symfony/dependency-injection": "^6.4|^7.0|^8.0", - "symfony/event-dispatcher": "^6.4|^7.0|^8.0", - "symfony/http-foundation": "^6.4|^7.0|^8.0", - "symfony/http-kernel": "^6.4|^7.0|^8.0", - "symfony/lock": "^6.4|^7.0|^8.0", - "symfony/messenger": "^6.4|^7.0|^8.0", - "symfony/process": "^6.4|^7.0|^8.0", - "symfony/stopwatch": "^6.4|^7.0|^8.0", - "symfony/var-dumper": "^6.4|^7.0|^8.0" + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" }, "type": "library", "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "files": [ + "src/getallheaders.php" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, + { + "name": "setasign/fpdi", + "version": "v2.6.4", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/setasign/fpdi/v2.6.4/setasign-fpdi-v2.6.4.zip", + "reference": "4b53852fde2734ec6a07e458a085db627c60eada", + "shasum": "" + }, + "require": { + "ext-zlib": "*", + "php": "^7.1 || ^8.0" + }, + "conflict": { + "setasign/tfpdf": "<1.31" + }, + "require-dev": { + "phpunit/phpunit": "^7", + "setasign/fpdf": "~1.8.6", + "setasign/tfpdf": "~1.33", + "squizlabs/php_codesniffer": "^3.5", + "tecnickcom/tcpdf": "^6.8" + }, + "suggest": { + "setasign/fpdf": "FPDI will extend this class but as it is also possible to use TCPDF or tFPDF as an alternative. There's no fixed dependency configured." + }, + "type": "library", + "autoload": { + "psr-4": { + "setasign\\Fpdi\\": "src/" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jan Slabon", + "email": "jan.slabon@setasign.com", + "homepage": "https://www.setasign.com" + }, + { + "name": "Maximilian Kresse", + "email": "maximilian.kresse@setasign.com", + "homepage": "https://www.setasign.com" + } + ], + "description": "FPDI is a collection of PHP classes facilitating developers to read pages from existing PDF documents and use them as templates in FPDF. Because it is also possible to use FPDI with TCPDF, there are no fixed dependencies defined. Please see suggestions for packages which evaluates the dependencies automatically.", + "homepage": "https://www.setasign.com/fpdi", + "keywords": [ + "fpdf", + "fpdi", + "pdf" + ], + "support": { + "issues": "https://github.com/Setasign/FPDI/issues", + "source": "https://github.com/Setasign/FPDI/tree/v2.6.4" + }, + "time": "2025-08-05T09:57:14+00:00" + }, + { + "name": "symfony/cache", + "version": "v7.4.6", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/cache/v7.4.6/symfony-cache-v7.4.6.zip", + "reference": "1d06192e8f164e2729b0031e6807d72a6195b8bb", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "psr/cache": "^2.0|^3.0", + "psr/log": "^1.1|^2|^3", + "symfony/cache-contracts": "^3.6", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/service-contracts": "^2.5|^3", + "symfony/var-exporter": "^6.4|^7.0|^8.0" + }, + "conflict": { + "doctrine/dbal": "<3.6", + "ext-redis": "<6.1", + "ext-relay": "<0.12.1", + "symfony/dependency-injection": "<6.4", + "symfony/http-kernel": "<6.4", + "symfony/var-dumper": "<6.4" + }, + "provide": { + "psr/cache-implementation": "2.0|3.0", + "psr/simple-cache-implementation": "1.0|2.0|3.0", + "symfony/cache-implementation": "1.1|2.0|3.0" + }, + "require-dev": { + "cache/integration-tests": "dev-master", + "doctrine/dbal": "^3.6|^4", + "predis/predis": "^1.1|^2.0", + "psr/simple-cache": "^1.0|^2.0|^3.0", + "symfony/clock": "^6.4|^7.0|^8.0", + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/filesystem": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Cache\\": "" + }, + "classmap": [ + "Traits/ValueWrapper.php" + ], + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Eases the creation of beautiful and testable command line interfaces", + "description": "Provides extended PSR-6, PSR-16 (and tags) implementations", "homepage": "https://symfony.com", "keywords": [ - "cli", - "command-line", - "console", - "terminal" + "caching", + "psr6" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.4.4" + "source": "https://github.com/symfony/cache/tree/v7.4.6" }, - "funding": [ + "time": "2026-02-21T23:29:27+00:00" + }, + { + "name": "symfony/cache-contracts", + "version": "v3.6.0", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/cache-contracts/v3.6.0/symfony-cache-contracts-v3.6.0.zip", + "reference": "5d68a57d66910405e5c0b63d6f0af941e66fc868", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/cache": "^3.0" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Cache\\": "" + } + }, + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "time": "2026-01-13T11:36:38+00:00" + "description": "Generic abstractions related to caching", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/cache-contracts/tree/v3.6.0" + }, + "time": "2025-03-13T15:25:07+00:00" }, { "name": "symfony/deprecation-contracts", "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/deprecation-contracts/v3.6.0/symfony-deprecation-contracts-v3.6.0.zip", "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, @@ -2118,7 +2777,6 @@ "function.php" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2137,33 +2795,14 @@ "support": { "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/error-handler", "version": "v7.4.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/error-handler.git", - "reference": "8da531f364ddfee53e36092a7eebbbd0b775f6b8" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/error-handler/zipball/8da531f364ddfee53e36092a7eebbbd0b775f6b8", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/error-handler/v7.4.4/symfony-error-handler-v7.4.4.zip", "reference": "8da531f364ddfee53e36092a7eebbbd0b775f6b8", "shasum": "" }, @@ -2196,7 +2835,6 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2215,37 +2853,14 @@ "support": { "source": "https://github.com/symfony/error-handler/tree/v7.4.4" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2026-01-20T16:42:42+00:00" }, { "name": "symfony/event-dispatcher", "version": "v7.4.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "dc2c0eba1af673e736bb851d747d266108aea746" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/dc2c0eba1af673e736bb851d747d266108aea746", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/event-dispatcher/v7.4.4/symfony-event-dispatcher-v7.4.4.zip", "reference": "dc2c0eba1af673e736bb851d747d266108aea746", "shasum": "" }, @@ -2281,7 +2896,6 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2300,37 +2914,14 @@ "support": { "source": "https://github.com/symfony/event-dispatcher/tree/v7.4.4" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2026-01-05T11:45:34+00:00" }, { "name": "symfony/event-dispatcher-contracts", "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/event-dispatcher-contracts/v3.6.0/symfony-event-dispatcher-contracts-v3.6.0.zip", "reference": "59eb412e93815df44f05f342958efa9f46b1e586", "shasum": "" }, @@ -2353,7 +2944,6 @@ "Symfony\\Contracts\\EventDispatcher\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2380,34 +2970,15 @@ "support": { "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/finder", - "version": "v6.4.33", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "24965ca011dac87431729640feef8bcf7b5523e0" - }, + "version": "v6.4.34", "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/24965ca011dac87431729640feef8bcf7b5523e0", - "reference": "24965ca011dac87431729640feef8bcf7b5523e0", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/finder/v6.4.34/symfony-finder-v6.4.34.zip", + "reference": "9590e86be1d1c57bfbb16d0dd040345378c20896", "shasum": "" }, "require": { @@ -2425,7 +2996,6 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2442,40 +3012,17 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.33" + "source": "https://github.com/symfony/finder/tree/v6.4.34" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-26T13:03:48+00:00" + "time": "2026-01-28T15:16:37+00:00" }, { "name": "symfony/http-foundation", - "version": "v6.4.33", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-foundation.git", - "reference": "f1a490cc9d595ba7ebe684220e625d1e472ad278" - }, + "version": "v6.4.34", "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-foundation/zipball/f1a490cc9d595ba7ebe684220e625d1e472ad278", - "reference": "f1a490cc9d595ba7ebe684220e625d1e472ad278", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/http-foundation/v6.4.34/symfony-http-foundation-v6.4.34.zip", + "reference": "5bb346d1b4b2a616e5c3d99b3ee4d5810735c535", "shasum": "" }, "require": { @@ -2506,7 +3053,6 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2523,40 +3069,17 @@ "description": "Defines an object-oriented layer for the HTTP specification", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-foundation/tree/v6.4.33" + "source": "https://github.com/symfony/http-foundation/tree/v6.4.34" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-27T15:04:55+00:00" + "time": "2026-02-21T15:48:41+00:00" }, { "name": "symfony/http-kernel", - "version": "v6.4.33", - "source": { - "type": "git", - "url": "https://github.com/symfony/http-kernel.git", - "reference": "73fa5c999d7f741ca544a97d3c791cc97890ae4d" - }, + "version": "v6.4.34", "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/http-kernel/zipball/73fa5c999d7f741ca544a97d3c791cc97890ae4d", - "reference": "73fa5c999d7f741ca544a97d3c791cc97890ae4d", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/http-kernel/v6.4.34/symfony-http-kernel-v6.4.34.zip", + "reference": "006a49fc4f41ee21a6ca61e69caed1c30b29f07c", "shasum": "" }, "require": { @@ -2597,7 +3120,7 @@ "symfony/config": "^6.1|^7.0", "symfony/console": "^5.4|^6.0|^7.0", "symfony/css-selector": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4.1|^7.0.1", "symfony/dom-crawler": "^5.4|^6.0|^7.0", "symfony/expression-language": "^5.4|^6.0|^7.0", "symfony/finder": "^5.4|^6.0|^7.0", @@ -2624,7 +3147,6 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2641,40 +3163,17 @@ "description": "Provides a structured process for converting a Request into a Response", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/http-kernel/tree/v6.4.33" + "source": "https://github.com/symfony/http-kernel/tree/v6.4.34" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-28T10:02:13+00:00" + "time": "2026-02-26T08:27:11+00:00" }, { "name": "symfony/mime", - "version": "v6.4.32", - "source": { - "type": "git", - "url": "https://github.com/symfony/mime.git", - "reference": "7409686879ca36c09fc970a5fa8ff6e93504dba4" - }, + "version": "v6.4.34", "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/mime/zipball/7409686879ca36c09fc970a5fa8ff6e93504dba4", - "reference": "7409686879ca36c09fc970a5fa8ff6e93504dba4", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/mime/v6.4.34/symfony-mime-v6.4.34.zip", + "reference": "2b32fbbe10b36a8379efab6e702ad8b917151839", "shasum": "" }, "require": { @@ -2709,7 +3208,6 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2730,39 +3228,16 @@ "mime-type" ], "support": { - "source": "https://github.com/symfony/mime/tree/v6.4.32" + "source": "https://github.com/symfony/mime/tree/v6.4.34" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-04T11:53:14+00:00" + "time": "2026-02-02T17:01:23+00:00" }, { "name": "symfony/polyfill-ctype", "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/polyfill-ctype/v1.33.0/symfony-polyfill-ctype-v1.33.0.zip", "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, @@ -2790,7 +3265,6 @@ "Symfony\\Polyfill\\Ctype\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2815,119 +3289,14 @@ "support": { "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2024-09-09T11:45:10+00:00" }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-27T09:58:17+00:00" - }, { "name": "symfony/polyfill-intl-idn", "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-idn.git", - "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-idn/zipball/9614ac4d8061dc257ecc64cba1b140873dce8ad3", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/polyfill-intl-idn/v1.33.0/symfony-polyfill-intl-idn-v1.33.0.zip", "reference": "9614ac4d8061dc257ecc64cba1b140873dce8ad3", "shasum": "" }, @@ -2953,7 +3322,6 @@ "Symfony\\Polyfill\\Intl\\Idn\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -2984,37 +3352,14 @@ "support": { "source": "https://github.com/symfony/polyfill-intl-idn/tree/v1.33.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2024-09-10T14:38:51+00:00" }, { "name": "symfony/polyfill-intl-normalizer", "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/polyfill-intl-normalizer/v1.33.0/symfony-polyfill-intl-normalizer-v1.33.0.zip", "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, @@ -3042,7 +3387,6 @@ "Resources/stubs" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3069,37 +3413,14 @@ "support": { "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2024-09-09T11:45:10+00:00" }, { "name": "symfony/polyfill-mbstring", "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/polyfill-mbstring/v1.33.0/symfony-polyfill-mbstring-v1.33.0.zip", "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, @@ -3128,7 +3449,6 @@ "Symfony\\Polyfill\\Mbstring\\": "" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3154,37 +3474,14 @@ "support": { "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/polyfill-php80", "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/polyfill-php80/v1.33.0/symfony-polyfill-php80-v1.33.0.zip", "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, @@ -3209,7 +3506,6 @@ "Resources/stubs" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3238,37 +3534,14 @@ "support": { "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2025-01-02T08:10:11+00:00" }, { "name": "symfony/polyfill-php83", "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php83.git", - "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/17f6f9a6b1735c0f163024d959f700cfbc5155e5", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/polyfill-php83/v1.33.0/symfony-polyfill-php83-v1.33.0.zip", "reference": "17f6f9a6b1735c0f163024d959f700cfbc5155e5", "shasum": "" }, @@ -3293,7 +3566,6 @@ "Resources/stubs" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3318,37 +3590,14 @@ "support": { "source": "https://github.com/symfony/polyfill-php83/tree/v1.33.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2025-07-08T02:45:35+00:00" }, { "name": "symfony/polyfill-php85", "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php85.git", - "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php85/zipball/d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/polyfill-php85/v1.33.0/symfony-polyfill-php85-v1.33.0.zip", "reference": "d4e5fcd4ab3d998ab16c0db48e6cbb9a01993f91", "shasum": "" }, @@ -3373,7 +3622,6 @@ "Resources/stubs" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3398,37 +3646,14 @@ "support": { "source": "https://github.com/symfony/polyfill-php85/tree/v1.33.0" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2025-06-23T16:12:55+00:00" }, { "name": "symfony/service-contracts", "version": "v3.6.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/service-contracts/v3.6.1/symfony-service-contracts-v3.6.1.zip", "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, @@ -3458,7 +3683,6 @@ "/Test/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3485,129 +3709,15 @@ "support": { "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2025-07-15T11:30:57+00:00" }, - { - "name": "symfony/string", - "version": "v7.4.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/1c4b10461bf2ec27537b5f36105337262f5f5d6f", - "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3.0", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.33", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/emoji": "^7.1|^8.0", - "symfony/http-client": "^6.4|^7.0|^8.0", - "symfony/intl": "^6.4|^7.0|^8.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0|^8.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v7.4.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-12T10:54:30+00:00" - }, { "name": "symfony/translation", - "version": "v6.4.32", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "d6cc8e2fdd484f2f41d25938b0e8e3915de3cfbc" - }, + "version": "v6.4.34", "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/d6cc8e2fdd484f2f41d25938b0e8e3915de3cfbc", - "reference": "d6cc8e2fdd484f2f41d25938b0e8e3915de3cfbc", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/translation/v6.4.34/symfony-translation-v6.4.34.zip", + "reference": "d07d117db41341511671b0a1a2be48f2772189ce", "shasum": "" }, "require": { @@ -3656,7 +3766,6 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3673,39 +3782,16 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.4.32" + "source": "https://github.com/symfony/translation/tree/v6.4.34" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2026-01-12T19:15:33+00:00" + "time": "2026-02-16T20:44:03+00:00" }, { "name": "symfony/translation-contracts", "version": "v3.6.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "65a8bc82080447fae78373aa10f8d13b38338977" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/65a8bc82080447fae78373aa10f8d13b38338977", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/translation-contracts/v3.6.1/symfony-translation-contracts-v3.6.1.zip", "reference": "65a8bc82080447fae78373aa10f8d13b38338977", "shasum": "" }, @@ -3730,7 +3816,6 @@ "/Test/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3757,38 +3842,15 @@ "support": { "source": "https://github.com/symfony/translation-contracts/tree/v3.6.1" }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], "time": "2025-07-15T13:41:35+00:00" }, { "name": "symfony/var-dumper", - "version": "v8.0.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "326e0406fc315eca57ef5740fa4a280b7a068c82" - }, + "version": "v8.0.6", "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/326e0406fc315eca57ef5740fa4a280b7a068c82", - "reference": "326e0406fc315eca57ef5740fa4a280b7a068c82", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/var-dumper/v8.0.6/symfony-var-dumper-v8.0.6.zip", + "reference": "2e14f7e0bf5ff02c6e63bd31cb8e4855a13d6209", "shasum": "" }, "require": { @@ -3821,7 +3883,6 @@ "/Tests/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3842,39 +3903,72 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v8.0.4" + "source": "https://github.com/symfony/var-dumper/tree/v8.0.6" }, - "funding": [ + "time": "2026-02-15T10:53:29+00:00" + }, + { + "name": "symfony/var-exporter", + "version": "v8.0.0", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/symfony/var-exporter/v8.0.0/symfony-var-exporter-v8.0.0.zip", + "reference": "7345f46c251f2eb27c7b3ebdb5bb076b3ffcae04", + "shasum": "" + }, + "require": { + "php": ">=8.4" + }, + "require-dev": { + "symfony/property-access": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\VarExporter\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "license": [ + "MIT" + ], + "authors": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "time": "2026-01-01T23:07:29+00:00" + "description": "Allows exporting any serializable PHP data structure to plain PHP code", + "homepage": "https://symfony.com", + "keywords": [ + "clone", + "construct", + "export", + "hydrate", + "instantiate", + "lazy-loading", + "proxy", + "serialize" + ], + "support": { + "source": "https://github.com/symfony/var-exporter/tree/v8.0.0" + }, + "time": "2025-11-05T18:53:00+00:00" }, { "name": "vlucas/phpdotenv", "version": "v5.6.3", - "source": { - "type": "git", - "url": "https://github.com/vlucas/phpdotenv.git", - "reference": "955e7815d677a3eaa7075231212f2110983adecc" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vlucas/phpdotenv/zipball/955e7815d677a3eaa7075231212f2110983adecc", + "url": "https://mirrors.cloud.tencent.com/repository/composer/vlucas/phpdotenv/v5.6.3/vlucas-phpdotenv-v5.6.3.zip", "reference": "955e7815d677a3eaa7075231212f2110983adecc", "shasum": "" }, @@ -3910,7 +4004,6 @@ "Dotenv\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -3936,29 +4029,14 @@ "issues": "https://github.com/vlucas/phpdotenv/issues", "source": "https://github.com/vlucas/phpdotenv/tree/v5.6.3" }, - "funding": [ - { - "url": "https://github.com/GrahamCampbell", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/vlucas/phpdotenv", - "type": "tidelift" - } - ], "time": "2025-12-27T19:49:13+00:00" }, { "name": "voku/portable-ascii", "version": "2.0.3", - "source": { - "type": "git", - "url": "https://github.com/voku/portable-ascii.git", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "url": "https://mirrors.cloud.tencent.com/repository/composer/voku/portable-ascii/2.0.3/voku-portable-ascii-2.0.3.zip", "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", "shasum": "" }, @@ -3977,7 +4055,6 @@ "voku\\": "src/voku/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -3998,95 +4075,14 @@ "issues": "https://github.com/voku/portable-ascii/issues", "source": "https://github.com/voku/portable-ascii/tree/2.0.3" }, - "funding": [ - { - "url": "https://www.paypal.me/moelleken", - "type": "custom" - }, - { - "url": "https://github.com/voku", - "type": "github" - }, - { - "url": "https://opencollective.com/portable-ascii", - "type": "open_collective" - }, - { - "url": "https://www.patreon.com/voku", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", - "type": "tidelift" - } - ], "time": "2024-11-21T01:49:47+00:00" }, - { - "name": "webman/console", - "version": "v2.2.1", - "source": { - "type": "git", - "url": "https://github.com/webman-php/console.git", - "reference": "3c1a50296f7b3b3eff3a8fcda8906276dae3a18f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webman-php/console/zipball/3c1a50296f7b3b3eff3a8fcda8906276dae3a18f", - "reference": "3c1a50296f7b3b3eff3a8fcda8906276dae3a18f", - "shasum": "" - }, - "require": { - "doctrine/inflector": "^2.0", - "php": ">=8.1", - "symfony/console": "^6.1 || ^7.0" - }, - "require-dev": { - "workerman/webman": "^2.1" - }, - "type": "library", - "autoload": { - "psr-4": { - "Webman\\Console\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "walkor", - "email": "walkor@workerman.net", - "homepage": "http://www.workerman.net", - "role": "Developer" - } - ], - "description": "Webman console", - "homepage": "http://www.workerman.net", - "keywords": [ - "webman console" - ], - "support": { - "email": "walkor@workerman.net", - "forum": "http://www.workerman.net/questions", - "issues": "https://github.com/webman-php/console/issues", - "source": "https://github.com/webman-php/console", - "wiki": "http://www.workerman.net/doc/webman" - }, - "time": "2026-02-23T02:46:47+00:00" - }, { "name": "webman/database", "version": "v2.1.9", - "source": { - "type": "git", - "url": "https://github.com/webman-php/database.git", - "reference": "5a1463c96c79b35225f1cbd2f3e65830a7b4da0e" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webman-php/database/zipball/5a1463c96c79b35225f1cbd2f3e65830a7b4da0e", + "url": "https://mirrors.cloud.tencent.com/repository/composer/webman/database/v2.1.9/webman-database-v2.1.9.zip", "reference": "5a1463c96c79b35225f1cbd2f3e65830a7b4da0e", "shasum": "" }, @@ -4103,7 +4099,6 @@ "Webman\\Database\\": "src" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -4117,14 +4112,9 @@ { "name": "webman/redis", "version": "v2.1.3", - "source": { - "type": "git", - "url": "https://github.com/webman-php/redis.git", - "reference": "559eb1692d39c6fef5cf526223fff728be6c0fb9" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webman-php/redis/zipball/559eb1692d39c6fef5cf526223fff728be6c0fb9", + "url": "https://mirrors.cloud.tencent.com/repository/composer/webman/redis/v2.1.3/webman-redis-v2.1.3.zip", "reference": "559eb1692d39c6fef5cf526223fff728be6c0fb9", "shasum": "" }, @@ -4139,7 +4129,6 @@ "Webman\\Redis\\": "src" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -4153,14 +4142,9 @@ { "name": "workerman/coroutine", "version": "v1.1.4", - "source": { - "type": "git", - "url": "https://github.com/workerman-php/coroutine.git", - "reference": "b0bebfa9d41b992ad0a835ddf2ee8fa5d58eca44" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/workerman-php/coroutine/zipball/b0bebfa9d41b992ad0a835ddf2ee8fa5d58eca44", + "url": "https://mirrors.cloud.tencent.com/repository/composer/workerman/coroutine/v1.1.4/workerman-coroutine-v1.1.4.zip", "reference": "b0bebfa9d41b992ad0a835ddf2ee8fa5d58eca44", "shasum": "" }, @@ -4178,7 +4162,6 @@ "Workerman\\Coroutine\\": "src" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -4190,23 +4173,61 @@ "time": "2025-10-11T15:09:08+00:00" }, { - "name": "workerman/webman", - "version": "v2.2.1", - "source": { - "type": "git", - "url": "https://github.com/walkor/webman.git", - "reference": "8f32669772113d62c5ef515a18066883b5fcf943" - }, + "name": "workerman/crontab", + "version": "v1.0.7", "dist": { "type": "zip", - "url": "https://api.github.com/repos/walkor/webman/zipball/8f32669772113d62c5ef515a18066883b5fcf943", - "reference": "8f32669772113d62c5ef515a18066883b5fcf943", + "url": "https://mirrors.cloud.tencent.com/repository/composer/workerman/crontab/v1.0.7/workerman-crontab-v1.0.7.zip", + "reference": "74f51ca8204e8eb628e57bc0e640561d570da2cb", + "shasum": "" + }, + "require": { + "php": ">=7.0", + "workerman/workerman": ">=4.0.20" + }, + "type": "library", + "autoload": { + "psr-4": { + "Workerman\\Crontab\\": "./src" + } + }, + "license": [ + "MIT" + ], + "authors": [ + { + "name": "walkor", + "email": "walkor@workerman.net", + "homepage": "http://www.workerman.net", + "role": "Developer" + } + ], + "description": "A crontab written in PHP based on workerman", + "homepage": "http://www.workerman.net", + "keywords": [ + "crontab" + ], + "support": { + "email": "walkor@workerman.net", + "forum": "http://wenda.workerman.net/", + "issues": "https://github.com/walkor/workerman/issues", + "source": "https://github.com/walkor/crontab", + "wiki": "http://doc.workerman.net/" + }, + "time": "2025-01-15T07:20:50+00:00" + }, + { + "name": "workerman/webman", + "version": "v2.2.3", + "dist": { + "type": "zip", + "url": "https://mirrors.cloud.tencent.com/repository/composer/workerman/webman/v2.2.3/workerman-webman-v2.2.3.zip", + "reference": "394e611efc496eab992fc6447d2c7ef2438b6902", "shasum": "" }, "require": { "monolog/monolog": "^2.0", "php": ">=8.1", - "webman/console": "^2.1", "workerman/webman-framework": "^2.1" }, "suggest": { @@ -4221,7 +4242,6 @@ "app\\View\\Components\\": "./app/view/components" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -4246,19 +4266,14 @@ "source": "https://github.com/walkor/webman", "wiki": "https://workerman.net/doc/webman" }, - "time": "2026-02-24T06:57:19+00:00" + "time": "2026-02-24T13:23:50+00:00" }, { "name": "workerman/webman-framework", "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/walkor/webman-framework.git", - "reference": "2da4e49259d41925f1732f95f6fb052a3f42ceee" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/walkor/webman-framework/zipball/2da4e49259d41925f1732f95f6fb052a3f42ceee", + "url": "https://mirrors.cloud.tencent.com/repository/composer/workerman/webman-framework/2.2.0/workerman-webman-framework-2.2.0.zip", "reference": "2da4e49259d41925f1732f95f6fb052a3f42ceee", "shasum": "" }, @@ -4287,7 +4302,6 @@ "Support\\Exception\\": "./src/support/exception" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -4317,14 +4331,9 @@ { "name": "workerman/workerman", "version": "v5.1.9", - "source": { - "type": "git", - "url": "https://github.com/walkor/workerman.git", - "reference": "fff0954628f8ceeccfe29d3e817f0fad87cfdbf2" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/walkor/workerman/zipball/fff0954628f8ceeccfe29d3e817f0fad87cfdbf2", + "url": "https://mirrors.cloud.tencent.com/repository/composer/workerman/workerman/v5.1.9/workerman-workerman-v5.1.9.zip", "reference": "fff0954628f8ceeccfe29d3e817f0fad87cfdbf2", "shasum": "" }, @@ -4351,7 +4360,6 @@ "Workerman\\": "src" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], @@ -4378,16 +4386,6 @@ "source": "https://github.com/walkor/workerman", "wiki": "https://www.workerman.net/doc/workerman/" }, - "funding": [ - { - "url": "https://opencollective.com/workerman", - "type": "open_collective" - }, - { - "url": "https://www.patreon.com/walkor", - "type": "patreon" - } - ], "time": "2026-01-09T03:26:15+00:00" } ], @@ -4395,14 +4393,9 @@ { "name": "hamcrest/hamcrest-php", "version": "v2.1.1", - "source": { - "type": "git", - "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "url": "https://mirrors.cloud.tencent.com/repository/composer/hamcrest/hamcrest-php/v2.1.1/hamcrest-hamcrest-php-v2.1.1.zip", "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", "shasum": "" }, @@ -4429,7 +4422,6 @@ "hamcrest" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -4446,14 +4438,9 @@ { "name": "mockery/mockery", "version": "1.6.12", - "source": { - "type": "git", - "url": "https://github.com/mockery/mockery.git", - "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "url": "https://mirrors.cloud.tencent.com/repository/composer/mockery/mockery/1.6.12/mockery-mockery-1.6.12.zip", "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", "shasum": "" }, @@ -4479,7 +4466,6 @@ "Mockery\\": "library/Mockery" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -4526,77 +4512,12 @@ }, "time": "2024-05-16T03:13:13+00:00" }, - { - "name": "myclabs/deep-copy", - "version": "1.13.4", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3 <3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpspec/prophecy": "^1.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2025-08-01T08:46:24+00:00" - }, { "name": "nikic/php-parser", "version": "v5.7.0", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/dca41cd15c2ac9d055ad70dbfd011130757d1f82", + "url": "https://mirrors.cloud.tencent.com/repository/composer/nikic/php-parser/v5.7.0/nikic-php-parser-v5.7.0.zip", "reference": "dca41cd15c2ac9d055ad70dbfd011130757d1f82", "shasum": "" }, @@ -4624,7 +4545,6 @@ "PhpParser\\": "lib/PhpParser" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -4647,14 +4567,9 @@ { "name": "phar-io/manifest", "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "54750ef60c58e43759730615a392c31c80e23176" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "url": "https://mirrors.cloud.tencent.com/repository/composer/phar-io/manifest/2.0.4/phar-io-manifest-2.0.4.zip", "reference": "54750ef60c58e43759730615a392c31c80e23176", "shasum": "" }, @@ -4677,7 +4592,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -4703,25 +4617,14 @@ "issues": "https://github.com/phar-io/manifest/issues", "source": "https://github.com/phar-io/manifest/tree/2.0.4" }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], "time": "2024-03-03T12:33:53+00:00" }, { "name": "phar-io/version", "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "url": "https://mirrors.cloud.tencent.com/repository/composer/phar-io/version/3.2.1/phar-io-version-3.2.1.zip", "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", "shasum": "" }, @@ -4734,7 +4637,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -4765,14 +4667,9 @@ { "name": "phpunit/php-code-coverage", "version": "10.1.16", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "7e308268858ed6baedc8704a304727d20bc07c77" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "url": "https://mirrors.cloud.tencent.com/repository/composer/phpunit/php-code-coverage/10.1.16/phpunit-php-code-coverage-10.1.16.zip", "reference": "7e308268858ed6baedc8704a304727d20bc07c77", "shasum": "" }, @@ -4809,7 +4706,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -4832,25 +4728,14 @@ "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.16" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2024-08-22T04:31:57+00:00" }, { "name": "phpunit/php-file-iterator", "version": "4.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "url": "https://mirrors.cloud.tencent.com/repository/composer/phpunit/php-file-iterator/4.1.0/phpunit-php-file-iterator-4.1.0.zip", "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", "shasum": "" }, @@ -4871,7 +4756,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -4893,25 +4777,14 @@ "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-08-31T06:24:48+00:00" }, { "name": "phpunit/php-invoker", "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "url": "https://mirrors.cloud.tencent.com/repository/composer/phpunit/php-invoker/4.0.0/phpunit-php-invoker-4.0.0.zip", "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", "shasum": "" }, @@ -4936,7 +4809,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -4956,25 +4828,14 @@ "issues": "https://github.com/sebastianbergmann/php-invoker/issues", "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-02-03T06:56:09+00:00" }, { "name": "phpunit/php-text-template", "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "url": "https://mirrors.cloud.tencent.com/repository/composer/phpunit/php-text-template/3.0.1/phpunit-php-text-template-3.0.1.zip", "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", "shasum": "" }, @@ -4995,7 +4856,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5016,25 +4876,14 @@ "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-08-31T14:07:24+00:00" }, { "name": "phpunit/php-timer", "version": "6.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "url": "https://mirrors.cloud.tencent.com/repository/composer/phpunit/php-timer/6.0.0/phpunit-php-timer-6.0.0.zip", "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", "shasum": "" }, @@ -5055,7 +4904,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5075,25 +4923,14 @@ "issues": "https://github.com/sebastianbergmann/php-timer/issues", "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-02-03T06:57:52+00:00" }, { "name": "phpunit/phpunit", "version": "10.5.63", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "33198268dad71e926626b618f3ec3966661e4d90" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/33198268dad71e926626b618f3ec3966661e4d90", + "url": "https://mirrors.cloud.tencent.com/repository/composer/phpunit/phpunit/10.5.63/phpunit-phpunit-10.5.63.zip", "reference": "33198268dad71e926626b618f3ec3966661e4d90", "shasum": "" }, @@ -5145,7 +4982,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5168,41 +5004,14 @@ "security": "https://github.com/sebastianbergmann/phpunit/security/policy", "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.63" }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], "time": "2026-01-27T05:48:37+00:00" }, { "name": "sebastian/cli-parser", "version": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/cli-parser/2.0.1/sebastian-cli-parser-2.0.1.zip", "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", "shasum": "" }, @@ -5223,7 +5032,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5241,25 +5049,14 @@ "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2024-03-02T07:12:49+00:00" }, { "name": "sebastian/code-unit", "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/code-unit/2.0.0/sebastian-code-unit-2.0.0.zip", "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", "shasum": "" }, @@ -5280,7 +5077,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5297,25 +5093,14 @@ "issues": "https://github.com/sebastianbergmann/code-unit/issues", "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-02-03T06:58:43+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/code-unit-reverse-lookup/3.0.0/sebastian-code-unit-reverse-lookup-3.0.0.zip", "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", "shasum": "" }, @@ -5336,7 +5121,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5352,25 +5136,14 @@ "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-02-03T06:59:15+00:00" }, { "name": "sebastian/comparator", "version": "5.0.5", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/55dfef806eb7dfeb6e7a6935601fef866f8ca48d", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/comparator/5.0.5/sebastian-comparator-5.0.5.zip", "reference": "55dfef806eb7dfeb6e7a6935601fef866f8ca48d", "shasum": "" }, @@ -5395,7 +5168,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5429,37 +5201,14 @@ "security": "https://github.com/sebastianbergmann/comparator/security/policy", "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.5" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", - "type": "tidelift" - } - ], "time": "2026-01-24T09:25:16+00:00" }, { "name": "sebastian/complexity", "version": "3.2.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68ff824baeae169ec9f2137158ee529584553799" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/complexity/3.2.0/sebastian-complexity-3.2.0.zip", "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, @@ -5481,7 +5230,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5499,25 +5247,14 @@ "security": "https://github.com/sebastianbergmann/complexity/security/policy", "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-12-21T08:37:17+00:00" }, { "name": "sebastian/diff", "version": "5.1.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/diff/5.1.1/sebastian-diff-5.1.1.zip", "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", "shasum": "" }, @@ -5539,7 +5276,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5566,25 +5302,14 @@ "security": "https://github.com/sebastianbergmann/diff/security/policy", "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2024-03-02T07:15:17+00:00" }, { "name": "sebastian/environment", "version": "6.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/environment/6.1.0/sebastian-environment-6.1.0.zip", "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", "shasum": "" }, @@ -5608,7 +5333,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5630,25 +5354,14 @@ "security": "https://github.com/sebastianbergmann/environment/security/policy", "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2024-03-23T08:47:14+00:00" }, { "name": "sebastian/exporter", "version": "5.1.4", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "0735b90f4da94969541dac1da743446e276defa6" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/0735b90f4da94969541dac1da743446e276defa6", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/exporter/5.1.4/sebastian-exporter-5.1.4.zip", "reference": "0735b90f4da94969541dac1da743446e276defa6", "shasum": "" }, @@ -5671,7 +5384,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5708,37 +5420,14 @@ "security": "https://github.com/sebastianbergmann/exporter/security/policy", "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.4" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", - "type": "tidelift" - } - ], "time": "2025-09-24T06:09:11+00:00" }, { "name": "sebastian/global-state", "version": "6.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/global-state/6.0.2/sebastian-global-state-6.0.2.zip", "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", "shasum": "" }, @@ -5762,7 +5451,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5782,25 +5470,14 @@ "security": "https://github.com/sebastianbergmann/global-state/security/policy", "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2024-03-02T07:19:19+00:00" }, { "name": "sebastian/lines-of-code", "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/lines-of-code/2.0.2/sebastian-lines-of-code-2.0.2.zip", "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, @@ -5822,7 +5499,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5840,25 +5516,14 @@ "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-12-21T08:38:20+00:00" }, { "name": "sebastian/object-enumerator", "version": "5.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/object-enumerator/5.0.0/sebastian-object-enumerator-5.0.0.zip", "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", "shasum": "" }, @@ -5881,7 +5546,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5897,25 +5561,14 @@ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-02-03T07:08:32+00:00" }, { "name": "sebastian/object-reflector", "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/object-reflector/3.0.0/sebastian-object-reflector-3.0.0.zip", "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, @@ -5936,7 +5589,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -5952,25 +5604,14 @@ "issues": "https://github.com/sebastianbergmann/object-reflector/issues", "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-02-03T07:06:18+00:00" }, { "name": "sebastian/recursion-context", "version": "5.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/47e34210757a2f37a97dcd207d032e1b01e64c7a", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/recursion-context/5.0.1/sebastian-recursion-context-5.0.1.zip", "reference": "47e34210757a2f37a97dcd207d032e1b01e64c7a", "shasum": "" }, @@ -5991,7 +5632,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -6016,37 +5656,14 @@ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.1" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", - "type": "tidelift" - } - ], "time": "2025-08-10T07:50:56+00:00" }, { "name": "sebastian/type", "version": "4.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/type/4.0.0/sebastian-type-4.0.0.zip", "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, @@ -6067,7 +5684,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -6084,25 +5700,14 @@ "issues": "https://github.com/sebastianbergmann/type/issues", "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-02-03T07:10:45+00:00" }, { "name": "sebastian/version", "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "url": "https://mirrors.cloud.tencent.com/repository/composer/sebastian/version/4.0.1/sebastian-version-4.0.1.zip", "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "shasum": "" }, @@ -6120,7 +5725,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -6137,25 +5741,14 @@ "issues": "https://github.com/sebastianbergmann/version/issues", "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], "time": "2023-02-07T11:34:05+00:00" }, { "name": "theseer/tokenizer", "version": "1.3.1", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "b7489ce515e168639d17feec34b8847c326b0b3c" - }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/b7489ce515e168639d17feec34b8847c326b0b3c", + "url": "https://mirrors.cloud.tencent.com/repository/composer/theseer/tokenizer/1.3.1/theseer-tokenizer-1.3.1.zip", "reference": "b7489ce515e168639d17feec34b8847c326b0b3c", "shasum": "" }, @@ -6171,7 +5764,6 @@ "src/" ] }, - "notification-url": "https://packagist.org/downloads/", "license": [ "BSD-3-Clause" ], @@ -6187,12 +5779,6 @@ "issues": "https://github.com/theseer/tokenizer/issues", "source": "https://github.com/theseer/tokenizer/tree/1.3.1" }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], "time": "2025-11-17T20:03:58+00:00" } ], @@ -6205,5 +5791,5 @@ "php": ">=8.1" }, "platform-dev": {}, - "plugin-api-version": "2.9.0" + "plugin-api-version": "2.6.0" } diff --git a/backend/config/process.php b/backend/config/process.php new file mode 100644 index 0000000..fb7467f --- /dev/null +++ b/backend/config/process.php @@ -0,0 +1,12 @@ + [ + 'handler' => app\process\Task::class + ], +]; diff --git a/backend/config/route.php b/backend/config/route.php index ad9643a..23d26bb 100644 --- a/backend/config/route.php +++ b/backend/config/route.php @@ -62,6 +62,22 @@ Route::group('/api/v1/business-trip', function () { Route::post('/approve/{id}', [app\controller\BusinessTripController::class, 'approve']); })->middleware($authMiddleware); +// 二手车路由(需要认证) +Route::group('/api/v1/usedcar', function () { + Route::get('/list', [app\controller\UsedCarController::class, 'list']); + Route::get('/detail', [app\controller\UsedCarController::class, 'detail']); + Route::post('/create', [app\controller\UsedCarController::class, 'create']); + Route::post('/update', [app\controller\UsedCarController::class, 'update']); + Route::post('/delete', [app\controller\UsedCarController::class, 'delete']); + Route::post('/status', [app\controller\UsedCarController::class, 'updateStatus']); + Route::post('/crawl/url', [app\controller\UsedCarController::class, 'crawlByUrl']); + Route::post('/crawl/mobile', [app\controller\UsedCarController::class, 'crawlMobileUrl']); + Route::post('/pdf/generate', [app\controller\UsedCarController::class, 'generatePdf']); + Route::get('/pdf/download', [app\controller\UsedCarController::class, 'downloadPdf']); + Route::get('/pdf/url', [app\controller\UsedCarController::class, 'getPdfUrl']); + Route::get('/pdf/preview', [app\controller\UsedCarController::class, 'previewPdf']); +})->middleware($authMiddleware); + // 默认路由 Route::fallback(function () { return json(['code' => 404, 'message' => 'Not Found']); diff --git a/backend/database/migrations/015_create_used_car_tables.sql b/backend/database/migrations/015_create_used_car_tables.sql new file mode 100644 index 0000000..feb5934 --- /dev/null +++ b/backend/database/migrations/015_create_used_car_tables.sql @@ -0,0 +1,56 @@ +-- 二手车相关数据表 +-- 创建时间: 2026-03-05 + +-- 二手车主表 +CREATE TABLE IF NOT EXISTS `used_car` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `code` varchar(100) NOT NULL DEFAULT '' COMMENT '唯一编号(用户拼音首字母+用户ID+日期+序号)', + `user_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '用户ID', + `role` tinyint(1) NOT NULL DEFAULT 1 COMMENT '用户角色:1管理员 2普通用户', + `product_name` varchar(255) NOT NULL DEFAULT '' COMMENT '产品名称/车辆标题', + `vin` varchar(50) DEFAULT NULL COMMENT '车辆识别码', + `price` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '价格(单位:分)', + `source_place` varchar(100) DEFAULT NULL COMMENT '车源地', + `plate_place` varchar(100) DEFAULT NULL COMMENT '上牌地点', + `plate_time` varchar(50) DEFAULT NULL COMMENT '上牌时间', + `displacement` varchar(50) DEFAULT NULL COMMENT '排量', + `mileage` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '里程(单位:公里)', + `power` varchar(50) DEFAULT NULL COMMENT '马力', + `body_color` varchar(50) DEFAULT NULL COMMENT '车身颜色', + `images` text DEFAULT NULL COMMENT '图片(逗号分隔的OSS路径)', + `condition_desc` text DEFAULT NULL COMMENT '车况介绍', + `attachment` text DEFAULT NULL COMMENT '附件(JSON格式:[{name:, src:}])', + `pdf_src` varchar(255) DEFAULT NULL COMMENT 'PDF文件路径', + `source_url` text DEFAULT NULL COMMENT '源URL(抓取来源)', + `crawl_title` varchar(255) DEFAULT NULL COMMENT '抓取标题', + `crawl_brand` varchar(100) DEFAULT NULL COMMENT '抓取品牌', + `crawl_brand_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '抓取品牌ID', + `crawl_series` varchar(100) DEFAULT NULL COMMENT '抓取车系', + `crawl_series_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '抓取车系ID', + `crawl_year` varchar(10) DEFAULT NULL COMMENT '抓取年份', + `crawl_msrp` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '抓取指导价(单位:分)', + `crawl_car_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '抓取车型ID', + `status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '状态:1正常 2归档 3删除', + `sort` int(11) NOT NULL DEFAULT 0 COMMENT '排序', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_code` (`code`), + KEY `idx_status` (`status`), + KEY `idx_created_at` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='二手车信息表'; + +-- 二手车PDF生成日志表 +CREATE TABLE IF NOT EXISTS `used_car_pdf_log` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '用户ID', + `used_car_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '二手车ID', + `price_snapshot` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '价格快照(单位:分)', + `pdf_path` varchar(255) NOT NULL DEFAULT '' COMMENT 'PDF路径', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_used_car_id` (`used_car_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='二手车PDF生成日志表'; diff --git a/backend/database/xserver.sql b/backend/database/xserver.sql index e5e0fdb..529dc20 100644 --- a/backend/database/xserver.sql +++ b/backend/database/xserver.sql @@ -900539,3 +900539,61 @@ INSERT INTO `users_copy1` VALUES (74, '263364', 3, '李永春', 'https://s3-imfi INSERT INTO `users_copy1` VALUES (75, '698484', 3, '曲江丽', 'https://s1-imfile.feishucdn.com/static-resource/v1/v3_00uh_6412ff58-77d6-4e5f-b35e-6b1a76ed31eg~?image_size=240x240&cut_type=&quality=&format=image&sticker_format=.webp', NULL, 'qujiangli@paikacar.com', '18339956922', '$2y$10$j9w0jkEYliIPbGbiPK5MSOhqGwwzY1Enf/koZC2MVb4JrXSFUhqDq', 1, 1, 'on_8f01d5bee7af07ce3de5e31df7075c99', 2, 'x_server/user_signature/20260210/6ad00820e9be180db7ac4aa729e4b6e9_698ad1464aae9.png', '2026-02-10 14:25:49', '2026-02-26 16:18:51'); SET FOREIGN_KEY_CHECKS = 1; + +-- 二手车相关数据表 (015_create_used_car_tables.sql) +-- 二手车相关数据表 +-- 创建时间: 2026-03-05 + +-- 二手车主表 +CREATE TABLE IF NOT EXISTS `used_car` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `code` varchar(100) NOT NULL DEFAULT '' COMMENT '唯一编号(用户拼音首字母+用户ID+日期+序号)', + `user_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '用户ID', + `role` tinyint(1) NOT NULL DEFAULT 1 COMMENT '用户角色:1管理员 2普通用户', + `product_name` varchar(255) NOT NULL DEFAULT '' COMMENT '产品名称/车辆标题', + `vin` varchar(50) DEFAULT NULL COMMENT '车辆识别码', + `price` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '价格(单位:分)', + `source_place` varchar(100) DEFAULT NULL COMMENT '车源地', + `plate_place` varchar(100) DEFAULT NULL COMMENT '上牌地点', + `plate_time` varchar(50) DEFAULT NULL COMMENT '上牌时间', + `displacement` varchar(50) DEFAULT NULL COMMENT '排量', + `mileage` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '里程(单位:公里)', + `power` varchar(50) DEFAULT NULL COMMENT '马力', + `body_color` varchar(50) DEFAULT NULL COMMENT '车身颜色', + `images` text DEFAULT NULL COMMENT '图片(逗号分隔的OSS路径)', + `condition_desc` text DEFAULT NULL COMMENT '车况介绍', + `attachment` text DEFAULT NULL COMMENT '附件(JSON格式:[{name:, src:}])', + `pdf_src` varchar(255) DEFAULT NULL COMMENT 'PDF文件路径', + `source_url` text DEFAULT NULL COMMENT '源URL(抓取来源)', + `crawl_title` varchar(255) DEFAULT NULL COMMENT '抓取标题', + `crawl_brand` varchar(100) DEFAULT NULL COMMENT '抓取品牌', + `crawl_brand_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '抓取品牌ID', + `crawl_series` varchar(100) DEFAULT NULL COMMENT '抓取车系', + `crawl_series_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '抓取车系ID', + `crawl_year` varchar(10) DEFAULT NULL COMMENT '抓取年份', + `crawl_msrp` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '抓取指导价(单位:分)', + `crawl_car_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '抓取车型ID', + `status` tinyint(1) NOT NULL DEFAULT 1 COMMENT '状态:1正常 2归档 3删除', + `sort` int(11) NOT NULL DEFAULT 0 COMMENT '排序', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_code` (`code`), + KEY `idx_status` (`status`), + KEY `idx_created_at` (`created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='二手车信息表'; + +-- 二手车PDF生成日志表 +CREATE TABLE IF NOT EXISTS `used_car_pdf_log` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `user_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '用户ID', + `used_car_id` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '二手车ID', + `price_snapshot` int(11) unsigned NOT NULL DEFAULT 0 COMMENT '价格快照(单位:分)', + `pdf_path` varchar(255) NOT NULL DEFAULT '' COMMENT 'PDF路径', + `created_at` timestamp NULL DEFAULT NULL, + `updated_at` timestamp NULL DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_user_id` (`user_id`), + KEY `idx_used_car_id` (`used_car_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='二手车PDF生成日志表'; diff --git a/backend/runtime/pdf/templates/contactCard.stub b/backend/runtime/pdf/templates/contactCard.stub new file mode 100644 index 0000000..5c44eda --- /dev/null +++ b/backend/runtime/pdf/templates/contactCard.stub @@ -0,0 +1,84 @@ + + +
+ + + + + +| = $field['price'] ?> | += $field['currency'].' ' ?>= number_format($car['price'], 2) ?> | +
| = $field['trade'] ?> | += $car['trade'] ?> | +
| = $field['plate_time'] ?> | += $car['plate_time'] ?> | +
| = $field['displacement'] ?> | += $car['displacement'] ?> | +
| = $field['mileage'] ?> | += $car['mileage'].'km' ?> | +
| = $field['power'] ?> | += $car['power'] ?> | +
| = $field['body_color'] ?> | += $car['body_color'] ?: '-' ?> | +
| = $field['created_at'] ?> | += $car['created_at'] ?> | +
|
+
+ = $field['images'] ?>
+
+ |
+ |
|---|---|
|
+ |
+
+
+ |