/** * 决策交互卡片 * 飞书消息卡片格式 + 回调处理 */ export type DecisionType = 'approve_reject' | 'multi_choice' | 'confirm'; export interface DecisionCard { title: string; description: string; type: DecisionType; options: DecisionOption[]; metadata?: Record; } export interface DecisionOption { key: string; label: string; style?: 'primary' | 'danger' | 'default'; value: string; } /** * 生成飞书消息卡片JSON */ export function buildDecisionCard(card: DecisionCard): Record { const elements: Record[] = [ { tag: 'div', text: { tag: 'plain_text', content: card.description }, }, ]; // Action buttons const actions = card.options.map((opt) => ({ tag: 'button', text: { tag: 'plain_text', content: opt.label }, type: opt.style === 'danger' ? 'danger' : opt.style === 'primary' ? 'primary' : 'default', value: { action: opt.key, ...card.metadata }, })); elements.push({ tag: 'action', actions }); return { msg_type: 'interactive', card: { header: { title: { tag: 'plain_text', content: card.title }, template: 'blue', }, elements, }, }; } /** * 决策记录 */ export interface DecisionRecord { id: string; cardTitle: string; type: DecisionType; options: string[]; chosenOption: string | null; status: 'pending' | 'responded' | 'expired'; createdAt: string; respondedAt?: string; } const pendingDecisions: Map = new Map(); /** * 创建决策请求 */ export function createDecision(card: DecisionCard): DecisionRecord { const id = `decision-${Date.now()}`; const record: DecisionRecord = { id, cardTitle: card.title, type: card.type, options: card.options.map((o) => o.key), chosenOption: null, status: 'pending', createdAt: new Date().toISOString(), }; pendingDecisions.set(id, record); return record; } /** * 处理决策回调 */ export function handleDecisionCallback( decisionId: string, actionKey: string ): { ok: boolean; record?: DecisionRecord; error?: string } { const record = pendingDecisions.get(decisionId); if (!record) { return { ok: false, error: 'Decision not found' }; } if (record.status !== 'pending') { return { ok: false, error: 'Already responded' }; } record.chosenOption = actionKey; record.status = 'responded'; record.respondedAt = new Date().toISOString(); return { ok: true, record }; } /** * 获取待决策列表 */ export function getPendingDecisions(): DecisionRecord[] { return Array.from(pendingDecisions.values()).filter((d) => d.status === 'pending'); } /** * 预定义的决策卡片模板 */ export const DECISION_TEMPLATES = { /** 章程审批 */ charterApproval: (projectName: string): DecisionCard => ({ title: '📋 项目章程审批', description: `项目「${projectName}」的章程已生成,请审批。`, type: 'approve_reject', options: [ { key: 'approve', label: '✅ 批准', style: 'primary', value: 'approved' }, { key: 'reject', label: '❌ 驳回', style: 'danger', value: 'rejected' }, { key: 'revise', label: '✏️ 需修改', value: 'needs_revision' }, ], metadata: { type: 'charter', project: projectName }, }), /** 风险应对 */ riskResponse: (riskDesc: string, priority: number): DecisionCard => ({ title: `⚠️ 风险应对决策(优先级${priority})`, description: riskDesc, type: 'multi_choice', options: [ { key: 'avoid', label: '🛡 规避', value: 'avoid' }, { key: 'transfer', label: '🔄 转移', value: 'transfer' }, { key: 'mitigate', label: '🔧 减轻', value: 'mitigate' }, { key: 'accept', label: '✅ 接受', value: 'accept' }, ], metadata: { type: 'risk', priority }, }), /** 变更审批 */ changeApproval: (changeDesc: string): DecisionCard => ({ title: '🔀 变更请求审批', description: changeDesc, type: 'approve_reject', options: [ { key: 'approve', label: '✅ 批准', style: 'primary', value: 'approved' }, { key: 'reject', label: '❌ 拒绝', style: 'danger', value: 'rejected' }, { key: 'defer', label: '⏳ 延后', value: 'deferred' }, ], metadata: { type: 'change' }, }), /** 里程碑确认 */ milestoneConfirm: (name: string, date: string): DecisionCard => ({ title: `🏁 里程碑达成确认`, description: `里程碑「${name}」(目标:${date})是否已达成?`, type: 'confirm', options: [ { key: 'done', label: '✅ 已达成', style: 'primary', value: 'reached' }, { key: 'missed', label: '❌ 未达成', style: 'danger', value: 'missed' }, ], metadata: { type: 'milestone', name, date }, }), };