任务时间: 2025年06月17日 12:30
任务类型: 架构重构方案
问题分析: 当前扫描数据库表而非Model类的设计缺陷
// 当前错误的做法:直接扫描数据库
private static function getAllTables(): array
{
$tables = DB::select("SHOW TABLES LIKE 'kku_%'");
// ...
}
问题:
// 当前存储表名
'table_name' => 'kku_farm_users'
// 应该存储Model类名
'model_class' => 'App\Module\Farm\Models\FarmUser'
// 当前直接操作表
DB::table($tableName)->where(...)->delete();
// 应该使用Model
$modelClass::where(...)->delete();
app/Module/*/Models/ 目录下的Model类ALTER TABLE kku_cleanup_configs
ADD COLUMN model_class VARCHAR(255) AFTER table_name,
ADD COLUMN model_info JSON COMMENT 'Model类信息',
ADD INDEX idx_model_class (model_class);
-- 迁移数据后删除 table_name 字段
-- ALTER TABLE kku_cleanup_configs DROP COLUMN table_name;
ALTER TABLE kku_cleanup_plan_contents
ADD COLUMN model_class VARCHAR(255) AFTER table_name,
ADD INDEX idx_model_class (model_class);
-- 迁移数据后删除 table_name 字段
-- ALTER TABLE kku_cleanup_plan_contents DROP COLUMN table_name;
class ModelScannerLogic
{
/**
* 扫描所有Model类
*/
public static function scanAllModels(bool $forceRefresh = false): array
{
$models = static::getAllModelClasses();
$result = [
'total_models' => count($models),
'scanned_models' => 0,
'new_models' => 0,
'updated_models' => 0,
'models' => [],
'scan_time' => 0,
];
foreach ($models as $modelClass) {
$modelInfo = static::scanModel($modelClass, $forceRefresh);
$result['models'][] = $modelInfo;
// ...
}
return $result;
}
/**
* 获取所有Model类
*/
private static function getAllModelClasses(): array
{
$models = [];
$modulePaths = glob(app_path('Module/*/Models'));
foreach ($modulePaths as $modulePath) {
$modelFiles = glob($modulePath . '/*.php');
foreach ($modelFiles as $modelFile) {
$modelClass = static::getModelClassFromFile($modelFile);
if ($modelClass && static::isValidModel($modelClass)) {
$models[] = $modelClass;
}
}
}
return $models;
}
/**
* 从文件路径获取Model类名
*/
private static function getModelClassFromFile(string $filePath): ?string
{
$relativePath = str_replace(app_path(), '', $filePath);
$relativePath = str_replace('.php', '', $relativePath);
$relativePath = str_replace('/', '\\', $relativePath);
return 'App' . $relativePath;
}
/**
* 验证是否为有效的Model类
*/
private static function isValidModel(string $modelClass): bool
{
if (!class_exists($modelClass)) {
return false;
}
$reflection = new \ReflectionClass($modelClass);
// 检查是否继承自ModelCore或Eloquent
return $reflection->isSubclassOf(\UCore\ModelCore::class) ||
$reflection->isSubclassOf(\Illuminate\Database\Eloquent\Model::class);
}
/**
* 扫描单个Model
*/
private static function scanModel(string $modelClass, bool $forceRefresh = false): array
{
$model = new $modelClass();
// 获取Model信息
$modelInfo = [
'class_name' => $modelClass,
'table_name' => $model->getTable(),
'module_name' => static::extractModuleName($modelClass),
'primary_key' => $model->getKeyName(),
'timestamps' => $model->timestamps,
'soft_deletes' => static::hasSoftDeletes($model),
'fillable' => $model->getFillable(),
'guarded' => $model->getGuarded(),
'casts' => $model->getCasts(),
'relations' => static::getModelRelations($model),
];
// 分析数据分类
$dataCategory = static::detectDataCategory($modelClass, $modelInfo);
// 生成默认配置
$defaultConfig = static::generateDefaultConfig($dataCategory, $modelInfo);
// 保存或更新配置
static::saveModelConfig($modelClass, $modelInfo, $dataCategory, $defaultConfig);
return $modelInfo;
}
}
class CleanupExecutorLogic
{
/**
* 执行表清理(使用Model)
*/
private static function executeModelCleanup(CleanupPlanContent $content, int $taskId): array
{
$modelClass = $content->model_class;
if (!class_exists($modelClass)) {
throw new \Exception("Model类不存在: {$modelClass}");
}
$model = new $modelClass();
$tableName = $model->getTable();
// 记录清理前的数量
$beforeCount = $modelClass::count();
// 根据清理类型执行不同的操作
$deletedRecords = match($content->cleanup_type) {
CLEANUP_TYPE::TRUNCATE->value => static::truncateModel($modelClass),
CLEANUP_TYPE::DELETE_ALL->value => static::deleteAllModel($modelClass),
CLEANUP_TYPE::DELETE_BY_TIME->value => static::deleteByTimeModel($modelClass, $content->conditions),
CLEANUP_TYPE::DELETE_BY_USER->value => static::deleteByUserModel($modelClass, $content->conditions),
CLEANUP_TYPE::DELETE_BY_CONDITION->value => static::deleteByConditionModel($modelClass, $content->conditions),
default => throw new \Exception("不支持的清理类型: {$content->cleanup_type}")
};
// 记录清理后的数量
$afterCount = $modelClass::count();
// 记录清理日志
CleanupLogLogic::createLog([
'task_id' => $taskId,
'model_class' => $modelClass,
'table_name' => $tableName,
'cleanup_type' => $content->cleanup_type,
'before_count' => $beforeCount,
'after_count' => $afterCount,
'deleted_records' => $deletedRecords,
'conditions' => $content->conditions,
]);
return [
'success' => true,
'deleted_records' => $deletedRecords,
'before_count' => $beforeCount,
'after_count' => $afterCount,
];
}
/**
* 按时间删除(使用Model)
*/
private static function deleteByTimeModel(string $modelClass, array $conditions): int
{
$timeField = $conditions['time_field'] ?? 'created_at';
$beforeTime = static::parseTimeCondition($conditions['before'] ?? '30_days_ago');
$query = $modelClass::where($timeField, '<', $beforeTime);
// 如果Model支持软删除,可以选择强制删除
if (isset($conditions['force_delete']) && $conditions['force_delete']) {
return $query->forceDelete();
}
return $query->delete();
}
/**
* 按用户删除(使用Model)
*/
private static function deleteByUserModel(string $modelClass, array $conditions): int
{
$userField = $conditions['user_field'] ?? 'user_id';
$userCondition = $conditions['user_condition'] ?? 'in';
$userValues = $conditions['user_values'] ?? [];
$query = $modelClass::query();
switch ($userCondition) {
case 'in':
$query->whereIn($userField, $userValues);
break;
case 'not_in':
$query->whereNotIn($userField, $userValues);
break;
case 'null':
$query->whereNull($userField);
break;
case 'not_null':
$query->whereNotNull($userField);
break;
}
return $query->delete();
}
/**
* 清空表(使用Model)
*/
private static function truncateModel(string $modelClass): int
{
$model = new $modelClass();
$beforeCount = $modelClass::count();
// 使用Model的truncate方法
$modelClass::truncate();
return $beforeCount;
}
}
class CleanupConfig extends ModelCore
{
protected $fillable = [
'model_class',
'module_name',
'data_category',
'default_cleanup_type',
'default_conditions',
'model_info',
// ...
];
/**
* 获取Model实例
*/
public function getModelInstance()
{
if (!class_exists($this->model_class)) {
throw new \Exception("Model类不存在: {$this->model_class}");
}
return new $this->model_class();
}
/**
* 获取表名
*/
public function getTableName(): string
{
return $this->getModelInstance()->getTable();
}
/**
* 获取记录数量
*/
public function getRecordCount(): int
{
$modelClass = $this->model_class;
return $modelClass::count();
}
/**
* 检查Model是否支持软删除
*/
public function supportsSoftDeletes(): bool
{
$model = $this->getModelInstance();
return method_exists($model, 'trashed');
}
}
model_class 和 model_infotable_name 字段用于兼容性ModelScannerLogic 类CleanupExecutorLogic 使用Modeltable_name 字段这个重构方案将彻底解决当前设计的架构问题,使清理系统更加健壮和灵活。