Skip to main content
Glama
im47cn

Feishu Project MCP Server

by im47cn
workflow.ts12.8 kB
import { WorkflowState, WorkflowStage, WorkflowTask, WorkflowFilter } from '../types/workflow.js'; import { RequirementAnalysisResult } from '../types/requirement.js'; import { ArchitectureDesignResult } from '../types/architecture.js'; import { CodeImplementationResult } from '../types/code.js'; import { TaskManager } from './task.js'; import { FeishuIntegrator } from '../integrators/feishu.js'; import { Logger } from '../utils/logger.js'; import fs from 'fs/promises'; import path from 'path'; import { v4 as uuidv4 } from 'uuid'; import { TaskStatus, TaskType } from '../types/task.js'; export interface WorkflowManagerConfig { storageDir: string; maxConcurrentWorkflows: number; } export class WorkflowManager { private taskManager: TaskManager; private feishuIntegrator: FeishuIntegrator; private logger: Logger; private config: WorkflowManagerConfig; private workflowsDir: string; constructor( taskManager: TaskManager, feishuIntegrator: FeishuIntegrator, config: WorkflowManagerConfig, logger: Logger ) { this.taskManager = taskManager; this.feishuIntegrator = feishuIntegrator; this.config = config; this.logger = logger; this.workflowsDir = path.join(config.storageDir, 'workflows'); } public async initialize(): Promise<void> { try { this.logger.info('Initializing WorkflowManager...'); // 确保工作流存储目录存在 await fs.mkdir(this.workflowsDir, { recursive: true }); this.logger.info('WorkflowManager initialized successfully'); } catch (error) { this.logger.error('Failed to initialize WorkflowManager', { error }); throw error; } } /** * 为需求创建并开始一个新的工作流 * @param requirementId 需求ID * @returns 新工作流的ID */ public async startWorkflow(requirementId: string): Promise<string> { this.logger.info('Starting workflow for requirement', { requirementId }); // 创建一个新的工作流ID const workflowId = uuidv4(); // 创建工作流状态对象 const workflowState: WorkflowState = { id: workflowId, requirementId, currentStage: WorkflowStage.REQUIREMENT_ANALYSIS, startedAt: new Date().toISOString(), updatedAt: new Date().toISOString(), }; // 保存工作流状态 await this.saveWorkflowState(workflowState); // 创建第一个工作流任务:需求分析 const task: WorkflowTask = { id: uuidv4(), type: TaskType.REQUIREMENT_ANALYSIS, status: TaskStatus.PENDING, itemId: requirementId, itemType: 'requirement', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), workflowId, workflowStage: WorkflowStage.REQUIREMENT_ANALYSIS, }; await this.taskManager.createTask(task); this.logger.info('Workflow started', { workflowId, requirementId, firstTaskId: task.id }); return workflowId; } /** * 获取工作流当前状态 * @param workflowId 工作流ID * @returns 工作流状态 */ public async getWorkflowState(workflowId: string): Promise<WorkflowState> { try { const filePath = path.join(this.workflowsDir, `${workflowId}.json`); const data = await fs.readFile(filePath, 'utf8'); return JSON.parse(data) as WorkflowState; } catch (error) { this.logger.error('Failed to get workflow state', { workflowId, error }); throw new Error(`Workflow ${workflowId} not found`); } } /** * 保存工作流状态 * @param state 工作流状态 */ private async saveWorkflowState(state: WorkflowState): Promise<void> { try { const filePath = path.join(this.workflowsDir, `${state.id}.json`); await fs.writeFile(filePath, JSON.stringify(state, null, 2), 'utf8'); this.logger.debug('Workflow state saved', { workflowId: state.id }); } catch (error) { this.logger.error('Failed to save workflow state', { workflowId: state.id, error }); throw error; } } /** * 更新工作流状态 * @param workflowId 工作流ID * @param updates 更新的字段 * @returns 更新后的工作流状态 */ public async updateWorkflowState( workflowId: string, updates: Partial<Omit<WorkflowState, 'id'>> ): Promise<WorkflowState> { // 获取当前状态 const currentState = await this.getWorkflowState(workflowId); // 更新状态 const newState: WorkflowState = { ...currentState, ...updates, updatedAt: new Date().toISOString(), }; // 保存更新后的状态 await this.saveWorkflowState(newState); this.logger.info('Workflow state updated', { workflowId, updates }); return newState; } /** * 将工作流推进到下一阶段 * @param workflowId 工作流ID * @param result 当前阶段的结果 * @returns 更新后的工作流状态 */ public async advanceWorkflow( workflowId: string, result?: RequirementAnalysisResult | ArchitectureDesignResult | CodeImplementationResult ): Promise<WorkflowState> { // 获取当前工作流状态 const currentState = await this.getWorkflowState(workflowId); const currentStage = currentState.currentStage; // 确定下一阶段 let nextStage: WorkflowStage; const updates: Partial<WorkflowState> = {}; switch (currentStage) { case WorkflowStage.REQUIREMENT_ANALYSIS: if (result as RequirementAnalysisResult) { const analysisResult = result as RequirementAnalysisResult; updates.analysisResult = analysisResult; // 如果需求不完整,则进入需求澄清阶段 if (!analysisResult.isComplete) { nextStage = WorkflowStage.REQUIREMENT_CLARIFICATION; // 更新飞书项目需求状态为"需求待完善" await this.feishuIntegrator.updateItemStatus( currentState.requirementId, 'requirement_incomplete' ); // 添加需求完善的评论 await this.feishuIntegrator.addComment( currentState.requirementId, this.generateClarificationComment(analysisResult) ); } else { // 需求完整,直接进入架构设计阶段 nextStage = WorkflowStage.ARCHITECTURE_DESIGN; } } else { throw new Error( 'Requirement analysis result is required to advance from REQUIREMENT_ANALYSIS stage' ); } break; case WorkflowStage.REQUIREMENT_CLARIFICATION: // 进入架构设计阶段 nextStage = WorkflowStage.ARCHITECTURE_DESIGN; break; case WorkflowStage.ARCHITECTURE_DESIGN: if (result as ArchitectureDesignResult) { updates.designResult = result as ArchitectureDesignResult; } // 进入代码实现阶段 nextStage = WorkflowStage.CODE_IMPLEMENTATION; break; case WorkflowStage.CODE_IMPLEMENTATION: if (result as CodeImplementationResult) { updates.implementationResult = result as CodeImplementationResult; } // 进入代码提交阶段 nextStage = WorkflowStage.CODE_SUBMISSION; break; case WorkflowStage.CODE_SUBMISSION: // 工作流完成 nextStage = WorkflowStage.COMPLETION; updates.completedAt = new Date().toISOString(); break; case WorkflowStage.COMPLETION: // 已经完成,不能再前进 throw new Error('Workflow is already completed'); default: throw new Error(`Unknown workflow stage: ${currentStage}`); } // 更新工作流状态 updates.currentStage = nextStage; const updatedState = await this.updateWorkflowState(workflowId, updates); // 为下一阶段创建任务 await this.createTaskForStage(workflowId, nextStage, currentState.requirementId); this.logger.info('Workflow advanced', { workflowId, fromStage: currentStage, toStage: nextStage, }); return updatedState; } /** * 为工作流阶段创建相应的任务 * @param workflowId 工作流ID * @param stage 工作流阶段 * @param itemId 项目项ID */ private async createTaskForStage( workflowId: string, stage: WorkflowStage, itemId: string ): Promise<void> { // 如果是完成阶段,不需要创建新任务 if (stage === WorkflowStage.COMPLETION) { return; } // 根据阶段确定任务类型 const taskType = this.getTaskTypeForStage(stage); const task: WorkflowTask = { id: uuidv4(), type: taskType, status: TaskStatus.PENDING, itemId, itemType: 'requirement', created_at: new Date().toISOString(), updated_at: new Date().toISOString(), workflowId, workflowStage: stage, }; await this.taskManager.createTask(task); } /** * 根据工作流阶段获取对应的任务类型 * @param stage 工作流阶段 * @returns 任务类型 */ private getTaskTypeForStage(stage: WorkflowStage): TaskType { switch (stage) { case WorkflowStage.REQUIREMENT_ANALYSIS: case WorkflowStage.REQUIREMENT_CLARIFICATION: case WorkflowStage.ARCHITECTURE_DESIGN: return TaskType.REQUIREMENT_ANALYSIS; case WorkflowStage.CODE_IMPLEMENTATION: case WorkflowStage.CODE_SUBMISSION: return TaskType.CODE_IMPLEMENTATION; default: throw new Error(`Unknown workflow stage: ${stage}`); } } /** * 根据过滤条件获取工作流列表 * @param filter 过滤条件 * @returns 工作流状态列表 */ public async getWorkflows(filter?: WorkflowFilter): Promise<WorkflowState[]> { try { // 读取所有工作流文件 const files = await fs.readdir(this.workflowsDir); const workflowFiles = files.filter(file => file.endsWith('.json')); // 读取所有工作流状态 const workflows: WorkflowState[] = []; for (const file of workflowFiles) { const filePath = path.join(this.workflowsDir, file); const data = await fs.readFile(filePath, 'utf8'); const workflow = JSON.parse(data) as WorkflowState; workflows.push(workflow); } // 应用过滤条件 if (filter) { return workflows.filter(workflow => { if (filter.requirementId && workflow.requirementId !== filter.requirementId) { return false; } if (filter.currentStage && workflow.currentStage !== filter.currentStage) { return false; } if (filter.startedAfter && new Date(workflow.startedAt) < new Date(filter.startedAfter)) { return false; } if ( filter.startedBefore && new Date(workflow.startedAt) > new Date(filter.startedBefore) ) { return false; } if (filter.updatedAfter && new Date(workflow.updatedAt) < new Date(filter.updatedAfter)) { return false; } if ( filter.updatedBefore && new Date(workflow.updatedAt) > new Date(filter.updatedBefore) ) { return false; } if (filter.completed !== undefined) { const isCompleted = !!workflow.completedAt; if (filter.completed !== isCompleted) { return false; } } return true; }); } return workflows; } catch (error) { this.logger.error('Failed to get workflows', { error }); throw error; } } /** * 为需求完整性分析结果生成澄清评论 * @param result 需求分析结果 * @returns 格式化的评论文本 */ private generateClarificationComment(result: RequirementAnalysisResult): string { let comment = `## 需求待完善通知\n\n`; comment += `您的需求文档需要补充一些信息才能继续开发。\n\n`; comment += `### 需求完整性评分\n`; comment += `当前完整性: ${result.completenessScore}/100\n\n`; if (result.missingAspects.length > 0) { comment += `### 缺失的需求方面\n`; result.missingAspects.forEach(aspect => { comment += `- ${aspect}\n`; }); comment += `\n`; } if (result.clarificationQuestions.length > 0) { comment += `### 澄清问题\n`; result.clarificationQuestions.forEach((question, index) => { comment += `${index + 1}. ${question}\n`; }); comment += `\n`; } comment += `请补充上述信息后通知开发团队继续处理。谢谢!\n`; return comment; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/im47cn/feishu-project-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server