101 行
3.0 KiB
PHP
101 行
3.0 KiB
PHP
<?php
|
||
/**
|
||
* 清理图片空间中「数据库有记录但物理文件不存在」的附件
|
||
*
|
||
* 用法(在项目根目录执行):
|
||
* php addons/shopro/cleanup_missing_attachments.php --dry-run # 仅预览
|
||
* php addons/shopro/cleanup_missing_attachments.php # 硬删除无效记录
|
||
* php addons/shopro/cleanup_missing_attachments.php --soft # 移入回收站(is_del=1)
|
||
*/
|
||
|
||
if (PHP_SAPI !== 'cli') {
|
||
http_response_code(403);
|
||
exit("请通过命令行运行:php addons/shopro/cleanup_missing_attachments.php [--dry-run] [--soft]\n");
|
||
}
|
||
|
||
define('APP_PATH', __DIR__ . '/../../application/');
|
||
define('ROOT_PATH', __DIR__ . '/../../');
|
||
|
||
if (!is_file(APP_PATH . 'admin/command/Install/install.lock')) {
|
||
fwrite(STDERR, "项目未安装,无法执行。\n");
|
||
exit(1);
|
||
}
|
||
|
||
require ROOT_PATH . 'thinkphp/base.php';
|
||
|
||
\think\App::initCommon();
|
||
|
||
use think\Db;
|
||
|
||
$argv = $GLOBALS['argv'] ?? [];
|
||
$dryRun = in_array('--dry-run', $argv, true);
|
||
$softDelete = in_array('--soft', $argv, true);
|
||
|
||
$publicPath = rtrim(ROOT_PATH, '/\\') . DIRECTORY_SEPARATOR . 'public';
|
||
$batchSize = 500;
|
||
$lastId = 0;
|
||
$checked = 0;
|
||
$missing = 0;
|
||
$now = time();
|
||
|
||
echo "扫描目录:{$publicPath}\n";
|
||
echo '模式:' . ($dryRun ? '预览(不写入)' : ($softDelete ? '软删除(回收站)' : '硬删除(直接删记录)')) . "\n\n";
|
||
|
||
while (true) {
|
||
$rows = Db::name('attachment')
|
||
->where('id', '>', $lastId)
|
||
->where('storage', 'local')
|
||
->order('id asc')
|
||
->limit($batchSize)
|
||
->field('id,url,filename,is_del,mimetype')
|
||
->select();
|
||
|
||
if (empty($rows)) {
|
||
break;
|
||
}
|
||
|
||
foreach ($rows as $row) {
|
||
$row = is_array($row) ? $row : $row->toArray();
|
||
$lastId = (int)$row['id'];
|
||
$checked++;
|
||
|
||
$url = trim((string)($row['url'] ?? ''));
|
||
if ($url === '') {
|
||
$exists = false;
|
||
} elseif (preg_match('#^https?://#i', $url)) {
|
||
// 外链不做本地存在性检查
|
||
continue;
|
||
} else {
|
||
$relative = '/' . ltrim(str_replace('\\', '/', $url), '/');
|
||
$file = $publicPath . str_replace('/', DIRECTORY_SEPARATOR, $relative);
|
||
$exists = is_file($file);
|
||
}
|
||
|
||
if ($exists) {
|
||
continue;
|
||
}
|
||
|
||
$missing++;
|
||
$name = $row['filename'] ?? '';
|
||
$delFlag = !empty($row['is_del']) ? ' [已在回收站]' : '';
|
||
echo "[缺失] id={$row['id']} url={$url} filename={$name}{$delFlag}\n";
|
||
|
||
if ($dryRun) {
|
||
continue;
|
||
}
|
||
|
||
if ($softDelete) {
|
||
Db::name('attachment')->where('id', $row['id'])->update([
|
||
'is_del' => 1,
|
||
'updatetime' => $now,
|
||
]);
|
||
} else {
|
||
Db::name('attachment')->where('id', $row['id'])->delete();
|
||
}
|
||
}
|
||
}
|
||
|
||
echo "\n完成:共检查 {$checked} 条本地附件,发现缺失 {$missing} 条";
|
||
echo $dryRun ? '(预览,未修改数据库)' : '(已清理)';
|
||
echo "\n";
|