Files
2meet-data-optimizer/admin/templates/migration-wizard.php
T
wpdev 76c01e44df refactor: 全部 128 個生產檔加入 declare(strict_types=1)(PR-H)
對齊 A v3.2.0。型別強制會把隱式轉換變成 TypeError,所以一次全檔加入
並跑完整測試(unit 451 / integration 398 全綠,無迴歸)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
2026-07-31 06:13:33 +08:00

220 lines
9.9 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
/**
* One-click User Entity Migration Wizard.
*
* Variables in scope (from class-tmdo-admin.php::render_migration_wizard):
* $preflight array TMDO_Migration_Orchestrator::preflight() snapshot.
* $status array TMDO_Migration_Orchestrator::get_status() snapshot.
*
* @package WP_Data_Optimizer
* @since 2.8.0
*/
declare(strict_types=1);
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
$ratio = $preflight['ratio'] ?? 0;
$users_count = $preflight['users'] ?? 0;
$usermeta_count = $preflight['usermeta'] ?? 0;
$eav_residue = $preflight['eav_rows'] ?? 0;
$current_mode = $preflight['mode'] ?? 'unknown';
$strategy = $preflight['estimated_strategy'] ?? 'sync';
$est_sec = $preflight['estimated_sec'] ?? 0;
$groups = $preflight['groups'] ?? array();
$state = $status['state'] ?? 'idle';
$active = in_array( $state, array( 'running', 'paused' ), true );
$completed = 'completed' === $state;
$failed = 'failed' === $state;
?>
<div class="wpdo-migration-wizard" data-state="<?php echo esc_attr( $state ); ?>">
<header class="wpdo-mw-header">
<h2><?php esc_html_e( 'User Entity 遷移精靈', '2meet-data-optimizer' ); ?></h2>
<p class="description">
<?php esc_html_e( '把 wp_usermeta 中已註冊為 entity field 的 legacy keys 一次遷移到 flat tables。流程包含自動備份、demote、bulk SQL backfill、shadow_read 驗證、aeav_only cutover、以及最終的 EAV 殘留清除。', '2meet-data-optimizer' ); ?>
</p>
</header>
<!-- Pre-flight diagnostic panel ─────────────────────────────────────── -->
<section class="wpdo-mw-panel wpdo-mw-preflight" id="wpdo-mw-preflight">
<h3><?php esc_html_e( '當前狀態', '2meet-data-optimizer' ); ?></h3>
<div class="wpdo-mw-metrics">
<div class="wpdo-mw-metric">
<span class="wpdo-mw-metric-label"><?php esc_html_e( 'wp_users', '2meet-data-optimizer' ); ?></span>
<span class="wpdo-mw-metric-value" id="wpdo-mw-users"><?php echo esc_html( number_format_i18n( $users_count ) ); ?></span>
</div>
<div class="wpdo-mw-metric">
<span class="wpdo-mw-metric-label"><?php esc_html_e( 'wp_usermeta', '2meet-data-optimizer' ); ?></span>
<span class="wpdo-mw-metric-value" id="wpdo-mw-usermeta"><?php echo esc_html( number_format_i18n( $usermeta_count ) ); ?></span>
</div>
<div class="wpdo-mw-metric wpdo-mw-metric-primary">
<span class="wpdo-mw-metric-label"><?php esc_html_e( '當前 ratio', '2meet-data-optimizer' ); ?></span>
<span class="wpdo-mw-metric-value" id="wpdo-mw-ratio">1:<?php echo esc_html( (string) $ratio ); ?></span>
</div>
<div class="wpdo-mw-metric">
<span class="wpdo-mw-metric-label"><?php esc_html_e( 'EAV 殘留', '2meet-data-optimizer' ); ?></span>
<span class="wpdo-mw-metric-value" id="wpdo-mw-residue"><?php echo esc_html( number_format_i18n( $eav_residue ) ); ?></span>
</div>
<div class="wpdo-mw-metric">
<span class="wpdo-mw-metric-label"><?php esc_html_e( 'user mode', '2meet-data-optimizer' ); ?></span>
<span class="wpdo-mw-metric-value wpdo-mw-mode wpdo-mw-mode-<?php echo esc_attr( $current_mode ); ?>"><?php echo esc_html( $current_mode ); ?></span>
</div>
</div>
<details class="wpdo-mw-groups">
<summary><?php esc_html_e( '各 entity group 詳細狀態', '2meet-data-optimizer' ); ?></summary>
<table class="widefat striped">
<thead>
<tr>
<th><?php esc_html_e( 'Group', '2meet-data-optimizer' ); ?></th>
<th><?php esc_html_e( 'Keys', '2meet-data-optimizer' ); ?></th>
<th><?php esc_html_e( 'Flat rows', '2meet-data-optimizer' ); ?></th>
<th><?php esc_html_e( 'EAV 殘留', '2meet-data-optimizer' ); ?></th>
<th><?php esc_html_e( 'Backfill 策略', '2meet-data-optimizer' ); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ( $groups as $name => $g ) : ?>
<tr>
<td><code><?php echo esc_html( $name ); ?></code></td>
<td><?php echo esc_html( (string) count( $g['keys'] ) ); ?></td>
<td><?php echo esc_html( (string) $g['flat_rows'] ); ?></td>
<td>
<?php if ( $g['residue'] > 0 ) : ?>
<strong><?php echo esc_html( (string) $g['residue'] ); ?></strong>
<?php else : ?>
<span class="wpdo-mw-zero">0</span>
<?php endif; ?>
</td>
<td><code><?php echo esc_html( $g['strategy'] ); ?></code></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</details>
</section>
<!-- Run-migration panel (idle state) ──────────────────────────────── -->
<section class="wpdo-mw-panel wpdo-mw-run" id="wpdo-mw-run-panel"
<?php echo $active || $completed ? 'hidden' : ''; ?>>
<?php if ( 0 === $eav_residue && 'aeav_only' === $current_mode ) : ?>
<div class="wpdo-mw-nothing-todo">
<h3>✅ <?php esc_html_e( '無事可做', '2meet-data-optimizer' ); ?></h3>
<p><?php esc_html_e( '所有 entity group 已 aeav_only / 0 EAV 殘留。', '2meet-data-optimizer' ); ?></p>
<p>
<?php
echo esc_html(
sprintf(
/* translators: %s: ratio */
__( '當前 ratio1:%s', '2meet-data-optimizer' ),
(string) $ratio
)
);
?>
</p>
</div>
<?php else : ?>
<h3><?php esc_html_e( '執行一鍵遷移', '2meet-data-optimizer' ); ?></h3>
<div class="wpdo-mw-estimate">
<?php
printf(
/* translators: 1: rows count, 2: strategy, 3: estimated seconds */
esc_html__( '將遷移 %1$s 行 EAV 殘留,預估策略:%2$s(約 %3$s 秒)。', '2meet-data-optimizer' ),
'<strong>' . esc_html( number_format_i18n( $eav_residue ) ) . '</strong>',
'<code>' . esc_html( $strategy ) . '</code>',
'<strong>' . esc_html( (string) $est_sec ) . '</strong>'
);
?>
</div>
<fieldset class="wpdo-mw-options">
<legend><?php esc_html_e( '進階選項', '2meet-data-optimizer' ); ?></legend>
<label>
<input type="checkbox" id="wpdo-mw-opt-backup" checked>
<?php esc_html_e( '自動備份 wp_usermeta 至 uploads/wpdo-backups/(推薦)', '2meet-data-optimizer' ); ?>
</label>
<label>
<input type="checkbox" id="wpdo-mw-opt-strict" checked>
<?php esc_html_e( '嚴格驗證(500 users / 10% 抽樣)', '2meet-data-optimizer' ); ?>
</label>
<label>
<input type="checkbox" id="wpdo-mw-opt-24h">
<?php esc_html_e( '24 小時 shadow_read 觀察視窗(生產環境用,會暫停 24h 才繼續)', '2meet-data-optimizer' ); ?>
</label>
<label>
<input type="checkbox" id="wpdo-mw-opt-async">
<?php esc_html_e( '強制 asynccron 推進,不阻塞 HTTP 請求)', '2meet-data-optimizer' ); ?>
</label>
<label>
<input type="checkbox" id="wpdo-mw-opt-dryrun">
<?php esc_html_e( 'Dry-run(不寫入、不刪除,僅模擬流程)', '2meet-data-optimizer' ); ?>
</label>
</fieldset>
<div class="wpdo-mw-confirm">
<label>
<input type="checkbox" id="wpdo-mw-confirm-backup" required>
<?php esc_html_e( '我已了解:流程將從 wp_usermeta 刪除已註冊欄位的 EAV 行,過程不可逆(自動備份提供救援路徑)。', '2meet-data-optimizer' ); ?>
</label>
</div>
<button type="button" class="button button-primary button-hero" id="wpdo-mw-start" disabled>
🚀 <?php esc_html_e( '開始一鍵遷移', '2meet-data-optimizer' ); ?>
</button>
<?php endif; ?>
</section>
<!-- Live progress panel (running state) ───────────────────────────── -->
<section class="wpdo-mw-panel wpdo-mw-progress" id="wpdo-mw-progress-panel"
<?php echo $active || $failed ? '' : 'hidden'; ?>>
<h3 id="wpdo-mw-progress-title"><?php esc_html_e( '執行中…', '2meet-data-optimizer' ); ?></h3>
<div class="wpdo-mw-progress-bar">
<div class="wpdo-mw-progress-fill" id="wpdo-mw-progress-fill"
style="width:<?php echo esc_attr( (string) ( $status['overall_progress'] ?? 0 ) ); ?>%"></div>
<span class="wpdo-mw-progress-pct" id="wpdo-mw-progress-pct"><?php echo esc_html( (string) ( $status['overall_progress'] ?? 0 ) ); ?>%</span>
</div>
<div class="wpdo-mw-current">
<span class="wpdo-mw-current-phase">
<?php esc_html_e( '當前階段:', '2meet-data-optimizer' ); ?>
<code id="wpdo-mw-current-phase-name"><?php echo esc_html( $status['phase'] ?? 'idle' ); ?></code>
</span>
<span class="wpdo-mw-elapsed" id="wpdo-mw-elapsed">0.0s</span>
</div>
<div class="wpdo-mw-live-ratio">
<span><?php esc_html_e( 'Live ratio', '2meet-data-optimizer' ); ?></span>
<span id="wpdo-mw-live-ratio-text">
1:<?php echo esc_html( (string) ( $status['metrics']['ratio_start'] ?? $ratio ) ); ?>
1:<?php echo esc_html( (string) ( $status['metrics']['ratio_now'] ?? $ratio ) ); ?>
</span>
</div>
<div class="wpdo-mw-log-wrap">
<h4><?php esc_html_e( 'Log', '2meet-data-optimizer' ); ?></h4>
<pre class="wpdo-mw-log" id="wpdo-mw-log"><?php echo esc_html( implode( "\n", (array) ( $status['log'] ?? array() ) ) ); ?></pre>
</div>
<div class="wpdo-mw-actions">
<button type="button" class="button" id="wpdo-mw-cancel"><?php esc_html_e( '取消', '2meet-data-optimizer' ); ?></button>
<button type="button" class="button button-primary" id="wpdo-mw-resume" hidden><?php esc_html_e( 'Resume', '2meet-data-optimizer' ); ?></button>
</div>
</section>
<!-- Completed state ───────────────────────────────────────────────── -->
<section class="wpdo-mw-panel wpdo-mw-done" id="wpdo-mw-done-panel"
<?php echo $completed ? '' : 'hidden'; ?>>
<h3>✅ <?php esc_html_e( '遷移完成', '2meet-data-optimizer' ); ?></h3>
<div class="wpdo-mw-done-summary" id="wpdo-mw-done-summary"></div>
<button type="button" class="button" id="wpdo-mw-reset"><?php esc_html_e( '回到診斷頁', '2meet-data-optimizer' ); ?></button>
</section>
</div>