> /dev/null 2>&1
* * * * * * cd /path/to/project && sleep 30 && php think qywx:sync-msg-archive --download >> /dev/null 2>&1
*
* 并发控制:通过文件锁,同一时刻只会跑一个,多开直接退出。
*/
class QywxSyncMsgArchive extends Command
{
protected function configure()
{
$this->setName('qywx:sync-msg-archive')
->addOption('download', 'd', Option::VALUE_NONE, '拉完消息后顺便下载 pending 媒体')
->addOption('only-media', null, Option::VALUE_NONE, '仅下载已登记的 pending 媒体')
->addOption('max-batches', 'b', Option::VALUE_REQUIRED, '单次运行最多拉多少批 (默认 20)', 20)
->addOption('max-media', 'm', Option::VALUE_REQUIRED, '单次最多下载多少条媒体 (默认 200)', 200)
->setDescription('拉取企业微信会话内容存档');
}
protected function execute(Input $input, Output $output)
{
$lockFile = runtime_path() . 'lock' . DIRECTORY_SEPARATOR . 'qywx_msg_archive.lock';
$lockDir = dirname($lockFile);
if (!is_dir($lockDir)) {
@mkdir($lockDir, 0755, true);
}
$fp = @fopen($lockFile, 'c');
if ($fp === false) {
$output->writeln('无法创建锁文件: ' . $lockFile . '');
return 1;
}
if (!flock($fp, LOCK_EX | LOCK_NB)) {
fclose($fp);
$output->writeln('另一个会话存档同步进程正在运行,跳过本次。');
return 0;
}
try {
$onlyMedia = (bool) $input->getOption('only-media');
$download = (bool) $input->getOption('download');
$maxBatches = max(1, (int) $input->getOption('max-batches'));
$maxMedia = max(1, (int) $input->getOption('max-media'));
if (!$onlyMedia) {
$pull = QywxMsgArchiveService::pullLoop($maxBatches);
if (!$pull['enabled']) {
$output->writeln('会话存档未启用或 SDK 不可用:' . implode('; ', $pull['errors']) . '');
return 0;
}
$output->writeln(sprintf(
'消息拉取:batches=%d pulled=%d inserted=%d updated=%d media_pending=%d last_seq=%d',
$pull['batches'],
$pull['pulled'],
$pull['inserted'],
$pull['updated'],
$pull['media_pending'],
$pull['last_seq']
));
if (!empty($pull['errors'])) {
foreach ($pull['errors'] as $err) {
Log::warning('qywx:sync-msg-archive pull error: ' . $err);
$output->writeln('error: ' . $err . '');
}
}
}
if ($onlyMedia || $download) {
$media = QywxMsgArchiveService::downloadPendingMedia($maxMedia);
if (!$media['enabled']) {
$output->writeln('媒体下载-SDK 未启用');
} else {
$output->writeln(sprintf(
'媒体下载:ok=%d failed=%d skipped=%d',
$media['ok'],
$media['failed'],
$media['skipped']
));
}
}
return 0;
} catch (\Throwable $e) {
$output->writeln('同步异常: ' . $e->getMessage() . '');
Log::error('qywx:sync-msg-archive 异常: ' . $e->getMessage());
return 1;
} finally {
flock($fp, LOCK_UN);
fclose($fp);
}
}
}