add daily paid view

This commit is contained in:
2026-05-07 14:21:30 +08:00
parent 785726caac
commit 597d8ae948
2 changed files with 138 additions and 0 deletions
@@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
use Hyperf\DbConnection\Db;
use Hyperf\Database\Migrations\Migration;
return new class extends Migration
{
public function up(): void
{
// PG 原生物化视图:按付款日聚合(仅已付订单)。
// 不带 WITH (timescaledb.continuous)TimescaleDB 连续聚合的 time_bucket
// 必须基于 hypertable 主时间列(created_date),无法对 paid_date 分桶。
// WITH NO DATA:避免 migrate 时长时间锁;首次填充由 P22.3 回填命令执行。
Db::statement(<<<'SQL'
CREATE MATERIALIZED VIEW orders_daily_by_paid AS
SELECT
paid_date::date AS day,
company_id,
platform_id,
store_id,
COUNT(*) AS paid_orders,
SUM(total_amount) AS sum_total_amount,
SUM(total_paid) AS sum_total_paid,
SUM(total_received) AS sum_total_received,
AVG(total_paid) AS avg_paid_amount,
SUM(freight_fee) AS sum_freight_fee,
SUM(tax_fee) AS sum_tax_fee,
SUM(commission_fee) AS sum_commission_fee,
SUM(discount_fee) AS sum_discount_fee
FROM orders
WHERE paid_date IS NOT NULL
GROUP BY day, company_id, platform_id, store_id
WITH NO DATA;
SQL);
// UNIQUE 索引:REFRESH MATERIALIZED VIEW CONCURRENTLY 的前置条件,缺失会直接报错。
Db::statement('CREATE UNIQUE INDEX idx_orders_daily_by_paid_unique ON orders_daily_by_paid (day, company_id, platform_id, store_id)');
// 5 复合索引:覆盖"最近 N 日按 X 维度"与"指定 X 全历史时间序列"两类高频查询。
Db::statement('CREATE INDEX idx_orders_daily_by_paid_day_company ON orders_daily_by_paid (day DESC, company_id)');
Db::statement('CREATE INDEX idx_orders_daily_by_paid_day_platform ON orders_daily_by_paid (day DESC, platform_id)');
Db::statement('CREATE INDEX idx_orders_daily_by_paid_day_store ON orders_daily_by_paid (day DESC, store_id)');
Db::statement('CREATE INDEX idx_orders_daily_by_paid_company_day ON orders_daily_by_paid (company_id, day DESC)');
Db::statement('CREATE INDEX idx_orders_daily_by_paid_store_day ON orders_daily_by_paid (store_id, day DESC)');
// by_created 连续聚合自动刷新策略:每小时刷新近 3 天水位线,1 小时写入缓冲避免与热数据冲突。
// 3 天前的修改由 Stage 23 aggregate_refresh_queue 兜底。
Db::statement(<<<'SQL'
SELECT add_continuous_aggregate_policy(
'orders_daily_by_created',
start_offset => INTERVAL '3 days',
end_offset => INTERVAL '1 hour',
schedule_interval => INTERVAL '1 hour'
);
SQL);
}
public function down(): void
{
// 先 remove policy,再 drop 视图 CASCADE。
// if_exists => true 处理 by_created 视图已被 P22.1 down CASCADE 间接带走的情况。
Db::statement("SELECT remove_continuous_aggregate_policy('orders_daily_by_created', if_exists => true)");
// CASCADE 一并 drop UNIQUE 索引和 5 复合索引。
Db::statement('DROP MATERIALIZED VIEW IF EXISTS orders_daily_by_paid CASCADE');
}
};