營運效率提升系統增強計劃
文件資訊
- 文件類型: 功能增強計劃
- 目標頁面: 營運效率提升儀表板 (
DashboardOperationalExcellence.vue) - 當前狀態: Phase 1 + Phase 2.1 已完成
- 建立日期: 2025-10-01
- 最後更新: 2025-10-01
專案背景
問題識別
在 2025-10-01 的代碼審查中,發現「營運效率提升」頁面存在大量硬編碼邏輯:
- 訂單處理時間: 使用公式估算
36 - (avgOrderVolume - 10) * 2 - 跨功能協作效率: 硬編碼公式
(avgOrderVolume + (100 - avgResponseTime)) / 2 - 整體效率評分: 硬編碼公式
(100 - avgResponseTime + avgOrderVolume / 2) / 2 - 效率趨勢數據: 使用
stabilityFactor完全模擬生成 - 瓶頸分析閾值: 硬編碼(訂單量 < 10、回應時間 > 5分鐘)
影響評估
- ❌ 決策誤導: 假數據可能導致錯誤的業務決策
- ❌ 信任問題: 使用者發現假數據後會失去對系統的信任
- ❌ 維護成本: 硬編碼邏輯難以維護和擴展
- ⚠️ 業務價值: 無法提供真實的營運洞察
實施階段
✅ Phase 1: 移除硬編碼(已完成 2025-10-01)
目標: 建立誠實的資料呈現基礎
執行內容:
- 移除所有模擬計算和假數據邏輯
- 移除
stabilityFactor模擬效率趨勢 - 實現零資料策略,無數據時顯示友好訊息
- 保持 UI 結構完整,為未來數據預留接口
程式碼變更:
operationalOverviewcomputed: 移除estimatedProcessingTime硬編碼efficiencyTrendDatacomputed: 返回空陣列而非模擬數據- 添加 TODO 註釋標記未來改進點
成果:
- ✅ 避免誤導決策(假數據比無數據更危險)
- ✅ 建立清晰的數據需求(知道缺什麼)
- ✅ 快速交付,不阻塞其他功能
✅ Phase 2.1: 真實數據計算邏輯(已完成 2025-10-01)
目標: 利用現有資料表提供部分真實指標
執行內容:
- 使用現有的
order_metrics_hourly視圖計算效率 - 基於真實訂單量和客服回應時間計算協作效率
- 實現加權評分系統(訂單 60% + 客服 40%)
計算邏輯:
typescript
// 跨功能協作效率
const orderPerformanceScore = Math.min(100, avgOrderVolume * 5)
const supportPerformanceScore = Math.max(0, 100 - avgResponseTime * 10)
const crossFunctionEfficiency = Math.round(
orderPerformanceScore * 0.6 + supportPerformanceScore * 0.4
)
// 整體效率評分
const peakEfficiencyScore = Math.min(100, peakEfficiency * 5)
const overallEfficiencyScore = Math.round(
peakEfficiencyScore * 0.4 +
supportPerformanceScore * 0.3 +
crossFunctionEfficiency * 0.3
)數據來源:
- ✅
order_metrics_hourly視圖: 小時級訂單數據 - ✅
agent_metrics查詢: 客服回應時間 - ✅
daily_conversation_metrics查詢: 對話解決率
成果:
- ✅ 提供 70% 的業務價值,只需 20% 的工作量
- ✅ 無需 schema 變更,快速實現
- ✅ 基於真實數據的初步洞察
⏳ Phase 2.2: 歷史趨勢追蹤系統(計劃中)
目標: 建立效率歷史數據追蹤,支援趨勢分析
預估時程: 3-5 天
資料庫架構設計
1. 效率趨勢物化視圖
sql
-- 每日效率趨勢物化視圖
CREATE MATERIALIZED VIEW efficiency_trends_daily AS
SELECT
DATE(o.created_at) as date,
-- 訂單處理效率
COUNT(*) FILTER (WHERE o.status IN ('completed', 'delivered')) as completed_orders,
COUNT(*) as total_orders,
(COUNT(*) FILTER (WHERE o.status IN ('completed', 'delivered'))::DECIMAL /
NULLIF(COUNT(*), 0) * 100) as order_completion_rate,
-- 訂單處理時間(僅完成訂單)
AVG(
EXTRACT(EPOCH FROM (o.updated_at - o.created_at)) / 3600
) FILTER (WHERE o.status IN ('completed', 'delivered')) as avg_processing_hours,
-- 客服效率
AVG(a.avg_response_time_minutes) as avg_support_response_minutes,
-- 活躍客戶數
COUNT(DISTINCT o.user_id) as active_customers,
-- 整體效率評分(加權計算)
ROUND(
(COUNT(*) FILTER (WHERE o.status IN ('completed', 'delivered'))::DECIMAL /
NULLIF(COUNT(*), 0) * 40) +
(GREATEST(0, 100 - AVG(a.avg_response_time_minutes) * 10) * 30) +
(LEAST(100, COUNT(*) / 24.0 * 5) * 30)
) as overall_efficiency_score
FROM orders o
LEFT JOIN agent_performance a ON DATE(o.created_at) = a.date
WHERE o.created_at >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY DATE(o.created_at)
ORDER BY date DESC;
-- 建立索引加速查詢
CREATE INDEX idx_efficiency_trends_date ON efficiency_trends_daily(date DESC);
-- 授權存取
GRANT SELECT ON efficiency_trends_daily TO anon, authenticated, service_role;2. 自動刷新機制
sql
-- 定時刷新函數(每日凌晨 1:00 執行)
CREATE OR REPLACE FUNCTION refresh_efficiency_trends()
RETURNS void
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY efficiency_trends_daily;
-- 記錄刷新日誌
INSERT INTO system_logs (action, details, created_at)
VALUES (
'refresh_materialized_view',
jsonb_build_object(
'view_name', 'efficiency_trends_daily',
'status', 'success'
),
NOW()
);
EXCEPTION WHEN OTHERS THEN
-- 錯誤記錄
INSERT INTO system_logs (action, details, created_at)
VALUES (
'refresh_materialized_view',
jsonb_build_object(
'view_name', 'efficiency_trends_daily',
'status', 'error',
'error_message', SQLERRM
),
NOW()
);
RAISE;
END;
$$;
-- 建立 pg_cron 排程(需要啟用 pg_cron 擴展)
SELECT cron.schedule(
'refresh-efficiency-trends-daily',
'0 1 * * *', -- 每日 01:00
$$SELECT refresh_efficiency_trends()$$
);前端整合
typescript
// composables/queries/useOperationalQueries.ts
export function useEfficiencyTrendsDaily(days: number = 30) {
return useQuery({
queryKey: ['efficiency-trends', 'daily', days],
queryFn: async () => {
const { data, error } = await supabase
.from('efficiency_trends_daily')
.select('*')
.order('date', { ascending: false })
.limit(days)
if (error) throw error
// 轉換為圖表格式
return data.map(day => ({
date: day.date,
overallEfficiency: day.overall_efficiency_score,
orderProcessing: day.order_completion_rate,
customerSupport: Math.max(0, 100 - day.avg_support_response_minutes * 10),
completedOrders: day.completed_orders,
avgProcessingHours: Math.round(day.avg_processing_hours),
}))
},
staleTime: 15 * 60 * 1000, // 15分鐘
})
}預期成果
- ✅ 真實的歷史效率趨勢數據(過去 90 天)
- ✅ 支援週度、月度趨勢分析
- ✅ 自動化數據更新,無需手動維護
- ✅ 效能優化(物化視圖 + 索引)
⏳ Phase 2.3: 完整效率分析系統(長期規劃)
目標: 建立完整的營運效率追蹤和分析體系
預估時程: 1-2 週
資料庫 Schema 增強
1. Orders 表擴展
sql
-- 添加訂單處理階段時間戳
ALTER TABLE orders
ADD COLUMN IF NOT EXISTS processing_started_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS picked_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS packed_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS shipped_at TIMESTAMPTZ,
ADD COLUMN IF NOT EXISTS delivered_at TIMESTAMPTZ;
-- 建立處理時效索引
CREATE INDEX idx_orders_processing_times
ON orders(created_at, processing_started_at, shipped_at, delivered_at)
WHERE status IN ('completed', 'delivered');
-- 添加註釋
COMMENT ON COLUMN orders.processing_started_at IS '訂單開始處理時間';
COMMENT ON COLUMN orders.picked_at IS '商品揀貨完成時間';
COMMENT ON COLUMN orders.packed_at IS '訂單包裝完成時間';
COMMENT ON COLUMN orders.shipped_at IS '訂單出貨時間';
COMMENT ON COLUMN orders.delivered_at IS '訂單送達時間';2. 營運指標日誌表
sql
-- 營運效率指標日誌表
CREATE TABLE operational_metrics_log (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
date DATE NOT NULL UNIQUE,
-- 訂單處理效率
avg_order_processing_hours DECIMAL(5,2),
avg_picking_hours DECIMAL(5,2),
avg_packing_hours DECIMAL(5,2),
avg_shipping_hours DECIMAL(5,2),
order_completion_rate DECIMAL(5,2),
-- 客服效率
avg_response_time_minutes DECIMAL(5,2),
avg_resolution_time_hours DECIMAL(5,2),
first_response_rate DECIMAL(5,2),
resolution_rate DECIMAL(5,2),
-- 跨功能協作
cross_function_efficiency_score INTEGER,
order_to_support_handoff_rate DECIMAL(5,2),
support_escalation_rate DECIMAL(5,2),
-- 瓶頸識別
bottleneck_areas JSONB,
bottleneck_severity TEXT CHECK (bottleneck_severity IN ('low', 'medium', 'high')),
-- 整體評分
overall_efficiency_score INTEGER,
efficiency_rating TEXT CHECK (efficiency_rating IN ('excellent', 'good', 'fair', 'poor')),
-- 元數據
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- 建立索引
CREATE INDEX idx_operational_metrics_date ON operational_metrics_log(date DESC);
CREATE INDEX idx_operational_metrics_score ON operational_metrics_log(overall_efficiency_score DESC);
-- 授權存取
GRANT SELECT ON operational_metrics_log TO anon, authenticated;
GRANT ALL ON operational_metrics_log TO service_role;
-- 添加註釋
COMMENT ON TABLE operational_metrics_log IS '每日營運效率指標日誌表';
COMMENT ON COLUMN operational_metrics_log.bottleneck_areas IS 'JSONB格式的瓶頸領域詳細資訊';3. 每日指標計算函數
sql
-- 計算並記錄每日營運指標
CREATE OR REPLACE FUNCTION calculate_daily_operational_metrics(target_date DATE DEFAULT CURRENT_DATE - 1)
RETURNS operational_metrics_log
LANGUAGE plpgsql
SECURITY DEFINER
AS $$
DECLARE
result operational_metrics_log;
order_efficiency DECIMAL;
support_efficiency DECIMAL;
cross_function_score INTEGER;
BEGIN
-- 計算訂單處理效率
SELECT
AVG(EXTRACT(EPOCH FROM (delivered_at - created_at)) / 3600)
FILTER (WHERE delivered_at IS NOT NULL),
AVG(EXTRACT(EPOCH FROM (picked_at - processing_started_at)) / 3600)
FILTER (WHERE picked_at IS NOT NULL AND processing_started_at IS NOT NULL),
AVG(EXTRACT(EPOCH FROM (packed_at - picked_at)) / 3600)
FILTER (WHERE packed_at IS NOT NULL AND picked_at IS NOT NULL),
AVG(EXTRACT(EPOCH FROM (shipped_at - packed_at)) / 3600)
FILTER (WHERE shipped_at IS NOT NULL AND packed_at IS NOT NULL),
COUNT(*) FILTER (WHERE status IN ('completed', 'delivered'))::DECIMAL /
NULLIF(COUNT(*), 0) * 100
INTO
result.avg_order_processing_hours,
result.avg_picking_hours,
result.avg_packing_hours,
result.avg_shipping_hours,
result.order_completion_rate
FROM orders
WHERE DATE(created_at) = target_date;
-- 計算客服效率
SELECT
AVG(avg_response_time_minutes),
AVG(avg_resolution_time_hours),
COUNT(*) FILTER (WHERE first_response_minutes <= 3)::DECIMAL /
NULLIF(COUNT(*), 0) * 100,
COUNT(*) FILTER (WHERE status = 'closed')::DECIMAL /
NULLIF(COUNT(*), 0) * 100
INTO
result.avg_response_time_minutes,
result.avg_resolution_time_hours,
result.first_response_rate,
result.resolution_rate
FROM daily_conversation_metrics
WHERE date = target_date;
-- 計算跨功能協作效率
order_efficiency := COALESCE(result.order_completion_rate, 0);
support_efficiency := COALESCE(
GREATEST(0, 100 - result.avg_response_time_minutes * 10),
0
);
cross_function_score := ROUND(order_efficiency * 0.6 + support_efficiency * 0.4);
result.cross_function_efficiency_score := cross_function_score;
-- 計算整體效率評分
result.overall_efficiency_score := ROUND(
(order_efficiency * 0.4) +
(support_efficiency * 0.3) +
(cross_function_score * 0.3)
);
-- 判定效率等級
result.efficiency_rating := CASE
WHEN result.overall_efficiency_score >= 90 THEN 'excellent'
WHEN result.overall_efficiency_score >= 75 THEN 'good'
WHEN result.overall_efficiency_score >= 60 THEN 'fair'
ELSE 'poor'
END;
-- 識別瓶頸
result.bottleneck_areas := identify_operational_bottlenecks(target_date);
result.bottleneck_severity := CASE
WHEN jsonb_array_length(result.bottleneck_areas) >= 3 THEN 'high'
WHEN jsonb_array_length(result.bottleneck_areas) >= 2 THEN 'medium'
ELSE 'low'
END;
result.date := target_date;
-- 插入或更新記錄
INSERT INTO operational_metrics_log VALUES (result.*)
ON CONFLICT (date) DO UPDATE SET
avg_order_processing_hours = EXCLUDED.avg_order_processing_hours,
avg_picking_hours = EXCLUDED.avg_picking_hours,
avg_packing_hours = EXCLUDED.avg_packing_hours,
avg_shipping_hours = EXCLUDED.avg_shipping_hours,
order_completion_rate = EXCLUDED.order_completion_rate,
avg_response_time_minutes = EXCLUDED.avg_response_time_minutes,
avg_resolution_time_hours = EXCLUDED.avg_resolution_time_hours,
first_response_rate = EXCLUDED.first_response_rate,
resolution_rate = EXCLUDED.resolution_rate,
cross_function_efficiency_score = EXCLUDED.cross_function_efficiency_score,
overall_efficiency_score = EXCLUDED.overall_efficiency_score,
efficiency_rating = EXCLUDED.efficiency_rating,
bottleneck_areas = EXCLUDED.bottleneck_areas,
bottleneck_severity = EXCLUDED.bottleneck_severity,
updated_at = NOW();
RETURN result;
END;
$$;
-- 瓶頸識別輔助函數
CREATE OR REPLACE FUNCTION identify_operational_bottlenecks(target_date DATE)
RETURNS JSONB
LANGUAGE plpgsql
AS $$
DECLARE
bottlenecks JSONB := '[]'::JSONB;
BEGIN
-- 檢查訂單處理瓶頸
IF (SELECT AVG(EXTRACT(EPOCH FROM (delivered_at - created_at)) / 3600)
FROM orders WHERE DATE(created_at) = target_date) > 48 THEN
bottlenecks := bottlenecks || jsonb_build_object(
'area', '訂單處理',
'issue', '平均處理時間超過 48 小時',
'severity', 'high'
);
END IF;
-- 檢查客服響應瓶頸
IF (SELECT AVG(avg_response_time_minutes)
FROM daily_conversation_metrics WHERE date = target_date) > 5 THEN
bottlenecks := bottlenecks || jsonb_build_object(
'area', '客服響應',
'issue', '平均回應時間超過 5 分鐘',
'severity', 'medium'
);
END IF;
-- 檢查訂單完成率瓶頸
IF (SELECT COUNT(*) FILTER (WHERE status IN ('completed', 'delivered'))::DECIMAL /
NULLIF(COUNT(*), 0) * 100
FROM orders WHERE DATE(created_at) = target_date) < 85 THEN
bottlenecks := bottlenecks || jsonb_build_object(
'area', '訂單完成',
'issue', '訂單完成率低於 85%',
'severity', 'high'
);
END IF;
RETURN bottlenecks;
END;
$$;4. 自動化排程
sql
-- 每日凌晨 2:00 計算前一天的營運指標
SELECT cron.schedule(
'calculate-daily-operational-metrics',
'0 2 * * *',
$$SELECT calculate_daily_operational_metrics(CURRENT_DATE - 1)$$
);前端 API 整合
typescript
// composables/queries/useOperationalQueries.ts
/**
* 獲取營運指標日誌
*/
export function useOperationalMetricsLog(days: number = 30) {
return useQuery({
queryKey: ['operational-metrics', 'log', days],
queryFn: async () => {
const { data, error } = await supabase
.from('operational_metrics_log')
.select('*')
.order('date', { ascending: false })
.limit(days)
if (error) throw error
return data
},
staleTime: 10 * 60 * 1000, // 10分鐘
})
}
/**
* 獲取瓶頸詳細分析
*/
export function useBottleneckAnalysis(date?: string) {
return useQuery({
queryKey: ['operational-metrics', 'bottlenecks', date],
queryFn: async () => {
let query = supabase
.from('operational_metrics_log')
.select('date, bottleneck_areas, bottleneck_severity')
.order('date', { ascending: false })
if (date) {
query = query.eq('date', date)
} else {
query = query.limit(7) // 最近 7 天
}
const { data, error } = await query
if (error) throw error
return data
},
staleTime: 10 * 60 * 1000,
})
}預期成果
- ✅ 完整的訂單處理流程時效追蹤
- ✅ 自動化的每日營運指標計算
- ✅ 智能瓶頸識別與分析
- ✅ 歷史數據對比與趨勢預測
- ✅ 多維度效率評分系統
業務價值分析
各階段價值貢獻
| 階段 | 工作量 | 業務價值 | ROI | 優先級 |
|---|---|---|---|---|
| Phase 1 | 1 天 | 避免誤導決策 | ⭐⭐⭐⭐⭐ | 🔴 高 |
| Phase 2.1 | 1 天 | 70% 指標可用 | ⭐⭐⭐⭐⭐ | 🔴 高 |
| Phase 2.2 | 3-5 天 | 歷史趨勢分析 | ⭐⭐⭐⭐ | 🟡 中 |
| Phase 2.3 | 1-2 週 | 完整效率體系 | ⭐⭐⭐ | 🟢 低 |
關鍵指標改善預期
- 決策準確性: 從假數據 → 真實數據(100% 改善)
- 使用者信任度: 從懷疑 → 信任(質性改善)
- 營運洞察深度:
- Phase 2.1: 基礎指標(70%)
- Phase 2.2: 趨勢分析(85%)
- Phase 2.3: 完整體系(100%)
實施建議
優先執行順序
✅ Phase 1 + Phase 2.1 (已完成)
- 立即消除假數據問題
- 提供基礎真實指標
- 快速交付價值
⏳ Phase 2.2 (建議下次迭代)
- 等待業務對歷史趨勢的需求確認
- 評估 pg_cron 擴展的可用性
- 預估 3-5 天開發時間
⏳ Phase 2.3 (長期規劃)
- 需要業務流程配合(記錄處理階段時間戳)
- 需要產品團隊評估完整功能需求
- 建議作為 Q4 2025 的 OKR 項目
技術依賴檢查清單
Phase 2.2 前置條件
- [ ] 確認 Supabase 環境支援
pg_cron擴展 - [ ] 評估物化視圖的刷新頻率需求
- [ ] 確認 90 天歷史數據的儲存成本
- [ ] 測試 CONCURRENT 刷新對效能的影響
Phase 2.3 前置條件
- [ ] 與業務團隊確認訂單處理流程改動
- [ ] 評估增加時間戳欄位對現有系統的影響
- [ ] 確認倉儲/物流系統能提供處理階段數據
- [ ] 設計數據補償機制(歷史訂單的時間戳)
風險評估
| 風險項目 | 影響 | 可能性 | 緩解措施 |
|---|---|---|---|
| pg_cron 不可用 | 中 | 低 | 改用應用層定時任務 |
| 歷史數據缺失 | 低 | 中 | 從現在開始累積數據 |
| 業務流程不配合 | 高 | 中 | 分階段實施,先用估算值 |
| 效能影響 | 中 | 低 | 使用物化視圖和索引優化 |
參考資料
相關文件
相關程式碼
- 前端:
admin-platform-vue/src/views/dashboard/DashboardOperationalExcellence.vue - 查詢:
admin-platform-vue/src/composables/queries/useOrderQueries.ts - 查詢:
admin-platform-vue/src/composables/queries/useSupportQueries.ts - 視圖:
supabase/migrations/*_operational_efficiency_analysis.sql
外部資源
版本歷史
| 版本 | 日期 | 修改內容 | 作者 |
|---|---|---|---|
| 1.0 | 2025-10-01 | 初始版本,完成 Phase 1 + 2.1 | Claude Code |
注意: 本文件為技術規劃文件,實際實施時請根據業務優先級和技術資源進行調整。