255 lines
8.2 KiB
PHP
255 lines
8.2 KiB
PHP
<?php
|
||
// application/common/library/GiftDataMigrator.php
|
||
|
||
namespace app\common\library;
|
||
|
||
use think\Db;
|
||
use think\Exception;
|
||
use think\facade\Log;
|
||
|
||
class GiftDataMigrator
|
||
{
|
||
/**
|
||
* 迁移单个月份的数据
|
||
* @param string $yearMonth 年月 202401
|
||
* @param int $batchSize 每批数量
|
||
* @param int $startId 起始ID
|
||
* @return array 迁移结果
|
||
*/
|
||
public static function migrateMonthData($yearMonth, $batchSize = 1000, $startId = 0)
|
||
{
|
||
// 确保目标表存在
|
||
GiftTableManager::createMonthTable($yearMonth);
|
||
|
||
$targetTable = 'fa_vs_give_gift_' . $yearMonth;
|
||
$startTime = strtotime($yearMonth . '01 00:00:00');
|
||
$endTime = strtotime(date('Y-m-01', strtotime('+1 month', $startTime)));
|
||
|
||
$totalMigrated = 0;
|
||
$lastId = $startId;
|
||
$hasMore = true;
|
||
|
||
Log::info("开始迁移 {$yearMonth} 数据,起始ID: {$startId}");
|
||
|
||
while ($hasMore) {
|
||
try {
|
||
// 查询原始表数据
|
||
$records = Db::name('vs_give_gift')
|
||
->where('id', '>', $lastId)
|
||
->where('createtime', '>=', $startTime)
|
||
->where('createtime', '<', $endTime)
|
||
->order('id', 'asc')
|
||
->limit($batchSize)
|
||
->select();
|
||
|
||
if (empty($records)) {
|
||
$hasMore = false;
|
||
break;
|
||
}
|
||
|
||
// 转换数据格式,生成雪花ID
|
||
$dataToInsert = [];
|
||
foreach ($records as $record) {
|
||
$newRecord = [
|
||
'id' => Snowflake::generate(),
|
||
'user_id' => $record['user_id'] ?? 0,
|
||
'gift_id' => $record['gift_id'] ?? 0,
|
||
'gift_type' => $record['gift_type'] ?? 1,
|
||
'number' => $record['number'] ?? 0,
|
||
'gift_user' => $record['gift_user'] ?? 0,
|
||
'from_id' => $record['from_id'] ?? 0,
|
||
'pit_number' => $record['pit_number'] ?? 0,
|
||
'total_price' => $record['total_price'] ?? 0,
|
||
'type' => $record['type'] ?? 1,
|
||
'from' => $record['from'] ?? 1,
|
||
'createtime' => $record['createtime'] ?? time(),
|
||
'updatetime' => $record['updatetime'] ?? time(),
|
||
];
|
||
$dataToInsert[] = $newRecord;
|
||
$lastId = $record['id']; // 使用原始表的ID
|
||
}
|
||
|
||
// 批量插入到分表
|
||
if (!empty($dataToInsert)) {
|
||
Db::table($targetTable)->insertAll($dataToInsert);
|
||
$totalMigrated += count($dataToInsert);
|
||
|
||
Log::info("迁移批次成功: {$yearMonth}, 本批: " . count($dataToInsert) . " 条,总计: {$totalMigrated} 条");
|
||
}
|
||
|
||
// 防止内存占用过高
|
||
unset($records, $dataToInsert);
|
||
|
||
// 小睡一下,防止CPU占用过高
|
||
usleep(10000);
|
||
|
||
} catch (\Exception $e) {
|
||
Log::error("迁移 {$yearMonth} 数据失败: " . $e->getMessage());
|
||
return [
|
||
'success' => false,
|
||
'message' => $e->getMessage(),
|
||
'total' => $totalMigrated,
|
||
'last_id' => $lastId
|
||
];
|
||
}
|
||
}
|
||
|
||
Log::info("完成迁移 {$yearMonth} 数据,总计: {$totalMigrated} 条");
|
||
|
||
return [
|
||
'success' => true,
|
||
'message' => '迁移完成',
|
||
'total' => $totalMigrated,
|
||
'last_id' => $lastId
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 迁移所有历史数据
|
||
* @param int $batchSize 每批数量
|
||
* @return array
|
||
*/
|
||
public static function migrateAllData($batchSize = 1000)
|
||
{
|
||
// 获取所有需要迁移的月份
|
||
$firstRecord = Db::name('vs_give_gift')
|
||
->order('createtime', 'asc')
|
||
->find();
|
||
$lastRecord = Db::name('vs_give_gift')
|
||
->order('createtime', 'desc')
|
||
->find();
|
||
|
||
if (!$firstRecord || !$lastRecord) {
|
||
return ['success' => true, 'message' => '没有数据需要迁移'];
|
||
}
|
||
|
||
$startMonth = date('Ym', $firstRecord['createtime']);
|
||
$endMonth = date('Ym', $lastRecord['createtime']);
|
||
|
||
// 生成月份列表
|
||
$months = self::generateMonthRange($startMonth, $endMonth);
|
||
|
||
Log::info("开始迁移数据,月份范围: {$startMonth} 到 {$endMonth},共 " . count($months) . " 个月");
|
||
|
||
$results = [];
|
||
foreach ($months as $month) {
|
||
Log::info("开始迁移 {$month} 数据");
|
||
|
||
$result = self::migrateMonthData($month, $batchSize);
|
||
$results[$month] = $result;
|
||
|
||
if (!$result['success']) {
|
||
Log::error("迁移 {$month} 数据失败,停止迁移");
|
||
break;
|
||
}
|
||
|
||
Log::info("完成迁移 {$month} 数据,共 {$result['total']} 条");
|
||
}
|
||
|
||
return [
|
||
'success' => true,
|
||
'message' => '所有数据迁移完成',
|
||
'results' => $results
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 生成月份范围
|
||
* @param string $startMonth 202401
|
||
* @param string $endMonth 202412
|
||
* @return array
|
||
*/
|
||
private static function generateMonthRange($startMonth, $endMonth)
|
||
{
|
||
$start = new \DateTime($startMonth . '01');
|
||
$end = new \DateTime($endMonth . '01');
|
||
|
||
$months = [];
|
||
while ($start <= $end) {
|
||
$months[] = $start->format('Ym');
|
||
$start->modify('+1 month');
|
||
}
|
||
|
||
return $months;
|
||
}
|
||
|
||
/**
|
||
* 获取迁移进度
|
||
* @return array
|
||
*/
|
||
public static function getMigrationProgress()
|
||
{
|
||
// 查询原始表数据量
|
||
$totalCount = Db::name('vs_give_gift')->count();
|
||
|
||
// 查询已迁移的数据量
|
||
$migratedCount = 0;
|
||
$tables = GiftTableManager::getAllTables();
|
||
|
||
foreach ($tables as $tableInfo) {
|
||
try {
|
||
$count = Db::table($tableInfo['table_name'])->count();
|
||
$migratedCount += $count;
|
||
} catch (\Exception $e) {
|
||
// 表可能不存在,跳过
|
||
}
|
||
}
|
||
|
||
$progress = $totalCount > 0 ? round($migratedCount / $totalCount * 100, 2) : 0;
|
||
|
||
return [
|
||
'total' => $totalCount,
|
||
'migrated' => $migratedCount,
|
||
'progress' => $progress,
|
||
'remaining' => $totalCount - $migratedCount
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 验证迁移数据一致性
|
||
* @param string $yearMonth 年月
|
||
* @return array
|
||
*/
|
||
public static function verifyMigration($yearMonth)
|
||
{
|
||
$startTime = strtotime($yearMonth . '01 00:00:00');
|
||
$endTime = strtotime(date('Y-m-01', strtotime('+1 month', $startTime)));
|
||
|
||
// 原始表数据量
|
||
$sourceCount = Db::name('vs_give_gift')
|
||
->where('createtime', '>=', $startTime)
|
||
->where('createtime', '<', $endTime)
|
||
->count();
|
||
|
||
// 目标表数据量
|
||
$targetTable = 'fa_vs_give_gift_' . $yearMonth;
|
||
$targetCount = 0;
|
||
try {
|
||
$targetCount = Db::table($targetTable)->count();
|
||
} catch (\Exception $e) {
|
||
Log::error("验证迁移数据失败,目标表不存在: " . $targetTable);
|
||
}
|
||
|
||
// 比较总价值
|
||
$sourceTotalPrice = Db::name('vs_give_gift')
|
||
->where('createtime', '>=', $startTime)
|
||
->where('createtime', '<', $endTime)
|
||
->sum('total_price');
|
||
|
||
$targetTotalPrice = 0;
|
||
try {
|
||
$targetTotalPrice = Db::table($targetTable)->sum('total_price');
|
||
} catch (\Exception $e) {
|
||
// 表可能不存在
|
||
}
|
||
|
||
return [
|
||
'source_count' => $sourceCount,
|
||
'target_count' => $targetCount,
|
||
'count_match' => $sourceCount == $targetCount,
|
||
'source_total_price' => $sourceTotalPrice,
|
||
'target_total_price' => $targetTotalPrice,
|
||
'price_match' => abs(floatval($sourceTotalPrice) - floatval($targetTotalPrice)) < 0.01
|
||
];
|
||
}
|
||
} |