Files
yusheng-php/application/common/command/GiftWallMigration.php

287 lines
9.1 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\common\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use think\Db;
class GiftWallMigration extends Command
{
// 每批处理的数据量
protected $batchSize = 10000;
protected function configure()
{
$this->setName('giftwall:migration')
->setDescription('迁移送礼数据到礼物墙');
}
protected function execute(Input $input, Output $output)
{
$output->writeln('开始迁移送礼数据到礼物墙...');
// 第一步:创建临时表存储汇总数据
$this->createTempTable($output);
// 第二步:分批处理原始数据
$this->processInBatches($output);
// 第三步:合并临时表数据到目标表
$this->mergeToTargetTable($output);
// 第四步:清理临时表
$this->cleanupTempTable($output);
$output->writeln('迁移完成!');
}
/**
* 创建临时汇总表
*/
protected function createTempTable(Output $output)
{
$output->writeln('创建临时汇总表...');
$sql = "CREATE TABLE IF NOT EXISTS `temp_gift_wall_summary` (
`user_id` int NOT NULL COMMENT '收礼用户',
`gift_id` int NOT NULL COMMENT '礼物ID',
`total_count` int NOT NULL DEFAULT '0' COMMENT '收到此礼物的总数',
`give_user_ids` text COMMENT '送礼用户ID逗号分隔',
PRIMARY KEY (`user_id`, `gift_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4";
Db::execute($sql);
// 清空临时表(如果存在旧数据)
Db::execute('TRUNCATE TABLE temp_gift_wall_summary');
$output->writeln('临时表创建完成');
}
/**
* 分批处理原始数据
*/
protected function processInBatches(Output $output)
{
$output->writeln('开始分批处理原始数据...');
// 获取总记录数
$total = Db::table('fa_vs_give_gift')->count();
$output->writeln("总记录数: {$total}");
// 计算总批次数
$totalBatches = ceil($total / $this->batchSize);
for ($batch = 0; $batch < $totalBatches; $batch++) {
$output->writeln("正在处理第 " . ($batch + 1) . "/{$totalBatches} 批...");
$offset = $batch * $this->batchSize;
// 分批查询原始数据
$records = Db::table('fa_vs_give_gift')
->field('user_id, gift_id, gift_user, number')
->limit($offset, $this->batchSize)
->select();
if (empty($records)) {
continue;
}
// 处理这批数据
$this->processBatchData($records);
// 释放内存
unset($records);
// 记录进度
$processed = min(($batch + 1) * $this->batchSize, $total);
$output->writeln("已处理: {$processed}/{$total}");
// 小休息一下,避免服务器压力过大
usleep(100000); // 0.1秒
}
}
/**
* 处理一批数据
*/
protected function processBatchData($records)
{
// 按收礼用户和礼物ID分组
$groupedData = [];
foreach ($records as $record) {
$key = $record['gift_user'] . '_' . $record['gift_id'];
if (!isset($groupedData[$key])) {
$groupedData[$key] = [
'user_id' => $record['gift_user'],
'gift_id' => $record['gift_id'],
'total_count' => 0,
'give_users' => []
];
}
// 累加礼物数量
$groupedData[$key]['total_count'] += $record['number'];
// 记录送礼用户(去重会在最后处理)
$groupedData[$key]['give_users'][] = $record['user_id'];
}
// 批量插入或更新到临时表
$this->batchUpdateTempTable($groupedData);
}
/**
* 批量更新临时表
*/
protected function batchUpdateTempTable($groupedData)
{
if (empty($groupedData)) {
return;
}
// 开启事务
Db::startTrans();
try {
foreach ($groupedData as $data) {
// 去重送礼用户ID
$uniqueGiveUsers = array_unique($data['give_users']);
$giveUserIdsStr = implode(',', $uniqueGiveUsers);
// 检查是否已存在该记录
$exists = Db::table('temp_gift_wall_summary')
->where('user_id', $data['user_id'])
->where('gift_id', $data['gift_id'])
->find();
if ($exists) {
// 更新现有记录
$newCount = $exists['total_count'] + $data['total_count'];
// 合并送礼用户ID去重
$existingUsers = explode(',', $exists['give_user_ids']);
$allUsers = array_merge($existingUsers, $uniqueGiveUsers);
$allUsers = array_unique($allUsers);
$mergedGiveUserIds = implode(',', $allUsers);
Db::table('temp_gift_wall_summary')
->where('user_id', $data['user_id'])
->where('gift_id', $data['gift_id'])
->update([
'total_count' => $newCount,
'give_user_ids' => $mergedGiveUserIds
]);
} else {
// 插入新记录
Db::table('temp_gift_wall_summary')
->insert([
'user_id' => $data['user_id'],
'gift_id' => $data['gift_id'],
'total_count' => $data['total_count'],
'give_user_ids' => $giveUserIdsStr
]);
}
}
Db::commit();
} catch (\Exception $e) {
Db::rollback();
throw $e;
}
}
/**
* 合并临时表数据到目标表
*/
protected function mergeToTargetTable(Output $output)
{
$output->writeln('开始合并数据到目标表...');
// 获取临时表总记录数
$total = Db::table('temp_gift_wall_summary')->count();
$output->writeln("需要合并的记录数: {$total}");
// 分批合并
$batchSize = 5000;
$totalBatches = ceil($total / $batchSize);
for ($batch = 0; $batch < $totalBatches; $batch++) {
$output->writeln("正在合并第 " . ($batch + 1) . "/{$totalBatches} 批...");
$offset = $batch * $batchSize;
// 分批获取临时表数据
$records = Db::table('temp_gift_wall_summary')
->limit($offset, $batchSize)
->select();
if (empty($records)) {
continue;
}
// 批量插入或更新到目标表
foreach ($records as $record) {
// 检查目标表是否已存在该记录
$exists = Db::table('fa_user_gift_wall')
->where('user_id', $record['user_id'])
->where('gift_id', $record['gift_id'])
->find();
if ($exists) {
// 更新现有记录
$newCount = $exists['count'] + $record['total_count'];
// 合并送礼用户ID去重
$existingUsers = explode(',', $exists['give_user_ids']);
$newUsers = explode(',', $record['give_user_ids']);
$allUsers = array_merge($existingUsers, $newUsers);
$allUsers = array_unique($allUsers);
$mergedGiveUserIds = implode(',', $allUsers);
Db::table('fa_user_gift_wall')
->where('id', $exists['id'])
->update([
'count' => $newCount,
'give_user_ids' => $mergedGiveUserIds,
'updatetime' => time()
]);
} else {
// 插入新记录
Db::table('fa_user_gift_wall')
->insert([
'user_id' => $record['user_id'],
'gift_id' => $record['gift_id'],
'count' => $record['total_count'],
'give_user_ids' => $record['give_user_ids'],
'updatetime' => time()
]);
}
}
// 释放内存
unset($records);
// 小休息一下
usleep(50000); // 0.05秒
}
$output->writeln('数据合并完成');
}
/**
* 清理临时表
*/
protected function cleanupTempTable(Output $output)
{
$output->writeln('清理临时表...');
Db::execute('DROP TABLE IF EXISTS temp_gift_wall_summary');
$output->writeln('临时表已清理');
}
}