feat: update report detail and verification flow

This commit is contained in:
wushumin
2026-05-22 13:31:02 +08:00
parent b98d6164a7
commit be64b8e5b7
14 changed files with 1336 additions and 282 deletions

View File

@@ -6,49 +6,59 @@ class ReportPdfGenerator
{
public function generate(array $payload): string
{
$content = $this->buildContentStream($payload);
$heroImage = $this->normalizeHeroImage($payload['hero_image'] ?? null);
$content = $this->buildContentStream($payload, $heroImage);
$resources = '<< /Font << /F1 5 0 R >>';
if ($heroImage) {
$resources .= ' /XObject << /Im1 7 0 R >>';
}
$resources .= ' >>';
$objects = [
1 => '<< /Type /Catalog /Pages 2 0 R >>',
2 => '<< /Type /Pages /Kids [3 0 R] /Count 1 >>',
3 => '<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources << /Font << /F1 5 0 R >> >> /Contents 4 0 R >>',
3 => sprintf('<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Resources %s /Contents 4 0 R >>', $resources),
4 => sprintf("<< /Length %d >>\nstream\n%s\nendstream", strlen($content), $content),
5 => '<< /Type /Font /Subtype /Type0 /BaseFont /STSong-Light /Encoding /UniGB-UCS2-H /DescendantFonts [6 0 R] >>',
6 => '<< /Type /Font /Subtype /CIDFontType0 /BaseFont /STSong-Light /CIDSystemInfo << /Registry (Adobe) /Ordering (GB1) /Supplement 4 >> /DW 1000 >>',
];
if ($heroImage) {
$objects[7] = sprintf(
"<< /Type /XObject /Subtype /Image /Width %d /Height %d /ColorSpace /DeviceRGB /BitsPerComponent 8 /Filter /DCTDecode /Length %d >>\nstream\n%s\nendstream",
$heroImage['width'],
$heroImage['height'],
strlen($heroImage['data']),
$heroImage['data']
);
}
return $this->renderPdf($objects);
}
private function buildContentStream(array $payload): string
private function buildContentStream(array $payload, ?array $heroImage): string
{
$title = $this->normalizeText((string)($payload['report_title'] ?? '鉴定报告'));
$serviceProviderText = $this->normalizeText((string)($payload['service_provider_text'] ?? '-'));
$institutionName = $this->normalizeText((string)($payload['institution_name'] ?? '-'));
$reportNo = $this->normalizeText((string)($payload['report_no'] ?? '-'));
$publishTime = $this->normalizeText((string)($payload['publish_time'] ?? '-'));
$resultText = $this->normalizeText((string)($payload['result_text'] ?? '-'));
$resultDesc = $this->normalizeText((string)($payload['result_desc'] ?? '-'));
$productName = $this->normalizeText((string)($payload['product_name'] ?? '-'));
$categoryBrand = $this->normalizeText((string)($payload['category_brand'] ?? '-'));
$specInfo = $this->normalizeText((string)($payload['spec_info'] ?? '-'));
$appraisers = $this->normalizeText((string)($payload['appraisers'] ?? '-'));
$conditionGrade = $this->normalizeText((string)($payload['condition_grade'] ?? '-'));
$valuationRange = $this->normalizeText((string)($payload['valuation_range'] ?? '-'));
$verifyInfo = $this->normalizeText((string)($payload['verify_info'] ?? '-'));
$riskNotice = $this->normalizeText((string)($payload['risk_notice_text'] ?? '-'));
$productItems = is_array($payload['product_items'] ?? null) ? $payload['product_items'] : [];
$heroImageLabels = is_array($payload['hero_image_labels'] ?? null) ? $payload['hero_image_labels'] : [];
$blocks = [];
$y = 790;
$blocks[] = $this->textBlock($title, 52, $y, 20);
$y -= 32;
$blocks[] = $this->textBlock('正式报告凭证,请以编号验真结果为准。', 52, $y, 10);
$blocks[] = $this->textBlock('正式报告凭证,请以报告页防伪查询结果为准。', 52, $y, 10);
$y -= 30;
foreach ([
sprintf('报告编号:%s', $reportNo),
sprintf('出具机构:%s', $institutionName),
sprintf('检测机构:%s', $institutionName),
sprintf('出具时间:%s', $publishTime),
sprintf('服务类型:%s', $serviceProviderText),
] as $line) {
@@ -56,29 +66,56 @@ class ReportPdfGenerator
$y -= 22;
}
$y -= 8;
$blocks[] = $this->textBlock(sprintf('鉴定结论:%s', $resultText), 52, $y, 16);
$y -= 26;
foreach ($this->wrapText('结果说明:' . $resultDesc, 30) as $line) {
$blocks[] = $this->textBlock($line, 52, $y, 11);
$y -= 18;
if ($heroImage) {
$maxWidth = 230;
$maxHeight = 150;
$scale = min($maxWidth / $heroImage['width'], $maxHeight / $heroImage['height'], 1);
$drawWidth = (int)round($heroImage['width'] * $scale);
$drawHeight = (int)round($heroImage['height'] * $scale);
$y -= $drawHeight + 6;
$blocks[] = $this->imageBlock('Im1', 52, $y, $drawWidth, $drawHeight);
$y -= 24;
} elseif ($heroImageLabels) {
foreach ($this->wrapText('鉴定图片:' . implode('、', array_slice($heroImageLabels, 0, 3)), 34) as $line) {
$blocks[] = $this->textBlock($line, 52, $y, 11);
$y -= 18;
}
$y -= 8;
}
$y -= 8;
foreach ([
sprintf('商品名称:%s', $productName),
sprintf('品类 / 品牌%s', $categoryBrand),
sprintf('颜色 / 规格:%s', $specInfo),
sprintf('鉴定师:%s', $appraisers),
sprintf('成色评级:%s', $conditionGrade),
sprintf('估值区间:%s', $valuationRange),
sprintf('验真信息:%s', $verifyInfo),
] as $line) {
$blocks[] = $this->textBlock($line, 52, $y, 11);
$y -= 20;
$blocks[] = $this->textBlock('产品信息', 52, $y, 15);
$y -= 24;
$blocks[] = $this->textBlock(sprintf('产品名称%s', $productName), 52, $y, 12);
$y -= 22;
foreach ($productItems as $item) {
if (!is_array($item)) {
continue;
}
$label = $this->normalizeText((string)($item['label'] ?? ''));
if ($label === '') {
continue;
}
$value = $this->normalizeText((string)($item['value'] ?? '-'));
$remark = $this->normalizeText((string)($item['remark'] ?? ''));
foreach ($this->wrapText(sprintf('%s%s', $label, $value !== '' ? $value : '-'), 34) as $line) {
$blocks[] = $this->textBlock($line, 52, $y, 11);
$y -= 18;
}
if ($remark !== '') {
foreach ($this->wrapText('说明:' . $remark, 34) as $line) {
$blocks[] = $this->textBlock($line, 72, $y, 10);
$y -= 16;
}
}
$y -= 2;
}
$y -= 6;
$blocks[] = $this->textBlock(sprintf('验真信息:%s', $verifyInfo), 52, $y, 11);
$y -= 22;
$y -= 8;
foreach ($this->wrapText('风险说明:' . $riskNotice, 30) as $line) {
$blocks[] = $this->textBlock($line, 52, $y, 10);
@@ -86,12 +123,32 @@ class ReportPdfGenerator
}
if ($y > 48) {
$blocks[] = $this->textBlock('安心验鉴定平台', 52, 42, 9);
$blocks[] = $this->textBlock('安心验鉴定平台', 52, 42, 9);
}
return implode("\n", array_filter($blocks));
}
private function normalizeHeroImage(mixed $image): ?array
{
if (!is_array($image)) {
return null;
}
$data = (string)($image['data'] ?? '');
$width = (int)($image['width'] ?? 0);
$height = (int)($image['height'] ?? 0);
if ($data === '' || $width <= 0 || $height <= 0) {
return null;
}
return [
'data' => $data,
'width' => $width,
'height' => $height,
];
}
private function wrapText(string $text, int $maxUnits): array
{
$normalized = $this->normalizeText($text);
@@ -137,6 +194,11 @@ class ReportPdfGenerator
);
}
private function imageBlock(string $name, int $x, int $y, int $width, int $height): string
{
return sprintf("q\n%d 0 0 %d %d %d cm\n/%s Do\nQ", $width, $height, $x, $y, $name);
}
private function normalizeText(string $text): string
{
$text = trim(str_replace(["\r\n", "\r", "\n", "\t"], [' ', ' ', ' ', ' '], $text));