From 420367200c119d1d7ddf6dae12e3bf9ab0588d7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8F=AF=E4=B9=90=E5=8A=A0=E5=86=B0?= <11869599+addie66@user.noreply.gitee.com> Date: Fri, 16 Oct 2026 09:59:32 +0800 Subject: [PATCH] =?UTF-8?q?```=20feat(shopro=5Fhuifu):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=88=86=E8=B4=A6=E6=89=A9=E5=B1=95=E6=9D=90=E6=96=99?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=8A=9F=E8=83=BD=E5=B9=B6=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 UploadSplitExtendedMaterials 命令用于上传 F469/F330 等分账补充材料 - 优化 ProbeSplitConfigDeps 命令中扩展材料配置的检查逻辑 - 支持 PDF 文件上传,增加文件大小限制(PDF 5MB,图片 2MB) - 修复 MCC 编码可作为 online_busi_type 备选值的问题 ``` --- .../command/ProbeSplitConfigDeps.php | 17 ++- .../command/UploadSplitExtendedMaterials.php | 119 ++++++++++++++++++ .../library/onboard/HuifuOnboardPicture.php | 42 +++++-- .../split/HuifuMerchantSplitService.php | 8 +- 4 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 addons/shopro_huifu/command/UploadSplitExtendedMaterials.php diff --git a/addons/shopro_huifu/command/ProbeSplitConfigDeps.php b/addons/shopro_huifu/command/ProbeSplitConfigDeps.php index 197c416..0522417 100644 --- a/addons/shopro_huifu/command/ProbeSplitConfigDeps.php +++ b/addons/shopro_huifu/command/ProbeSplitConfigDeps.php @@ -67,8 +67,21 @@ class ProbeSplitConfigDeps extends Command $output->writeln(''); $extMat = trim((string) $cfg->get('split_config_extended_material_json', '')); - $output->writeln('--- extended_material_list (按编码表可选/必填) ---'); - $output->writeln('split_config_extended_material_json: ' . ($extMat !== '' ? '已配置' : '(空,若下一报错要求补充材料再填)')); + $output->writeln('--- extended_material_list (H5921L 等常需 F469/F330) ---'); + if ($extMat === '') { + $output->writeln('split_config_extended_material_json: (空)'); + $output->writeln('[FAIL] 酒类分账常见缺 F469 协议 + F330 食品证 → upload-split-ext-materials'); + } else { + $output->writeln('split_config_extended_material_json: 已配置'); + $decoded = json_decode($extMat, true); + if (is_array($decoded)) { + $types = array_column($decoded, 'file_type'); + foreach (['F469', 'F330'] as $need) { + $ok = in_array($need, $types, true); + $output->writeln(' ' . ($ok ? '[OK]' : '[FAIL]') . ' ' . $need); + } + } + } $client = new HuifuClient(); $basic = $client->queryMerchantBasicdata($main, true); diff --git a/addons/shopro_huifu/command/UploadSplitExtendedMaterials.php b/addons/shopro_huifu/command/UploadSplitExtendedMaterials.php new file mode 100644 index 0000000..f8ee99d --- /dev/null +++ b/addons/shopro_huifu/command/UploadSplitExtendedMaterials.php @@ -0,0 +1,119 @@ +setName('shopro_huifu:upload-split-ext-materials') + ->addOption('f469-url', null, Option::VALUE_OPTIONAL, 'F469 分账配置-服务佣金相关协议(pdf/jpg/png URL)') + ->addOption('f330-url', null, Option::VALUE_OPTIONAL, 'F330 食品经营许可证(pdf/jpg/png URL)') + ->addOption('file-type', null, Option::VALUE_OPTIONAL, '自定义:格式 F469=https://... 可多次传') + ->addOption('online-busi-type', null, Option::VALUE_OPTIONAL, '一并写入 split_config_online_busi_type,如 H5921L') + ->addOption('save-config', null, Option::VALUE_NONE, '写入 split_config_extended_material_json') + ->setDescription('Upload F469/F330 etc. for split/config extended_material_list'); + } + + protected function execute(Input $input, Output $output) + { + if (!Bridge::isEnabled()) { + $output->writeln('[FAIL] 汇付插件未启用'); + return 1; + } + + $tasks = []; + $f469 = trim((string) $input->getOption('f469-url')); + $f330 = trim((string) $input->getOption('f330-url')); + if ($f469 !== '') { + $tasks['F469'] = $f469; + } + if ($f330 !== '') { + $tasks['F330'] = $f330; + } + $custom = trim((string) $input->getOption('file-type')); + if ($custom !== '' && strpos($custom, '=') !== false) { + list($ft, $url) = explode('=', $custom, 2); + $ft = trim($ft); + $url = trim($url); + if ($ft !== '' && $url !== '') { + $tasks[$ft] = $url; + } + } + + if ($tasks === []) { + $output->writeln('[FAIL] 至少提供 --f469-url 与 --f330-url(酒类 H5921L 分账常见必填)'); + $output->writeln('示例:'); + $output->writeln(' php think shopro_huifu:upload-split-ext-materials \\'); + $output->writeln(' --f469-url=https://域名/多方分账协议.pdf \\'); + $output->writeln(' --f330-url=https://域名/食品经营许可证.pdf \\'); + $output->writeln(' --online-busi-type=H5921L --save-config'); + return 1; + } + + $picture = new HuifuOnboardPicture(); + $list = []; + foreach ($tasks as $fileType => $url) { + $output->writeln('上传 ' . $fileType . ' ...'); + $fileId = $picture->uploadByUrl($fileType, $url); + $output->writeln(' file_id: ' . $fileId); + $list[] = [ + 'file_id' => $fileId, + 'file_type' => $fileType, + ]; + } + + $json = json_encode($list, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $output->writeln(''); + $output->writeln('split_config_extended_material_json:'); + $output->writeln($json); + + if ($input->getOption('save-config')) { + $this->upsertConfig('split_config_extended_material_json', $json, 'split/config extended_material_list JSON'); + HuifuConfig::reload(); + $output->writeln('[OK] 已写入 split_config_extended_material_json'); + } + + $busiType = trim((string) $input->getOption('online-busi-type')); + if ($busiType !== '') { + if ($input->getOption('save-config')) { + $this->upsertConfig('split_config_online_busi_type', $busiType, '线上业务类型编码'); + HuifuConfig::reload(); + $output->writeln('[OK] 已写入 split_config_online_busi_type=' . $busiType); + } else { + $output->writeln('[提示] 未 --save-config,online_busi_type 未写入库'); + } + } + + $output->writeln(''); + $output->writeln('下一步: php think shopro_huifu:sync-split-receivers'); + return 0; + } + + protected function upsertConfig($name, $value, $description) + { + $row = Db::name('shopro_huifu_config')->where('name', $name)->find(); + if ($row) { + Db::name('shopro_huifu_config')->where('name', $name)->update(['value' => $value]); + } else { + Db::name('shopro_huifu_config')->insert([ + 'name' => $name, + 'value' => $value, + 'group' => 'split', + 'description' => $description, + ]); + } + } +} diff --git a/addons/shopro_huifu/library/onboard/HuifuOnboardPicture.php b/addons/shopro_huifu/library/onboard/HuifuOnboardPicture.php index ed907d5..f06d55a 100644 --- a/addons/shopro_huifu/library/onboard/HuifuOnboardPicture.php +++ b/addons/shopro_huifu/library/onboard/HuifuOnboardPicture.php @@ -29,10 +29,14 @@ class HuifuOnboardPicture $request->setFileType($fileType); list($sourcePath, $sourceTemp) = $this->resolveUploadFile($imageUrl); - list($uploadPath, $uploadTemp) = $this->normalizeImageForHuifu($sourcePath, $sourceTemp); + if ($this->isPdfFile($sourcePath)) { + list($uploadPath, $uploadTemp) = [$sourcePath, $sourceTemp]; + } else { + list($uploadPath, $uploadTemp) = $this->normalizeImageForHuifu($sourcePath, $sourceTemp); + } $filename = $this->buildUploadFilename($fileType, $uploadPath); - $mime = $this->detectMime($uploadPath); + $mime = $this->isPdfFile($uploadPath) ? 'application/pdf' : $this->detectMime($uploadPath); $merchantHuifuId = $this->sdk->getPictureMerchantHuifuId(); $extend = $this->sdk->mergeSkillHuifuId([ @@ -166,9 +170,11 @@ class HuifuOnboardPicture 'F12' => 'bank_card', 'F13' => 'bank_card', ]; - $base = $map[$fileType] ?? 'upload'; + $base = $map[$fileType] ?? strtolower($fileType); $ext = strtolower(pathinfo($localPath, PATHINFO_EXTENSION)); - if (!in_array($ext, ['jpg', 'jpeg', 'png'], true)) { + if ($this->isPdfFile($localPath)) { + $ext = 'pdf'; + } elseif (!in_array($ext, ['jpg', 'jpeg', 'png'], true)) { $ext = 'jpg'; } if ($ext === 'jpeg') { @@ -177,6 +183,15 @@ class HuifuOnboardPicture return substr($base, 0, 20) . '.' . $ext; } + protected function isPdfFile($localPath) + { + if (!is_file($localPath)) { + return false; + } + $head = @file_get_contents($localPath, false, null, 0, 5); + return is_string($head) && strncmp($head, '%PDF-', 5) === 0; + } + protected function detectMime($localPath) { if (function_exists('mime_content_type')) { @@ -293,6 +308,12 @@ class HuifuOnboardPicture { $localPath = $this->resolveLocalPath($imageUrl); if ($localPath && is_file($localPath)) { + $isPdf = $this->isPdfFile($localPath); + $maxBytes = $isPdf ? (5 * 1024 * 1024) : (2 * 1024 * 1024); + $size = filesize($localPath); + if ($size !== false && $size > $maxBytes) { + throw new \RuntimeException($isPdf ? 'PDF 超过 5MB,请压缩后重新上传' : '图片超过 2MB,请压缩后重新上传'); + } return [$localPath, false]; } $absoluteUrl = $this->resolveAbsoluteUrl($imageUrl); @@ -300,10 +321,12 @@ class HuifuOnboardPicture if ($content === '') { throw new \RuntimeException('无法读取图片:' . $absoluteUrl); } - if (strlen($content) > 2 * 1024 * 1024) { - throw new \RuntimeException('图片超过 2MB,请压缩后重新上传'); + $isPdf = strncmp($content, '%PDF-', 5) === 0; + $maxBytes = $isPdf ? (5 * 1024 * 1024) : (2 * 1024 * 1024); + if (strlen($content) > $maxBytes) { + throw new \RuntimeException($isPdf ? 'PDF 超过 5MB,请压缩后重新上传' : '图片超过 2MB,请压缩后重新上传'); } - $ext = $this->guessImageExt($content, $absoluteUrl); + $ext = $isPdf ? 'pdf' : $this->guessImageExt($content, $absoluteUrl); $tmp = tempnam(sys_get_temp_dir(), 'hf_pic_'); if ($tmp === false) { throw new \RuntimeException('创建临时图片文件失败'); @@ -345,7 +368,10 @@ class HuifuOnboardPicture } $path = parse_url($url, PHP_URL_PATH); $ext = strtolower(pathinfo((string) $path, PATHINFO_EXTENSION)); - if (in_array($ext, ['jpg', 'jpeg', 'png'], true)) { + if (strncmp($binary, '%PDF-', 5) === 0) { + return 'pdf'; + } + if (in_array($ext, ['jpg', 'jpeg', 'png', 'pdf'], true)) { return $ext === 'jpeg' ? 'jpg' : $ext; } return 'jpg'; diff --git a/addons/shopro_huifu/library/split/HuifuMerchantSplitService.php b/addons/shopro_huifu/library/split/HuifuMerchantSplitService.php index 81a6835..5d6dc3f 100644 --- a/addons/shopro_huifu/library/split/HuifuMerchantSplitService.php +++ b/addons/shopro_huifu/library/split/HuifuMerchantSplitService.php @@ -448,8 +448,14 @@ class HuifuMerchantSplitService try { $basic = $this->client->queryMerchantBasicdata($mainHuifuId); if (!empty($basic['ok']) && is_array($basic['data'] ?? null)) { - $found = HuifuClient::collectNestedKeys($basic['data'], ['online_busi_type']); + $found = HuifuClient::collectNestedKeys($basic['data'], ['online_busi_type', 'mcc']); $v = trim((string) ($found['online_busi_type'] ?? '')); + if ($v === '') { + $mcc = trim((string) ($found['mcc'] ?? '')); + if ($mcc !== '' && preg_match('/^H[A-Z0-9]{4,}$/i', $mcc)) { + $v = strtoupper($mcc); + } + } if ($v !== '') { return $v; }