From 6751c69bc221973fdcccf2ad8067f1e9cc32be5e Mon Sep 17 00:00:00 2001 From: wpdev Date: Fri, 31 Jul 2026 10:34:40 +0800 Subject: [PATCH] =?UTF-8?q?fix(boundary):=20=E6=A0=B8=E5=BF=83=E4=B8=8D?= =?UTF-8?q?=E5=BE=97=E7=84=A1=E5=AE=88=E8=A1=9B=E5=91=BC=E5=8F=AB=20AddOn?= =?UTF-8?q?=20=E7=9A=84=20TMDO=5FListing=5FStats?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 實機渲染時 Dashboard 分頁 fatal:"Class TMDO_Listing_Stats not found"。 該類別住在 hivepress-addon,核心有 6 處直呼,沒裝 AddOn 的站台會炸掉 Dashboard 與兩個 REST 端點(GET /listing/{id}、POST /listing/{id}/view)。 - TMDO_Zone_Warm 新增 VIEW_KEY / VIEW_TTL 常數(值與 AddOn 的 TMDO_Listing_Stats::VIEW_KEY 完全相同的 'wpdo_views',指向同一批列, 無資料遷移) - CLI benchmark 改用核心常數 - REST 兩個 handler 改走新的 read_view_count() / bump_view_count(): AddOn 在場時仍委派過去(保留 hp_view_count postmeta fallback), 否則核心自己讀寫 warm 列 - wp tmdo cleanup --archive-expired 加守衛,AddOn 缺席時印 warning 並跳過 --- cli/class-tmdo-cli.php | 19 +++++++----- includes/class-tmdo-rest-api.php | 39 +++++++++++++++++++++++-- includes/zones/class-tmdo-zone-warm.php | 13 +++++++++ 3 files changed, 61 insertions(+), 10 deletions(-) diff --git a/cli/class-tmdo-cli.php b/cli/class-tmdo-cli.php index 822d5f9..9048b53 100644 --- a/cli/class-tmdo-cli.php +++ b/cli/class-tmdo-cli.php @@ -798,12 +798,12 @@ class TMDO_CLI { if ( ! empty( $warm_posts ) ) { // Seed warm zone entries so reads are non-trivial. foreach ( $warm_posts as $pid ) { - TMDO_Zone_Warm::set( (int) $pid, TMDO_Listing_Stats::VIEW_KEY, '1', DAY_IN_SECONDS ); + TMDO_Zone_Warm::set( (int) $pid, TMDO_Zone_Warm::VIEW_KEY, '1', DAY_IN_SECONDS ); } $start = microtime( true ); foreach ( $warm_posts as $pid ) { - TMDO_Zone_Warm::get( (int) $pid, TMDO_Listing_Stats::VIEW_KEY ); + TMDO_Zone_Warm::get( (int) $pid, TMDO_Zone_Warm::VIEW_KEY ); } $warm_ms = ( microtime( true ) - $start ) * 1000; @@ -1148,12 +1148,17 @@ class TMDO_CLI { WP_CLI::log( "Deleted {$warm_deleted} expired warm entries." ); if ( $archive_expired ) { - WP_CLI::log( 'Archiving expired listing fields to Zone D...' ); - $archived = TMDO_Listing_Stats::archive_expired_listings(); - WP_CLI::log( "Archived {$archived} fields from expired listings." ); + // Listing archival is HivePress-specific and ships in that AddOn. + if ( ! class_exists( 'TMDO_Listing_Stats' ) ) { + WP_CLI::warning( '--archive-expired needs 2meet-data-optimizer-hivepress-addon; skipping.' ); + } else { + WP_CLI::log( 'Archiving expired listing fields to Zone D...' ); + $archived = TMDO_Listing_Stats::archive_expired_listings(); + WP_CLI::log( "Archived {$archived} fields from expired listings." ); - $stats = TMDO_Zone_Archive::stats(); - WP_CLI::log( "Zone D total: {$stats['total_rows']} rows, {$stats['compressed_rows']} compressed." ); + $stats = TMDO_Zone_Archive::stats(); + WP_CLI::log( "Zone D total: {$stats['total_rows']} rows, {$stats['compressed_rows']} compressed." ); + } } WP_CLI::success( 'Cleanup complete.' ); diff --git a/includes/class-tmdo-rest-api.php b/includes/class-tmdo-rest-api.php index bb34b14..e3616b0 100644 --- a/includes/class-tmdo-rest-api.php +++ b/includes/class-tmdo-rest-api.php @@ -646,7 +646,7 @@ class TMDO_REST_API { return $err; } - $views = TMDO_Listing_Stats::get_view_count( $post_id ); + $views = self::read_view_count( $post_id ); return new WP_REST_Response( array( @@ -730,8 +730,8 @@ class TMDO_REST_API { // Mark as counted for this IP/session. set_transient( $rl_key, 1, HOUR_IN_SECONDS ); - TMDO_Listing_Stats::increment_view( $post_id ); - $views = TMDO_Listing_Stats::get_view_count( $post_id ); + self::bump_view_count( $post_id ); + $views = self::read_view_count( $post_id ); $response = new WP_REST_Response( array( @@ -1642,4 +1642,37 @@ class TMDO_REST_API { $result = TMDO_Migration_Orchestrator::resume(); return new WP_REST_Response( $result, ! empty( $result['ok'] ) ? 200 : 409 ); } + + /** + * Read a post's view counter. + * + * Delegates to the HivePress AddOn when it is active, because that class + * adds an hp_view_count postmeta fallback for listings migrated before the + * warm zone existed. Without the AddOn — the normal case for a plain + * install — core reads its own warm row directly. Calling the AddOn class + * unconditionally used to fatal these endpoints on any site without it. + * + * @param int $post_id Post ID. + * @return int + */ + private static function read_view_count( int $post_id ): int { + if ( class_exists( 'TMDO_Listing_Stats' ) ) { + return (int) TMDO_Listing_Stats::get_view_count( $post_id ); + } + return (int) ( TMDO_Zone_Warm::get( $post_id, TMDO_Zone_Warm::VIEW_KEY ) ?? 0 ); + } + + /** + * Increment a post's view counter. + * + * @param int $post_id Post ID. + * @return void + */ + private static function bump_view_count( int $post_id ): void { + if ( class_exists( 'TMDO_Listing_Stats' ) ) { + TMDO_Listing_Stats::increment_view( $post_id ); + return; + } + TMDO_Zone_Warm::increment( $post_id, TMDO_Zone_Warm::VIEW_KEY, 1, TMDO_Zone_Warm::VIEW_TTL ); + } } diff --git a/includes/zones/class-tmdo-zone-warm.php b/includes/zones/class-tmdo-zone-warm.php index 43a0eeb..02d858b 100644 --- a/includes/zones/class-tmdo-zone-warm.php +++ b/includes/zones/class-tmdo-zone-warm.php @@ -24,6 +24,19 @@ if ( ! defined( 'ABSPATH' ) ) { */ class TMDO_Zone_Warm { + /** + * Warm key used for per-post view counters. + * + * Owned by core because the row lives in this zone's table and core's admin, + * CLI and REST layers all read it. The HivePress AddOn's + * TMDO_Listing_Stats::VIEW_KEY carries the identical literal, so the two + * address the same rows — nothing to migrate either way. + */ + const VIEW_KEY = 'wpdo_views'; + + /** TTL applied when core increments the view counter itself. */ + const VIEW_TTL = DAY_IN_SECONDS; + /** * Get the warm table name. */