CleanupPlanContent.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497
  1. <?php
  2. namespace App\Module\Cleanup\Models;
  3. use App\Module\Cleanup\Enums\CLEANUP_TYPE;
  4. use UCore\ModelCore;
  5. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  6. /**
  7. * 计划内容模型
  8. *
  9. * 存储计划的具体内容,即计划具体处理哪些表,怎么清理
  10. */
  11. class CleanupPlanContent extends ModelCore
  12. {
  13. /**
  14. * 数据表名
  15. */
  16. protected $table = 'cleanup_plan_contents';
  17. // attrlist start
  18. /**
  19. * 可批量赋值的属性
  20. */
  21. protected $fillable = [
  22. 'plan_id',
  23. 'table_name',
  24. 'model_class',
  25. 'cleanup_type',
  26. 'conditions',
  27. 'priority',
  28. 'batch_size',
  29. 'is_enabled',
  30. 'backup_enabled',
  31. 'notes',
  32. ];
  33. // attrlist end
  34. // field start
  35. /**
  36. * @property int $id 主键ID
  37. * @property int $plan_id 计划ID
  38. * @property string $table_name 表名(兼容性保留)
  39. * @property string $model_class Model类名
  40. * @property int $cleanup_type 清理类型:1清空表,2删除所有,3按时间删除,4按用户删除,5按条件删除
  41. * @property array $conditions 清理条件JSON配置
  42. * @property int $priority 清理优先级
  43. * @property int $batch_size 批处理大小
  44. * @property bool $is_enabled 是否启用
  45. * @property bool $backup_enabled 是否启用备份
  46. * @property string $notes 备注说明
  47. * @property \Carbon\Carbon $created_at 创建时间
  48. * @property \Carbon\Carbon $updated_at 更新时间
  49. */
  50. // field end
  51. /**
  52. * 字段类型转换
  53. */
  54. protected $casts = [
  55. 'plan_id' => 'integer',
  56. 'cleanup_type' => 'integer',
  57. 'conditions' => 'array',
  58. 'priority' => 'integer',
  59. 'batch_size' => 'integer',
  60. 'is_enabled' => 'boolean',
  61. 'backup_enabled' => 'boolean',
  62. 'created_at' => 'datetime',
  63. 'updated_at' => 'datetime',
  64. ];
  65. /**
  66. * 获取清理类型枚举
  67. */
  68. public function getCleanupTypeEnumAttribute(): CLEANUP_TYPE
  69. {
  70. return CLEANUP_TYPE::from($this->cleanup_type);
  71. }
  72. /**
  73. * 获取清理类型描述
  74. */
  75. public function getCleanupTypeNameAttribute(): string
  76. {
  77. return $this->getCleanupTypeEnumAttribute()->getDescription();
  78. }
  79. /**
  80. * 获取启用状态文本
  81. */
  82. public function getEnabledTextAttribute(): string
  83. {
  84. return $this->is_enabled ? '启用' : '禁用';
  85. }
  86. /**
  87. * 获取启用状态颜色
  88. */
  89. public function getEnabledColorAttribute(): string
  90. {
  91. return $this->is_enabled ? 'success' : 'secondary';
  92. }
  93. /**
  94. * 获取备份状态文本
  95. */
  96. public function getBackupTextAttribute(): string
  97. {
  98. return $this->backup_enabled ? '启用' : '禁用';
  99. }
  100. /**
  101. * 获取备份状态颜色
  102. */
  103. public function getBackupColorAttribute(): string
  104. {
  105. return $this->backup_enabled ? 'success' : 'warning';
  106. }
  107. /**
  108. * 获取优先级文本
  109. */
  110. public function getPriorityTextAttribute(): string
  111. {
  112. if ($this->priority <= 50) {
  113. return '高';
  114. } elseif ($this->priority <= 200) {
  115. return '中';
  116. } else {
  117. return '低';
  118. }
  119. }
  120. /**
  121. * 获取优先级颜色
  122. */
  123. public function getPriorityColorAttribute(): string
  124. {
  125. if ($this->priority <= 50) {
  126. return 'danger';
  127. } elseif ($this->priority <= 200) {
  128. return 'warning';
  129. } else {
  130. return 'info';
  131. }
  132. }
  133. /**
  134. * 判断是否需要条件配置
  135. */
  136. public function getNeedsConditionsAttribute(): bool
  137. {
  138. return $this->getCleanupTypeEnumAttribute()->needsConditions();
  139. }
  140. /**
  141. * 判断是否支持回滚
  142. */
  143. public function getIsRollbackableAttribute(): bool
  144. {
  145. return $this->getCleanupTypeEnumAttribute()->isRollbackable();
  146. }
  147. /**
  148. * 获取时间字段
  149. */
  150. public function getTimeFieldAttribute(): ?string
  151. {
  152. return $this->conditions['time_field'] ?? null;
  153. }
  154. /**
  155. * 获取时间条件
  156. */
  157. public function getTimeConditionAttribute(): ?string
  158. {
  159. return $this->conditions['time_condition'] ?? null;
  160. }
  161. /**
  162. * 获取时间值
  163. */
  164. public function getTimeValueAttribute(): ?int
  165. {
  166. return $this->conditions['time_value'] ?? null;
  167. }
  168. /**
  169. * 获取时间单位
  170. */
  171. public function getTimeUnitAttribute(): ?string
  172. {
  173. return $this->conditions['time_unit'] ?? null;
  174. }
  175. /**
  176. * 获取用户字段
  177. */
  178. public function getUserFieldAttribute(): ?string
  179. {
  180. return $this->conditions['user_field'] ?? null;
  181. }
  182. /**
  183. * 获取用户条件
  184. */
  185. public function getUserConditionAttribute(): ?string
  186. {
  187. return $this->conditions['user_condition'] ?? null;
  188. }
  189. /**
  190. * 获取用户值列表
  191. */
  192. public function getUserValuesAttribute(): array
  193. {
  194. return $this->conditions['user_values'] ?? [];
  195. }
  196. /**
  197. * 获取自定义条件
  198. */
  199. public function getCustomConditionsAttribute(): array
  200. {
  201. return $this->conditions['conditions'] ?? [];
  202. }
  203. /**
  204. * 获取条件逻辑
  205. */
  206. public function getConditionLogicAttribute(): string
  207. {
  208. return $this->conditions['logic'] ?? 'AND';
  209. }
  210. /**
  211. * 获取条件描述
  212. */
  213. public function getConditionsDescriptionAttribute(): string
  214. {
  215. if (!$this->needs_conditions || empty($this->conditions)) {
  216. return '无条件';
  217. }
  218. $descriptions = [];
  219. switch ($this->cleanup_type) {
  220. case CLEANUP_TYPE::DELETE_BY_TIME->value:
  221. if ($this->time_field && $this->time_value && $this->time_unit) {
  222. $descriptions[] = "删除 {$this->time_field} 字段 {$this->time_value} {$this->time_unit} 前的记录";
  223. }
  224. break;
  225. case CLEANUP_TYPE::DELETE_BY_USER->value:
  226. if ($this->user_field && !empty($this->user_values)) {
  227. $userList = implode(', ', array_slice($this->user_values, 0, 3));
  228. if (count($this->user_values) > 3) {
  229. $userList .= ' 等' . count($this->user_values) . '个用户';
  230. }
  231. $descriptions[] = "删除 {$this->user_field} 为 {$userList} 的记录";
  232. }
  233. break;
  234. case CLEANUP_TYPE::DELETE_BY_CONDITION->value:
  235. if (!empty($this->custom_conditions)) {
  236. $descriptions[] = "自定义条件:" . count($this->custom_conditions) . " 个条件";
  237. }
  238. break;
  239. }
  240. return empty($descriptions) ? '条件配置不完整' : implode('; ', $descriptions);
  241. }
  242. /**
  243. * 关联清理计划
  244. */
  245. public function plan(): BelongsTo
  246. {
  247. return $this->belongsTo(CleanupPlan::class, 'plan_id');
  248. }
  249. /**
  250. * 关联清理配置
  251. */
  252. public function config(): BelongsTo
  253. {
  254. return $this->belongsTo(CleanupConfig::class, 'table_name', 'table_name');
  255. }
  256. /**
  257. * 作用域:按计划筛选
  258. */
  259. public function scopeByPlan($query, int $planId)
  260. {
  261. return $query->where('plan_id', $planId);
  262. }
  263. /**
  264. * 作用域:按表名筛选
  265. */
  266. public function scopeByTable($query, string $tableName)
  267. {
  268. return $query->where('table_name', $tableName);
  269. }
  270. /**
  271. * 作用域:只查询启用的内容
  272. */
  273. public function scopeEnabled($query)
  274. {
  275. return $query->where('is_enabled', true);
  276. }
  277. /**
  278. * 作用域:按优先级排序
  279. */
  280. public function scopeOrderByPriority($query)
  281. {
  282. return $query->orderBy('priority')->orderBy('table_name');
  283. }
  284. /**
  285. * 作用域:按清理类型筛选
  286. */
  287. public function scopeByCleanupType($query, int $type)
  288. {
  289. return $query->where('cleanup_type', $type);
  290. }
  291. /**
  292. * 作用域:启用备份的内容
  293. */
  294. public function scopeBackupEnabled($query)
  295. {
  296. return $query->where('backup_enabled', true);
  297. }
  298. /**
  299. * 作用域:按表名搜索
  300. */
  301. public function scopeSearchTable($query, string $search)
  302. {
  303. return $query->where('table_name', 'like', "%{$search}%");
  304. }
  305. /**
  306. * 获取清理类型统计
  307. */
  308. public static function getCleanupTypeStats(int $planId = null): array
  309. {
  310. $query = static::selectRaw('cleanup_type, COUNT(*) as count')
  311. ->groupBy('cleanup_type');
  312. if ($planId) {
  313. $query->where('plan_id', $planId);
  314. }
  315. $stats = $query->get()->keyBy('cleanup_type')->toArray();
  316. $result = [];
  317. foreach (CLEANUP_TYPE::cases() as $type) {
  318. $result[$type->value] = [
  319. 'name' => $type->getDescription(),
  320. 'count' => $stats[$type->value]['count'] ?? 0,
  321. ];
  322. }
  323. return $result;
  324. }
  325. /**
  326. * 获取启用状态统计
  327. */
  328. public static function getEnabledStats(int $planId = null): array
  329. {
  330. $query = static::query();
  331. if ($planId) {
  332. $query->where('plan_id', $planId);
  333. }
  334. return [
  335. 'enabled' => $query->clone()->where('is_enabled', true)->count(),
  336. 'disabled' => $query->clone()->where('is_enabled', false)->count(),
  337. 'backup_enabled' => $query->clone()->where('backup_enabled', true)->count(),
  338. 'total' => $query->count(),
  339. ];
  340. }
  341. /**
  342. * 获取Model实例
  343. */
  344. public function getModelInstance()
  345. {
  346. if (empty($this->model_class)) {
  347. throw new \Exception("Model类名为空");
  348. }
  349. if (!class_exists($this->model_class)) {
  350. throw new \Exception("Model类不存在: {$this->model_class}");
  351. }
  352. return new $this->model_class();
  353. }
  354. /**
  355. * 获取实际表名(优先从Model获取)
  356. */
  357. public function getActualTableName(): string
  358. {
  359. if (!empty($this->model_class)) {
  360. try {
  361. return $this->getModelInstance()->getTable();
  362. } catch (\Exception $e) {
  363. // 如果Model有问题,回退到table_name
  364. }
  365. }
  366. return $this->table_name;
  367. }
  368. /**
  369. * 获取目标名称(Model类名或表名)
  370. */
  371. public function getTargetName(): string
  372. {
  373. return $this->model_class ?: $this->table_name;
  374. }
  375. /**
  376. * 检查是否基于Model类
  377. */
  378. public function isModelBased(): bool
  379. {
  380. return !empty($this->model_class);
  381. }
  382. /**
  383. * 检查Model是否支持软删除
  384. */
  385. public function supportsSoftDeletes(): bool
  386. {
  387. if (!$this->isModelBased()) {
  388. return false;
  389. }
  390. try {
  391. $model = $this->getModelInstance();
  392. return in_array(\Illuminate\Database\Eloquent\SoftDeletes::class, class_uses_recursive($model));
  393. } catch (\Exception $e) {
  394. return false;
  395. }
  396. }
  397. /**
  398. * 获取记录数量(优先使用Model)
  399. */
  400. public function getRecordCount(): int
  401. {
  402. if ($this->isModelBased()) {
  403. try {
  404. $modelClass = $this->model_class;
  405. return $modelClass::count();
  406. } catch (\Exception $e) {
  407. // 如果Model有问题,回退到直接查询表
  408. }
  409. }
  410. return \DB::table($this->table_name)->count();
  411. }
  412. /**
  413. * 作用域:只查询有Model类的内容
  414. */
  415. public function scopeWithModel($query)
  416. {
  417. return $query->whereNotNull('model_class')->where('model_class', '!=', '');
  418. }
  419. /**
  420. * 作用域:只查询没有Model类的内容(旧数据)
  421. */
  422. public function scopeWithoutModel($query)
  423. {
  424. return $query->where(function($q) {
  425. $q->whereNull('model_class')->orWhere('model_class', '');
  426. });
  427. }
  428. /**
  429. * 作用域:按Model类筛选
  430. */
  431. public function scopeByModel($query, string $modelClass)
  432. {
  433. return $query->where('model_class', $modelClass);
  434. }
  435. }