2025-12-30 11:37:21 +08:00
|
|
|
|
<?php
|
|
|
|
|
|
// application/common/library/GiftDataMigrator.php
|
|
|
|
|
|
|
|
|
|
|
|
namespace app\common\library;
|
|
|
|
|
|
|
|
|
|
|
|
use think\Db;
|
|
|
|
|
|
use think\Exception;
|
2025-12-30 15:25:58 +08:00
|
|
|
|
use think\Log;
|
2025-12-30 11:37:21 +08:00
|
|
|
|
|
|
|
|
|
|
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')
|
2025-12-30 17:32:16 +08:00
|
|
|
|
->where('createtime', '>', 0)
|
2025-12-30 11:37:21 +08:00
|
|
|
|
->order('createtime', 'asc')
|
|
|
|
|
|
->find();
|
|
|
|
|
|
$lastRecord = Db::name('vs_give_gift')
|
2025-12-30 17:32:16 +08:00
|
|
|
|
->where('createtime', '>', 0)
|
2025-12-30 11:37:21 +08:00
|
|
|
|
->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
|
|
|
|
|
|
];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|