first commit
This commit is contained in:
@@ -0,0 +1,303 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Services;
|
||||
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Modules\SystemTool\Enum\SiteConfigType;
|
||||
use Modules\SystemTool\Models\SysConfigGroupModel;
|
||||
use Modules\SystemTool\Models\SysConfigItemsModel;
|
||||
|
||||
/**
|
||||
* 系统设置服务
|
||||
*/
|
||||
class SysConfigService
|
||||
{
|
||||
/**
|
||||
* 缓存过期时间(秒),默认30天
|
||||
*/
|
||||
private const CACHE_TTL = 60 * 60 * 24 * 30;
|
||||
|
||||
/**
|
||||
* 刷新系统设置缓存
|
||||
* @return bool
|
||||
*/
|
||||
public static function refreshConfig(): bool
|
||||
{
|
||||
try {
|
||||
$groups = SysConfigGroupModel::with('settings')->get();
|
||||
$settings = [];
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$settings[$group->key] = [];
|
||||
foreach ($group->settings as $setting) {
|
||||
// 根据类型自动转换值
|
||||
$settings[$group->key][$setting->key] = self::castValue($setting->values, $setting->type);
|
||||
}
|
||||
}
|
||||
|
||||
// 使用带过期时间的缓存,而非永久缓存
|
||||
Cache::put(self::getCacheKey(), $settings, self::CACHE_TTL);
|
||||
|
||||
Log::info('系统配置缓存已刷新', ['settings_count' => count($settings)]);
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('刷新系统配置缓存失败', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取设置
|
||||
* @param string|null $name 设置名称,格式:'group.key' 或 'group',为null时返回所有配置
|
||||
* @param mixed $default 默认值
|
||||
* @return mixed
|
||||
*/
|
||||
public static function getConfig(?string $name = null, mixed $default = null): mixed
|
||||
{
|
||||
$settings = Cache::get(self::getCacheKey());
|
||||
|
||||
// 缓存不存在时重新加载
|
||||
if (empty($settings)) {
|
||||
self::refreshConfig();
|
||||
$settings = Cache::get(self::getCacheKey(), []);
|
||||
}
|
||||
|
||||
// 返回所有配置
|
||||
if (is_null($name)) {
|
||||
return $settings;
|
||||
}
|
||||
|
||||
// 解析配置路径
|
||||
$keys = explode('.', $name);
|
||||
|
||||
// 支持多级获取:group.key 或 group
|
||||
if (count($keys) === 2) {
|
||||
return $settings[$keys[0]][$keys[1]] ?? $default;
|
||||
} elseif (count($keys) === 1) {
|
||||
return $settings[$keys[0]] ?? $default;
|
||||
}
|
||||
|
||||
return $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置配置项(更新配置并刷新缓存)
|
||||
* @param int $id 设置ID
|
||||
* @param mixed $value 设置值
|
||||
* @return bool
|
||||
*/
|
||||
public static function setConfig(int $id, mixed $value): bool
|
||||
{
|
||||
try {
|
||||
|
||||
// 查找设置组和设置项
|
||||
$setting = SysConfigItemsModel::find($id);
|
||||
if (!$setting) {
|
||||
throw new \RuntimeException("设置项不存在: {$id}");
|
||||
}
|
||||
|
||||
// 更新值
|
||||
$setting->values = is_array($value) || is_object($value) ? json_encode($value) : (string)$value;
|
||||
$setting->save();
|
||||
|
||||
return true;
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('设置配置项失败', [
|
||||
'id' => $id,
|
||||
'value' => $value,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存设置项
|
||||
*
|
||||
* 先验证所有 ID 是否存在,再在事务内统一更新。
|
||||
* 任一保存失败则全部回滚。
|
||||
*
|
||||
* @param array $settings [['id' => int, 'values' => mixed], ...]
|
||||
* @return array{success: bool, errors: array}
|
||||
*/
|
||||
public static function batchSetConfig(array $settings): array
|
||||
{
|
||||
$errors = [];
|
||||
$ids = array_column($settings, 'id');
|
||||
$items = SysConfigItemsModel::whereIn('id', $ids)->get()->keyBy('id');
|
||||
|
||||
// 先验证所有 ID 是否存在,收集缺失项信息
|
||||
foreach ($settings as $item) {
|
||||
$id = (int)$item['id'];
|
||||
if (!$items->has($id)) {
|
||||
$errors[] = [
|
||||
'key' => "unknown_{$id}",
|
||||
'title' => "未知设置(ID: {$id})",
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($errors)) {
|
||||
return ['success' => false, 'errors' => $errors];
|
||||
}
|
||||
|
||||
// 事务内执行批量更新
|
||||
try {
|
||||
DB::transaction(function () use ($settings, $items) {
|
||||
foreach ($settings as $item) {
|
||||
$id = (int)$item['id'];
|
||||
$model = $items->get($id);
|
||||
$value = $item['value'];
|
||||
$model->values = is_array($value) || is_object($value) ? json_encode($value) : (string)$value;
|
||||
$model->save();
|
||||
}
|
||||
});
|
||||
return ['success' => true, 'errors' => []];
|
||||
} catch (\Throwable $e) {
|
||||
Log::error('批量保存设置项事务失败', [
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
// 事务已回滚,收集所有项的报告信息
|
||||
foreach ($settings as $item) {
|
||||
$id = (int)$item['id'];
|
||||
$model = $items->get($id);
|
||||
$errors[] = [
|
||||
'key' => $model ? $model->key : "unknown_{$id}",
|
||||
'title' => $model ? $model->title : "未知设置(ID: {$id})",
|
||||
];
|
||||
}
|
||||
return ['success' => false, 'errors' => $errors];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查配置项是否存在
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public static function has(string $name): bool
|
||||
{
|
||||
$keys = explode('.', $name);
|
||||
$settings = Cache::get(self::getCacheKey(), []);
|
||||
|
||||
if (count($keys) === 2) {
|
||||
return isset($settings[$keys[0]][$keys[1]]);
|
||||
} elseif (count($keys) === 1) {
|
||||
return isset($settings[$keys[0]]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除设置缓存
|
||||
* @return bool
|
||||
*/
|
||||
public static function clearCache(): bool
|
||||
{
|
||||
return Cache::forget(self::getCacheKey());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据类型转换值
|
||||
* @param mixed $value 原始值
|
||||
* @param string $type 类型(对应前端表单组件类型)
|
||||
* @return mixed
|
||||
*/
|
||||
private static function castValue(mixed $value, string $type): mixed
|
||||
{
|
||||
// 空值直接返回
|
||||
if (is_null($value) || $value === '') {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// 尝试转换为枚举类型
|
||||
$settingType = SiteConfigType::fromString($type);
|
||||
|
||||
// 如果无法识别类型,返回字符串
|
||||
if (is_null($settingType)) {
|
||||
return (string)$value;
|
||||
}
|
||||
|
||||
// 使用枚举判断进行类型转换
|
||||
if ($settingType->isNumeric()) {
|
||||
return is_numeric($value)
|
||||
? (str_contains((string)$value, '.') ? (float)$value : (int)$value)
|
||||
: $value;
|
||||
}
|
||||
|
||||
if ($settingType->isBoolean()) {
|
||||
return self::toBool($value);
|
||||
}
|
||||
|
||||
if ($settingType->isArray()) {
|
||||
return self::toArrayValue($value);
|
||||
}
|
||||
|
||||
return (string)$value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为布尔值
|
||||
* @param mixed $value
|
||||
* @return bool
|
||||
*/
|
||||
private static function toBool(mixed $value): bool
|
||||
{
|
||||
if (is_bool($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
if (is_numeric($value)) {
|
||||
return (bool)(int)$value;
|
||||
}
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = strtolower(trim($value));
|
||||
return in_array($value, ['true', '1', 'yes', 'on'], true);
|
||||
}
|
||||
|
||||
return filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为数组值(兼容 Checkbox、JSON、Array 类型)
|
||||
* @param mixed $value
|
||||
* @return array|bool
|
||||
*/
|
||||
private static function toArrayValue(mixed $value): array|bool
|
||||
{
|
||||
// 如果是 JSON 字符串,尝试解析为数组
|
||||
if (is_string($value)) {
|
||||
$decoded = json_decode($value, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
|
||||
return $decoded;
|
||||
}
|
||||
// Checkbox 单个值转为布尔
|
||||
return self::toBool($value);
|
||||
}
|
||||
|
||||
// 已经是数组直接返回
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
|
||||
// 其他情况转为布尔(Checkbox 单选)
|
||||
return self::toBool($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取缓存KEY
|
||||
* @return string
|
||||
*/
|
||||
private static function getCacheKey(): string
|
||||
{
|
||||
return config('site_config.cache_key', 'site_config');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
<?php
|
||||
|
||||
namespace Modules\SystemTool\Services;
|
||||
|
||||
use App\Exceptions\HttpResponseException;
|
||||
use Illuminate\Filesystem\FilesystemAdapter;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Modules\SystemTool\Enum\FileType;
|
||||
use Modules\SystemTool\Models\SysFileModel;
|
||||
use Modules\SystemTool\Settings\StorageSettings;
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||
|
||||
/**
|
||||
* 文件服务类
|
||||
*/
|
||||
class SysFileService
|
||||
{
|
||||
|
||||
/**
|
||||
* 获取回收站列表
|
||||
* @param array $params
|
||||
* @return array
|
||||
*/
|
||||
public function getTrashedList(array $params = []): array
|
||||
{
|
||||
$pageSize = $params['pageSize'] ?? 10;
|
||||
return SysFileModel::onlyTrashed()
|
||||
->paginate($pageSize)
|
||||
->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取存储磁盘实例
|
||||
*/
|
||||
protected function disk($disk = null): FilesystemAdapter
|
||||
{
|
||||
if(!$disk) {
|
||||
$disk = config('filesystems.default');
|
||||
}
|
||||
if ($disk === 's3' && !self::isS3Configured()) {
|
||||
throw new HttpResponseException(['success' => false, 'msg' => __('system.storage.s3_not_configured')]);
|
||||
}
|
||||
/** @var FilesystemAdapter */
|
||||
return Storage::disk($disk);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成存储路径
|
||||
*/
|
||||
protected function generateStoragePath(string $extension): string
|
||||
{
|
||||
return date('Ymd') . '/' . uniqid() . '.' . $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
* @param UploadedFile $file 文件
|
||||
* @param int $groupId 分组 ID
|
||||
* @param int $channel 上传来源 0:匿名用户,10:后台用户,20:APP用户
|
||||
* @param int|null $user_id 上传用户 ID
|
||||
* @return array
|
||||
*/
|
||||
public function upload(UploadedFile $file, int $groupId = 0, int $channel = 0, ?int $user_id = null): array
|
||||
{
|
||||
// 文件扩展名
|
||||
$fileExt = strtolower($file->getClientOriginalExtension() ?: $file->extension());
|
||||
|
||||
if (empty($fileExt)) {
|
||||
throw new HttpResponseException(['success' => false, 'msg' => '无法识别的文件扩展名']);
|
||||
}
|
||||
// 推断文件类型
|
||||
$fileType = FileType::guessFromExtension($fileExt);
|
||||
// 获取储存路径
|
||||
$storagePath = $this->generateStoragePath($fileExt);
|
||||
// 获取磁盘
|
||||
$disk = StorageSettings::get('filesystems.default', 'local');
|
||||
// 存储文件并设置可见性
|
||||
$stored = $this->disk($disk)->put($storagePath, $file->getContent(), 'public');
|
||||
if (!$stored) {
|
||||
throw new HttpResponseException(['success' => false, 'msg' => __('system.file.upload_failed')]);
|
||||
}
|
||||
// 保存到数据库
|
||||
$model = new SysFileModel();
|
||||
$model->disk = $disk;
|
||||
$model->group_id = $groupId;
|
||||
$model->channel = $channel;
|
||||
$model->file_type = $fileType->value;
|
||||
$model->file_path = $storagePath;
|
||||
$model->file_name = $file->getClientOriginalName();
|
||||
$model->file_size = $file->getSize();
|
||||
$model->file_ext = $fileExt;
|
||||
$model->uploader_id = $user_id;
|
||||
$model->save();
|
||||
return $model->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 软删除文件(移入回收站)
|
||||
*/
|
||||
public function delete(int $id): bool
|
||||
{
|
||||
$file = SysFileModel::find($id);
|
||||
if (!$file) {
|
||||
throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
|
||||
}
|
||||
|
||||
return (bool) $file->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量软删除文件
|
||||
*/
|
||||
public function batchDelete(array $fileIds): int
|
||||
{
|
||||
return SysFileModel::whereIn('id', $fileIds)->delete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复已删除的文件
|
||||
*/
|
||||
public function restore(int $fileId): bool
|
||||
{
|
||||
$file = SysFileModel::withTrashed()->find($fileId);
|
||||
if (!$file) {
|
||||
throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
|
||||
}
|
||||
|
||||
return $file->restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量恢复文件
|
||||
*/
|
||||
public function batchRestore(array $fileIds): int
|
||||
{
|
||||
return SysFileModel::withTrashed()->whereIn('id', $fileIds)->restore();
|
||||
}
|
||||
|
||||
/**
|
||||
* 彻底删除文件(含物理删除)
|
||||
*/
|
||||
public function forceDelete(int $fileId): bool
|
||||
{
|
||||
$file = SysFileModel::withTrashed()->find($fileId);
|
||||
if (!$file) {
|
||||
throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
|
||||
}
|
||||
|
||||
// 删除物理文件
|
||||
$this->disk($file->disk)->delete($file->file_path);
|
||||
|
||||
return (bool) $file->forceDelete();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量彻底删除文件
|
||||
*/
|
||||
public function batchForceDelete(array $fileIds): int
|
||||
{
|
||||
$files = SysFileModel::withTrashed()->whereIn('id', $fileIds)->get();
|
||||
$count = 0;
|
||||
|
||||
foreach ($files as $file) {
|
||||
$this->disk($file->disk)->delete($file->file_path);
|
||||
$file->forceDelete();
|
||||
$count++;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*/
|
||||
public function download(int $fileId, ?string $filename = null): StreamedResponse
|
||||
{
|
||||
$file = SysFileModel::find($fileId);
|
||||
if (!$file) {
|
||||
throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
|
||||
}
|
||||
|
||||
$downloadName = $filename ?? $file->file_name;
|
||||
return $this->disk($file->disk)->download($file->file_path, $downloadName);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件流式响应(用于在线预览等场景)
|
||||
*/
|
||||
public function stream(int $fileId): StreamedResponse
|
||||
{
|
||||
$file = SysFileModel::find($fileId);
|
||||
if (!$file) {
|
||||
throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
|
||||
}
|
||||
|
||||
$mimeType = $this->getMimeType($file->file_ext);
|
||||
|
||||
return response()->stream(
|
||||
function () use ($file) {
|
||||
$stream = $this->disk($file->disk)->readStream($file->file_path);
|
||||
fpassthru($stream);
|
||||
if (is_resource($stream)) {
|
||||
fclose($stream);
|
||||
}
|
||||
},
|
||||
200,
|
||||
[
|
||||
'Content-Type' => $mimeType,
|
||||
'Content-Disposition' => 'inline; filename="' . $file->file_name . '"',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件内容
|
||||
*/
|
||||
public function getContent(int $fileId): string
|
||||
{
|
||||
$file = SysFileModel::find($fileId);
|
||||
if (!$file) {
|
||||
throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
|
||||
}
|
||||
|
||||
return $this->disk($file->disk)->get($file->file_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件访问URL
|
||||
*/
|
||||
public function getUrl(int $fileId): ?string
|
||||
{
|
||||
$file = SysFileModel::find($fileId);
|
||||
if (!$file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->disk($file->disk)->url($file->file_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据路径获取访问URL
|
||||
*/
|
||||
public function getUrlByPath(string $path, ?string $disk = null): string
|
||||
{
|
||||
return $this->disk($disk)->url($path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件元数据信息
|
||||
*/
|
||||
public function getMetadata(int $fileId): ?array
|
||||
{
|
||||
$file = SysFileModel::find($fileId);
|
||||
if (!$file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$disk = $this->disk($file->disk);
|
||||
|
||||
return [
|
||||
'id' => $file->id,
|
||||
'name' => $file->file_name,
|
||||
'path' => $file->file_path,
|
||||
'disk' => $file->disk,
|
||||
'size' => $file->file_size,
|
||||
'extension' => $file->file_ext,
|
||||
'mime_type' => $this->getMimeType($file->file_ext),
|
||||
'last_modified' => $disk->exists($file->file_path)
|
||||
? date('Y-m-d H:i:s', $disk->lastModified($file->file_path))
|
||||
: null,
|
||||
'url' => $this->getUrl($file->id),
|
||||
'group_id' => $file->group_id,
|
||||
'uploader_id' => $file->uploader_id,
|
||||
'created_at' => $file->created_at?->format('Y-m-d H:i:s'),
|
||||
'updated_at' => $file->updated_at?->format('Y-m-d H:i:s'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制文件
|
||||
*/
|
||||
public function copy(int $fileId, int $targetGroupId = 0): array
|
||||
{
|
||||
$file = SysFileModel::find($fileId);
|
||||
if (!$file) {
|
||||
throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
|
||||
}
|
||||
|
||||
$newPath = $this->generateStoragePath($file->file_ext);
|
||||
|
||||
$this->disk($file->disk)->copy($file->file_path, $newPath);
|
||||
|
||||
// 创建新的文件记录
|
||||
$newFile = SysFileModel::create([
|
||||
'disk' => $file->disk,
|
||||
'group_id' => $targetGroupId,
|
||||
'channel' => $file->channel,
|
||||
'file_name' => $file->file_name,
|
||||
'file_type' => $file->file_type,
|
||||
'file_path' => $newPath,
|
||||
'file_size' => $file->file_size,
|
||||
'file_ext' => $file->file_ext,
|
||||
'uploader_id' => Auth::id(),
|
||||
]);
|
||||
|
||||
return $newFile->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量复制文件
|
||||
*/
|
||||
public function batchCopy(array $fileIds, int $targetGroupId = 0): bool
|
||||
{
|
||||
$files = SysFileModel::whereIn('id', $fileIds)->get();
|
||||
$fileArray = [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
|
||||
$newPath = $this->generateStoragePath($file->file_ext);
|
||||
|
||||
$this->disk($file->disk)->copy($file->file_path, $newPath);
|
||||
|
||||
$fileArray[] =[
|
||||
'disk' => $file->disk,
|
||||
'group_id' => $targetGroupId,
|
||||
'channel' => $file->channel,
|
||||
'file_name' => $file->file_name,
|
||||
'file_type' => $file->file_type,
|
||||
'file_path' => $newPath,
|
||||
'file_size' => $file->file_size,
|
||||
'file_ext' => $file->file_ext,
|
||||
'uploader_id' => Auth::id(),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
];
|
||||
}
|
||||
return SysFileModel::insert($fileArray);
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动文件
|
||||
*/
|
||||
public function move(int $fileId, int $groupId): bool
|
||||
{
|
||||
$file = SysFileModel::find($fileId);
|
||||
return $file->update(['group_id' => $groupId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量移动
|
||||
*/
|
||||
public function batchMove(array $fileIds, int $groupId): int
|
||||
{
|
||||
return SysFileModel::whereIn('id', $fileIds)->update(['group_id' => $groupId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重命名文件
|
||||
*/
|
||||
public function rename(int $fileId, string $newName): bool
|
||||
{
|
||||
$file = SysFileModel::find($fileId);
|
||||
if (!$file) {
|
||||
throw new HttpResponseException(['success' => false, 'msg' => __('system.file.not_found')]);
|
||||
}
|
||||
|
||||
return $file->update(['file_name' => $newName]);
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否存在
|
||||
*/
|
||||
public function exists(int $fileId): bool
|
||||
{
|
||||
$file = SysFileModel::find($fileId);
|
||||
if (!$file) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->disk($file->disk)->exists($file->file_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件大小(字节)
|
||||
*/
|
||||
public function getSize(int $fileId): ?int
|
||||
{
|
||||
$file = SysFileModel::find($fileId);
|
||||
if (!$file) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $this->disk($file->disk)->size($file->file_path);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取MIME类型
|
||||
*/
|
||||
protected function getMimeType(string $extension): string
|
||||
{
|
||||
$mimeTypes = [
|
||||
// 图片
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'png' => 'image/png',
|
||||
'gif' => 'image/gif',
|
||||
'webp' => 'image/webp',
|
||||
'avif' => 'image/avif',
|
||||
'bmp' => 'image/bmp',
|
||||
'svg' => 'image/svg+xml',
|
||||
// 音频
|
||||
'mp3' => 'audio/mpeg',
|
||||
'wav' => 'audio/wav',
|
||||
'ogg' => 'audio/ogg',
|
||||
'flac' => 'audio/flac',
|
||||
'aac' => 'audio/aac',
|
||||
// 视频
|
||||
'mp4' => 'video/mp4',
|
||||
'webm' => 'video/webm',
|
||||
'mkv' => 'video/x-matroska',
|
||||
'mov' => 'video/quicktime',
|
||||
'avi' => 'video/x-msvideo',
|
||||
// 文档
|
||||
'pdf' => 'application/pdf',
|
||||
'doc' => 'application/msword',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'xls' => 'application/vnd.ms-excel',
|
||||
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
// 压缩包
|
||||
'zip' => 'application/zip',
|
||||
'rar' => 'application/vnd.rar',
|
||||
'7z' => 'application/x-7z-compressed',
|
||||
// 其他
|
||||
'json' => 'application/json',
|
||||
'xml' => 'application/xml',
|
||||
'txt' => 'text/plain',
|
||||
];
|
||||
|
||||
return $mimeTypes[strtolower($extension)] ?? 'application/octet-stream';
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空回收站文件
|
||||
*/
|
||||
public function cleanTrashed(): int
|
||||
{
|
||||
$expiredFiles = SysFileModel::onlyTrashed()->get();
|
||||
$count = 0;
|
||||
foreach ($expiredFiles as $file) {
|
||||
$this->disk($file->disk)->delete($file->file_path);
|
||||
$file->forceDelete();
|
||||
$count++;
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 检查 S3 配置是否有效
|
||||
* @return bool
|
||||
*/
|
||||
public static function isS3Configured(): bool
|
||||
{
|
||||
$storageConfig = config('filesystems.disks.s3');
|
||||
|
||||
if (empty($storageConfig)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !empty($storageConfig['key'])
|
||||
&& !empty($storageConfig['secret'])
|
||||
&& !empty($storageConfig['bucket'])
|
||||
&& !empty($storageConfig['region']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user