Files
ewoooc/migrations/044_external_market_offer_normalization.sql
OoO 9260cc1740
All checks were successful
CD Pipeline / deploy (push) Successful in 1m4s
V10.607 建立外部市場來源正規化層
2026-06-15 16:19:03 +08:00

176 lines
6.7 KiB
SQL

-- =============================================================================
-- Migration 044: external market source / offer normalization
-- MOMO PRO / PChome revenue growth automation
-- 2026-06-15 Taipei
-- =============================================================================
-- Notes:
-- Additive only. This migration does not drop, truncate, or rewrite existing
-- competitor_prices / competitor_price_history / market_* tables.
-- =============================================================================
CREATE TABLE IF NOT EXISTS external_market_sources (
id BIGSERIAL PRIMARY KEY,
code VARCHAR(80) NOT NULL UNIQUE,
display_name VARCHAR(160) NOT NULL,
platform_code VARCHAR(80) NOT NULL,
source_kind VARCHAR(60) NOT NULL,
status VARCHAR(40) NOT NULL DEFAULT 'paused',
enabled BOOLEAN NOT NULL DEFAULT FALSE,
write_enabled BOOLEAN NOT NULL DEFAULT FALSE,
allowed_input_methods_json TEXT,
quality_policy_json TEXT,
plain_note TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_external_market_sources_code
ON external_market_sources (code);
CREATE INDEX IF NOT EXISTS idx_external_market_sources_platform_code
ON external_market_sources (platform_code);
CREATE INDEX IF NOT EXISTS idx_external_market_sources_source_kind
ON external_market_sources (source_kind);
CREATE INDEX IF NOT EXISTS idx_external_market_sources_status
ON external_market_sources (status, enabled);
CREATE TABLE IF NOT EXISTS external_offers (
id BIGSERIAL PRIMARY KEY,
source_code VARCHAR(80) NOT NULL REFERENCES external_market_sources(code),
platform_code VARCHAR(80) NOT NULL,
source_product_id VARCHAR(220) NOT NULL,
source_offer_key VARCHAR(260) NOT NULL,
title TEXT NOT NULL,
brand VARCHAR(180),
category_text VARCHAR(320),
product_url TEXT,
image_url TEXT,
price DOUBLE PRECISION,
original_price DOUBLE PRECISION,
currency VARCHAR(12) NOT NULL DEFAULT 'TWD',
stock_status VARCHAR(80),
sold_count INTEGER,
rating DOUBLE PRECISION,
review_count INTEGER,
observed_at TIMESTAMP NOT NULL DEFAULT NOW(),
expires_at TIMESTAMP,
ingestion_method VARCHAR(60) NOT NULL,
connector_key VARCHAR(120),
pchome_product_id VARCHAR(120),
momo_sku VARCHAR(80),
match_status VARCHAR(40) NOT NULL DEFAULT 'unmatched',
quality_score DOUBLE PRECISION NOT NULL DEFAULT 0,
data_quality_status VARCHAR(40) NOT NULL DEFAULT 'needs_review',
quality_notes_json TEXT,
raw_payload_json TEXT,
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP NOT NULL DEFAULT NOW(),
CONSTRAINT uq_external_offer_source_product_observed
UNIQUE (source_code, source_product_id, observed_at, ingestion_method)
);
CREATE INDEX IF NOT EXISTS idx_external_offers_source_code
ON external_offers (source_code);
CREATE INDEX IF NOT EXISTS idx_external_offers_platform_code
ON external_offers (platform_code);
CREATE INDEX IF NOT EXISTS idx_external_offers_source_product_id
ON external_offers (source_product_id);
CREATE INDEX IF NOT EXISTS idx_external_offers_brand
ON external_offers (brand);
CREATE INDEX IF NOT EXISTS idx_external_offers_category_text
ON external_offers (category_text);
CREATE INDEX IF NOT EXISTS idx_external_offers_stock_status
ON external_offers (stock_status);
CREATE INDEX IF NOT EXISTS idx_external_offers_observed_at
ON external_offers (observed_at);
CREATE INDEX IF NOT EXISTS idx_external_offers_expires_at
ON external_offers (expires_at);
CREATE INDEX IF NOT EXISTS idx_external_offers_ingestion_method
ON external_offers (ingestion_method);
CREATE INDEX IF NOT EXISTS idx_external_offers_connector_key
ON external_offers (connector_key);
CREATE INDEX IF NOT EXISTS idx_external_offers_pchome_product_id
ON external_offers (pchome_product_id);
CREATE INDEX IF NOT EXISTS idx_external_offers_momo_sku
ON external_offers (momo_sku);
CREATE INDEX IF NOT EXISTS idx_external_offers_match_status
ON external_offers (match_status);
CREATE INDEX IF NOT EXISTS idx_external_offers_data_quality_status
ON external_offers (data_quality_status);
CREATE INDEX IF NOT EXISTS idx_external_offers_source_seen
ON external_offers (source_code, observed_at);
CREATE INDEX IF NOT EXISTS idx_external_offers_platform_product
ON external_offers (platform_code, source_product_id);
CREATE INDEX IF NOT EXISTS idx_external_offers_pchome_product
ON external_offers (pchome_product_id, source_code);
CREATE INDEX IF NOT EXISTS idx_external_offers_match_quality
ON external_offers (match_status, data_quality_status, quality_score);
INSERT INTO external_market_sources (
code,
display_name,
platform_code,
source_kind,
status,
enabled,
write_enabled,
allowed_input_methods_json,
quality_policy_json,
plain_note
) VALUES
(
'momo_reference',
'MOMO 外部價格參考',
'momo',
'legacy_bridge',
'active',
TRUE,
FALSE,
'["legacy_competitor_cache","manual_csv","provider_api"]',
'{"minimum_match_status":"verified","minimum_quality_score":76}',
'目前只採用已確認同款的 MOMO 參考價。'
),
(
'shopee',
'蝦皮',
'shopee',
'connector_contract',
'paused',
FALSE,
FALSE,
'["official_api","provider_api","manual_csv"]',
'{"minimum_match_status":"verified","manual_review_required":true}',
'先暫停,不進告警;未來可接官方 API、供應商資料或手動 CSV。'
),
(
'coupang',
'酷澎',
'coupang',
'connector_contract',
'paused',
FALSE,
FALSE,
'["official_api","provider_api","manual_csv"]',
'{"minimum_match_status":"verified","manual_review_required":true}',
'先暫停,不進告警;未來可接官方 API、供應商資料或手動 CSV。'
)
ON CONFLICT (code) DO UPDATE SET
display_name = EXCLUDED.display_name,
platform_code = EXCLUDED.platform_code,
source_kind = EXCLUDED.source_kind,
status = EXCLUDED.status,
enabled = EXCLUDED.enabled,
allowed_input_methods_json = EXCLUDED.allowed_input_methods_json,
quality_policy_json = EXCLUDED.quality_policy_json,
plain_note = EXCLUDED.plain_note,
updated_at = NOW();
GRANT ALL PRIVILEGES ON external_market_sources TO momo;
GRANT ALL PRIVILEGES ON external_offers TO momo;
GRANT USAGE, SELECT ON SEQUENCE external_market_sources_id_seq TO momo;
GRANT USAGE, SELECT ON SEQUENCE external_offers_id_seq TO momo;
DO $$
BEGIN
RAISE NOTICE 'Migration 044 complete: external market source and offer normalization is ready';
END $$;