Files
SBrainCO/client/src/lib/decision-aggregator.ts
T
selfrelease d373fb5f0c feat: 新增智脑决策中心模块(决策中心/AI员工组织/知识资产)
- 新增 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>
2026-08-14 19:34:04 +08:00

204 lines
7.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 决策聚合逻辑
*
* 将利润机会、风险告警、异常任务 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 自动',
}