forked from niuniu/llm-intelligence
feat(imports): add real pricing and subscription collectors
Add plan catalog and subscription schema support, seed baselines, and real importers for core domestic subscriptions plus stable official pricing sources. This commit also hardens the shared fetch layers so the importers can support live collection and database writes instead of relying on manual placeholders alone.
This commit is contained in:
61
db/migrations/008_plan_catalog_inventory.sql
Normal file
61
db/migrations/008_plan_catalog_inventory.sql
Normal file
@@ -0,0 +1,61 @@
|
||||
-- Phase 2: Token Plan / Coding Plan 基础目录清单
|
||||
|
||||
CREATE TABLE IF NOT EXISTS plan_catalog_inventory (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
provider_id BIGINT REFERENCES model_provider(id) ON DELETE SET NULL,
|
||||
operator_id BIGINT REFERENCES operator(id) ON DELETE SET NULL,
|
||||
catalog_code TEXT NOT NULL UNIQUE,
|
||||
platform_name TEXT NOT NULL,
|
||||
platform_name_cn TEXT,
|
||||
platform_type TEXT NOT NULL,
|
||||
plan_family TEXT NOT NULL,
|
||||
plan_status TEXT NOT NULL DEFAULT 'confirmed',
|
||||
source_url TEXT NOT NULL,
|
||||
source_title TEXT,
|
||||
source_kind TEXT NOT NULL DEFAULT 'official_doc',
|
||||
region TEXT NOT NULL DEFAULT 'global',
|
||||
currency TEXT,
|
||||
billing_cycle TEXT,
|
||||
last_checked_at TIMESTAMP NOT NULL,
|
||||
importer_key TEXT,
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by TEXT DEFAULT 'system',
|
||||
updated_by TEXT DEFAULT 'system',
|
||||
CONSTRAINT chk_plan_catalog_platform_type
|
||||
CHECK (platform_type IN ('official_vendor', 'cloud_operator', 'relay_platform')),
|
||||
CONSTRAINT chk_plan_catalog_family
|
||||
CHECK (plan_family IN ('token_plan', 'coding_plan', 'package_plan', 'pay_as_you_go', 'unknown')),
|
||||
CONSTRAINT chk_plan_catalog_status
|
||||
CHECK (plan_status IN ('confirmed', 'pending_verification', 'retired')),
|
||||
CONSTRAINT chk_plan_catalog_source_kind
|
||||
CHECK (source_kind IN ('official_doc', 'official_pricing', 'official_product_page', 'official_community', 'inferred')),
|
||||
CONSTRAINT chk_plan_catalog_currency
|
||||
CHECK (currency IS NULL OR currency IN ('CNY', 'USD', 'EUR'))
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_plan_catalog_provider_id ON plan_catalog_inventory(provider_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_plan_catalog_operator_id ON plan_catalog_inventory(operator_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_plan_catalog_family ON plan_catalog_inventory(plan_family);
|
||||
CREATE INDEX IF NOT EXISTS idx_plan_catalog_platform_type ON plan_catalog_inventory(platform_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_plan_catalog_status ON plan_catalog_inventory(plan_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_plan_catalog_last_checked_at ON plan_catalog_inventory(last_checked_at);
|
||||
|
||||
COMMENT ON TABLE plan_catalog_inventory IS 'Token Plan / Coding Plan / 套餐包 / 按量计费基础目录清单,用于后续 importer 排期与证据管理';
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_proc WHERE proname = 'update_updated_at_column')
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_trigger
|
||||
WHERE tgname = 'plan_catalog_inventory_updated_at'
|
||||
) THEN
|
||||
CREATE TRIGGER plan_catalog_inventory_updated_at
|
||||
BEFORE UPDATE ON plan_catalog_inventory
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
35
db/migrations/009_plan_catalog_inventory_segments.sql
Normal file
35
db/migrations/009_plan_catalog_inventory_segments.sql
Normal file
@@ -0,0 +1,35 @@
|
||||
-- Phase 2: 基础目录增加榜单分组与排名信息
|
||||
|
||||
ALTER TABLE plan_catalog_inventory
|
||||
ADD COLUMN IF NOT EXISTS catalog_segment TEXT NOT NULL DEFAULT 'general',
|
||||
ADD COLUMN IF NOT EXISTS market_rank INTEGER;
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'chk_plan_catalog_segment'
|
||||
) THEN
|
||||
ALTER TABLE plan_catalog_inventory
|
||||
ADD CONSTRAINT chk_plan_catalog_segment
|
||||
CHECK (catalog_segment IN ('general', 'vendor_top20', 'relay_top20plus', 'global_reference'));
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'chk_plan_catalog_market_rank'
|
||||
) THEN
|
||||
ALTER TABLE plan_catalog_inventory
|
||||
ADD CONSTRAINT chk_plan_catalog_market_rank
|
||||
CHECK (market_rank IS NULL OR market_rank > 0);
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_plan_catalog_segment ON plan_catalog_inventory(catalog_segment);
|
||||
CREATE INDEX IF NOT EXISTS idx_plan_catalog_market_rank ON plan_catalog_inventory(market_rank);
|
||||
|
||||
COMMENT ON COLUMN plan_catalog_inventory.catalog_segment IS '目录分组:general / vendor_top20 / relay_top20plus / global_reference';
|
||||
COMMENT ON COLUMN plan_catalog_inventory.market_rank IS '榜单排序,数字越小优先级越高';
|
||||
22
db/migrations/010_operator_type_column.sql
Normal file
22
db/migrations/010_operator_type_column.sql
Normal file
@@ -0,0 +1,22 @@
|
||||
-- 补齐 operator.type 字段,避免订阅与目录 importer 在新库中失败
|
||||
|
||||
ALTER TABLE operator
|
||||
ADD COLUMN IF NOT EXISTS type TEXT NOT NULL DEFAULT 'reseller';
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conname = 'chk_operator_type'
|
||||
) THEN
|
||||
ALTER TABLE operator
|
||||
ADD CONSTRAINT chk_operator_type
|
||||
CHECK (type IN ('official', 'cloud', 'relay', 'reseller'));
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_operator_type ON operator(type);
|
||||
|
||||
COMMENT ON COLUMN operator.type IS '运营方类型:official / cloud / relay / reseller';
|
||||
8
db/migrations/011_subscription_plan_package_family.sql
Normal file
8
db/migrations/011_subscription_plan_package_family.sql
Normal file
@@ -0,0 +1,8 @@
|
||||
-- Phase 2: 订阅套餐表支持 package_plan
|
||||
|
||||
ALTER TABLE subscription_plan
|
||||
DROP CONSTRAINT IF EXISTS subscription_plan_plan_family_check;
|
||||
|
||||
ALTER TABLE subscription_plan
|
||||
ADD CONSTRAINT subscription_plan_plan_family_check
|
||||
CHECK (plan_family IN ('token_plan', 'coding_plan', 'package_plan'));
|
||||
41
db/migrations/012_daily_signal_snapshot.sql
Normal file
41
db/migrations/012_daily_signal_snapshot.sql
Normal file
@@ -0,0 +1,41 @@
|
||||
-- 第一模块:每日关键信号快照
|
||||
|
||||
CREATE TABLE IF NOT EXISTS daily_signal_snapshot (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
signal_date DATE NOT NULL UNIQUE,
|
||||
status TEXT NOT NULL DEFAULT 'generated',
|
||||
new_models INTEGER NOT NULL DEFAULT 0,
|
||||
price_changes INTEGER NOT NULL DEFAULT 0,
|
||||
official_free INTEGER NOT NULL DEFAULT 0,
|
||||
aggregator_free INTEGER NOT NULL DEFAULT 0,
|
||||
unknown_free INTEGER NOT NULL DEFAULT 0,
|
||||
event_count INTEGER NOT NULL DEFAULT 0,
|
||||
page_mode TEXT NOT NULL DEFAULT 'standard',
|
||||
event_type_counts JSONB NOT NULL DEFAULT '{}'::jsonb,
|
||||
top_events JSONB NOT NULL DEFAULT '[]'::jsonb,
|
||||
source_audit TEXT,
|
||||
generated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_daily_signal_snapshot_date ON daily_signal_snapshot(signal_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_daily_signal_snapshot_status ON daily_signal_snapshot(status);
|
||||
|
||||
COMMENT ON TABLE daily_signal_snapshot IS '第一模块产出的每日关键信号快照,用于日报与其他下游形态消费';
|
||||
COMMENT ON COLUMN daily_signal_snapshot.top_events IS '已筛选的关键事件数组,JSONB 序列化 ModelEvent';
|
||||
COMMENT ON COLUMN daily_signal_snapshot.event_type_counts IS '按事件类型聚合的数量统计';
|
||||
|
||||
DO $$
|
||||
BEGIN
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_trigger
|
||||
WHERE tgname = 'daily_signal_snapshot_updated_at'
|
||||
) THEN
|
||||
CREATE TRIGGER daily_signal_snapshot_updated_at
|
||||
BEFORE UPDATE ON daily_signal_snapshot
|
||||
FOR EACH ROW
|
||||
EXECUTE FUNCTION update_updated_at_column();
|
||||
END IF;
|
||||
END
|
||||
$$;
|
||||
@@ -0,0 +1,21 @@
|
||||
DO $$
|
||||
BEGIN
|
||||
IF EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'chk_models_date_source_kind') THEN
|
||||
ALTER TABLE models DROP CONSTRAINT chk_models_date_source_kind;
|
||||
END IF;
|
||||
|
||||
ALTER TABLE models
|
||||
ADD CONSTRAINT chk_models_date_source_kind
|
||||
CHECK (
|
||||
date_source_kind IN (
|
||||
'official_announcement',
|
||||
'official_product_page',
|
||||
'official_pricing',
|
||||
'secondary_authoritative_report',
|
||||
'catalog_backfill',
|
||||
'unknown'
|
||||
)
|
||||
);
|
||||
END $$;
|
||||
|
||||
COMMENT ON COLUMN models.date_source_kind IS '发布日期证据来源类型:official_announcement / official_product_page / official_pricing / secondary_authoritative_report / catalog_backfill / unknown';
|
||||
Reference in New Issue
Block a user