Files
anxinyan/server-api/app/support/MaterialLocalResourceService.php
2026-05-25 16:45:48 +08:00

163 lines
4.8 KiB
PHP

<?php
namespace app\support;
use support\think\Db;
class MaterialLocalResourceService
{
public const RETENTION_MESSAGE = '系统仅保留3个批次图片';
public function qrRelativePath(string $batchNo, string $verifyCode): string
{
return sprintf(
'uploads/material-qrcodes/%s/%s.png',
$this->safeName($batchNo, 'batch'),
$this->safeName($verifyCode, 'code')
);
}
public function qrBatchDirectory(string $batchNo): string
{
return 'uploads/material-qrcodes/' . $this->safeName($batchNo, 'batch');
}
public function packageRelativePath(string $batchNo): string
{
$safeBatchNo = $this->safeName($batchNo, 'batch');
return sprintf(
'uploads/material-packages/%s/material-batch-%s.zip',
$safeBatchNo,
$safeBatchNo
);
}
public function packageBatchDirectory(string $batchNo): string
{
return 'uploads/material-packages/' . $this->safeName($batchNo, 'batch');
}
public function materialTagTemplatePath(): string
{
return dirname(__DIR__, 2) . '/resources/material-tag-template.jpg';
}
public function publicPath(string $relativePath): string
{
return public_path() . '/' . ltrim($relativePath, '/');
}
public function publicUrl(string $relativePath): string
{
$baseUrl = $this->localBaseUrl();
if ($baseUrl === '') {
throw new \RuntimeException('本地文件公开访问域名未配置,无法生成二维码下载链接');
}
return rtrim($baseUrl, '/') . '/' . ltrim($relativePath, '/');
}
public function ensureParentDirectory(string $filePath): void
{
$dir = dirname($filePath);
if (!is_dir($dir) && !mkdir($dir, 0775, true) && !is_dir($dir)) {
throw new \RuntimeException('本地文件目录创建失败');
}
}
public function deleteBatchResources(string $batchNo): void
{
$this->deleteDirectory($this->publicPath($this->qrBatchDirectory($batchNo)));
$this->deleteDirectory($this->publicPath($this->packageBatchDirectory($batchNo)));
}
public function deleteDirectory(string $dir): void
{
if (!is_dir($dir)) {
return;
}
$items = scandir($dir);
if ($items === false) {
return;
}
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $dir . DIRECTORY_SEPARATOR . $item;
if (is_dir($path)) {
$this->deleteDirectory($path);
continue;
}
if (is_file($path)) {
@unlink($path);
}
}
@rmdir($dir);
}
public function safeName(string $value, string $fallback): string
{
$safe = preg_replace('/[^a-zA-Z0-9_-]/', '-', trim($value));
$safe = trim((string)$safe, '-_');
return $safe !== '' ? $safe : $fallback;
}
private function localBaseUrl(): string
{
foreach (['MATERIAL_LOCAL_BASE_URL', 'APP_PUBLIC_BASE_URL', 'PUBLIC_FILE_BASE_URL'] as $key) {
$value = trim((string)($_ENV[$key] ?? ''));
if ($value !== '') {
return $this->normalizeBaseUrl($value);
}
}
$notifyUrl = Db::name('system_configs')
->where('config_group', 'payment')
->where('config_key', 'notify_url')
->value('config_value');
if (is_string($notifyUrl) && trim($notifyUrl) !== '') {
return $this->extractOrigin($notifyUrl);
}
if (!in_array(strtolower((string)($_ENV['APP_ENV'] ?? '')), ['production', 'prod'], true)) {
return 'http://' . '127.0.0.' . '1:8787';
}
return '';
}
private function normalizeBaseUrl(string $baseUrl): string
{
$baseUrl = trim($baseUrl);
if ($baseUrl === '') {
return '';
}
if (!preg_match('/^https?:\/\//i', $baseUrl)) {
$baseUrl = 'https://' . ltrim($baseUrl, '/');
}
return rtrim($baseUrl, '/');
}
private function extractOrigin(string $url): string
{
$parts = parse_url(trim($url));
$scheme = (string)($parts['scheme'] ?? '');
$host = (string)($parts['host'] ?? '');
$port = (string)($parts['port'] ?? '');
if ($host === '') {
return $this->normalizeBaseUrl($url);
}
$origin = ($scheme !== '' ? $scheme : 'https') . '://' . $host;
if ($port !== '' && !(($scheme === 'http' && $port === '80') || ($scheme === 'https' && $port === '443'))) {
$origin .= ':' . $port;
}
return rtrim($origin, '/');
}
}