Skip to content

Commit 100f446

Browse files
frankslinclaude
andauthored
feat: 新增 CBDB 內部表維護管理界面 (cbdb-project#452)
* feat: 新增 CBDB 內部表維護管理界面 新增 web 界面用於管理兩個 CBDB 內部表的重建操作: - CBDB__TRAD_SIMP_MAP(繁簡映射表) - CBDB__NAME_FTS(姓名搜尋倒排索引) 功能特性: - 支援 truncate 選項(清空後重建或增量更新) - 姓名索引支援 id_from/id_to 範圍參數 - 使用 Artisan::call() 執行對應的 artisan 命令 - 提供即時執行狀態反饋和結果顯示 - 記錄操作日誌到 CBDB__MAINTENANCE_LOG 表 實作細節: - 控制器:CbdbTableMaintenanceController - 視圖:cbdb-table-maintenance.blade.php - 路由:/admin/cbdb-table-maintenance * 修正 CBDB 維護頁面呼叫 Artisan 錯誤 * feat: 為姓名索引重建添加後台任務和進度追蹤 為 CBDB__NAME_FTS 表的重建操作實現後台任務處理機制,避免長時間請求卡頓: - 使用 register_shutdown_function 在後台執行重建任務 - 透過 Cache 儲存任務進度和狀態資訊 - 前端每 5 秒輪詢進度 API 並更新進度條 - 支援完成和錯誤狀態的自動處理 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * 改善內部表維護進度條與避免預設清空 --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 746afed commit 100f446

6 files changed

Lines changed: 798 additions & 9 deletions

File tree

app/Console/Commands/RebuildNameSearchIndex.php

Lines changed: 68 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
namespace App\Console\Commands;
44

5+
use App\Services\NameFtsProgressService;
56
use Illuminate\Console\Command;
67
use Illuminate\Support\Facades\DB;
7-
use Illuminate\Support\Facades\Log;
88
use Illuminate\Support\Facades\Schema;
99

1010
class RebuildNameSearchIndex extends Command
@@ -19,7 +19,8 @@ class RebuildNameSearchIndex extends Command
1919
{--batch=500 : Number of records to insert per batch}
2020
{--commit-interval=5000 : Number of records to commit per transaction}
2121
{--id-from= : Start from this c_personid (inclusive)}
22-
{--id-to= : End at this c_personid (inclusive)}';
22+
{--id-to= : End at this c_personid (inclusive)}
23+
{--task-id= : 內部使用:前端輪詢的進度任務編號}';
2324

2425
/**
2526
* The console command description.
@@ -35,6 +36,27 @@ class RebuildNameSearchIndex extends Command
3536
*/
3637
protected $tradSimpMap = [];
3738

39+
/**
40+
* 目前進行中的進度任務
41+
*
42+
* @var string|null
43+
*/
44+
protected $taskId;
45+
46+
/**
47+
* 上一次回報進度時已處理的姓名數
48+
*
49+
* @var int
50+
*/
51+
protected $lastProgressCount = 0;
52+
53+
/**
54+
* 回報進度的最小間隔
55+
*
56+
* @var int
57+
*/
58+
protected $progressReportInterval = 1000;
59+
3860
/**
3961
* Execute the console command.
4062
*
@@ -47,6 +69,12 @@ public function handle()
4769
return 1;
4870
}
4971

72+
$this->taskId = $this->option('task-id') ? (string) $this->option('task-id') : null;
73+
$this->lastProgressCount = 0;
74+
if ($this->taskId) {
75+
NameFtsProgressService::update($this->taskId, 12, '正在初始化索引重建作業…', 'running');
76+
}
77+
5078
$batchSize = max(1, (int) $this->option('batch'));
5179
$commitInterval = max(100, (int) $this->option('commit-interval'));
5280
$idFrom = $this->option('id-from') ? (int) $this->option('id-from') : null;
@@ -73,7 +101,7 @@ public function handle()
73101

74102
// 流式處理:邊收集邊生成邊插入
75103
$this->info('流式處理姓名資料...');
76-
$this->streamProcessNames($batchSize, $commitInterval, $idFrom, $idTo);
104+
$this->streamProcessNames($batchSize, $commitInterval, $idFrom, $idTo, $this->taskId);
77105

78106
$this->info('索引重建完成!');
79107
$this->displayStatistics();
@@ -89,12 +117,12 @@ public function handle()
89117
* @param int|null $idFrom
90118
* @param int|null $idTo
91119
*/
92-
protected function streamProcessNames(int $batchSize, int $commitInterval, ?int $idFrom = null, ?int $idTo = null)
120+
protected function streamProcessNames(int $batchSize, int $commitInterval, ?int $idFrom = null, ?int $idTo = null, ?string $taskId = null)
93121
{
94122
$recordBuffer = [];
95123
$totalInserted = 0;
96124

97-
$this->streamProcessNamesInTransaction($batchSize, $recordBuffer, $totalInserted, $commitInterval, $idFrom, $idTo);
125+
$this->streamProcessNamesInTransaction($batchSize, $recordBuffer, $totalInserted, $commitInterval, $idFrom, $idTo, $taskId);
98126

99127
$this->info(sprintf('成功插入 %s 條倒排記錄', number_format($totalInserted)));
100128
}
@@ -109,7 +137,7 @@ protected function streamProcessNames(int $batchSize, int $commitInterval, ?int
109137
* @param int|null $idFrom
110138
* @param int|null $idTo
111139
*/
112-
protected function streamProcessNamesInTransaction(int $batchSize, array &$recordBuffer, int &$totalInserted, int $commitInterval, ?int $idFrom = null, ?int $idTo = null)
140+
protected function streamProcessNamesInTransaction(int $batchSize, array &$recordBuffer, int &$totalInserted, int $commitInterval, ?int $idFrom = null, ?int $idTo = null, ?string $taskId = null)
113141
{
114142
// 禁用查詢日誌以避免記憶體累積
115143
DB::connection()->disableQueryLog();
@@ -140,11 +168,12 @@ protected function streamProcessNamesInTransaction(int $batchSize, array &$recor
140168
$totalAltNames = $altQuery->count();
141169
$totalNames = $totalMainNames + $totalAltNames;
142170

143-
$bar = $this->output->createProgressBar($totalNames);
171+
$bar = $this->output->createProgressBar($totalNames ?: 1);
144172
$bar->start();
145173

146174
$lastCommitCount = 0; // 上次提交時的記錄數
147175
$timestamp = now(); // 快取時間戳,避免每次都創建新物件
176+
$processedNames = 0;
148177

149178
// 開始第一個事務
150179
DB::beginTransaction();
@@ -163,11 +192,13 @@ protected function streamProcessNamesInTransaction(int $batchSize, array &$recor
163192
}
164193
$mainProcessQuery
165194
->orderBy('c_personid')
166-
->chunkById(1000, function ($rows) use (&$recordBuffer, &$totalInserted, &$lastCommitCount, $batchSize, $commitInterval, &$bar, $totalNames, $timestamp) {
195+
->chunkById(1000, function ($rows) use (&$recordBuffer, &$totalInserted, &$lastCommitCount, $batchSize, $commitInterval, &$bar, $totalNames, $timestamp, &$processedNames, $taskId) {
167196
foreach ($rows as $row) {
168197
$fullName = $this->normalizeName($row->c_name_chn);
198+
$processedNames++;
169199
if (!$fullName) {
170200
$bar->advance();
201+
$this->reportProgressIfNeeded($taskId, $processedNames, $totalNames, '正在處理本名資料…');
171202
continue;
172203
}
173204

@@ -206,6 +237,7 @@ protected function streamProcessNamesInTransaction(int $batchSize, array &$recor
206237
}
207238

208239
$bar->advance();
240+
$this->reportProgressIfNeeded($taskId, $processedNames, $totalNames, '正在處理本名資料…');
209241
}
210242
}, 'c_personid');
211243

@@ -230,11 +262,13 @@ protected function streamProcessNamesInTransaction(int $batchSize, array &$recor
230262
}
231263
$altProcessQuery
232264
->orderBy('ad.c_personid')
233-
->chunk(1000, function ($rows) use (&$recordBuffer, &$totalInserted, &$lastCommitCount, $batchSize, $commitInterval, &$bar, $totalNames, $timestamp) {
265+
->chunk(1000, function ($rows) use (&$recordBuffer, &$totalInserted, &$lastCommitCount, $batchSize, $commitInterval, &$bar, $totalNames, $timestamp, &$processedNames, $taskId) {
234266
foreach ($rows as $row) {
235267
$fullName = $this->normalizeName($row->c_alt_name_chn);
268+
$processedNames++;
236269
if (!$fullName) {
237270
$bar->advance();
271+
$this->reportProgressIfNeeded($taskId, $processedNames, $totalNames, '正在處理別名資料…');
238272
continue;
239273
}
240274

@@ -277,6 +311,7 @@ protected function streamProcessNamesInTransaction(int $batchSize, array &$recor
277311
}
278312

279313
$bar->advance();
314+
$this->reportProgressIfNeeded($taskId, $processedNames, $totalNames, '正在處理別名資料…');
280315
}
281316
});
282317

@@ -291,6 +326,30 @@ protected function streamProcessNamesInTransaction(int $batchSize, array &$recor
291326

292327
$bar->finish();
293328
$this->line('');
329+
330+
$this->reportProgressIfNeeded($taskId, $processedNames, $totalNames, '姓名資料已全部處理,正在整理索引結果…');
331+
}
332+
333+
protected function reportProgressIfNeeded(?string $taskId, int $processed, int $total, string $message = null): void
334+
{
335+
if (!$taskId || $total <= 0) {
336+
return;
337+
}
338+
339+
$shouldReport = $processed === $total || ($processed - $this->lastProgressCount) >= $this->progressReportInterval;
340+
if (!$shouldReport) {
341+
return;
342+
}
343+
344+
$percent = (int) floor(($processed / $total) * 75) + 15;
345+
$percent = max(12, min(90, $percent));
346+
$message = $message ?? sprintf('已處理 %s / %s 筆姓名…',
347+
number_format($processed),
348+
number_format($total)
349+
);
350+
351+
NameFtsProgressService::update($taskId, $percent, $message, 'running');
352+
$this->lastProgressCount = $processed;
294353
}
295354

296355
/**

0 commit comments

Comments
 (0)