Files
anxinyan/server-api/app/controller/app/ReportsController.php
wushumin 9aac78b8da first
2026-05-11 15:28:27 +08:00

297 lines
12 KiB
PHP

<?php
namespace app\controller\app;
use app\model\Report;
use app\support\AppraisalEvidenceService;
use app\support\AppAuthService;
use app\support\ContentService;
use app\support\FileStorageService;
use app\support\ReportPdfGenerator;
use support\Request;
use support\think\Db;
class ReportsController
{
public function index(Request $request)
{
$userId = app_user_id($request);
$rows = Db::name('orders')
->alias('o')
->leftJoin('reports r', 'r.order_id = o.id AND r.report_status = "published"')
->leftJoin('order_products p', 'p.order_id = o.id')
->field([
'o.id AS order_id',
'o.order_status',
'o.display_status',
'o.service_provider',
'p.product_name',
'p.product_cover',
'r.id AS report_id',
'r.report_no',
'r.institution_name',
'r.publish_time',
])
->where('o.user_id', $userId)
->whereIn('o.order_status', ['in_first_review', 'in_final_review', 'generating_report', 'report_published', 'completed'])
->order('o.id', 'desc')
->select()
->toArray();
$list = array_map(function (array $item) {
$published = !empty($item['report_id']);
return [
'report_id' => $published ? (int)$item['report_id'] : null,
'order_id' => (int)$item['order_id'],
'report_no' => $item['report_no'] ?: '',
'product_name' => $item['product_name'] ?: '',
'product_cover' => $item['product_cover'] ?: '',
'service_provider' => $item['service_provider'],
'status' => $published ? '已出报告' : '待出报告',
'result_text' => $published ? '正品' : '待出报告',
'institution_name' => $item['institution_name'] ?: '安心验',
'publish_time' => $item['publish_time'],
];
}, $rows);
return api_success(['list' => $list]);
}
public function detail(Request $request)
{
$id = (int)$request->input('id', 0);
$reportNo = trim((string)$request->input('report_no', ''));
if (!$id && $reportNo === '') {
return api_error('报告标识不能为空', 422);
}
$report = null;
if ($reportNo !== '') {
$report = Report::where('report_status', 'published')
->where('report_no', $reportNo)
->find();
} elseif ($id > 0) {
$userInfo = app_user($request) ?: (new AppAuthService())->current($request);
if (!$userInfo) {
return api_error('未登录或登录已过期', 401);
}
$report = Db::name('reports')
->alias('r')
->join('orders o', 'o.id = r.order_id')
->where('r.id', $id)
->where('r.report_status', 'published')
->where('o.user_id', (int)$userInfo['id'])
->field('r.*')
->find();
}
if (!$report) {
return api_error('报告不存在', 404);
}
$reportData = is_array($report) ? $report : $report->toArray();
$content = Db::name('report_contents')->where('report_id', $reportData['id'])->find();
$verify = Db::name('report_verifies')->where('report_id', $reportData['id'])->find();
$verify = $this->normalizeVerifyInfo($reportData, $verify ?: []);
$pdfUrl = $this->ensurePdfFile($request, $reportData, $content ?: [], $verify ?: []);
$evidenceAttachments = $this->evidenceService()->normalize($content['evidence_attachments_json'] ?? null, $request);
$defaultRiskNotice = (new ContentService())->getReportRiskNotice((string)($reportData['report_type'] ?? 'appraisal'));
$payload = [
'product_snapshot' => $this->decodeJsonField($content['product_snapshot_json'] ?? null),
'result_snapshot' => $this->decodeJsonField($content['result_snapshot_json'] ?? null),
'appraisal_snapshot' => $this->decodeJsonField($content['appraisal_snapshot_json'] ?? null),
'valuation_snapshot' => $this->decodeJsonField($content['valuation_snapshot_json'] ?? null),
'risk_notice_text' => ($content['risk_notice_text'] ?? '') !== '' ? $content['risk_notice_text'] : $defaultRiskNotice,
];
return api_success([
'report_header' => [
'report_id' => (int)$reportData['id'],
'report_no' => $reportData['report_no'],
'report_type' => $reportData['report_type'] ?? 'appraisal',
'report_title' => $reportData['report_title'],
'report_status' => $reportData['report_status'],
'service_provider' => $reportData['service_provider'],
'institution_name' => $reportData['institution_name'],
'publish_time' => $reportData['publish_time'],
],
'result_info' => $payload['result_snapshot'],
'product_info' => $payload['product_snapshot'],
'appraisal_info' => $payload['appraisal_snapshot'],
'valuation_info' => $payload['valuation_snapshot'],
'evidence_attachments' => $evidenceAttachments,
'risk_notice_text' => $payload['risk_notice_text'],
'verify_info' => [
'report_no' => $reportData['report_no'],
'verify_status' => $verify['verify_status'] ?? 'valid',
'verify_url' => $verify['verify_url'] ?? '',
'verify_qrcode_url' => $verify['verify_qrcode_url'] ?? '',
],
'file_info' => [
'pdf_url' => $pdfUrl,
],
]);
}
private function decodeJsonField(mixed $value): array
{
if (is_array($value)) {
return $value;
}
if (is_string($value) && $value !== '') {
return json_decode($value, true) ?: [];
}
return [];
}
private function normalizeVerifyInfo(array $report, array $verify): array
{
$reportNo = (string)($report['report_no'] ?? '');
$verifyPageUrl = $this->buildPublicPageUrl('/pages/verify/result', ['report_no' => $reportNo]);
$verify['report_no'] = $verify['report_no'] ?? $reportNo;
$verify['verify_status'] = $verify['verify_status'] ?? 'valid';
$rawVerifyUrl = trim((string)($verify['verify_url'] ?? ''));
if ($rawVerifyUrl === '' || str_starts_with($rawVerifyUrl, '/api/app/verify')) {
$verify['verify_url'] = $verifyPageUrl;
}
$rawQrValue = trim((string)($verify['verify_qrcode_url'] ?? ''));
if ($rawQrValue === '' || str_starts_with($rawQrValue, '/api/app/verify') || str_contains($rawQrValue, '/pages/report/detail')) {
$verify['verify_qrcode_url'] = $verify['verify_url'];
}
return $verify;
}
private function ensurePdfFile(Request $request, array $report, array $content, array $verify): string
{
$existingFile = Db::name('report_files')
->where('report_id', (int)$report['id'])
->where('file_type', 'pdf')
->find();
if ($existingFile && !empty($existingFile['file_url'])) {
$relativeUrl = ltrim((string)$existingFile['file_url'], '/');
if ($this->storage()->exists($relativeUrl)) {
return $this->storage()->publicUrl($request, $relativeUrl);
}
}
$productInfo = $this->decodeJsonField($content['product_snapshot_json'] ?? null);
$resultInfo = $this->decodeJsonField($content['result_snapshot_json'] ?? null);
$appraisalInfo = $this->decodeJsonField($content['appraisal_snapshot_json'] ?? null);
$valuationInfo = $this->decodeJsonField($content['valuation_snapshot_json'] ?? null);
$publishTime = (string)($report['publish_time'] ?: date('Y-m-d H:i:s'));
$defaultRiskNotice = (new ContentService())->getReportRiskNotice((string)($report['report_type'] ?? 'appraisal'));
$relativeDir = 'uploads/reports/' . date('Ymd', strtotime($publishTime));
$filename = $report['report_no'] . '.pdf';
$relativePath = $relativeDir . '/' . $filename;
$generator = new ReportPdfGenerator();
$pdfBinary = $generator->generate([
'report_title' => $report['report_title'] ?? '鉴定报告',
'service_provider_text' => ($report['service_provider'] ?? 'anxinyan') === 'zhongjian' ? '中检鉴定' : '实物鉴定',
'institution_name' => $report['institution_name'] ?? '安心验',
'report_no' => $report['report_no'] ?? '',
'publish_time' => $publishTime,
'result_text' => $resultInfo['result_text'] ?? '-',
'result_desc' => $resultInfo['result_desc'] ?? '-',
'product_name' => $productInfo['product_name'] ?? '-',
'category_brand' => trim(($productInfo['category_name'] ?? '-') . ' / ' . ($productInfo['brand_name'] ?? '-')),
'spec_info' => trim(($productInfo['color'] ?? '-') . ' / ' . ($productInfo['size_spec'] ?? '-')),
'appraisers' => trim((string)($appraisalInfo['appraiser_name'] ?? '-')),
'condition_grade' => $valuationInfo['condition_grade'] ?? '-',
'valuation_range' => sprintf(
'¥%s - ¥%s',
$valuationInfo['valuation_min'] ?? 0,
$valuationInfo['valuation_max'] ?? 0
),
'verify_info' => sprintf(
'%s / %s',
$verify['report_no'] ?? ($report['report_no'] ?? '-'),
($verify['verify_status'] ?? 'valid') === 'valid' ? '有效' : ($verify['verify_status'] ?? '-')
),
'risk_notice_text' => ($content['risk_notice_text'] ?? '') !== '' ? $content['risk_notice_text'] : ($defaultRiskNotice !== '' ? $defaultRiskNotice : '-'),
]);
$this->storage()->putContents($relativePath, $pdfBinary);
$now = date('Y-m-d H:i:s');
$filePayload = [
'report_id' => (int)$report['id'],
'file_type' => 'pdf',
'file_url' => '/' . $relativePath,
'file_status' => 'ready',
'updated_at' => $now,
];
if ($existingFile) {
Db::name('report_files')->where('id', $existingFile['id'])->update($filePayload);
} else {
$filePayload['created_at'] = $now;
Db::name('report_files')->insert($filePayload);
}
return $this->storage()->publicUrl($request, $relativePath);
}
private function buildPublicPageUrl(string $pagePath, array $query = []): string
{
$baseUrl = $this->normalizeH5BaseUrl($this->getSystemConfigValue('h5', 'page_base_url'));
$page = ltrim($pagePath, '/');
$queryString = http_build_query($query);
$hashPath = '/#/' . $page;
if ($queryString !== '') {
$hashPath .= '?' . $queryString;
}
if ($baseUrl === '') {
return $hashPath;
}
return $baseUrl . $hashPath;
}
private function normalizeH5BaseUrl(string $value): string
{
$baseUrl = trim($value);
if ($baseUrl === '') {
return '';
}
$hashPos = strpos($baseUrl, '#');
if ($hashPos !== false) {
$baseUrl = substr($baseUrl, 0, $hashPos);
}
if (!preg_match('/^https?:\/\//i', $baseUrl)) {
$baseUrl = 'https://' . ltrim($baseUrl, '/');
}
return rtrim($baseUrl, '/');
}
private function getSystemConfigValue(string $groupCode, string $configKey): string
{
$row = Db::name('system_configs')
->where('config_group', $groupCode)
->where('config_key', $configKey)
->find();
return trim((string)($row['config_value'] ?? ''));
}
private function evidenceService(): AppraisalEvidenceService
{
return new AppraisalEvidenceService();
}
private function storage(): FileStorageService
{
return new FileStorageService();
}
}