Files
SBrainCO/server/sql/02_create_functions.sql
T
2026-07-26 22:48:08 +08:00

200 lines
8.8 KiB
PL/PgSQL

-- ============================================================
-- 02_create_functions.sql
-- 自动分级函数 + 任务自动生成函数 + 通知推送函数
-- ============================================================
-- 自动分级函数:基于 v_store_action_priority_deep_april 逻辑
-- 输入月份参数,输出门店分级结果
CREATE OR REPLACE FUNCTION analytics.f_auto_grade_stores(p_month DATE)
RETURNS TABLE(
store_code TEXT,
store_name TEXT,
priority TEXT,
problem_count INT,
problem_combination TEXT,
received NUMERIC,
scale_tier TEXT,
business_type TEXT
) AS $$
BEGIN
-- 当前仅4月数据可用,直接查询现有视图
-- 后续月份需先刷新物化视图再查询
RETURN QUERY
SELECT v.store_code, v.store_name, v.action_priority AS priority,
v.problem_count, v.problem_combination,
v.received, v.scale_tier, v.business_type
FROM analytics.v_store_action_priority_deep_april v
ORDER BY CASE v.action_priority
WHEN 'P0-修复数据口径' THEN 1
WHEN 'P0-综合专项整改' THEN 2
WHEN 'P1-重点整改' THEN 3
WHEN 'P2-单项改善' THEN 4
WHEN '标杆候选' THEN 5
ELSE 6
END, v.received DESC;
END;
$$ LANGUAGE plpgsql STABLE;
-- 任务自动生成函数:按分级结果自动生成门店任务
-- 每店最多2个核心指标,P0门店可增加数据修复任务
CREATE OR REPLACE FUNCTION analytics.f_generate_store_tasks(p_month DATE)
RETURNS TABLE(generated INT, message TEXT) AS $$
DECLARE
v_count INT := 0;
v_store RECORD;
v_task1_indicator TEXT;
v_task1_action TEXT;
v_task1_target TEXT;
v_task2_indicator TEXT;
v_task2_action TEXT;
v_task2_target TEXT;
v_deadline DATE;
BEGIN
v_deadline := (p_month + INTERVAL '1 month' - INTERVAL '1 day')::date;
FOR v_store IN
SELECT * FROM analytics.f_auto_grade_stores(p_month)
LOOP
-- 根据问题组合生成任务1
v_task1_indicator := NULL;
v_task1_action := NULL;
v_task1_target := NULL;
-- 根据问题组合确定第一个任务
IF v_store.problem_combination LIKE '%优惠偏高%' THEN
v_task1_indicator := '优惠率';
v_task1_action := '拆解平台折扣和营销方案,每周复核高折扣账单,退出低毛利满减商品';
v_task1_target := '优惠率降低2个百分点';
ELSIF v_store.problem_combination LIKE '%实际成本严重超耗%' THEN
v_task1_indicator := '实际成本率';
v_task1_action := '获取SKU成本,分析超耗原料,调整低毛利商品和套餐';
v_task1_target := '实际成本率降低至理论+10%以内';
ELSIF v_store.problem_combination LIKE '%理论毛利偏低%' THEN
v_task1_indicator := '理论毛利率';
v_task1_action := '分析商品结构,提升高毛利品类占比,优化套餐组合';
v_task1_target := '理论毛利率提升1个百分点';
ELSIF v_store.problem_combination LIKE '%会员复购偏低%' THEN
v_task1_indicator := '会员复购率';
v_task1_action := '执行消费后第3天和第7天触达,使用会员价和复购券';
v_task1_target := '复购率提升5个百分点';
ELSIF v_store.problem_combination LIKE '%异常%' THEN
v_task1_indicator := '异常率';
v_task1_action := '全额优惠必须填写原因和审批人,周度抽查收银员及营销方案';
v_task1_target := '异常率降低至1.5%以下';
ELSIF v_store.problem_combination LIKE '%饮品搭售偏低%' THEN
v_task1_indicator := '饮品搭售率';
v_task1_action := '按午晚市设计主食加饮品组合销售,培训搭售话术';
v_task1_target := '饮品搭售率提升5个百分点';
ELSIF v_store.problem_combination LIKE '%平台成本偏高%' THEN
v_task1_indicator := '平台加权成本率';
v_task1_action := '设置平台成本率红线,优化满减和折扣策略';
v_task1_target := '平台成本率降低1个百分点';
ELSIF v_store.priority = '标杆候选' THEN
v_task1_indicator := '经验输出';
v_task1_action := '总结核心SKU结构、会员运营、平台价格纪律等可推广经验';
v_task1_target := '输出至少1个标准化经验模块';
ELSIF v_store.priority = '持续跟踪' THEN
v_task1_indicator := '经营稳定性';
v_task1_action := '保持现有经营水平,关注核心指标波动';
v_task1_target := '核心指标不恶化';
END IF;
-- 生成第一个任务
IF v_task1_indicator IS NOT NULL THEN
INSERT INTO analytics.store_task
(plan_month, store_code, store_name, priority, problem_indicator,
problem_description, action_required, owner, deadline, status,
verification_indicator)
VALUES (p_month, v_store.store_code, v_store.store_name, v_store.priority,
v_task1_indicator, v_store.problem_combination,
v_task1_action, '店长/区域经理', v_deadline, '待启动',
v_task1_target)
ON CONFLICT DO NOTHING;
v_count := v_count + 1;
END IF;
-- 根据问题组合生成第二个任务(如果有多个问题)
IF v_store.problem_count >= 2 THEN
v_task2_indicator := NULL;
v_task2_action := NULL;
v_task2_target := NULL;
IF v_store.problem_combination LIKE '%优惠偏高%' AND v_task1_indicator != '优惠率' THEN
v_task2_indicator := '优惠率';
v_task2_action := '拆解平台折扣和营销方案,每周复核高折扣账单';
v_task2_target := '优惠率降低2个百分点';
ELSIF v_store.problem_combination LIKE '%会员复购偏低%' AND v_task1_indicator != '会员复购率' THEN
v_task2_indicator := '会员复购率';
v_task2_action := '执行消费后第3天和第7天触达,使用会员价和复购券';
v_task2_target := '复购率提升5个百分点';
ELSIF v_store.problem_combination LIKE '%异常%' AND v_task1_indicator != '异常率' THEN
v_task2_indicator := '异常率';
v_task2_action := '全额优惠必须填写原因和审批人,周度抽查';
v_task2_target := '异常率降低至1.5%以下';
ELSIF v_store.problem_combination LIKE '%理论毛利偏低%' AND v_task1_indicator != '理论毛利率' THEN
v_task2_indicator := '理论毛利率';
v_task2_action := '分析商品结构,提升高毛利品类占比';
v_task2_target := '理论毛利率提升1个百分点';
ELSIF v_store.problem_combination LIKE '%饮品搭售偏低%' AND v_task1_indicator != '饮品搭售率' THEN
v_task2_indicator := '饮品搭售率';
v_task2_action := '按午晚市设计主食加饮品组合销售';
v_task2_target := '饮品搭售率提升5个百分点';
END IF;
IF v_task2_indicator IS NOT NULL THEN
INSERT INTO analytics.store_task
(plan_month, store_code, store_name, priority, problem_indicator,
problem_description, action_required, owner, deadline, status,
verification_indicator)
VALUES (p_month, v_store.store_code, v_store.store_name, v_store.priority,
v_task2_indicator, v_store.problem_combination,
v_task2_action, '店长/区域经理', v_deadline, '待启动',
v_task2_target)
ON CONFLICT DO NOTHING;
v_count := v_count + 1;
END IF;
END IF;
-- P0-修复数据口径 门店增加数据修复任务
IF v_store.priority = 'P0-修复数据口径' THEN
INSERT INTO analytics.store_task
(plan_month, store_code, store_name, priority, problem_indicator,
problem_description, action_required, owner, deadline, status,
verification_indicator)
VALUES (p_month, v_store.store_code, v_store.store_name, v_store.priority,
'数据口径修复', '成本口径异常',
'核实成本单位与门店映射关系,修正盘点数据口径', '信息部/财务部',
v_deadline, '待启动', '成本口径校验通过')
ON CONFLICT DO NOTHING;
v_count := v_count + 1;
END IF;
END LOOP;
RETURN QUERY SELECT v_count, format('已为 %s 家门店生成 %s 条任务',
(SELECT count(DISTINCT store_code) FROM analytics.store_task WHERE plan_month = p_month),
v_count);
END;
$$ LANGUAGE plpgsql;
-- 每日通知推送函数
CREATE OR REPLACE FUNCTION analytics.f_dispatch_daily_notifications()
RETURNS TABLE(dispatched INT, message TEXT) AS $$
DECLARE
v_count INT := 0;
v_store RECORD;
BEGIN
-- 为每家门店的待启动任务标记为已推送
-- 实际推送由应用层处理,这里只做标记
UPDATE analytics.store_task
SET updated_at = NOW()
WHERE status = '待启动' AND updated_at < NOW() - INTERVAL '1 day';
GET DIAGNOSTICS v_count = ROW_COUNT;
RETURN QUERY SELECT v_count, format('已推送 %s 条待办任务通知', v_count);
END;
$$ LANGUAGE plpgsql;