feat(client): vendor digital employee source
This commit is contained in:
@@ -0,0 +1,310 @@
|
||||
import type { PlanMessage } from '@/lib/api';
|
||||
import type {
|
||||
DashboardOverview,
|
||||
JournalEntry,
|
||||
Mission,
|
||||
MissionStep,
|
||||
SkillSummary,
|
||||
} from '@/lib/consoleDataAdapter';
|
||||
import type { DebugPlan, DebugRun, DebugTask, FlowResponse } from '@/types/api';
|
||||
|
||||
function todayAt(hour: number, minute: number): string {
|
||||
const date = new Date();
|
||||
date.setHours(hour, minute, 0, 0);
|
||||
return date.toISOString();
|
||||
}
|
||||
|
||||
function makeTask(
|
||||
missionId: string,
|
||||
stepId: string,
|
||||
title: string,
|
||||
status: string,
|
||||
skillId: string,
|
||||
startedAt: string | null,
|
||||
finishedAt: string | null,
|
||||
): MissionStep['tasks'][number] {
|
||||
return {
|
||||
id: `${missionId}-${stepId}-task`,
|
||||
title,
|
||||
status,
|
||||
assignedSkillId: skillId,
|
||||
startedAt,
|
||||
finishedAt,
|
||||
};
|
||||
}
|
||||
|
||||
function makeMission(
|
||||
id: string,
|
||||
title: string,
|
||||
objective: string,
|
||||
status: string,
|
||||
updatedAt: string,
|
||||
steps: MissionStep[],
|
||||
): Mission {
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
objective,
|
||||
status,
|
||||
createdAt: todayAt(8, 0),
|
||||
updatedAt,
|
||||
steps,
|
||||
sourceIds: { planId: id, taskIds: steps.flatMap((step) => step.tasks.map((task) => task.id)) },
|
||||
};
|
||||
}
|
||||
|
||||
const DEMO_SKILLS: SkillSummary[] = [
|
||||
{ id: 'whitecollar-day-browser-portal-check', name: '工作台巡检', version: '1.0.0', enabled: true, description: '检查工作台首页、待办数量和重点入口是否可用。', category: '办公', capabilityLevel: 'standard', source: 'skill-api' },
|
||||
{ id: 'whitecollar-day-browser-inbox-triage', name: '收件箱分拣', version: '1.0.0', enabled: true, description: '读取邮件和通知列表,按紧急程度整理待办。', category: '通信', capabilityLevel: 'standard', source: 'skill-api' },
|
||||
{ id: 'whitecollar-day-calendar-brief', name: '日程简报', version: '1.0.0', enabled: true, description: '生成当日会议、回访和报表节点的时间提醒。', category: '办公', capabilityLevel: 'standard', source: 'skill-api' },
|
||||
{ id: 'whitecollar-day-browser-crm-followup', name: '客户跟进', version: '1.0.0', enabled: true, description: '读取客户跟进页面,形成回访摘要和下一步建议。', category: '营销', capabilityLevel: 'specialized', source: 'skill-api' },
|
||||
{ id: 'whitecollar-day-browser-expense-approval', name: '费用审批核对', version: '1.0.0', enabled: true, description: '检查待审批费用单,筛出异常和超限项。', category: '财务', capabilityLevel: 'specialized', source: 'skill-api' },
|
||||
{ id: 'whitecollar-day-sales-snapshot', name: '经营快照', version: '1.0.0', enabled: true, description: '汇总客户、项目和当日经营看板。', category: '报表', capabilityLevel: 'standard', source: 'skill-api' },
|
||||
{ id: 'whitecollar-day-report-export', name: '报表导出', version: '1.0.0', enabled: true, description: '整理并导出面向领导汇报的简版报表。', category: '报表', capabilityLevel: 'standard', source: 'skill-api' },
|
||||
{ id: 'whitecollar-day-browser-knowledge-check', name: '知识库核查', version: '1.0.0', enabled: true, description: '检索知识库资料,确认制度和模板是否最新。', category: '数字化', capabilityLevel: 'standard', source: 'skill-api' },
|
||||
{ id: 'whitecollar-day-task-board-sync', name: '任务看板同步', version: '1.0.0', enabled: true, description: '把今日处理结果同步到任务看板和协同清单。', category: '办公', capabilityLevel: 'standard', source: 'skill-api' },
|
||||
{ id: 'whitecollar-day-browser-day-close', name: '日终收口', version: '1.0.0', enabled: true, description: '汇总今日动作、风险和明日重点。', category: '办公', capabilityLevel: 'specialized', source: 'skill-api' },
|
||||
];
|
||||
|
||||
const DEMO_MISSIONS: Mission[] = [
|
||||
makeMission(
|
||||
'whitecollar-day',
|
||||
'白领一天端到端验收',
|
||||
'模拟业务人员一天内通过数字员工完成网页采集、客户回访、资料整理和日报输出。',
|
||||
'active',
|
||||
todayAt(10, 35),
|
||||
[
|
||||
{ id: 'contact', title: '读取供应商联系人', status: 'completed', preferredSkills: ['whitecollar-day-legacy-contact'], tasks: [makeTask('whitecollar-day', 'contact', '整理联系人名单', 'completed', 'whitecollar-day-legacy-contact', todayAt(8, 30), todayAt(8, 33))] },
|
||||
{ id: 'news', title: '读取行业动态', status: 'completed', preferredSkills: ['whitecollar-day-legacy-news'], tasks: [makeTask('whitecollar-day', 'news', '整理行业动态摘要', 'completed', 'whitecollar-day-legacy-news', todayAt(8, 35), todayAt(8, 41))] },
|
||||
{ id: 'form', title: '预填客户回访登记', status: 'completed', preferredSkills: ['whitecollar-day-legacy-form'], tasks: [makeTask('whitecollar-day', 'form', '填充回访登记表', 'completed', 'whitecollar-day-legacy-form', todayAt(8, 45), todayAt(8, 51))] },
|
||||
{ id: 'compare', title: '比对设备方案', status: 'running', preferredSkills: ['whitecollar-day-legacy-compare'], tasks: [makeTask('whitecollar-day', 'compare', '生成方案差异结论', 'running', 'whitecollar-day-legacy-compare', todayAt(10, 28), null)] },
|
||||
{ id: 'todos', title: '读取待办系统', status: 'pending', preferredSkills: ['whitecollar-day-legacy-todos'], tasks: [makeTask('whitecollar-day', 'todos', '汇总个人待办', 'pending', 'whitecollar-day-legacy-todos', null, null)] },
|
||||
{ id: 'customers', title: '读取客户清单', status: 'pending', preferredSkills: ['whitecollar-day-legacy-customers'], tasks: [makeTask('whitecollar-day', 'customers', '提取重点客户', 'pending', 'whitecollar-day-legacy-customers', null, null)] },
|
||||
{ id: 'download', title: '识别最新资料下载链接', status: 'pending', preferredSkills: ['whitecollar-day-legacy-download'], tasks: [makeTask('whitecollar-day', 'download', '识别最新制度材料', 'pending', 'whitecollar-day-legacy-download', null, null)] },
|
||||
{ id: 'email', title: '生成客户跟进邮件', status: 'pending', preferredSkills: ['whitecollar-day-legacy-email'], tasks: [makeTask('whitecollar-day', 'email', '生成回访邮件草稿', 'pending', 'whitecollar-day-legacy-email', null, null)] },
|
||||
{ id: 'daily-report', title: '生成工作日报', status: 'pending', preferredSkills: ['whitecollar-day-legacy-daily-report'], tasks: [makeTask('whitecollar-day', 'daily-report', '生成工作日报', 'pending', 'whitecollar-day-legacy-daily-report', null, null)] },
|
||||
{ id: 'tomorrow-plan', title: '生成明日计划', status: 'pending', preferredSkills: ['whitecollar-day-legacy-tomorrow-plan'], tasks: [makeTask('whitecollar-day', 'tomorrow-plan', '整理明日计划', 'pending', 'whitecollar-day-legacy-tomorrow-plan', null, null)] },
|
||||
],
|
||||
),
|
||||
makeMission('whitecollar-day-0830-browser-portal-check', '白领一天 08:30 工作台巡检', '巡检工作台首页、待办数量和关键入口状态。', 'completed', todayAt(8, 34), [
|
||||
{ id: 'browser-portal-check', title: '浏览器工作台巡检', status: 'completed', preferredSkills: ['whitecollar-day-browser-portal-check'], tasks: [makeTask('whitecollar-day-0830-browser-portal-check', 'browser-portal-check', '检查门户首页与入口', 'completed', 'whitecollar-day-browser-portal-check', todayAt(8, 30), todayAt(8, 34))] },
|
||||
]),
|
||||
makeMission('whitecollar-day-0900-browser-inbox-triage', '白领一天 09:00 收件箱分拣', '识别高优先级通知并形成今日待办。', 'completed', todayAt(9, 6), [
|
||||
{ id: 'browser-inbox-triage', title: '浏览器收件箱分拣', status: 'completed', preferredSkills: ['whitecollar-day-browser-inbox-triage'], tasks: [makeTask('whitecollar-day-0900-browser-inbox-triage', 'browser-inbox-triage', '分拣通知与邮件', 'completed', 'whitecollar-day-browser-inbox-triage', todayAt(9, 0), todayAt(9, 6))] },
|
||||
]),
|
||||
makeMission('whitecollar-day-0930-calendar-brief', '白领一天 09:30 日程简报', '整理会议、回访与汇报节点,形成上午排程。', 'completed', todayAt(9, 32), [
|
||||
{ id: 'calendar-brief', title: '生成日程简报', status: 'completed', preferredSkills: ['whitecollar-day-calendar-brief'], tasks: [makeTask('whitecollar-day-0930-calendar-brief', 'calendar-brief', '整理今日日程', 'completed', 'whitecollar-day-calendar-brief', todayAt(9, 30), todayAt(9, 32))] },
|
||||
]),
|
||||
makeMission('whitecollar-day-1030-browser-crm-followup', '白领一天 10:30 客户跟进', '汇总重点客户情况,生成下一步回访建议。', 'running', todayAt(10, 35), [
|
||||
{ id: 'browser-crm-followup', title: '浏览器客户跟进', status: 'running', preferredSkills: ['whitecollar-day-browser-crm-followup'], tasks: [makeTask('whitecollar-day-1030-browser-crm-followup', 'browser-crm-followup', '提取客户回访摘要', 'running', 'whitecollar-day-browser-crm-followup', todayAt(10, 30), null)] },
|
||||
]),
|
||||
makeMission('whitecollar-day-1130-browser-expense-approval', '白领一天 11:30 费用审批核对', '检查待审批单据并输出异常说明。', 'approved', todayAt(11, 20), [
|
||||
{ id: 'browser-expense-approval', title: '浏览器费用审批核对', status: 'pending', preferredSkills: ['whitecollar-day-browser-expense-approval'], tasks: [makeTask('whitecollar-day-1130-browser-expense-approval', 'browser-expense-approval', '核对费用审批单', 'pending', 'whitecollar-day-browser-expense-approval', null, null)] },
|
||||
]),
|
||||
makeMission('whitecollar-day-1330-sales-snapshot', '白领一天 13:30 经营快照', '汇总客户、项目和经营看板,形成午后快照。', 'draft', todayAt(13, 20), [
|
||||
{ id: 'sales-snapshot', title: '生成经营快照', status: 'pending', preferredSkills: ['whitecollar-day-sales-snapshot'], tasks: [makeTask('whitecollar-day-1330-sales-snapshot', 'sales-snapshot', '整理经营快照', 'pending', 'whitecollar-day-sales-snapshot', null, null)] },
|
||||
]),
|
||||
makeMission('whitecollar-day-1430-report-export', '白领一天 14:30 报表导出', '导出领导汇报所需的简版统计报表。', 'draft', todayAt(14, 10), [
|
||||
{ id: 'report-export', title: '导出汇报报表', status: 'pending', preferredSkills: ['whitecollar-day-report-export'], tasks: [makeTask('whitecollar-day-1430-report-export', 'report-export', '导出统计报表', 'pending', 'whitecollar-day-report-export', null, null)] },
|
||||
]),
|
||||
makeMission('whitecollar-day-1530-browser-knowledge-check', '白领一天 15:30 知识库核查', '核查制度、模板与常见问题资料是否最新。', 'draft', todayAt(15, 15), [
|
||||
{ id: 'browser-knowledge-check', title: '浏览器知识库核查', status: 'pending', preferredSkills: ['whitecollar-day-browser-knowledge-check'], tasks: [makeTask('whitecollar-day-1530-browser-knowledge-check', 'browser-knowledge-check', '核查知识库资料', 'pending', 'whitecollar-day-browser-knowledge-check', null, null)] },
|
||||
]),
|
||||
makeMission('whitecollar-day-1630-task-board-sync', '白领一天 16:30 任务看板同步', '把今日处理结果同步到看板和协同清单。', 'draft', todayAt(16, 10), [
|
||||
{ id: 'task-board-sync', title: '同步任务看板', status: 'pending', preferredSkills: ['whitecollar-day-task-board-sync'], tasks: [makeTask('whitecollar-day-1630-task-board-sync', 'task-board-sync', '同步任务状态', 'pending', 'whitecollar-day-task-board-sync', null, null)] },
|
||||
]),
|
||||
makeMission('whitecollar-day-1730-browser-day-close', '白领一天 17:30 日终收口', '汇总今日成果、风险和明日重点,形成日终总结。', 'draft', todayAt(17, 10), [
|
||||
{ id: 'task-board-sync', title: '同步任务看板', status: 'pending', preferredSkills: ['whitecollar-day-task-board-sync'], tasks: [makeTask('whitecollar-day-1730-browser-day-close', 'task-board-sync', '同步看板结果', 'pending', 'whitecollar-day-task-board-sync', null, null)] },
|
||||
{ id: 'browser-day-close', title: '浏览器日终收口', status: 'pending', preferredSkills: ['whitecollar-day-browser-day-close'], tasks: [makeTask('whitecollar-day-1730-browser-day-close', 'browser-day-close', '整理日终收口', 'pending', 'whitecollar-day-browser-day-close', null, null)] },
|
||||
]),
|
||||
makeMission('zhihu-hotlist-monitor', '知乎热榜采集', '通过计划模板调用知乎热榜采集技能,导出表格文件并生成当天热点摘要。', 'approved', todayAt(10, 12), [
|
||||
{ id: 'collect-zhihu', title: '采集知乎热榜', status: 'pending', preferredSkills: ['zhihu-hotlist'], tasks: [makeTask('zhihu-hotlist-monitor', 'collect-zhihu', '采集知乎热榜', 'pending', 'zhihu-hotlist', null, null)] },
|
||||
{ id: 'export-xlsx', title: '导出表格文件', status: 'pending', preferredSkills: ['office-export-xlsx'], tasks: [makeTask('zhihu-hotlist-monitor', 'export-xlsx', '导出热榜表格', 'pending', 'office-export-xlsx', null, null)] },
|
||||
]),
|
||||
];
|
||||
|
||||
function cloneMission(mission: Mission): Mission {
|
||||
return {
|
||||
...mission,
|
||||
steps: mission.steps.map((step) => ({
|
||||
...step,
|
||||
preferredSkills: [...step.preferredSkills],
|
||||
tasks: step.tasks.map((task) => ({ ...task })),
|
||||
})),
|
||||
sourceIds: { planId: mission.sourceIds.planId, taskIds: [...mission.sourceIds.taskIds] },
|
||||
};
|
||||
}
|
||||
|
||||
export function getDemoMissions(): Mission[] {
|
||||
return DEMO_MISSIONS.map(cloneMission);
|
||||
}
|
||||
|
||||
export function getDemoSkills(): SkillSummary[] {
|
||||
return DEMO_SKILLS.map((skill) => ({ ...skill }));
|
||||
}
|
||||
|
||||
export function buildDemoTasks(missions: Mission[]): DebugTask[] {
|
||||
return missions.flatMap((mission) => mission.steps.flatMap((step) => step.tasks.map((task) => ({
|
||||
task_id: task.id,
|
||||
title: task.title,
|
||||
status: task.status,
|
||||
plan_id: mission.id,
|
||||
step_id: step.id,
|
||||
assigned_skill_id: task.assignedSkillId || '',
|
||||
description: `${mission.title} · ${step.title}`,
|
||||
created_at: mission.createdAt || todayAt(8, 0),
|
||||
updated_at: mission.updatedAt || mission.createdAt || todayAt(8, 0),
|
||||
started_at: task.startedAt,
|
||||
finished_at: task.finishedAt,
|
||||
}))));
|
||||
}
|
||||
|
||||
export function buildDemoRuns(tasks: DebugTask[]): DebugRun[] {
|
||||
return tasks
|
||||
.filter((task) => ['completed', 'running'].includes(task.status))
|
||||
.map((task, index) => ({
|
||||
run_id: `demo-run-${index + 1}`,
|
||||
task_id: task.task_id,
|
||||
plan_id: task.plan_id,
|
||||
attempt_no: 1,
|
||||
lifecycle: task.status === 'running' ? 'Running' : 'Succeeded',
|
||||
created_at: task.started_at || task.created_at,
|
||||
completed_at: task.finished_at,
|
||||
}));
|
||||
}
|
||||
|
||||
export function buildDemoOverview(tasks: DebugTask[]): DashboardOverview {
|
||||
return {
|
||||
generatedAt: new Date().toISOString(),
|
||||
isPartial: true,
|
||||
sources: [{ endpoint: 'digital-demo', ok: true, loadedAt: new Date().toISOString() }],
|
||||
taskTotals: {
|
||||
total: tasks.length,
|
||||
queued: tasks.filter((task) => task.status === 'pending').length,
|
||||
running: tasks.filter((task) => task.status === 'running').length,
|
||||
succeeded: tasks.filter((task) => task.status === 'completed').length,
|
||||
failed: 0,
|
||||
},
|
||||
approvals: { total: 1, pending: 1 },
|
||||
health: { activeTasks: tasks.filter((task) => task.status === 'running').length, failedTasks: 0, pendingConfirmations: 1 },
|
||||
};
|
||||
}
|
||||
|
||||
export function buildDemoPlans(missions: Mission[]): DebugPlan[] {
|
||||
return missions.map((mission) => ({
|
||||
plan_id: mission.id,
|
||||
title: mission.title,
|
||||
objective: mission.objective,
|
||||
status: mission.status,
|
||||
created_at: mission.createdAt || todayAt(8, 0),
|
||||
steps: mission.steps.map((step) => ({
|
||||
step_id: step.id,
|
||||
title: step.title,
|
||||
depends_on: [],
|
||||
preferred_skills: step.preferredSkills,
|
||||
tasks: step.tasks.map((task) => ({
|
||||
task_id: task.id,
|
||||
title: task.title,
|
||||
status: task.status,
|
||||
})),
|
||||
})),
|
||||
}));
|
||||
}
|
||||
|
||||
export function buildDemoJournalEntries(missions: Mission[]): JournalEntry[] {
|
||||
return missions
|
||||
.slice(0, 8)
|
||||
.map((mission, index) => ({
|
||||
id: `demo-journal-${index + 1}`,
|
||||
kind: mission.status === 'completed' ? 'completed' : mission.status === 'active' || mission.status === 'running' ? 'start' : 'pending_authorization',
|
||||
source: 'digital-demo',
|
||||
message: `${mission.title}:${mission.objective}`,
|
||||
occurredAt: mission.updatedAt,
|
||||
planId: mission.id,
|
||||
taskId: mission.sourceIds.taskIds[0] || null,
|
||||
}));
|
||||
}
|
||||
|
||||
export function buildDemoFlowEvents(mission: Mission): NonNullable<FlowResponse['events']> {
|
||||
const activeStep = mission.steps.find((step) => step.status === 'running') || mission.steps.find((step) => step.status === 'pending') || mission.steps[0];
|
||||
const events: NonNullable<FlowResponse['events']> = [
|
||||
{ event_id: `${mission.id}-event-1`, kind: 'entered_session', occurred_at: mission.createdAt || todayAt(8, 0), message: `已接收场景:${mission.title}` },
|
||||
{ event_id: `${mission.id}-event-2`, kind: 'start', occurred_at: mission.updatedAt || mission.createdAt || todayAt(8, 0), message: `当前目标:${mission.objective}` },
|
||||
];
|
||||
if (activeStep) {
|
||||
events.push({
|
||||
event_id: `${mission.id}-event-3`,
|
||||
kind: mission.status === 'completed' ? 'completed' : mission.status === 'approved' || mission.status === 'draft' ? 'pending_authorization' : 'RunStarted',
|
||||
occurred_at: mission.updatedAt || mission.createdAt || todayAt(8, 0),
|
||||
message: `${activeStep.title}`,
|
||||
});
|
||||
}
|
||||
if (mission.status === 'completed') {
|
||||
events.push({
|
||||
event_id: `${mission.id}-event-4`,
|
||||
kind: 'RunCompleted',
|
||||
occurred_at: mission.updatedAt || mission.createdAt || todayAt(8, 0),
|
||||
message: '已完成本场景执行并回写结果。',
|
||||
});
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
export function buildDemoPlanMessages(mission: Mission): PlanMessage[] {
|
||||
const completedSteps = mission.steps.filter((step) => step.status === 'completed').length;
|
||||
const runningStep = mission.steps.find((step) => step.status === 'running');
|
||||
const closing =
|
||||
mission.status === 'completed'
|
||||
? `当前已完成全部 ${mission.steps.length} 个步骤,结果已整理完毕。`
|
||||
: runningStep
|
||||
? `当前正在处理“${runningStep.title}”,已完成 ${completedSteps} 个步骤。`
|
||||
: `当前场景已准备就绪,可直接发起执行。`;
|
||||
|
||||
return [
|
||||
{ role: 'user', content: `请执行场景:${mission.title}` },
|
||||
{ role: 'assistant', content: `已接收。业务目标:${mission.objective}` },
|
||||
{ role: 'assistant', content: closing },
|
||||
];
|
||||
}
|
||||
|
||||
export function runDemoMissionOnce(missions: Mission[], missionId: string): Mission[] {
|
||||
return missions.map((mission) => {
|
||||
if (mission.id !== missionId) return mission;
|
||||
return {
|
||||
...mission,
|
||||
status: 'running',
|
||||
updatedAt: new Date().toISOString(),
|
||||
steps: mission.steps.map((step, index) => ({
|
||||
...step,
|
||||
status: index === 0 ? 'running' : step.status === 'completed' ? 'completed' : 'pending',
|
||||
tasks: step.tasks.map((task) => ({
|
||||
...task,
|
||||
status: index === 0 ? 'running' : task.status === 'completed' ? 'completed' : 'pending',
|
||||
startedAt: index === 0 ? new Date().toISOString() : task.startedAt,
|
||||
finishedAt: index === 0 ? null : task.finishedAt,
|
||||
})),
|
||||
})),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function completeDemoMissionOnce(missions: Mission[], missionId: string): Mission[] {
|
||||
return missions.map((mission) => {
|
||||
if (mission.id !== missionId) return mission;
|
||||
return {
|
||||
...mission,
|
||||
status: 'completed',
|
||||
updatedAt: new Date().toISOString(),
|
||||
steps: mission.steps.map((step) => ({
|
||||
...step,
|
||||
status: 'completed',
|
||||
tasks: step.tasks.map((task) => ({
|
||||
...task,
|
||||
status: 'completed',
|
||||
startedAt: task.startedAt || new Date().toISOString(),
|
||||
finishedAt: new Date().toISOString(),
|
||||
})),
|
||||
})),
|
||||
};
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user