mirror of
https://github.com/kuaifan/dootask.git
synced 2026-06-28 18:22:22 +00:00
- composer: framework ^13.0、php ^8.3、laravel-s ~3.8.0、predis ^2.3、 phpunit ^11.5、tinker ^3、excel ^3.1.69、captcha ^3.5、avatar ^6.5、 ldaprecord-laravel ^4、pinyin ^5.3、notify 锁 ~1.28.0; 移除 fideloper/proxy、fruitcake/laravel-cors、facade/ignition、 laravel/sail、madnest/madzipper、手动钉的 symfony/mailer; symfony/console 锁 ^7.4(LaravelS Portal 与 console 8 的 configure(): void 类型断言不兼容) - $dates 移除:AbstractModel 改 getCasts() 合并默认 datetime 列, 3 个子模型改 $casts - Carbon 3:4 处 diffInSeconds 补 absolute 参数并取整 - LdapRecord v4:config use_ssl/use_tls→use_tls/use_starttls(env 变量名不变), LdapUser::$objectClasses 补类型声明 - Madzipper→原生 ZipArchive(Base::zipAddFiles,4 处调用) - pinyin v5 静态 API(Base::getFirstCharter/cn2pinyin) - laravolt/avatar 6.5:PatchedAvatar 修上游纵向对齐 bug (intervention 4.1.3 枚举无 middle),avatar 响应改 response()->file() - TrustProxies 改框架内置基类,CORS 改 Illuminate\Http\Middleware\HandleCors - Symfony Console 8 兼容:ManticoreSyncLock::handleSignal 新签名, pcntl 回调解耦 - 非 Swoole 运行时守卫:AbstractTask::task / PushTask::push / AbstractData(swoole table),artisan/测试上下文不再炸 Target class [swoole] does not exist - Laravel 11+ change() 丢修饰符:2023_12_07 与 2025_08_10 迁移重申 nullable/default/comment(修复 fresh 安装) - Setting/Ihttp 缺键访问加 ?? 守卫(PHP 8 警告在测试中转异常) - phpunit.xml 迁移 11 schema;UserImportParseTest 改为自建部门数据 验证:8.4 容器内 migrate:fresh --seed 213 全过;php artisan test 145 passed/1 skipped;LaravelS(Swoole 6.2.1) /health 200、登录、 token 认证、WebSocket 握手、Task 投递、头像、图片裁剪冒烟全过 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
100 lines
2.4 KiB
PHP
100 lines
2.4 KiB
PHP
<?php
|
||
|
||
namespace App\Console\Commands\Traits;
|
||
|
||
use Cache;
|
||
|
||
/**
|
||
* Manticore 同步命令通用锁机制
|
||
*
|
||
* 提供:
|
||
* - 锁的获取、设置、释放
|
||
* - 信号处理(优雅退出)
|
||
* - 通用的命令初始化检查
|
||
*/
|
||
trait ManticoreSyncLock
|
||
{
|
||
private bool $shouldStop = false;
|
||
|
||
/**
|
||
* 获取锁信息
|
||
*/
|
||
private function getLock(): ?array
|
||
{
|
||
$lockKey = $this->getLockKey();
|
||
return Cache::has($lockKey) ? Cache::get($lockKey) : null;
|
||
}
|
||
|
||
/**
|
||
* 设置锁(30分钟有效期,持续处理时需不断刷新)
|
||
*/
|
||
private function setLock(): void
|
||
{
|
||
$lockKey = $this->getLockKey();
|
||
Cache::put($lockKey, ['started_at' => date('Y-m-d H:i:s')], 1800);
|
||
}
|
||
|
||
/**
|
||
* 释放锁
|
||
*/
|
||
private function releaseLock(): void
|
||
{
|
||
$lockKey = $this->getLockKey();
|
||
Cache::forget($lockKey);
|
||
}
|
||
|
||
/**
|
||
* 获取锁的缓存键
|
||
*/
|
||
private function getLockKey(): string
|
||
{
|
||
return md5($this->signature);
|
||
}
|
||
|
||
/**
|
||
* 信号处理器(SIGINT/SIGTERM),签名须兼容 Symfony Console 的 Command::handleSignal
|
||
*/
|
||
public function handleSignal(int $signal, int|false $previousExitCode = 0): int|false
|
||
{
|
||
$this->markShouldStop();
|
||
return false; // 继续执行,由批次循环优雅退出
|
||
}
|
||
|
||
/**
|
||
* 标记优雅退出(pcntl 回调第二参是 siginfo,不能直接复用 handleSignal)
|
||
*/
|
||
private function markShouldStop(): void
|
||
{
|
||
$this->info("\n收到信号,将在当前批次完成后退出...");
|
||
$this->shouldStop = true;
|
||
}
|
||
|
||
/**
|
||
* 注册信号处理器
|
||
*/
|
||
private function registerSignalHandlers(): void
|
||
{
|
||
if (extension_loaded('pcntl')) {
|
||
pcntl_async_signals(true);
|
||
pcntl_signal(SIGINT, fn () => $this->markShouldStop());
|
||
pcntl_signal(SIGTERM, fn () => $this->markShouldStop());
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 检查命令是否可以启动(锁检查)
|
||
*
|
||
* @return bool 返回 true 表示可以启动,false 表示已被占用
|
||
*/
|
||
private function acquireLock(): bool
|
||
{
|
||
$lockInfo = $this->getLock();
|
||
if ($lockInfo) {
|
||
$this->error("命令已在运行中,开始时间: {$lockInfo['started_at']}");
|
||
return false;
|
||
}
|
||
$this->setLock();
|
||
return true;
|
||
}
|
||
}
|