AI功能提交
This commit is contained in:
100
Server/application/command/CleanExpiredGroupMessages.php
Normal file
100
Server/application/command/CleanExpiredGroupMessages.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
namespace app\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\console\input\Option;
|
||||
use think\Db;
|
||||
|
||||
class CleanExpiredGroupMessages extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('clean:expired_group_messages')
|
||||
->setDescription('Clean expired group messages from the database')
|
||||
->addOption('days', 'd', Option::VALUE_OPTIONAL, 'Number of days to keep messages (default: 90)', 90)
|
||||
->addOption('dry-run', null, Option::VALUE_NONE, 'Perform a dry run without deleting any data')
|
||||
->addOption('batch-size', 'b', Option::VALUE_OPTIONAL, 'Batch size for deletion (default: 1000)', 1000);
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$days = (int)$input->getOption('days');
|
||||
$dryRun = $input->getOption('dry-run');
|
||||
$batchSize = (int)$input->getOption('batch-size');
|
||||
|
||||
if ($dryRun) {
|
||||
$output->writeln("<info>Running in dry-run mode. No data will be deleted.</info>");
|
||||
}
|
||||
|
||||
$cutoffDate = date('Y-m-d H:i:s', strtotime("-{$days} days"));
|
||||
$output->writeln("<info>Cleaning group messages older than {$cutoffDate} (keeping last {$days} days)</info>");
|
||||
|
||||
// 清理微信群组消息
|
||||
$this->cleanWechatGroupMessages($cutoffDate, $dryRun, $batchSize, $output);
|
||||
|
||||
$output->writeln("<info>Group message cleanup completed successfully.</info>");
|
||||
}
|
||||
|
||||
protected function cleanWechatGroupMessages($cutoffDate, $dryRun, $batchSize, Output $output)
|
||||
{
|
||||
$output->writeln("\nCleaning s2_wechat_group_message table...");
|
||||
|
||||
// 获取符合条件的消息总数
|
||||
$totalCount = Db::table('s2_wechat_group_message')
|
||||
->where('createTime', '<', $cutoffDate)
|
||||
->count();
|
||||
|
||||
if ($totalCount === 0) {
|
||||
$output->writeln(" <comment>No expired group messages found.</comment>");
|
||||
return;
|
||||
}
|
||||
|
||||
$output->writeln(" Found {$totalCount} group messages to clean up.");
|
||||
|
||||
if ($dryRun) {
|
||||
$output->writeln(" <comment>Dry run mode: would delete {$totalCount} group messages.</comment>");
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算需要执行的批次数
|
||||
$batches = ceil($totalCount / $batchSize);
|
||||
$deletedCount = 0;
|
||||
|
||||
$output->writeln(" Deleting in {$batches} batches of {$batchSize} records...");
|
||||
|
||||
// 分批删除数据
|
||||
for ($i = 0; $i < $batches; $i++) {
|
||||
// 获取一批要删除的ID
|
||||
$ids = Db::table('s2_wechat_group_message')
|
||||
->where('createTime', '<', $cutoffDate)
|
||||
->limit($batchSize)
|
||||
->column('id');
|
||||
|
||||
if (empty($ids)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 删除这批数据
|
||||
$count = Db::table('s2_wechat_group_message')
|
||||
->whereIn('id', $ids)
|
||||
->delete();
|
||||
|
||||
$deletedCount += $count;
|
||||
$progress = round(($deletedCount / $totalCount) * 100, 2);
|
||||
$output->write(" Progress: {$progress}% ({$deletedCount}/{$totalCount})\r");
|
||||
|
||||
// 短暂暂停,减轻数据库负担
|
||||
usleep(500000); // 暂停0.5秒
|
||||
}
|
||||
|
||||
$output->writeln("");
|
||||
$output->writeln(" <info>Successfully deleted {$deletedCount} expired group messages.</info>");
|
||||
|
||||
// 优化表
|
||||
$output->writeln(" Optimizing table...");
|
||||
Db::execute("OPTIMIZE TABLE s2_wechat_group_message");
|
||||
$output->writeln(" <info>Table optimization completed.</info>");
|
||||
}
|
||||
}
|
||||
100
Server/application/command/CleanExpiredMessages.php
Normal file
100
Server/application/command/CleanExpiredMessages.php
Normal file
@@ -0,0 +1,100 @@
|
||||
<?php
|
||||
namespace app\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\console\input\Option;
|
||||
use think\Db;
|
||||
|
||||
class CleanExpiredMessages extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('clean:expired_messages')
|
||||
->setDescription('Clean expired messages from the database')
|
||||
->addOption('days', 'd', Option::VALUE_OPTIONAL, 'Number of days to keep messages (default: 90)', 90)
|
||||
->addOption('dry-run', null, Option::VALUE_NONE, 'Perform a dry run without deleting any data')
|
||||
->addOption('batch-size', 'b', Option::VALUE_OPTIONAL, 'Batch size for deletion (default: 1000)', 1000);
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$days = (int)$input->getOption('days');
|
||||
$dryRun = $input->getOption('dry-run');
|
||||
$batchSize = (int)$input->getOption('batch-size');
|
||||
|
||||
if ($dryRun) {
|
||||
$output->writeln("<info>Running in dry-run mode. No data will be deleted.</info>");
|
||||
}
|
||||
|
||||
$cutoffDate = date('Y-m-d H:i:s', strtotime("-{$days} days"));
|
||||
$output->writeln("<info>Cleaning messages older than {$cutoffDate} (keeping last {$days} days)</info>");
|
||||
|
||||
// 清理微信消息
|
||||
$this->cleanWechatMessages($cutoffDate, $dryRun, $batchSize, $output);
|
||||
|
||||
$output->writeln("<info>Message cleanup completed successfully.</info>");
|
||||
}
|
||||
|
||||
protected function cleanWechatMessages($cutoffDate, $dryRun, $batchSize, Output $output)
|
||||
{
|
||||
$output->writeln("\nCleaning s2_wechat_message table...");
|
||||
|
||||
// 获取符合条件的消息总数
|
||||
$totalCount = Db::table('s2_wechat_message')
|
||||
->where('createTime', '<', $cutoffDate)
|
||||
->count();
|
||||
|
||||
if ($totalCount === 0) {
|
||||
$output->writeln(" <comment>No expired messages found.</comment>");
|
||||
return;
|
||||
}
|
||||
|
||||
$output->writeln(" Found {$totalCount} messages to clean up.");
|
||||
|
||||
if ($dryRun) {
|
||||
$output->writeln(" <comment>Dry run mode: would delete {$totalCount} messages.</comment>");
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算需要执行的批次数
|
||||
$batches = ceil($totalCount / $batchSize);
|
||||
$deletedCount = 0;
|
||||
|
||||
$output->writeln(" Deleting in {$batches} batches of {$batchSize} records...");
|
||||
|
||||
// 分批删除数据
|
||||
for ($i = 0; $i < $batches; $i++) {
|
||||
// 获取一批要删除的ID
|
||||
$ids = Db::table('s2_wechat_message')
|
||||
->where('createTime', '<', $cutoffDate)
|
||||
->limit($batchSize)
|
||||
->column('id');
|
||||
|
||||
if (empty($ids)) {
|
||||
break;
|
||||
}
|
||||
|
||||
// 删除这批数据
|
||||
$count = Db::table('s2_wechat_message')
|
||||
->whereIn('id', $ids)
|
||||
->delete();
|
||||
|
||||
$deletedCount += $count;
|
||||
$progress = round(($deletedCount / $totalCount) * 100, 2);
|
||||
$output->write(" Progress: {$progress}% ({$deletedCount}/{$totalCount})\r");
|
||||
|
||||
// 短暂暂停,减轻数据库负担
|
||||
usleep(500000); // 暂停0.5秒
|
||||
}
|
||||
|
||||
$output->writeln("");
|
||||
$output->writeln(" <info>Successfully deleted {$deletedCount} expired messages.</info>");
|
||||
|
||||
// 优化表
|
||||
$output->writeln(" Optimizing table...");
|
||||
Db::execute("OPTIMIZE TABLE s2_wechat_message");
|
||||
$output->writeln(" <info>Table optimization completed.</info>");
|
||||
}
|
||||
}
|
||||
112
Server/application/command/OptimizeMessageIndexes.php
Normal file
112
Server/application/command/OptimizeMessageIndexes.php
Normal file
@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
namespace app\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\Db;
|
||||
|
||||
class OptimizeMessageIndexes extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('optimize:message_indexes')
|
||||
->setDescription('Optimize database indexes for message-related tables');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$output->writeln("Starting index optimization for message-related tables...");
|
||||
|
||||
// 优化 s2_wechat_message 表索引
|
||||
$this->optimizeWechatMessageIndexes($output);
|
||||
|
||||
// 优化 s2_wechat_chatroom 表索引
|
||||
$this->optimizeWechatChatroomIndexes($output);
|
||||
|
||||
// 优化 s2_wechat_friend 表索引
|
||||
$this->optimizeWechatFriendIndexes($output);
|
||||
|
||||
$output->writeln("Index optimization completed successfully.");
|
||||
}
|
||||
|
||||
protected function optimizeWechatMessageIndexes(Output $output)
|
||||
{
|
||||
$output->writeln("Optimizing s2_wechat_message table indexes...");
|
||||
|
||||
// 检查并添加 wechatChatroomId 索引
|
||||
$this->addIndexIfNotExists('s2_wechat_message', 'idx_chatroom_id', 'wechatChatroomId', $output);
|
||||
|
||||
// 检查并添加 wechatFriendId 索引
|
||||
$this->addIndexIfNotExists('s2_wechat_message', 'idx_friend_id', 'wechatFriendId', $output);
|
||||
|
||||
// 检查并添加 isRead 索引
|
||||
$this->addIndexIfNotExists('s2_wechat_message', 'idx_is_read', 'isRead', $output);
|
||||
|
||||
// 检查并添加 type 索引
|
||||
$this->addIndexIfNotExists('s2_wechat_message', 'idx_type', 'type', $output);
|
||||
|
||||
// 检查并添加 createTime 索引
|
||||
$this->addIndexIfNotExists('s2_wechat_message', 'idx_create_time', 'createTime', $output);
|
||||
|
||||
// 检查并添加组合索引 (wechatChatroomId, isRead)
|
||||
$this->addIndexIfNotExists('s2_wechat_message', 'idx_chatroom_read', 'wechatChatroomId,isRead', $output);
|
||||
|
||||
// 检查并添加组合索引 (wechatFriendId, isRead)
|
||||
$this->addIndexIfNotExists('s2_wechat_message', 'idx_friend_read', 'wechatFriendId,isRead', $output);
|
||||
}
|
||||
|
||||
protected function optimizeWechatChatroomIndexes(Output $output)
|
||||
{
|
||||
$output->writeln("Optimizing s2_wechat_chatroom table indexes...");
|
||||
|
||||
// 检查并添加 accountId 索引
|
||||
$this->addIndexIfNotExists('s2_wechat_chatroom', 'idx_account_id', 'accountId', $output);
|
||||
|
||||
// 检查并添加 isDeleted 索引
|
||||
$this->addIndexIfNotExists('s2_wechat_chatroom', 'idx_is_deleted', 'isDeleted', $output);
|
||||
|
||||
// 检查并添加组合索引 (accountId, isDeleted)
|
||||
$this->addIndexIfNotExists('s2_wechat_chatroom', 'idx_account_deleted', 'accountId,isDeleted', $output);
|
||||
}
|
||||
|
||||
protected function optimizeWechatFriendIndexes(Output $output)
|
||||
{
|
||||
$output->writeln("Optimizing s2_wechat_friend table indexes...");
|
||||
|
||||
// 检查并添加 accountId 索引
|
||||
$this->addIndexIfNotExists('s2_wechat_friend', 'idx_account_id', 'accountId', $output);
|
||||
|
||||
// 检查并添加 isDeleted 索引
|
||||
$this->addIndexIfNotExists('s2_wechat_friend', 'idx_is_deleted', 'isDeleted', $output);
|
||||
|
||||
// 检查并添加组合索引 (accountId, isDeleted)
|
||||
$this->addIndexIfNotExists('s2_wechat_friend', 'idx_account_deleted', 'accountId,isDeleted', $output);
|
||||
}
|
||||
|
||||
protected function addIndexIfNotExists($table, $indexName, $columns, Output $output)
|
||||
{
|
||||
try {
|
||||
// 检查索引是否已存在
|
||||
$indexExists = false;
|
||||
$indexes = Db::query("SHOW INDEX FROM {$table}");
|
||||
|
||||
foreach ($indexes as $index) {
|
||||
if ($index['Key_name'] === $indexName) {
|
||||
$indexExists = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$indexExists) {
|
||||
// 添加索引
|
||||
Db::execute("ALTER TABLE {$table} ADD INDEX {$indexName} ({$columns})");
|
||||
$output->writeln(" - Added index {$indexName} on {$table}({$columns})");
|
||||
} else {
|
||||
$output->writeln(" - Index {$indexName} already exists on {$table}");
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$output->writeln(" - Error adding index {$indexName} to {$table}: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Server/application/command/ScheduleMessageMaintenance.php
Normal file
121
Server/application/command/ScheduleMessageMaintenance.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
namespace app\command;
|
||||
|
||||
use think\console\Command;
|
||||
use think\console\Input;
|
||||
use think\console\Output;
|
||||
use think\console\input\Option;
|
||||
|
||||
class ScheduleMessageMaintenance extends Command
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this->setName('schedule:message_maintenance')
|
||||
->setDescription('Schedule and run message maintenance tasks')
|
||||
->addOption('optimize-indexes', null, Option::VALUE_NONE, 'Run index optimization')
|
||||
->addOption('clean-messages', null, Option::VALUE_NONE, 'Clean expired messages')
|
||||
->addOption('days', 'd', Option::VALUE_OPTIONAL, 'Number of days to keep messages (default: 90)', 90)
|
||||
->addOption('batch-size', 'b', Option::VALUE_OPTIONAL, 'Batch size for deletion (default: 1000)', 1000)
|
||||
->addOption('dry-run', null, Option::VALUE_NONE, 'Perform a dry run without deleting any data');
|
||||
}
|
||||
|
||||
protected function execute(Input $input, Output $output)
|
||||
{
|
||||
$optimizeIndexes = $input->getOption('optimize-indexes');
|
||||
$cleanMessages = $input->getOption('clean-messages');
|
||||
$days = (int)$input->getOption('days');
|
||||
$batchSize = (int)$input->getOption('batch-size');
|
||||
$dryRun = $input->getOption('dry-run');
|
||||
|
||||
// 如果没有指定任何选项,则运行所有维护任务
|
||||
if (!$optimizeIndexes && !$cleanMessages) {
|
||||
$optimizeIndexes = true;
|
||||
$cleanMessages = true;
|
||||
}
|
||||
|
||||
$output->writeln("<info>Starting scheduled message maintenance tasks...</info>");
|
||||
$startTime = microtime(true);
|
||||
|
||||
// 运行索引优化
|
||||
if ($optimizeIndexes) {
|
||||
$this->runCommand($output, 'optimize:message_indexes');
|
||||
}
|
||||
|
||||
// 清理过期消息
|
||||
if ($cleanMessages) {
|
||||
$options = [];
|
||||
|
||||
if ($days !== 90) {
|
||||
$options[] = "--days={$days}";
|
||||
}
|
||||
|
||||
if ($batchSize !== 1000) {
|
||||
$options[] = "--batch-size={$batchSize}";
|
||||
}
|
||||
|
||||
if ($dryRun) {
|
||||
$options[] = "--dry-run";
|
||||
}
|
||||
|
||||
$this->runCommand($output, 'clean:expired_messages', $options);
|
||||
$this->runCommand($output, 'clean:expired_group_messages', $options);
|
||||
}
|
||||
|
||||
$endTime = microtime(true);
|
||||
$executionTime = round($endTime - $startTime, 2);
|
||||
$output->writeln("<info>All maintenance tasks completed in {$executionTime} seconds.</info>");
|
||||
}
|
||||
|
||||
protected function runCommand(Output $output, $command, array $options = [])
|
||||
{
|
||||
$output->writeln("\n<comment>Running command: {$command}</comment>");
|
||||
|
||||
$optionsStr = implode(' ', $options);
|
||||
$fullCommand = "php think {$command} {$optionsStr}";
|
||||
|
||||
$output->writeln("Executing: {$fullCommand}");
|
||||
$output->writeln("\n<info>Command output:</info>");
|
||||
|
||||
// 执行命令并实时输出结果
|
||||
$descriptorSpec = [
|
||||
0 => ["pipe", "r"], // stdin
|
||||
1 => ["pipe", "w"], // stdout
|
||||
2 => ["pipe", "w"] // stderr
|
||||
];
|
||||
|
||||
$process = proc_open($fullCommand, $descriptorSpec, $pipes);
|
||||
|
||||
if (is_resource($process)) {
|
||||
// 关闭标准输入
|
||||
fclose($pipes[0]);
|
||||
|
||||
// 读取标准输出
|
||||
while (!feof($pipes[1])) {
|
||||
$line = fgets($pipes[1]);
|
||||
if ($line !== false) {
|
||||
$output->write($line);
|
||||
}
|
||||
}
|
||||
fclose($pipes[1]);
|
||||
|
||||
// 读取标准错误
|
||||
$errorOutput = stream_get_contents($pipes[2]);
|
||||
fclose($pipes[2]);
|
||||
|
||||
// 获取命令执行结果
|
||||
$exitCode = proc_close($process);
|
||||
|
||||
if ($exitCode !== 0) {
|
||||
$output->writeln("\n<error>Command failed with exit code {$exitCode}</error>");
|
||||
if (!empty($errorOutput)) {
|
||||
$output->writeln("<error>Error output:</error>");
|
||||
$output->writeln($errorOutput);
|
||||
}
|
||||
} else {
|
||||
$output->writeln("\n<info>Command completed successfully.</info>");
|
||||
}
|
||||
} else {
|
||||
$output->writeln("<error>Failed to execute command.</error>");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user