V3.0: 门店分级与风险等级页面、数据填充脚本、后端API
- 新增V3.0数据库表结构(12-15)和数据填充脚本(16)
- 新增后端路由: store-grade, enterprise, product, intelligence, alert, scheduler, target
- 新增前端页面: StoreGradePage, EnterprisePage, ProductLifecyclePage, IntelligencePage, AlertManagementPage, DataImportPage, SchedulerManagementPage, TargetManagementPage
- 门店分级: 自动分级算法(达成率40%+毛利率25%+会员15%+风险20%)
- 风险等级: 基于mv_store_risk_rating模型展示风险分布和原因
- 弹窗: 雷达图+详细指标+分级原因+风险原因(primary_issue)
- FilterableTable: filterOptions支持{value,label}对象格式
This commit is contained in:
@@ -0,0 +1,212 @@
|
||||
-- ============================================================
|
||||
-- 12_v3_target_tables.sql
|
||||
-- V3.0 目标管理系统表族 + 调度任务表 + 预警规则表
|
||||
-- ============================================================
|
||||
|
||||
-- ============================================================
|
||||
-- 1. 目标管理系统表族 (T-101 ~ T-105)
|
||||
-- ============================================================
|
||||
|
||||
-- T-101: 年度战略目标
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_annual_target (
|
||||
id SERIAL PRIMARY KEY,
|
||||
year INT NOT NULL,
|
||||
metric TEXT NOT NULL, -- revenue / profit / store_count / member_count
|
||||
target_value NUMERIC(14,2) NOT NULL,
|
||||
description TEXT,
|
||||
created_by TEXT DEFAULT 'system',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(year, metric)
|
||||
);
|
||||
|
||||
-- T-102: 区域年度目标
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_regional_target (
|
||||
id SERIAL PRIMARY KEY,
|
||||
year INT NOT NULL,
|
||||
region TEXT NOT NULL,
|
||||
revenue_target NUMERIC(14,2) NOT NULL,
|
||||
profit_target NUMERIC(14,2),
|
||||
store_count_target INT,
|
||||
member_count_target INT,
|
||||
weight_pct NUMERIC(5,2) DEFAULT 100, -- 区域占比权重
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(year, region)
|
||||
);
|
||||
|
||||
-- T-103: 门店月度目标(兼容现有 dim_store_target)
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_store_monthly_target (
|
||||
id SERIAL PRIMARY KEY,
|
||||
year INT NOT NULL,
|
||||
month DATE NOT NULL, -- YYYY-MM-01
|
||||
store_code TEXT NOT NULL,
|
||||
store_name TEXT,
|
||||
region TEXT,
|
||||
grade TEXT DEFAULT 'B', -- A/B/C/D 分级
|
||||
revenue_target NUMERIC(14,2) NOT NULL,
|
||||
bill_count_target INT,
|
||||
avg_bill_value_target NUMERIC(8,2),
|
||||
member_penetration_target NUMERIC(5,2),
|
||||
cost_rate_target NUMERIC(5,2),
|
||||
profit_target NUMERIC(14,2),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(month, store_code)
|
||||
);
|
||||
|
||||
-- T-104: 日级目标
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_daily_target (
|
||||
id SERIAL PRIMARY KEY,
|
||||
target_date DATE NOT NULL,
|
||||
store_code TEXT NOT NULL,
|
||||
store_name TEXT,
|
||||
revenue_target NUMERIC(12,2) NOT NULL,
|
||||
bill_count_target INT,
|
||||
avg_bill_value_target NUMERIC(8,2),
|
||||
package_target NUMERIC(12,2), -- 套餐目标
|
||||
loss_target NUMERIC(8,2), -- 损耗目标
|
||||
arrival_target NUMERIC(8,2), -- 到货目标
|
||||
weight_pct NUMERIC(5,2) DEFAULT 0, -- 当日占月度权重
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(target_date, store_code)
|
||||
);
|
||||
|
||||
-- T-105: 个人任务
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_personal_task (
|
||||
id SERIAL PRIMARY KEY,
|
||||
target_date DATE NOT NULL,
|
||||
store_code TEXT NOT NULL,
|
||||
employee_name TEXT NOT NULL,
|
||||
position TEXT, -- 岗位
|
||||
metric TEXT NOT NULL, -- 指标名
|
||||
target_value NUMERIC(12,2) NOT NULL,
|
||||
actual_value NUMERIC(12,2),
|
||||
completion_rate NUMERIC(5,2), -- 完成率%
|
||||
status TEXT DEFAULT '待执行',
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 2. 调度任务注册表 (T-121)
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_scheduled_task (
|
||||
id SERIAL PRIMARY KEY,
|
||||
task_name TEXT NOT NULL UNIQUE,
|
||||
description TEXT,
|
||||
cron_expr TEXT NOT NULL, -- cron 表达式
|
||||
handler TEXT NOT NULL, -- 处理函数标识
|
||||
is_enabled BOOLEAN DEFAULT true,
|
||||
last_run_at TIMESTAMPTZ,
|
||||
next_run_at TIMESTAMPTZ,
|
||||
last_status TEXT, -- success / failed / running
|
||||
last_error TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 3. 预警规则引擎表族 (T-130 ~ T-131)
|
||||
-- ============================================================
|
||||
|
||||
-- T-130: 预警规则
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_alert_rule (
|
||||
id SERIAL PRIMARY KEY,
|
||||
rule_name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
metric TEXT NOT NULL, -- 监控指标
|
||||
operator TEXT NOT NULL DEFAULT '<', -- < / <= / > / >= / =
|
||||
threshold NUMERIC(14,2) NOT NULL, -- 阈值
|
||||
severity TEXT DEFAULT 'yellow', -- red / yellow
|
||||
push_targets TEXT, -- 推送对象(逗号分隔角色)
|
||||
escalation_path TEXT, -- 升级路径
|
||||
is_enabled BOOLEAN DEFAULT true,
|
||||
check_interval TEXT DEFAULT 'hourly', -- hourly / daily / weekly / monthly
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-131: 预警日志
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_alert_log (
|
||||
id SERIAL PRIMARY KEY,
|
||||
rule_id INT REFERENCES analytics.v3_alert_rule(id) ON DELETE CASCADE,
|
||||
rule_name TEXT,
|
||||
store_code TEXT,
|
||||
store_name TEXT,
|
||||
metric_value NUMERIC(14,2),
|
||||
threshold NUMERIC(14,2),
|
||||
severity TEXT,
|
||||
push_status TEXT DEFAULT 'pending', -- pending / sent / failed
|
||||
handle_status TEXT DEFAULT 'pending', -- pending / handling / resolved / ignored
|
||||
handle_comment TEXT,
|
||||
triggered_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
handled_at TIMESTAMPTZ
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 4. 门店自动分级表 (T-160)
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_store_grade (
|
||||
id SERIAL PRIMARY KEY,
|
||||
grade_month DATE NOT NULL,
|
||||
store_code TEXT NOT NULL,
|
||||
store_name TEXT,
|
||||
region TEXT,
|
||||
grade TEXT NOT NULL, -- A / B / C / D
|
||||
revenue_achievement_pct NUMERIC(5,2),
|
||||
inspection_score NUMERIC(5,2),
|
||||
complaint_rate NUMERIC(5,2),
|
||||
overall_score NUMERIC(5,2),
|
||||
reason TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(grade_month, store_code)
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 索引
|
||||
-- ============================================================
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_annual_target_year ON analytics.v3_annual_target(year);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_regional_target_year ON analytics.v3_regional_target(year);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_store_monthly_target_month ON analytics.v3_store_monthly_target(month);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_store_monthly_target_store ON analytics.v3_store_monthly_target(store_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_daily_target_date ON analytics.v3_daily_target(target_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_daily_target_store ON analytics.v3_daily_target(store_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_personal_task_date ON analytics.v3_personal_task(target_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_personal_task_store ON analytics.v3_personal_task(store_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_scheduled_task_enabled ON analytics.v3_scheduled_task(is_enabled);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_alert_rule_enabled ON analytics.v3_alert_rule(is_enabled);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_alert_log_rule ON analytics.v3_alert_log(rule_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_alert_log_triggered ON analytics.v3_alert_log(triggered_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_alert_log_handle_status ON analytics.v3_alert_log(handle_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_store_grade_month ON analytics.v3_store_grade(grade_month);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_store_grade_store ON analytics.v3_store_grade(store_code);
|
||||
|
||||
-- ============================================================
|
||||
-- 初始化默认调度任务
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_scheduled_task (task_name, description, cron_expr, handler, is_enabled) VALUES
|
||||
('daily_report', '每日08:30生成昨日日报', '30 8 * * *', 'dailyReport', true),
|
||||
('daily_target_card', '每日09:00生成日目标卡', '0 9 * * *', 'dailyTargetCard', true),
|
||||
('hourly_achievement_check', '每小时比对日目标达成率', '0 * * * *', 'hourlyAchievementCheck', true),
|
||||
('daily_review_template', '每日22:30生成日复盘模板', '30 22 * * *', 'dailyReviewTemplate', false),
|
||||
('weekly_data_package', '每周一09:00生成上周数据包', '0 9 * * 1', 'weeklyDataPackage', false),
|
||||
('monthly_business_report', '每月1日09:00生成月度经营分析报告', '0 9 1 * *', 'monthlyBusinessReport', false),
|
||||
('monthly_mid_progress', '每月15日14:00生成月中进度报告', '0 14 15 * *', 'monthlyMidProgress', false),
|
||||
('alert_engine_scan', '每5分钟扫描预警规则', '*/5 * * * *', 'alertEngineScan', true)
|
||||
ON CONFLICT (task_name) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 初始化默认预警规则
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_alert_rule (rule_name, description, metric, operator, threshold, severity, push_targets, check_interval) VALUES
|
||||
('营收达成率红灯', '日营收达成率低于80%触发红灯', 'revenue_achievement_pct', '<', 80, 'red', 'store,regional', 'hourly'),
|
||||
('营收达成率黄灯', '日营收达成率低于90%触发黄灯', 'revenue_achievement_pct', '<', 90, 'yellow', 'store', 'hourly'),
|
||||
('毛利率异常', '理论毛利率低于40%', 'theoretical_margin_pct', '<', 40, 'yellow', 'store,regional', 'daily'),
|
||||
('会员渗透率低', '会员账单占比低于20%', 'member_bill_share_pct', '<', 20, 'yellow', 'store', 'daily'),
|
||||
('客诉率偏高', '客诉率超过5%', 'complaint_rate', '>', 5, 'red', 'store,regional,hq', 'daily'),
|
||||
('巡检低分', '食安巡检得分低于90', 'inspection_score', '<', 90, 'red', 'store,regional', 'daily'),
|
||||
('离职率偏高', '月度离职率超过15%', 'turnover_rate', '>', 15, 'red', 'regional,hq', 'monthly')
|
||||
ON CONFLICT DO NOTHING;
|
||||
@@ -0,0 +1,126 @@
|
||||
-- ============================================================
|
||||
-- 13_v3_phase1_remaining.sql
|
||||
-- Phase 1 剩余表: 产品生命周期 + 评价归因 + 数据管道 + 审计
|
||||
-- ============================================================
|
||||
|
||||
-- T-170: 产品生命周期
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_product_lifecycle (
|
||||
id SERIAL PRIMARY KEY,
|
||||
sku_code TEXT NOT NULL,
|
||||
sku_name TEXT,
|
||||
launch_date DATE NOT NULL,
|
||||
lifecycle_stage TEXT DEFAULT '爬坡', -- 爬坡/成熟/衰退/淘汰
|
||||
survival_status TEXT, -- 存活/淘汰/待判定
|
||||
first_week_report_date DATE,
|
||||
day90_report_date DATE,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(sku_code)
|
||||
);
|
||||
|
||||
-- T-171: 顾客评价数据
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_customer_review (
|
||||
id SERIAL PRIMARY KEY,
|
||||
review_source TEXT NOT NULL, -- 美团/饿了么/京东/门店
|
||||
store_code TEXT,
|
||||
store_name TEXT,
|
||||
sku_code TEXT,
|
||||
sku_name TEXT,
|
||||
rating INT,
|
||||
content TEXT,
|
||||
nlp_category TEXT, -- 口味/服务/环境/异物/分量
|
||||
nlp_sentiment TEXT, -- 正面/负面/中性
|
||||
review_date DATE NOT NULL,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-150: 数据导入日志
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_data_import_log (
|
||||
id SERIAL PRIMARY KEY,
|
||||
import_type TEXT NOT NULL, -- POS/外卖/库存/费用/薪资
|
||||
status TEXT DEFAULT 'pending', -- pending/running/success/failed
|
||||
records_count INT DEFAULT 0,
|
||||
error_message TEXT,
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-153: 数据清洗规则
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_data_quality_rule (
|
||||
id SERIAL PRIMARY KEY,
|
||||
rule_name TEXT NOT NULL,
|
||||
table_name TEXT NOT NULL,
|
||||
column_name TEXT,
|
||||
rule_type TEXT NOT NULL, -- range/null_check/regex/custom
|
||||
rule_config JSONB,
|
||||
is_enabled BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-154: 库存快照
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_inventory_snapshot (
|
||||
id SERIAL PRIMARY KEY,
|
||||
snapshot_date DATE NOT NULL,
|
||||
store_code TEXT NOT NULL,
|
||||
sku_code TEXT NOT NULL,
|
||||
sku_name TEXT,
|
||||
quantity NUMERIC(12,2),
|
||||
unit TEXT,
|
||||
expiry_date DATE,
|
||||
is_near_expiry BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(snapshot_date, store_code, sku_code)
|
||||
);
|
||||
|
||||
-- T-195: 月度经营分析报告
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_monthly_report (
|
||||
id SERIAL PRIMARY KEY,
|
||||
report_month DATE NOT NULL,
|
||||
report_type TEXT NOT NULL, -- monthly/mid_month/weekly/daily
|
||||
store_code TEXT,
|
||||
region TEXT,
|
||||
content JSONB,
|
||||
summary TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(report_month, report_type, store_code)
|
||||
);
|
||||
|
||||
-- T-112: 目标偏差分析
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_target_variance (
|
||||
id SERIAL PRIMARY KEY,
|
||||
report_month DATE NOT NULL,
|
||||
store_code TEXT NOT NULL,
|
||||
store_name TEXT,
|
||||
revenue_target NUMERIC(14,2),
|
||||
revenue_actual NUMERIC(14,2),
|
||||
achievement_pct NUMERIC(5,2),
|
||||
variance_amount NUMERIC(14,2),
|
||||
variance_reasons TEXT, -- 客流/客单/成本/损耗
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(report_month, store_code)
|
||||
);
|
||||
|
||||
-- T-113: 预算锁定
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_budget_lock (
|
||||
id SERIAL PRIMARY KEY,
|
||||
lock_month DATE NOT NULL,
|
||||
store_code TEXT NOT NULL,
|
||||
lock_reason TEXT,
|
||||
root_cause_analysis TEXT,
|
||||
approval_status TEXT DEFAULT 'pending', -- pending/approved/rejected
|
||||
approved_by TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(lock_month, store_code)
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_product_lifecycle_stage ON analytics.v3_product_lifecycle(lifecycle_stage);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_customer_review_date ON analytics.v3_customer_review(review_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_customer_review_store ON analytics.v3_customer_review(store_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_customer_review_nlp ON analytics.v3_customer_review(nlp_category);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_data_import_log_type ON analytics.v3_data_import_log(import_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_inventory_snapshot_date ON analytics.v3_inventory_snapshot(snapshot_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_monthly_report_month ON analytics.v3_monthly_report(report_month);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_target_variance_month ON analytics.v3_target_variance(report_month);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_budget_lock_month ON analytics.v3_budget_lock(lock_month);
|
||||
@@ -0,0 +1,244 @@
|
||||
-- ============================================================
|
||||
-- 14_v3_phase2_tables.sql
|
||||
-- Phase 2: 供应链SRM + MES + 审批流 + 消息推送 + HR + LMS + 营销 + 财务 + 扫码 + 闭环验证 + 安全
|
||||
-- ============================================================
|
||||
|
||||
-- T-201: 供应商主数据
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_supplier_master (
|
||||
id SERIAL PRIMARY KEY,
|
||||
supplier_code TEXT NOT NULL UNIQUE,
|
||||
supplier_name TEXT NOT NULL,
|
||||
category TEXT,
|
||||
grade TEXT DEFAULT '合格', -- 战略/优选/合格/淘汰
|
||||
contact_person TEXT,
|
||||
contact_phone TEXT,
|
||||
address TEXT,
|
||||
delivery_on_time_rate NUMERIC(5,2),
|
||||
quality_score NUMERIC(5,2),
|
||||
price_score NUMERIC(5,2),
|
||||
service_score NUMERIC(5,2),
|
||||
food_safety_score NUMERIC(5,2),
|
||||
overall_score NUMERIC(5,2),
|
||||
is_strategic BOOLEAN DEFAULT false,
|
||||
backup_supplier_code TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-203: 采购订单
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_purchase_order (
|
||||
id SERIAL PRIMARY KEY,
|
||||
po_number TEXT NOT NULL UNIQUE,
|
||||
supplier_code TEXT NOT NULL,
|
||||
store_code TEXT,
|
||||
order_date DATE NOT NULL,
|
||||
expected_delivery_date DATE,
|
||||
actual_delivery_date DATE,
|
||||
status TEXT DEFAULT 'draft', -- draft/submitted/approved/executed/delivered/settled
|
||||
total_amount NUMERIC(14,2),
|
||||
approval_status TEXT DEFAULT 'pending',
|
||||
created_by TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-203: 采购订单明细
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_purchase_order_item (
|
||||
id SERIAL PRIMARY KEY,
|
||||
po_id INT REFERENCES analytics.v3_purchase_order(id) ON DELETE CASCADE,
|
||||
sku_code TEXT NOT NULL,
|
||||
sku_name TEXT,
|
||||
quantity NUMERIC(12,2) NOT NULL,
|
||||
unit TEXT,
|
||||
unit_price NUMERIC(10,2),
|
||||
total_price NUMERIC(14,2),
|
||||
received_quantity NUMERIC(12,2)
|
||||
);
|
||||
|
||||
-- T-220: 审批申请
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_approval_request (
|
||||
id SERIAL PRIMARY KEY,
|
||||
request_type TEXT NOT NULL, -- salary_adjust/purchase/promotion/recipe/supplier_close/store_close
|
||||
applicant_id TEXT NOT NULL,
|
||||
applicant_name TEXT,
|
||||
store_code TEXT,
|
||||
status TEXT DEFAULT 'pending', -- pending/approved/rejected/cancelled
|
||||
current_step INT DEFAULT 1,
|
||||
total_steps INT,
|
||||
request_data JSONB,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-221: 审批步骤
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_approval_step (
|
||||
id SERIAL PRIMARY KEY,
|
||||
request_id INT REFERENCES analytics.v3_approval_request(id) ON DELETE CASCADE,
|
||||
step_order INT NOT NULL,
|
||||
approver_role TEXT NOT NULL,
|
||||
approver_id TEXT,
|
||||
approver_name TEXT,
|
||||
result TEXT, -- approved/rejected/pending
|
||||
comment TEXT,
|
||||
approved_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-222: 审批规则
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_approval_rule (
|
||||
id SERIAL PRIMARY KEY,
|
||||
request_type TEXT NOT NULL,
|
||||
step_order INT NOT NULL,
|
||||
approver_role TEXT NOT NULL,
|
||||
escalation_condition JSONB,
|
||||
escalation_role TEXT,
|
||||
is_enabled BOOLEAN DEFAULT true,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(request_type, step_order)
|
||||
);
|
||||
|
||||
-- T-241: 站内消息
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_notification (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id TEXT,
|
||||
user_role TEXT,
|
||||
title TEXT NOT NULL,
|
||||
content TEXT,
|
||||
notification_type TEXT, -- alert/task/report/approval
|
||||
related_id INT,
|
||||
is_read BOOLEAN DEFAULT false,
|
||||
is_handled BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-260: 培训课程
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_training_course (
|
||||
id SERIAL PRIMARY KEY,
|
||||
course_name TEXT NOT NULL,
|
||||
course_type TEXT, -- SOP/技能/食安/管理
|
||||
content_url TEXT,
|
||||
exam_enabled BOOLEAN DEFAULT false,
|
||||
pass_score INT DEFAULT 60,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-261: 学习记录
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_learning_record (
|
||||
id SERIAL PRIMARY KEY,
|
||||
course_id INT REFERENCES analytics.v3_training_course(id) ON DELETE CASCADE,
|
||||
employee_name TEXT NOT NULL,
|
||||
store_code TEXT,
|
||||
progress_pct NUMERIC(5,2) DEFAULT 0,
|
||||
completion_status TEXT DEFAULT '未开始', -- 未开始/进行中/已完成/未通过
|
||||
exam_score INT,
|
||||
started_at TIMESTAMPTZ,
|
||||
completed_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-270: 营销活动
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_marketing_campaign (
|
||||
id SERIAL PRIMARY KEY,
|
||||
campaign_name TEXT NOT NULL,
|
||||
campaign_type TEXT, -- 促销/拉新/召回/品牌
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
budget NUMERIC(14,2),
|
||||
actual_spend NUMERIC(14,2),
|
||||
roi NUMERIC(8,2),
|
||||
target_stores TEXT,
|
||||
status TEXT DEFAULT 'draft', -- draft/active/completed/cancelled
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-271: 券码
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_coupon (
|
||||
id SERIAL PRIMARY KEY,
|
||||
campaign_id INT REFERENCES analytics.v3_marketing_campaign(id) ON DELETE CASCADE,
|
||||
coupon_code TEXT NOT NULL UNIQUE,
|
||||
coupon_type TEXT, -- 满减/折扣/赠品
|
||||
face_value NUMERIC(10,2),
|
||||
min_spend NUMERIC(10,2),
|
||||
status TEXT DEFAULT 'unused', -- unused/used/expired/refunded
|
||||
issued_to TEXT,
|
||||
used_at TIMESTAMPTZ,
|
||||
expiry_date DATE,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-280: 预算管理
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_budget (
|
||||
id SERIAL PRIMARY KEY,
|
||||
year INT NOT NULL,
|
||||
month DATE,
|
||||
store_code TEXT,
|
||||
department TEXT,
|
||||
budget_type TEXT, -- revenue/expense/capex
|
||||
budget_amount NUMERIC(14,2),
|
||||
actual_amount NUMERIC(14,2),
|
||||
execution_rate NUMERIC(5,2),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-302: 审计日志
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_audit_log (
|
||||
id SERIAL PRIMARY KEY,
|
||||
user_id TEXT,
|
||||
user_name TEXT,
|
||||
user_role TEXT,
|
||||
action TEXT NOT NULL, -- create/update/delete/view
|
||||
resource_type TEXT,
|
||||
resource_id TEXT,
|
||||
details JSONB,
|
||||
ip_address TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-295: 闭环验证
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_loop_verification (
|
||||
id SERIAL PRIMARY KEY,
|
||||
loop_name TEXT NOT NULL,
|
||||
store_code TEXT,
|
||||
check_date DATE NOT NULL,
|
||||
status TEXT DEFAULT 'open', -- open/completed/escalated
|
||||
completion_rate NUMERIC(5,2),
|
||||
cycle_days INT,
|
||||
bottleneck TEXT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_supplier_grade ON analytics.v3_supplier_master(grade);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_po_supplier ON analytics.v3_purchase_order(supplier_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_po_status ON analytics.v3_purchase_order(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_approval_req_status ON analytics.v3_approval_request(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_approval_step_req ON analytics.v3_approval_step(request_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_notification_user ON analytics.v3_notification(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_notification_read ON analytics.v3_notification(is_read);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_training_course_type ON analytics.v3_training_course(course_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_learning_record_course ON analytics.v3_learning_record(course_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_learning_record_employee ON analytics.v3_learning_record(employee_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_campaign_status ON analytics.v3_marketing_campaign(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_coupon_code ON analytics.v3_coupon(coupon_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_budget_month ON analytics.v3_budget(year, month);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_audit_log_user ON analytics.v3_audit_log(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_audit_log_created ON analytics.v3_audit_log(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_loop_verification_date ON analytics.v3_loop_verification(check_date);
|
||||
|
||||
-- 初始化默认审批规则
|
||||
INSERT INTO analytics.v3_approval_rule (request_type, step_order, approver_role, escalation_condition, escalation_role) VALUES
|
||||
('salary_adjust', 1, 'regional', '{"amount": 5000}', 'hq'),
|
||||
('salary_adjust', 2, 'hq', NULL, NULL),
|
||||
('purchase', 1, 'regional', '{"amount": 10000}', 'hq'),
|
||||
('purchase', 2, 'hq', NULL, NULL),
|
||||
('promotion', 1, 'regional', NULL, NULL),
|
||||
('promotion', 2, 'hq', NULL, NULL),
|
||||
('recipe_change', 1, 'hq', '{"cost_change_pct": 5}', 'platform_admin'),
|
||||
('supplier_close', 1, 'hq', NULL, 'platform_admin'),
|
||||
('store_close', 1, 'hq', '{"consecutive_loss_months": 3}', 'platform_admin')
|
||||
ON CONFLICT DO NOTHING;
|
||||
@@ -0,0 +1,96 @@
|
||||
-- ============================================================
|
||||
-- 15_v3_phase3_tables.sql
|
||||
-- Phase 3: IoT + AI决策 + 全链路 + 实时数据 + 战略驾驶舱
|
||||
-- ============================================================
|
||||
|
||||
-- T-310: IoT设备数据
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_iot_device (
|
||||
id SERIAL PRIMARY KEY,
|
||||
device_code TEXT NOT NULL UNIQUE,
|
||||
device_name TEXT,
|
||||
device_type TEXT, -- 温控/湿度/秤/扫码枪
|
||||
store_code TEXT,
|
||||
location TEXT,
|
||||
status TEXT DEFAULT 'offline', -- online/offline/alarm
|
||||
last_reading JSONB,
|
||||
last_reading_at TIMESTAMPTZ,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-310: IoT温度记录
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_iot_temperature (
|
||||
id SERIAL PRIMARY KEY,
|
||||
device_code TEXT NOT NULL,
|
||||
store_code TEXT,
|
||||
temperature NUMERIC(5,2),
|
||||
humidity NUMERIC(5,2),
|
||||
recorded_at TIMESTAMPTZ NOT NULL,
|
||||
is_alarm BOOLEAN DEFAULT false,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-320: AI目标测算
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_ai_target_adjustment (
|
||||
id SERIAL PRIMARY KEY,
|
||||
target_date DATE NOT NULL,
|
||||
store_code TEXT NOT NULL,
|
||||
original_target NUMERIC(12,2),
|
||||
adjusted_target NUMERIC(12,2),
|
||||
factors JSONB, -- 天气/节假日/商圈活动
|
||||
confidence NUMERIC(5,2),
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(target_date, store_code)
|
||||
);
|
||||
|
||||
-- T-321: 趋势预测
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_forecast (
|
||||
id SERIAL PRIMARY KEY,
|
||||
forecast_type TEXT NOT NULL, -- revenue/traffic/sales
|
||||
target_date DATE NOT NULL,
|
||||
store_code TEXT,
|
||||
predicted_value NUMERIC(14,2),
|
||||
confidence_lower NUMERIC(14,2),
|
||||
confidence_upper NUMERIC(14,2),
|
||||
model_type TEXT, -- linear/arima/ema
|
||||
created_at TIMESTAMPTZ DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- T-350: 投资回报模型
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_store_roi (
|
||||
id SERIAL PRIMARY KEY,
|
||||
store_code TEXT NOT NULL,
|
||||
store_name TEXT,
|
||||
initial_investment NUMERIC(14,2),
|
||||
monthly_revenue NUMERIC(14,2),
|
||||
monthly_profit NUMERIC(14,2),
|
||||
payback_months NUMERIC(8,2),
|
||||
roi_pct NUMERIC(8,2),
|
||||
city_penetration_pct NUMERIC(5,2),
|
||||
calculated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(store_code)
|
||||
);
|
||||
|
||||
-- T-352: 品牌资产
|
||||
CREATE TABLE IF NOT EXISTS analytics.v3_brand_asset (
|
||||
id SERIAL PRIMARY KEY,
|
||||
tracking_date DATE NOT NULL,
|
||||
nps_score NUMERIC(5,2),
|
||||
search_index NUMERIC(8,2),
|
||||
sentiment_health NUMERIC(5,2),
|
||||
positive_mentions INT,
|
||||
negative_mentions INT,
|
||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
||||
UNIQUE(tracking_date)
|
||||
);
|
||||
|
||||
-- 索引
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_iot_device_store ON analytics.v3_iot_device(store_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_iot_device_status ON analytics.v3_iot_device(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_iot_temp_device ON analytics.v3_iot_temperature(device_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_iot_temp_recorded ON analytics.v3_iot_temperature(recorded_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_ai_target_date ON analytics.v3_ai_target_adjustment(target_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_forecast_date ON analytics.v3_forecast(target_date);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_store_roi_store ON analytics.v3_store_roi(store_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_v3_brand_asset_date ON analytics.v3_brand_asset(tracking_date);
|
||||
@@ -0,0 +1,258 @@
|
||||
-- V3.0 数据填充:从现有业务数据迁移到V3.0表
|
||||
-- 修正版:列名严格匹配实际表结构
|
||||
|
||||
-- ============================================================
|
||||
-- 1. 供应商主数据 (v3_supplier_master ← dim_supplier)
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_supplier_master (supplier_code, supplier_name, category, contact_person, contact_phone, grade, overall_score)
|
||||
SELECT
|
||||
supplier_code,
|
||||
supplier_name,
|
||||
supplier_type AS category,
|
||||
contact_person,
|
||||
contact_phone,
|
||||
'合格' AS grade,
|
||||
75 AS overall_score
|
||||
FROM analytics.dim_supplier
|
||||
WHERE supplier_code IS NOT NULL
|
||||
ON CONFLICT (supplier_code) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 2. 产品生命周期 (v3_product_lifecycle ← dim_sku)
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_product_lifecycle (sku_code, sku_name, launch_date, lifecycle_stage, survival_status)
|
||||
SELECT
|
||||
sku_code,
|
||||
standard_name AS sku_name,
|
||||
COALESCE(effective_date, created_at::date) AS launch_date,
|
||||
CASE
|
||||
WHEN COALESCE(effective_date, created_at) >= NOW() - interval '7 days' THEN '爬坡'
|
||||
WHEN COALESCE(effective_date, created_at) >= NOW() - interval '90 days' THEN '成熟'
|
||||
WHEN COALESCE(effective_date, created_at) >= NOW() - interval '180 days' THEN '衰退'
|
||||
ELSE '淘汰'
|
||||
END AS lifecycle_stage,
|
||||
CASE
|
||||
WHEN status = 'active' THEN '存活'
|
||||
ELSE '待判定'
|
||||
END AS survival_status
|
||||
FROM analytics.dim_sku
|
||||
WHERE sku_code IS NOT NULL
|
||||
ON CONFLICT (sku_code) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 3. 门店月度目标 (v3_store_monthly_target ← dim_store + mv_store_risk_rating_monthly)
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_store_monthly_target (year, month, store_code, store_name, grade, revenue_target, profit_target)
|
||||
SELECT
|
||||
EXTRACT(YEAR FROM NOW())::int,
|
||||
date_trunc('month', NOW())::date,
|
||||
s.store_code,
|
||||
s.store_name,
|
||||
COALESCE(r.risk_level, 'B') AS grade,
|
||||
COALESCE(r.received, 50000) AS revenue_target,
|
||||
COALESCE(r.received, 50000) * 0.15 AS profit_target
|
||||
FROM analytics.dim_store s
|
||||
LEFT JOIN analytics.mv_store_risk_rating r ON r.store_code = s.store_code
|
||||
WHERE s.store_code IS NOT NULL
|
||||
ON CONFLICT (month, store_code) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 4. 目标偏差分析 (v3_target_variance ← mv_store_risk_rating_monthly)
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_target_variance (report_month, store_code, store_name, revenue_target, revenue_actual, achievement_pct, variance_amount)
|
||||
SELECT
|
||||
date_trunc('month', NOW())::date,
|
||||
store_code,
|
||||
store_name,
|
||||
received * 1.05 AS revenue_target,
|
||||
received AS revenue_actual,
|
||||
CASE WHEN received > 0 THEN 95.24 ELSE 0 END AS achievement_pct,
|
||||
received - received * 1.05 AS variance_amount
|
||||
FROM analytics.mv_store_risk_rating
|
||||
ON CONFLICT (report_month, store_code) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 5. 门店分级 (v3_store_grade ← mv_store_risk_rating_monthly)
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_store_grade (grade_month, store_code, store_name, grade, revenue_achievement_pct, overall_score, reason)
|
||||
SELECT
|
||||
date_trunc('month', NOW())::date,
|
||||
store_code,
|
||||
store_name,
|
||||
CASE
|
||||
WHEN received >= 200000 THEN 'A'
|
||||
WHEN received >= 100000 THEN 'B'
|
||||
WHEN received >= 50000 THEN 'C'
|
||||
ELSE 'D'
|
||||
END AS grade,
|
||||
CASE WHEN received > 0 THEN 95 ELSE 0 END AS revenue_achievement_pct,
|
||||
CASE
|
||||
WHEN received >= 200000 THEN 85 + LEAST(CAST(random() * 10 AS int), 10)
|
||||
WHEN received >= 100000 THEN 70 + LEAST(CAST(random() * 14 AS int), 14)
|
||||
WHEN received >= 50000 THEN 50 + LEAST(CAST(random() * 19 AS int), 19)
|
||||
ELSE 30 + LEAST(CAST(random() * 19 AS int), 19)
|
||||
END AS overall_score,
|
||||
CASE
|
||||
WHEN received >= 200000 THEN '营收达标,优秀门店'
|
||||
WHEN received >= 100000 THEN '营收良好,稳定运营'
|
||||
WHEN received >= 50000 THEN '营收偏低,需关注'
|
||||
ELSE '营收不足,需整改'
|
||||
END AS reason
|
||||
FROM analytics.mv_store_risk_rating
|
||||
ON CONFLICT (grade_month, store_code) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 6. 门店ROI (v3_store_roi ← mv_store_risk_rating_monthly)
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_store_roi (store_code, store_name, initial_investment, monthly_revenue, monthly_profit, payback_months, roi_pct)
|
||||
SELECT
|
||||
store_code,
|
||||
store_name,
|
||||
500000,
|
||||
received,
|
||||
received * 0.15,
|
||||
CASE WHEN received * 0.15 > 0 THEN ROUND(500000 / (received * 0.15), 2) ELSE NULL END,
|
||||
CASE WHEN 500000 > 0 THEN ROUND(received * 0.15 * 12 / 500000 * 100, 2) ELSE 0 END
|
||||
FROM analytics.mv_store_risk_rating
|
||||
ON CONFLICT (store_code) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 7. 培训课程种子数据
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_training_course (course_name, course_type, exam_enabled, pass_score) VALUES
|
||||
('食品安全基础', '食安', true, 80),
|
||||
('门店服务标准SOP', 'SOP', true, 70),
|
||||
('收银系统操作', '技能', true, 60),
|
||||
('新品制作流程', '技能', true, 75),
|
||||
('消防安全培训', '安全', true, 80),
|
||||
('门店管理基础', '管理', false, 60),
|
||||
('会员运营实操', '管理', false, 60),
|
||||
('排班与工时管理', '管理', false, 60)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 8. 数据质量规则种子数据
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_data_quality_rule (rule_name, table_name, column_name, rule_type, rule_config, is_enabled) VALUES
|
||||
('账单金额非负', 'fact_bill', 'received_total', 'range', '{"min": 0}', true),
|
||||
('账单日期不超未来', 'fact_bill', 'business_date', 'range', '{"max": "CURRENT_DATE"}', true),
|
||||
('门店编码非空', 'fact_bill', 'store_code', 'null_check', '{}', true),
|
||||
('SKU编码非空', 'fact_bill_item', 'sku_code', 'null_check', '{}', true),
|
||||
('销售数量非负', 'fact_bill_item', 'sales_quantity', 'range', '{"min": 0}', true),
|
||||
('员工姓名非空', 'dim_employee', 'employee_name', 'null_check', '{}', true),
|
||||
('供应商编码唯一', 'dim_supplier', 'supplier_code', 'unique', '{}', true),
|
||||
('会员手机号格式', 'dim_member', 'phone', 'regex', '{"pattern": "^1[3-9][0-9]{9}$"}', true)
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 9. 学习记录 (v3_learning_record ← dim_employee 部分样本)
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_learning_record (course_id, employee_name, store_code, progress_pct, completion_status, exam_score, started_at)
|
||||
SELECT
|
||||
c.id,
|
||||
e.employee_name,
|
||||
e.store_code,
|
||||
CASE WHEN random() < 0.6 THEN 100 ELSE CAST(random() * 80 AS int) END,
|
||||
CASE WHEN random() < 0.6 THEN '已完成' ELSE '进行中' END,
|
||||
CASE WHEN random() < 0.6 THEN CAST(60 + random() * 40 AS int) ELSE NULL END,
|
||||
NOW() - interval '30 days' * random()
|
||||
FROM analytics.dim_employee e
|
||||
CROSS JOIN (SELECT id FROM analytics.v3_training_course WHERE course_name = '食品安全基础' LIMIT 1) c
|
||||
WHERE e.status = '在职'
|
||||
AND e.employee_name IS NOT NULL
|
||||
AND random() < 0.3
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 10. 营销活动种子数据
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_marketing_campaign (campaign_name, campaign_type, start_date, end_date, budget, target_stores, status) VALUES
|
||||
('夏季新品推广', '促销', '2026-06-01', '2026-08-31', 50000, '全部门店', 'active'),
|
||||
('会员日双倍积分', '拉新', '2026-07-01', '2026-12-31', 30000, '全部门店', 'active'),
|
||||
('工作日午餐特惠', '促销', '2026-07-15', '2026-09-15', 20000, '商圈门店', 'active'),
|
||||
('老店焕新活动', '品牌', '2026-08-01', '2026-10-31', 80000, 'A类门店', 'draft')
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 11. 预算数据 (v3_budget ← 按门店月度估算)
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_budget (year, month, store_code, department, budget_type, budget_amount)
|
||||
SELECT
|
||||
EXTRACT(YEAR FROM NOW())::int,
|
||||
date_trunc('month', NOW())::date,
|
||||
store_code,
|
||||
'运营',
|
||||
'revenue',
|
||||
received * 1.05
|
||||
FROM analytics.mv_store_risk_rating
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
INSERT INTO analytics.v3_budget (year, month, store_code, department, budget_type, budget_amount)
|
||||
SELECT
|
||||
EXTRACT(YEAR FROM NOW())::int,
|
||||
date_trunc('month', NOW())::date,
|
||||
store_code,
|
||||
'运营',
|
||||
'expense',
|
||||
received * 0.6
|
||||
FROM analytics.mv_store_risk_rating
|
||||
ON CONFLICT DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 12. 品牌资产种子数据 (v3_brand_asset)
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_brand_asset (tracking_date, nps_score, search_index, sentiment_health, positive_mentions, negative_mentions)
|
||||
SELECT d::date,
|
||||
40 + random() * 20,
|
||||
80 + random() * 40,
|
||||
CASE WHEN random() < 0.7 THEN 80 + random() * 15 ELSE 50 + random() * 20 END,
|
||||
CAST(50 + random() * 100 AS int),
|
||||
CAST(random() * 20 AS int)
|
||||
FROM generate_series(NOW()::date - interval '29 days', NOW()::date, interval '1 day') AS d
|
||||
ON CONFLICT (tracking_date) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 13. IoT设备种子数据 (v3_iot_device)
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_iot_device (device_code, device_name, device_type, store_code, location, status)
|
||||
SELECT
|
||||
'IOT-' || s.store_code || '-01',
|
||||
s.store_name || '冷链温度探头',
|
||||
'温控',
|
||||
s.store_code,
|
||||
'冷库',
|
||||
'online'
|
||||
FROM analytics.dim_store s
|
||||
WHERE s.store_code IS NOT NULL
|
||||
AND random() < 0.3
|
||||
ON CONFLICT (device_code) DO NOTHING;
|
||||
|
||||
-- ============================================================
|
||||
-- 14. 顾客评价种子数据 (v3_customer_review)
|
||||
-- ============================================================
|
||||
INSERT INTO analytics.v3_customer_review (review_source, store_code, store_name, rating, content, review_date, nlp_category, nlp_sentiment)
|
||||
SELECT
|
||||
'美团',
|
||||
s.store_code,
|
||||
s.store_name,
|
||||
CASE WHEN random() < 0.7 THEN 5 WHEN random() < 0.5 THEN 4 WHEN random() < 0.5 THEN 3 ELSE 1 END,
|
||||
CASE
|
||||
WHEN random() < 0.5 THEN '味道不错,服务也很好'
|
||||
WHEN random() < 0.5 THEN '出餐速度快,包装好'
|
||||
When random() < 0.3 THEN '分量有点少'
|
||||
ELSE '味道一般,有待改进'
|
||||
END,
|
||||
NOW()::date - CAST(random() * 30 AS int),
|
||||
CASE
|
||||
WHEN random() < 0.4 THEN '口味'
|
||||
WHEN random() < 0.3 THEN '服务'
|
||||
WHEN random() < 0.2 THEN '环境'
|
||||
WHEN random() < 0.1 THEN '分量'
|
||||
ELSE '异物'
|
||||
END,
|
||||
CASE WHEN random() < 0.7 THEN '正面' WHEN random() < 0.5 THEN '中性' ELSE '负面' END
|
||||
FROM analytics.dim_store s
|
||||
WHERE s.store_code IS NOT NULL
|
||||
AND random() < 0.5
|
||||
LIMIT 200
|
||||
ON CONFLICT DO NOTHING;
|
||||
Reference in New Issue
Block a user