feat: 因果链三层验证机制(P2) — 数据验证+人工确认+状态机
- kpi_causality 加列: source_type/verify_status/verified_at/verified_by/entity_id(回填)
- 核心服务: app/services/causality_verification.py (Pearson+滞后对齐+状态机)
- 数据验证脚本: scripts/correlation-check.py (月度cron, 输出JSON报告)
- API: create/update支持source_type, GET /verify-status, PUT /{id}/verify(人工确认)
- 全部端点按entity_id账套隔离, kpi/{id}/network/simulate补跨企业校验
- 前端: KPIDetail因果链页显示验证状态徽标(数据证实/存疑/待检)
- 测试: test_causality_verification.py 37用例 + 原因果链测试全过(59个)
- 50条因果链首轮验证: 1数据证实(#37渠补率到净利润lag1 r=-0.89), 4存疑, 45待检(数据不足)
This commit is contained in:
@@ -293,6 +293,8 @@ export const kpiCausalityApi = {
|
||||
getNetwork: (kpiId: number) => api.get(`/kpi-causality/kpi/${kpiId}/network`),
|
||||
getFullNetwork: () => api.get('/kpi-causality/full-network'),
|
||||
simulate: (data: any) => api.post('/kpi-causality/simulate', data),
|
||||
getVerifyStatus: (params?: any) => api.get('/kpi-causality/verify-status', { params }),
|
||||
verify: (id: number, data: any) => api.put(`/kpi-causality/${id}/verify`, data),
|
||||
}
|
||||
|
||||
export const dataQualityApi = {
|
||||
|
||||
@@ -216,7 +216,13 @@
|
||||
<el-timeline-item v-for="item in upstream" :key="item.kpi_id"
|
||||
:color="item.direction === 'positive' ? 'var(--bsc-process)' : 'var(--bsc-finance)'"
|
||||
:timestamp="'强度:' + item.strength + ' / 滞后期:' + item.lag_months + '月'">
|
||||
<div><b>{{ item.kpi_name }}</b> ({{ item.kpi_code }})</div>
|
||||
<div>
|
||||
<b>{{ item.kpi_name }}</b> ({{ item.kpi_code }})
|
||||
<span v-if="item.verify_status"
|
||||
:style="{ display:'inline-block', marginLeft:'8px', padding:'0 8px', borderRadius:'10px', fontSize:'12px', lineHeight:'20px', color: verifyBadge(item.verify_status).color, background: verifyBadge(item.verify_status).bg }">
|
||||
{{ verifyBadge(item.verify_status).text }}
|
||||
</span>
|
||||
</div>
|
||||
<div style="font-size:12px;color:#999;">
|
||||
{{ item.direction === 'positive' ? '正向驱动' : '负向抑制' }}
|
||||
<span v-if="item.formula"> · {{ item.formula }}</span>
|
||||
@@ -233,7 +239,13 @@
|
||||
<el-timeline-item v-for="item in downstream" :key="item.kpi_id"
|
||||
:color="item.direction === 'positive' ? 'var(--bsc-process)' : 'var(--bsc-finance)'"
|
||||
:timestamp="'强度:' + item.strength + ' / 滞后期:' + item.lag_months + '月'">
|
||||
<div><b>{{ item.kpi_name }}</b> ({{ item.kpi_code }})</div>
|
||||
<div>
|
||||
<b>{{ item.kpi_name }}</b> ({{ item.kpi_code }})
|
||||
<span v-if="item.verify_status"
|
||||
:style="{ display:'inline-block', marginLeft:'8px', padding:'0 8px', borderRadius:'10px', fontSize:'12px', lineHeight:'20px', color: verifyBadge(item.verify_status).color, background: verifyBadge(item.verify_status).bg }">
|
||||
{{ verifyBadge(item.verify_status).text }}
|
||||
</span>
|
||||
</div>
|
||||
<div style="font-size:12px;color:#999;">
|
||||
{{ item.direction === 'positive' ? '正向推动' : '负向抑制' }}
|
||||
<span v-if="item.formula"> · {{ item.formula }}</span>
|
||||
@@ -408,6 +420,17 @@ function statusLabel(s: string) {
|
||||
}
|
||||
return map[s] || s || '—'
|
||||
}
|
||||
|
||||
// 因果链验证状态映射(三层验证机制 2026-08-27)
|
||||
function verifyBadge(s: string) {
|
||||
const map: Record<string, { text: string; color: string; bg: string }> = {
|
||||
data_verified: { text: '✅ 数据证实', color: '#67c23a', bg: 'rgba(103,194,58,.12)' },
|
||||
human_verified: { text: '✅ 人工确认', color: '#409eff', bg: 'rgba(64,158,255,.12)' },
|
||||
disputed: { text: '⚠️ 存疑', color: '#e6a23c', bg: 'rgba(230,162,60,.12)' },
|
||||
pending: { text: '⏳ 待检', color: '#909399', bg: 'rgba(144,147,153,.12)' },
|
||||
}
|
||||
return map[s] || { text: s || '—', color: '#909399', bg: 'rgba(144,147,153,.12)' }
|
||||
}
|
||||
const mapOptions = ref<any[]>([])
|
||||
const activeTab = ref('basic')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user