d373fb5f0c
- 新增 DecisionsPage、AgentsPage、KnowledgePage 三个页面 - 新增 DecisionCard、DecisionDrawer、DecisionSummary、KnowledgeCard、LoopHealthBar、AgentCard 组件 - 新增 decision-aggregator 决策聚合逻辑和 agents 数据 - Layout 导航重构:新增智脑决策分组,调整系统配置和经营本体分组 - BossPage 集成决策摘要和闭环健康度,利润机会池支持警告样式 - TasksPage 增强任务展示 - 抽屉式详情统一改为居中弹窗风格 - data.ts 后端调整 - run.md 更新数据库连接说明 - 新增玄谋智脑实际版建设方案文档 Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
204 lines
7.2 KiB
TypeScript
204 lines
7.2 KiB
TypeScript
/**
|
||
* 决策聚合逻辑
|
||
*
|
||
* 将利润机会、风险告警、异常任务 3 类信号统一聚合为"决策卡"格式。
|
||
* 阶段一为前端聚合(不持久化),阶段二将迁移至后端 analytics.decision_card 表。
|
||
*/
|
||
|
||
/** 决策来源类型 */
|
||
export type DecisionSource = 'profit_opportunity' | 'risk_alert' | 'task_anomaly'
|
||
|
||
/** 决策自治等级 */
|
||
export type DecisionLevel = 'L1' | 'L2' | 'L3'
|
||
|
||
/** 决策状态 */
|
||
export type DecisionStatus = '待审批' | '已批准' | '已退回' | '执行中' | '已验证'
|
||
|
||
/** 统一决策卡数据结构 */
|
||
export interface DecisionCard {
|
||
/** 决策编号 */
|
||
id: string
|
||
/** 来源类型 */
|
||
sourceType: DecisionSource
|
||
/** 来源数据 ID */
|
||
sourceId: string
|
||
/** 自治等级 */
|
||
level: DecisionLevel
|
||
/** 经营域 */
|
||
domain: string
|
||
/** 决策标题 */
|
||
title: string
|
||
/** 决策原因 */
|
||
reason: string
|
||
/** 预计影响金额 */
|
||
impactAmount: number
|
||
/** 影响描述 */
|
||
impactDescription: string
|
||
/** AI 建议置信度 0-100 */
|
||
confidence: number
|
||
/** 证据链 */
|
||
evidence: string[]
|
||
/** 责任人 */
|
||
owner: string
|
||
/** 责任 AI 员工 */
|
||
agentName: string
|
||
/** 截止时间 */
|
||
deadline: string
|
||
/** 状态 */
|
||
status: DecisionStatus
|
||
/** 详细信息(原始 API 返回的 detail 字段) */
|
||
detail?: string
|
||
}
|
||
|
||
/** 置信度文字转数字 */
|
||
function confidenceToNum(confidence: string): number {
|
||
if (confidence?.includes('高')) return 90
|
||
if (confidence?.includes('中高')) return 85
|
||
if (confidence?.includes('中')) return 75
|
||
if (confidence?.includes('中低')) return 65
|
||
if (confidence?.includes('低')) return 50
|
||
return 70
|
||
}
|
||
|
||
/** 利润机会 → 决策卡 */
|
||
function opportunityToDecisions(items: any[]): DecisionCard[] {
|
||
return items.map((o, i) => ({
|
||
id: `OPP-${String(i + 1).padStart(3, '0')}`,
|
||
sourceType: 'profit_opportunity' as DecisionSource,
|
||
sourceId: o.category,
|
||
level: 'L1' as DecisionLevel,
|
||
domain: o.owner?.split('/')[0]?.trim() || '经营',
|
||
title: o.category,
|
||
reason: o.evidence || '基于利润机会池分析',
|
||
impactAmount: Math.max(Number(o.opportunity || 0), 0),
|
||
impactDescription: `预计月度利润提升 ${Math.max(Number(o.opportunity || 0), 0).toLocaleString('zh-CN')} 元`,
|
||
confidence: confidenceToNum(o.confidence),
|
||
evidence: [
|
||
`基线: ${o.baseline}`,
|
||
`验收方式: ${o.evidence}`,
|
||
`责任方: ${o.owner}`,
|
||
],
|
||
owner: o.owner || '待分配',
|
||
agentName: mapOwnerToAgent(o.owner),
|
||
deadline: '30天内',
|
||
status: '待审批' as DecisionStatus,
|
||
detail: o.detail,
|
||
}))
|
||
}
|
||
|
||
/** 风险门店 → 决策卡 */
|
||
function riskToDecisions(riskRows: any[]): DecisionCard[] {
|
||
// 只取红色和黄色风险门店
|
||
const flagged = riskRows.filter((r) => r.risk_level === '红色' || r.risk_level === '黄色')
|
||
return flagged.slice(0, 10).map((r, i) => ({
|
||
id: `RISK-${String(i + 1).padStart(3, '0')}`,
|
||
sourceType: 'risk_alert' as DecisionSource,
|
||
sourceId: r.store_code || r.store_name,
|
||
level: (r.risk_level === '红色' ? 'L1' : 'L2') as DecisionLevel,
|
||
domain: '门店经营',
|
||
title: `${r.store_name} ${r.risk_level}门店整改`,
|
||
reason: `风险评级为${r.risk_level},营收 ${Number(r.received || 0).toLocaleString('zh-CN')} 元,需关注经营异常`,
|
||
impactAmount: Number(r.received || 0) * 0.05,
|
||
impactDescription: `预计挽回营收损失 ${Math.round(Number(r.received || 0) * 0.05).toLocaleString('zh-CN')} 元`,
|
||
confidence: r.risk_level === '红色' ? 92 : 78,
|
||
evidence: [
|
||
`门店: ${r.store_name}(${r.store_code || '-'})`,
|
||
`风险等级: ${r.risk_level}`,
|
||
`实收: ¥${Number(r.received || 0).toLocaleString('zh-CN')}`,
|
||
`风险评分: ${r.risk_score || '-'}`,
|
||
],
|
||
owner: '区域经理',
|
||
agentName: '门店经营分析师',
|
||
deadline: r.risk_level === '红色' ? '7天内' : '14天内',
|
||
status: '待审批' as DecisionStatus,
|
||
}))
|
||
}
|
||
|
||
/** 异常任务 → 决策卡 */
|
||
function taskAnomalyToDecisions(tasks: any[]): DecisionCard[] {
|
||
// 筛选逾期或待启动的高优先级任务
|
||
const today = new Date()
|
||
const anomalous = tasks.filter((t) => {
|
||
const isOverdue = t.deadline && new Date(t.deadline) < today && t.status !== '已验收'
|
||
const isPendingP0 = t.priority?.startsWith('P0') && t.status === '待启动'
|
||
return isOverdue || isPendingP0
|
||
})
|
||
return anomalous.slice(0, 10).map((t, i) => ({
|
||
id: `TASK-${String(i + 1).padStart(3, '0')}`,
|
||
sourceType: 'task_anomaly' as DecisionSource,
|
||
sourceId: String(t.task_id),
|
||
level: (t.priority?.startsWith('P0') ? 'L1' : 'L2') as DecisionLevel,
|
||
domain: '组织执行',
|
||
title: `${t.store_name} - ${t.problem_indicator || '任务异常'}`,
|
||
reason: t.problem_description || t.action_required || '任务存在异常需关注',
|
||
impactAmount: 0,
|
||
impactDescription: t.verification_result || '需评估验收价值',
|
||
confidence: 80,
|
||
evidence: [
|
||
`门店: ${t.store_name}`,
|
||
`问题指标: ${t.problem_indicator || '-'}`,
|
||
`当前值: ${t.current_value ?? '-'} / 目标值: ${t.target_value ?? '-'}`,
|
||
`负责人: ${t.owner}`,
|
||
`截止日: ${t.deadline?.substring(0, 10) || '-'}`,
|
||
],
|
||
owner: t.owner || '待分配',
|
||
agentName: '经营督办员',
|
||
deadline: t.deadline?.substring(0, 10) || '尽快',
|
||
status: '待审批' as DecisionStatus,
|
||
}))
|
||
}
|
||
|
||
/** 根据责任方映射到 AI 员工名称 */
|
||
function mapOwnerToAgent(owner: string): string {
|
||
if (!owner) return '经营参谋长'
|
||
if (owner.includes('供应链') || owner.includes('商品')) return '菜品利润优化师'
|
||
if (owner.includes('运营') || owner.includes('人力')) return '费用稽核员'
|
||
if (owner.includes('工程') || owner.includes('门店')) return '门店经营分析师'
|
||
if (owner.includes('营销')) return '会员运营师'
|
||
if (owner.includes('外卖') || owner.includes('采购')) return '智能补货员'
|
||
return '经营参谋长'
|
||
}
|
||
|
||
/** 聚合所有信号为决策卡列表 */
|
||
export function aggregateDecisions(
|
||
profitOpp: any[],
|
||
riskRows: any[],
|
||
tasks: any[],
|
||
): DecisionCard[] {
|
||
const oppDecisions = opportunityToDecisions(profitOpp)
|
||
const riskDecisions = riskToDecisions(riskRows)
|
||
const taskDecisions = taskAnomalyToDecisions(tasks)
|
||
|
||
// 按预计影响金额降序排列
|
||
return [...oppDecisions, ...riskDecisions, ...taskDecisions].sort(
|
||
(a, b) => b.impactAmount - a.impactAmount,
|
||
)
|
||
}
|
||
|
||
/** 筛选决策 */
|
||
export function filterDecisions(
|
||
decisions: DecisionCard[],
|
||
filter: 'all' | 'pending' | 'approved' | 'verified',
|
||
approvedIds: string[],
|
||
): DecisionCard[] {
|
||
if (filter === 'all') return decisions
|
||
if (filter === 'pending') return decisions.filter((d) => d.status === '待审批' && !approvedIds.includes(d.id))
|
||
if (filter === 'approved') return decisions.filter((d) => approvedIds.includes(d.id))
|
||
if (filter === 'verified') return decisions.filter((d) => d.status === '已验证')
|
||
return decisions
|
||
}
|
||
|
||
/** 决策来源标签 */
|
||
export const SOURCE_LABELS: Record<DecisionSource, string> = {
|
||
profit_opportunity: '利润机会',
|
||
risk_alert: '风险告警',
|
||
task_anomaly: '任务异常',
|
||
}
|
||
|
||
/** 自治等级标签 */
|
||
export const LEVEL_LABELS: Record<DecisionLevel, string> = {
|
||
L1: 'L1 人工审批',
|
||
L2: 'L2 受控',
|
||
L3: 'L3 自动',
|
||
}
|