职场新人工作流程可视化系统
一、实际应用场景描述
场景:某数字文化公司新入职的运营专员小王,每天需要处理:
- 内容选题 → 素材收集 → 设计制作 → 审核发布 → 数据复盘
- 跨部门沟通(设计部/市场部/技术部)
- 任务进度跟踪与汇报
当前痛点:
- 流程不透明,不知道下一步该找谁
- 任务卡在哪个环节不清楚
- 新人学习成本高,试错成本大
- 无法量化工作效率和瓶颈
二、引入痛点
graph TD A[新人入职] --> B{遇到问题} B --> C[问同事: 流程是什么?] B --> D[自己摸索: 看文档/试错] C --> E[同事忙, 解释不清] D --> F[走弯路, 效率低] E --> G[新人焦虑, 成长慢] F --> G G --> H[团队整体效率下降]
痛点分析表:
痛点 影响 解决思路 流程黑盒 新人不知从何下手 可视化流程图 责任模糊 推诿扯皮, 效率低下 明确节点负责人 进度不透明 无法预估完成时间 实时状态追踪 知识断层 依赖口口相传 结构化知识沉淀
三、核心逻辑讲解
3.1 系统架构
┌─────────────────────────────────────────────────────┐ │ 表现层 (View) │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ 流程图 │ │ 看板 │ │ 统计 │ │ 帮助 │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ ├─────────────────────────────────────────────────────┤ │ 业务层 (Service) │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ 流程 │ │ 任务 │ │ 用户 │ │ 报告 │ │ │ │ 引擎 │ │ 管理 │ │ 权限 │ │ 生成 │ │ │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ ├─────────────────────────────────────────────────────┤ │ 数据层 (Data) │ │ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ │ │ 流程 │ │ 任务 │ │ 操作 │ │ │ │ 定义 │ │ 记录 │ │ 日志 │ │ │ └─────────┘ └─────────┘ └─────────┘ │ └─────────────────────────────────────────────────────┘
3.2 核心概念
- 工作流(Workflow):由多个任务节点组成的有向无环图(DAG)
- 任务节点(TaskNode):流程中的最小执行单元,包含负责人、时限、状态
- 流程实例(ProcessInstance):工作流的一次具体执行
- 状态机(StateMachine):管理任务在"待办→进行中→已完成→已归档"间的转换
四、代码模块化实现
4.1 项目结构
workflow_visualizer/ ├── main.py # 程序入口 ├── config/ │ └── settings.py # 配置文件 ├── models/ │ ├── init.py │ ├── task_node.py # 任务节点模型 │ ├── workflow.py # 工作流模型 │ └── process_instance.py # 流程实例模型 ├── services/ │ ├── init.py │ ├── workflow_engine.py # 工作流引擎 │ ├── task_service.py # 任务服务 │ └── report_service.py # 报告服务 ├── utils/ │ ├── init.py │ ├── visualizer.py # 可视化工具 │ └── helpers.py # 辅助函数 ├── data/ │ └── sample_data.py # 示例数据 ├── static/ # 静态资源(HTML模板) └── README.md # 项目说明
4.2 核心代码实现
"models/task_node.py" - 任务节点模型
""" 任务节点模型模块 功能:定义工作流中的基本执行单元 核心概念:每个节点代表一个具体的工作任务 """
from dataclasses import dataclass, field from enum import Enum, auto from typing import Optional, List import uuid from datetime import datetime
class NodeStatus(Enum): """任务节点状态枚举""" PENDING = auto() # 待办 IN_PROGRESS = auto() # 进行中 COMPLETED = auto() # 已完成 BLOCKED = auto() # 阻塞 ARCHIVED = auto() # 已归档
class NodeType(Enum): """任务类型枚举""" CONTENT = "内容创作" # 内容相关任务 DESIGN = "视觉设计" # 设计相关任务 REVIEW = "审核确认" # 审核类任务 COMMUNICATION = "跨部门沟通" # 沟通协调 DATA = "数据分析" # 数据复盘
@dataclass class TaskNode: """ 任务节点类
属性:
node_id: 唯一标识符
name: 任务名称
description: 任务描述
node_type: 任务类型
assignee: 负责人(部门/角色)
deadline: 截止时间
status: 当前状态
dependencies: 前置依赖节点ID列表
output: 任务产出物描述
created_at: 创建时间
completed_at: 完成时间
方法:
can_start: 检查任务是否可开始(所有依赖已完成)
complete: 完成任务并更新状态
"""
# 基础信息
node_id: str = field(default_factory=lambda: str(uuid.uuid4())[:8])
name: str = ""
description: str = ""
node_type: NodeType = NodeType.CONTENT
# 人员与时间
assignee: str = "" # 如: "运营部-小王", "设计部"
deadline: Optional[datetime] = None
# 状态管理
status: NodeStatus = NodeStatus.PENDING
dependencies: List[str] = field(default_factory=list) # 依赖的node_id列表
# 产出与追踪
output: str = "" # 任务完成后产出的内容/文件
created_at: datetime = field(default_factory=datetime.now)
completed_at: Optional[datetime] = None
def can_start(self, completed_nodes: set) -> bool:
"""
判断任务是否可以开始
参数:
completed_nodes: 已完成节点的ID集合
返回:
bool: True表示所有依赖已完成,可以开始
业务逻辑:
只有当任务的所有前置依赖节点都已完成时,
该任务才能进入"进行中"状态
"""
if not self.dependencies:
return True # 无依赖任务可直接开始
return all(dep_id in completed_nodes for dep_id in self.dependencies)
def complete(self, output_content: str = "") -> None:
"""
完成任务
参数:
output_content: 任务产出内容
业务逻辑:
1. 验证当前状态是否为"进行中"
2. 更新状态为"已完成"
3. 记录完成时间和产出物
"""
if self.status != NodeStatus.IN_PROGRESS:
raise ValueError(f"任务 {self.name} 当前状态为 {self.status.name},无法直接完成")
self.status = NodeStatus.COMPLETED
self.completed_at = datetime.now()
if output_content:
self.output = output_content
def to_dict(self) -> dict:
"""转换为字典,用于JSON序列化"""
return {
"node_id": self.node_id,
"name": self.name,
"description": self.description,
"type": self.node_type.value,
"assignee": self.assignee,
"deadline": self.deadline.isoformat() if self.deadline else None,
"status": self.status.name,
"dependencies": self.dependencies,
"output": self.output,
"created_at": self.created_at.isoformat(),
"completed_at": self.completed_at.isoformat() if self.completed_at else None
}
"models/workflow.py" - 工作流模型
""" 工作流模型模块 功能:定义完整的工作流程结构和元数据 核心概念:工作流是任务节点的有向无环图(DAG) """
from dataclasses import dataclass, field from typing import Dict, List, Optional from datetime import datetime import uuid
from models.task_node import TaskNode, NodeStatus
@dataclass class Workflow: """ 工作流类
属性:
workflow_id: 工作流唯一标识
name: 工作流名称
description: 工作流描述
nodes: 任务节点字典 {node_id: TaskNode}
edges: 边列表 [(from_node_id, to_node_id), ...]
version: 版本号
created_by: 创建者
created_at: 创建时间
tags: 标签列表(用于分类检索)
方法:
add_node: 添加任务节点
add_edge: 添加依赖关系
get_start_nodes: 获取起始节点(无依赖的任务)
validate: 验证工作流是否有效(DAG检查)
visualize_mermaid: 生成Mermaid流程图代码
"""
workflow_id: str = field(default_factory=lambda: f"WF-{uuid.uuid4().hex[:6].upper()}")
name: str = ""
description: str = ""
nodes: Dict[str, TaskNode] = field(default_factory=dict)
edges: List[tuple] = field(default_factory=list)
version: str = "1.0"
created_by: str = ""
created_at: datetime = field(default_factory=datetime.now)
tags: List[str] = field(default_factory=list)
def add_node(self, node: TaskNode) -> None:
"""
添加任务节点到工作流
参数:
node: TaskNode实例
异常:
ValueError: 节点ID已存在时抛出
"""
if node.node_id in self.nodes:
raise ValueError(f"节点ID {node.node_id} 已存在")
self.nodes[node.node_id] = node
def add_edge(self, from_node_id: str, to_node_id: str) -> None:
"""
添加节点间的依赖关系(边)
参数:
from_node_id: 前置节点ID
to_node_id: 后置节点ID
业务逻辑:
1. 验证两个节点都存在
2. 防止循环依赖
3. 添加到edges列表,并更新后置节点的dependencies
"""
if from_node_id not in self.nodes:
raise ValueError(f"前置节点 {from_node_id} 不存在")
if to_node_id not in self.nodes:
raise ValueError(f"后置节点 {to_node_id} 不存在")
# 防止重复添加
if (from_node_id, to_node_id) not in self.edges:
self.edges.append((from_node_id, to_node_id))
self.nodes[to_node_id].dependencies.append(from_node_id)
def get_start_nodes(self) -> List[TaskNode]:
"""
获取起始节点(没有任何依赖的任务)
返回:
List[TaskNode]: 起始节点列表
应用场景:
用于确定工作流的入口点,新人可以从这里开始执行
"""
start_nodes = []
for node in self.nodes.values():
if not node.dependencies:
start_nodes.append(node)
return start_nodes
def validate(self) -> tuple:
"""
验证工作流的有效性
返回:
tuple: (is_valid: bool, errors: List[str])
验证逻辑:
1. 至少有一个节点
2. 无循环依赖
3. 所有边的节点都存在
"""
errors = []
# 检查是否有节点
if not self.nodes:
errors.append("工作流必须至少包含一个任务节点")
return False, errors
# 检查循环依赖(DFS检测)
visited = set()
rec_stack = set()
def has_cycle(node_id: str) -> bool:
visited.add(node_id)
rec_stack.add(node_id)
for _, to_id in self.edges:
if to_id == node_id:
continue
if to_id not in self.nodes:
errors.append(f"边引用了不存在的节点: {to_id}")
return True
if to_id in rec_stack:
errors.append(f"检测到循环依赖: 包含节点 {node_id} 和 {to_id}")
return True
if to_id not in visited and has_cycle(to_id):
return True
rec_stack.remove(node_id)
return False
for node_id in self.nodes:
if node_id not in visited:
if has_cycle(node_id):
break
is_valid = len(errors) == 0
return is_valid, errors
def visualize_mermaid(self) -> str:
"""
生成Mermaid格式的流程图代码
返回:
str: Mermaid流程图代码
应用场景:
可直接粘贴到支持Mermaid的工具(Markdown编辑器、Notion等)
生成可视化流程图
"""
lines = ["graph TD"]
# 状态颜色映射
color_map = {
NodeStatus.PENDING: "#FFE4B5", # 待办-浅橙
NodeStatus.IN_PROGRESS: "#87CEEB", # 进行中-天蓝
NodeStatus.COMPLETED: "#90EE90", # 已完成-浅绿
NodeStatus.BLOCKED: "#FFA07A", # 阻塞-浅红
NodeStatus.ARCHIVED: "#D3D3D3" # 已归档-灰色
}
# 添加节点定义
for node in self.nodes.values():
# 转义特殊字符
safe_name = node.name.replace('"', '\\"')
safe_desc = node.description.replace('"', '\\"')[:30] + "..." if len(node.description) > 30 else node.description.replace('"', '\\"')
# 节点样式
color = color_map.get(node.status, "#FFFFFF")
node_label = f'{node.node_id}["<b>{safe_name}</b><br/>{node.node_type.value}<br/>{safe_desc}"]'
node_style = f'style {node.node_id} fill:{color},stroke:#333,stroke-width:2px'
lines.append(f" {node_label}")
lines.append(f" {node_style}")
# 添加边定义
for from_id, to_id in self.edges:
lines.append(f" {from_id} --> {to_id}")
return "\n".join(lines)
def to_dict(self) -> dict:
"""转换为字典,用于JSON序列化"""
return {
"workflow_id": self.workflow_id,
"name": self.name,
"description": self.description,
"version": self.version,
"created_by": self.created_by,
"created_at": self.created_at.isoformat(),
"tags": self.tags,
"nodes": {k: v.to_dict() for k, v in self.nodes.items()},
"edges": self.edges,
"mermaid_code": self.visualize_mermaid()
}
"services/workflow_engine.py" - 工作流引擎
""" 工作流引擎模块 功能:驱动工作流实例的执行和管理 核心概念:引擎负责状态流转、任务调度、事件处理 """
from dataclasses import dataclass, field from typing import Dict, List, Optional, Callable from datetime import datetime import uuid import copy
from models.task_node import TaskNode, NodeStatus, NodeType from models.workflow import Workflow from models.process_instance import ProcessInstance
class WorkflowEngine: """ 工作流引擎类
职责:
1. 创建工作流实例
2. 推进工作流执行
3. 处理任务状态变更
4. 触发事件回调
5. 生成执行报告
设计模式:
- 观察者模式: 通过回调函数通知状态变化
- 命令模式: 将操作封装为可撤销的命令
"""
def __init__(self):
self._workflows: Dict[str, Workflow] = {} # 注册的工作流模板
self._instances: Dict[str, ProcessInstance] = {} # 运行中的实例
self._event_handlers: Dict[str, List[Callable]] = {
"task_started": [],
"task_completed": [],
"workflow_completed": []
}
def register_workflow(self, workflow: Workflow) -> None:
"""
注册工作流模板
参数:
workflow: 要注册的工作流
"""
is_valid, errors = workflow.validate()
if not is_valid:
raise ValueError(f"工作流验证失败: {'; '.join(errors)}")
self._workflows[workflow.workflow_id] = workflow
print(f"✅ 工作流 '{workflow.name}' 已注册 (ID: {workflow.workflow_id})")
def create_instance(self, workflow_id: str, instance_name: str = "",
creator: str = "系统") -> ProcessInstance:
"""
根据工作流模板创建实例
参数:
workflow_id: 工作流模板ID
instance_name: 实例名称(可选)
creator: 创建者
返回:
ProcessInstance: 新创建的流程实例
"""
if workflow_id not in self._workflows:
raise ValueError(f"工作流模板 {workflow_id} 未找到")
template = self._workflows[workflow_id]
# 深拷贝节点,避免修改模板
instance_nodes = {}
for node_id, node in template.nodes.items():
new_node = copy.deepcopy(node)
new_node.status = NodeStatus.PENDING
new_node.output = ""
new_node.completed_at = None
instance_nodes[node_id] = new_node
# 创建实例
instance = ProcessInstance(
instance_id=f"PI-{uuid.uuid4().hex[:8].upper()}",
workflow_id=workflow_id,
workflow_name=template.name,
name=instance_name or f"{template.name} - 实例",
nodes=instance_nodes,
creator=creator
)
self._instances[instance.instance_id] = instance
print(f"🚀 流程实例已创建: {instance.instance_id}")
return instance
def get_available_tasks(self, instance_id: str) -> List[TaskNode]:
"""
获取当前可执行的任务列表
参数:
instance_id: 流程实例ID
返回:
List[TaskNode]: 可开始执行的任务列表
业务逻辑:
1. 获取所有已完成的任务
2. 遍历所有待办任务
3. 检查其所有依赖是否已完成
"""
if instance_id not in self._instances:
raise ValueError(f"流程实例 {instance_id} 未找到")
instance = self._instances[instance_id]
completed_ids = instance.get_completed_node_ids()
available = []
for node in instance.nodes.values():
if node.status == NodeStatus.PENDING and node.can_start(completed_ids):
available.append(node)
return available
def start_task(self, instance_id: str, node_id: str,
executor: str = "") -> TaskNode:
"""
开始执行任务
参数:
instance_id: 流程实例ID
node_id: 要开始的节点ID
executor: 执行人
返回:
TaskNode: 更新后的任务节点
"""
if instance_id not in self._instances:
raise ValueError(f"流程实例 {instance_id} 未找到")
instance = self._instances[instance_id]
if node_id not in instance.nodes:
raise ValueError(f"节点 {node_id} 不在流程实例中")
node = instance.nodes[node_id]
if node.status != NodeStatus.PENDING:
raise ValueError(f"任务 {node.name} 当前状态为 {node.status.name},无法开始")
# 检查依赖
completed_ids = instance.get_completed_node_ids()
if not node.can_start(completed_ids):
missing = [d for d in node.dependencies if d not in completed_ids]
raise ValueError(f"任务 {node.name} 的依赖未完成: {missing}")
# 更新状态
node.status = NodeStatus.IN_PROGRESS
if executor:
node.assignee = executor
# 触发事件
self._trigger_event("task_started", instance, node)
print(f"▶️ 任务已开始: {node.name} (执行人: {node.assignee})")
return node
def complete_task(self, instance_id: str, node_id: str,
output: str = "") -> TaskNode:
"""
完成任务
参数:
instance_id: 流程实例ID
node_id: 要完成的节点ID
output: 任务产出内容
返回:
TaskNode: 更新后的任务节点
"""
if instance_id not in self._instances:
raise ValueError(f"流程实例 {instance_id} 未找到")
instance = self._instances[instance_id]
if node_id not in instance.nodes:
raise ValueError(f"节点 {node_id} 不在流程实例中")
node = instance.nodes[node_id]
node.complete(output)
# 触发事件
self._trigger_event("task_completed", instance, node)
# 检查工作流是否完成
if instance.is_completed():
self._trigger_event("workflow_completed", instance, None)
print(f"🎉 工作流已完成: {instance.name}")
print(f"✅ 任务已完成: {node.name}")
return node
def get_instance_status(self, instance_id: str) -> dict:
"""
获取流程实例状态概览
参数:
instance_id: 流程实例ID
返回:
dict: 状态概览
"""
if instance_id not in self._instances:
raise ValueError(f"流程实例 {instance_id} 未找到")
instance = self._instances[instance_id]
return instance.get_status_summary()
def register_event_handler(self, event_type: str, handler: Callable) -> None:
"""
注册事件处理器
参数:
event_type: 事件类型 ("task_started", "task_completed", "workflow_completed")
handler: 处理函数
"""
if event_type in self._event_handlers:
self._event_handlers[event_type].append(handler)
def _trigger_event(self, event_type: str, instance: ProcessInstance,
node: Optional[TaskNode]) -> None:
"""触发事件,调用所有注册的handler"""
for handler in self._event_handlers.get(event_type, []):
try:
handler(instance, node)
except Exception as e:
print(f"⚠️ 事件处理器出错: {e}")
def list_instances(self) -> List[dict]:
"""列出所有流程实例"""
return [
{
"instance_id": inst.instance_id,
"name": inst.name,
"workflow_name": inst.workflow_name,
"status": inst.status.name,
"progress": inst.progress_percentage,
"creator": inst.creator
}
for inst in self._instances.values()
]
"services/report_service.py" - 报告服务
""" 报告服务模块 功能:生成各类分析报告,帮助新人学习和管理者决策 """
from typing import Dict, List from collections import defaultdict from datetime import datetime, timedelta
from models.process_instance import ProcessInstance
class ReportService: """ 报告服务类
提供以下报告:
1. 新人上手报告 - 展示标准流程和关键节点
2. 工作效率报告 - 统计任务耗时和瓶颈
3. 团队协作报告 - 展示跨部门协作情况
"""
def __init__(self, engine):
self.engine = engine
def generate_onboarding_report(self, workflow_id: str) -> str:
"""
生成新人上手指南报告
参数:
workflow_id: 工作流模板ID
返回:
str: Markdown格式的报告
"""
workflow = self.engine._workflows.get(workflow_id)
if not workflow:
return "工作流未找到"
lines = [
f"# 📚 {workflow.name} - 新人上手指南",
"",
利用AI解决实际问题,如果你觉得这个工具好用,欢迎关注长安牧笛!