CleanupBackup.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. <?php
  2. namespace App\Module\Cleanup\Models;
  3. use App\Module\Cleanup\Enums\BACKUP_TYPE;
  4. use App\Module\Cleanup\Enums\COMPRESSION_TYPE;
  5. use App\Module\Cleanup\Enums\BACKUP_STATUS;
  6. use UCore\ModelCore;
  7. use Illuminate\Database\Eloquent\Relations\BelongsTo;
  8. use Illuminate\Database\Eloquent\Relations\HasMany;
  9. /**
  10. * 备份记录模型
  11. *
  12. * 存储计划的备份方案和备份内容
  13. * field start
  14. * @property int $id 主键ID
  15. * @property int $plan_id 关联的清理计划ID
  16. * @property int $task_id 关联的清理任务ID(如果是任务触发的备份)
  17. * @property string $backup_name 备份名称
  18. * @property int $backup_type 备份类型:1SQL,2JSON,3CSV
  19. * @property int $compression_type 压缩类型:1none,2gzip,3zip
  20. * @property string $backup_path 备份文件路径
  21. * @property int $backup_size 备份文件大小(字节)
  22. * @property int $original_size 原始数据大小(字节)
  23. * @property int $tables_count 备份表数量
  24. * @property int $records_count 备份记录数量
  25. * @property int $backup_status 备份状态:1进行中,2已完成,3已失败
  26. * @property string $backup_hash 备份文件MD5哈希
  27. * @property array $backup_config 备份配置信息
  28. * @property \Carbon\Carbon $started_at 备份开始时间
  29. * @property \Carbon\Carbon $completed_at 备份完成时间
  30. * @property \Carbon\Carbon $expires_at 备份过期时间
  31. * @property string $error_message 错误信息
  32. * @property int $created_by 创建者用户ID
  33. * @property \Carbon\Carbon $created_at 创建时间
  34. * @property \Carbon\Carbon $updated_at 更新时间
  35. * field end
  36. */
  37. class CleanupBackup extends ModelCore
  38. {
  39. /**
  40. * 数据表名
  41. */
  42. protected $table = 'cleanup_backups';
  43. /**
  44. * 字段类型转换
  45. */
  46. protected $casts = [
  47. 'plan_id' => 'integer',
  48. 'task_id' => 'integer',
  49. 'backup_type' => 'integer',
  50. 'compression_type' => 'integer',
  51. 'backup_size' => 'integer',
  52. 'original_size' => 'integer',
  53. 'tables_count' => 'integer',
  54. 'records_count' => 'integer',
  55. 'backup_status' => 'integer',
  56. 'backup_config' => 'array',
  57. 'created_by' => 'integer',
  58. 'started_at' => 'datetime',
  59. 'completed_at' => 'datetime',
  60. 'expires_at' => 'datetime',
  61. 'created_at' => 'datetime',
  62. 'updated_at' => 'datetime',
  63. ];
  64. /**
  65. * 获取备份类型枚举
  66. */
  67. public function getBackupTypeEnumAttribute(): BACKUP_TYPE
  68. {
  69. return BACKUP_TYPE::from($this->backup_type);
  70. }
  71. /**
  72. * 获取压缩类型枚举
  73. */
  74. public function getCompressionTypeEnumAttribute(): COMPRESSION_TYPE
  75. {
  76. return COMPRESSION_TYPE::from($this->compression_type);
  77. }
  78. /**
  79. * 获取备份状态枚举
  80. */
  81. public function getBackupStatusEnumAttribute(): BACKUP_STATUS
  82. {
  83. return BACKUP_STATUS::from($this->backup_status);
  84. }
  85. /**
  86. * 获取备份类型描述
  87. */
  88. public function getBackupTypeNameAttribute(): string
  89. {
  90. return $this->getBackupTypeEnumAttribute()->getDescription();
  91. }
  92. /**
  93. * 获取压缩类型描述
  94. */
  95. public function getCompressionTypeNameAttribute(): string
  96. {
  97. return $this->getCompressionTypeEnumAttribute()->getDescription();
  98. }
  99. /**
  100. * 获取备份状态描述
  101. */
  102. public function getBackupStatusNameAttribute(): string
  103. {
  104. return $this->getBackupStatusEnumAttribute()->getDescription();
  105. }
  106. /**
  107. * 获取备份状态颜色
  108. */
  109. public function getBackupStatusColorAttribute(): string
  110. {
  111. return $this->getBackupStatusEnumAttribute()->getColor();
  112. }
  113. /**
  114. * 获取备份状态图标
  115. */
  116. public function getBackupStatusIconAttribute(): string
  117. {
  118. return $this->getBackupStatusEnumAttribute()->getIcon();
  119. }
  120. /**
  121. * 获取格式化的备份大小
  122. */
  123. public function getBackupSizeFormattedAttribute(): string
  124. {
  125. return $this->formatBytes($this->backup_size);
  126. }
  127. /**
  128. * 获取格式化的原始大小
  129. */
  130. public function getOriginalSizeFormattedAttribute(): string
  131. {
  132. return $this->formatBytes($this->original_size);
  133. }
  134. /**
  135. * 获取压缩率
  136. */
  137. public function getCompressionRatioAttribute(): float
  138. {
  139. if ($this->original_size == 0) {
  140. return 0;
  141. }
  142. return (1 - $this->backup_size / $this->original_size) * 100;
  143. }
  144. /**
  145. * 获取格式化的压缩率
  146. */
  147. public function getCompressionRatioFormattedAttribute(): string
  148. {
  149. return number_format($this->compression_ratio, 2) . '%';
  150. }
  151. /**
  152. * 获取备份持续时间
  153. */
  154. public function getDurationAttribute(): ?float
  155. {
  156. if (!$this->started_at || !$this->completed_at) {
  157. return null;
  158. }
  159. return $this->started_at->diffInSeconds($this->completed_at);
  160. }
  161. /**
  162. * 获取格式化的备份持续时间
  163. */
  164. public function getDurationFormattedAttribute(): ?string
  165. {
  166. $duration = $this->duration;
  167. return $duration ? $this->formatDuration($duration) : null;
  168. }
  169. /**
  170. * 判断备份是否已过期
  171. */
  172. public function getIsExpiredAttribute(): bool
  173. {
  174. return $this->expires_at && $this->expires_at->isPast();
  175. }
  176. /**
  177. * 获取过期状态文本
  178. */
  179. public function getExpiryStatusAttribute(): string
  180. {
  181. if (!$this->expires_at) {
  182. return '永不过期';
  183. }
  184. if ($this->is_expired) {
  185. return '已过期';
  186. }
  187. return '剩余 ' . $this->expires_at->diffForHumans();
  188. }
  189. /**
  190. * 获取过期状态颜色
  191. */
  192. public function getExpiryColorAttribute(): string
  193. {
  194. if (!$this->expires_at) {
  195. return 'info';
  196. }
  197. if ($this->is_expired) {
  198. return 'danger';
  199. }
  200. $daysLeft = now()->diffInDays($this->expires_at);
  201. if ($daysLeft <= 3) {
  202. return 'warning';
  203. }
  204. return 'success';
  205. }
  206. /**
  207. * 获取文件扩展名
  208. */
  209. public function getFileExtensionAttribute(): string
  210. {
  211. $backupExt = $this->getBackupTypeEnumAttribute()->getFileExtension();
  212. $compressExt = $this->getCompressionTypeEnumAttribute()->getFileExtension();
  213. return $backupExt . $compressExt;
  214. }
  215. /**
  216. * 获取完整文件名
  217. */
  218. public function getFullFilenameAttribute(): string
  219. {
  220. return $this->backup_name . '.' . $this->file_extension;
  221. }
  222. /**
  223. * 判断文件是否存在
  224. */
  225. public function getFileExistsAttribute(): bool
  226. {
  227. return file_exists($this->backup_path);
  228. }
  229. /**
  230. * 关联清理计划
  231. */
  232. public function plan(): BelongsTo
  233. {
  234. return $this->belongsTo(CleanupPlan::class, 'plan_id');
  235. }
  236. /**
  237. * 关联清理任务
  238. */
  239. public function task(): BelongsTo
  240. {
  241. return $this->belongsTo(CleanupTask::class, 'task_id');
  242. }
  243. /**
  244. * 关联备份文件
  245. */
  246. public function files(): HasMany
  247. {
  248. return $this->hasMany(CleanupBackupFile::class, 'backup_id');
  249. }
  250. /**
  251. * 关联SQL备份记录
  252. */
  253. public function sqlBackups(): HasMany
  254. {
  255. return $this->hasMany(CleanupSqlBackup::class, 'backup_id');
  256. }
  257. /**
  258. * 作用域:按计划筛选
  259. */
  260. public function scopeByPlan($query, int $planId)
  261. {
  262. return $query->where('plan_id', $planId);
  263. }
  264. /**
  265. * 作用域:按任务筛选
  266. */
  267. public function scopeByTask($query, int $taskId)
  268. {
  269. return $query->where('task_id', $taskId);
  270. }
  271. /**
  272. * 作用域:按备份状态筛选
  273. */
  274. public function scopeByStatus($query, int $status)
  275. {
  276. return $query->where('backup_status', $status);
  277. }
  278. /**
  279. * 作用域:已完成的备份
  280. */
  281. public function scopeCompleted($query)
  282. {
  283. return $query->where('backup_status', BACKUP_STATUS::COMPLETED->value);
  284. }
  285. /**
  286. * 作用域:已过期的备份
  287. */
  288. public function scopeExpired($query)
  289. {
  290. return $query->where('expires_at', '<', now());
  291. }
  292. /**
  293. * 作用域:即将过期的备份
  294. */
  295. public function scopeExpiringSoon($query, int $days = 7)
  296. {
  297. return $query->whereBetween('expires_at', [now(), now()->addDays($days)]);
  298. }
  299. /**
  300. * 作用域:按备份名称搜索
  301. */
  302. public function scopeSearchName($query, string $search)
  303. {
  304. return $query->where('backup_name', 'like', "%{$search}%");
  305. }
  306. /**
  307. * 格式化字节大小
  308. */
  309. private function formatBytes(int $bytes): string
  310. {
  311. if ($bytes == 0) {
  312. return '0 B';
  313. }
  314. $units = ['B', 'KB', 'MB', 'GB', 'TB'];
  315. $i = floor(log($bytes, 1024));
  316. return round($bytes / pow(1024, $i), 2) . ' ' . $units[$i];
  317. }
  318. /**
  319. * 格式化持续时间
  320. */
  321. private function formatDuration(float $seconds): string
  322. {
  323. if ($seconds < 60) {
  324. return number_format($seconds, 2) . ' 秒';
  325. } elseif ($seconds < 3600) {
  326. return number_format($seconds / 60, 2) . ' 分钟';
  327. } else {
  328. return number_format($seconds / 3600, 2) . ' 小时';
  329. }
  330. }
  331. /**
  332. * 获取备份状态统计
  333. */
  334. public static function getStatusStats(): array
  335. {
  336. $stats = static::selectRaw('backup_status, COUNT(*) as count')
  337. ->groupBy('backup_status')
  338. ->get()
  339. ->keyBy('backup_status')
  340. ->toArray();
  341. $result = [];
  342. foreach (BACKUP_STATUS::cases() as $status) {
  343. $result[$status->value] = [
  344. 'name' => $status->getDescription(),
  345. 'count' => $stats[$status->value]['count'] ?? 0,
  346. 'color' => $status->getColor(),
  347. 'icon' => $status->getIcon(),
  348. ];
  349. }
  350. return $result;
  351. }
  352. /**
  353. * 获取存储统计
  354. */
  355. public static function getStorageStats(): array
  356. {
  357. $stats = static::selectRaw('
  358. COUNT(*) as total_backups,
  359. SUM(backup_size) as total_backup_size,
  360. SUM(original_size) as total_original_size,
  361. AVG(backup_size) as avg_backup_size
  362. ')->first();
  363. return [
  364. 'total_backups' => $stats->total_backups ?? 0,
  365. 'total_backup_size' => $stats->total_backup_size ?? 0,
  366. 'total_original_size' => $stats->total_original_size ?? 0,
  367. 'avg_backup_size' => $stats->avg_backup_size ?? 0,
  368. 'total_compression_ratio' => $stats->total_original_size > 0
  369. ? (1 - $stats->total_backup_size / $stats->total_original_size) * 100
  370. : 0,
  371. ];
  372. }
  373. }