个人编制软件展示

PSI - Purchase Sale Inventory 进销存软件

PSI 进销存系统工作流引擎设计与流程自动化实现

引言

工作流引擎是进销存系统的核心组件,负责管理采购审批、销售审批、费用报销等业务流程。本文将介绍 PSI 进销存系统中工作流引擎的设计与实现,支持业务流程自定义配置。

工作流架构

工作流引擎整体架构:

组件 职责 技术实现
流程设计器 可视化流程配置 前端组件
流程引擎 流程执行与调度 状态机
任务分配器 待办任务分配 规则引擎
通知服务 消息推送 消息队列

核心代码实现

工作流引擎实现:

// 工作流引擎
class WorkflowEngine {
  constructor(config) {
    this.processDefinitions = new Map();
    this.taskService = config.taskService;
    this.notificationService = config.notificationService;
    this.historyService = config.historyService;
  }

  // 部署流程定义
  async deployProcess(processDef) {
    const process = new ProcessDefinition(processDef);
    this.processDefinitions.set(process.id, process);
    return process;
  }

  // 启动流程实例
  async startProcess(processDefId, businessKey, initiator, variables = {}) {
    const processDef = this.processDefinitions.get(processDefId);
    if (!processDef) {
      throw new Error(`Process definition not found: ${processDefId}`);
    }

    // 创建流程实例
    const processInstance = new ProcessInstance({
      processDefId,
      businessKey,
      initiator,
      status: 'running',
      variables: { ...variables }
    });

    // 执行开始节点
    const startNode = processDef.getStartNode();
    processInstance.currentNode = startNode.id;

    // 执行流转
    await this.executeNode(processInstance, startNode);

    return processInstance;
  }

  // 执行节点
  async executeNode(processInstance, node) {
    // 记录节点进入历史
    await this.historyService.recordNodeEntry(processInstance, node);

    switch (node.type) {
      case 'start':
        // 查找下一步
        const outgoing = processDef.getOutgoingFlows(node.id);
        for (const flow of outgoing) {
          if (await this.evaluateCondition(processInstance, flow.condition)) {
            const nextNode = processDef.getNode(flow.targetRef);
            await this.executeNode(processInstance, nextNode);
          }
        }
        break;

      case 'userTask':
        // 创建用户任务
        await this.createUserTask(processInstance, node);
        break;

      case 'serviceTask':
        // 执行服务任务
        await this.executeServiceTask(processInstance, node);
        break;

      case 'exclusiveGateway':
        // 排他网关
        const conditions = processDef.getOutgoingFlows(node.id);
        for (const flow of conditions) {
          if (await this.evaluateCondition(processInstance, flow.condition)) {
            const nextNode = processDef.getNode(flow.targetRef);
            await this.executeNode(processInstance, nextNode);
            return;
          }
        }
        break;

      case 'parallelGateway':
        // 并行网关 - 开启所有分支
        const parallelFlows = processDef.getOutgoingFlows(node.id);
        await Promise.all(parallelFlows.map(async (flow) => {
          const nextNode = processDef.getNode(flow.targetRef);
          await this.executeNode(processInstance, nextNode);
        }));
        break;

      case 'end':
        // 流程结束
        processInstance.status = 'completed';
        processInstance.endTime = new Date();
        await this.historyService.recordProcessEnd(processInstance);
        break;
    }
  }

  // 创建用户任务
  async createUserTask(processInstance, node) {
    // 获取任务分配
    const candidateUsers = await this.resolveCandidateUsers(
      processInstance, node.candidateUsers
    );
    const candidateGroups = await this.resolveCandidateGroups(
      processInstance, node.candidateGroups
    );

    const task = {
      id: this.generateId(),
      processInstanceId: processInstance.id,
      nodeId: node.id,
      name: node.name,
      description: node.documentation,
      status: 'pending',
      candidateUsers,
      candidateGroups,
      createTime: new Date(),
      dueDate: node.dueDate ?
        this.calculateDueDate(node.dueDate, processInstance) : null
    };

    await this.taskService.createTask(task);

    // 发送通知
    await this.notificationService.notifyUsers(
      candidateUsers,
      '您有新的待办任务',
      { taskId: task.id, taskName: task.name }
    );
  }

  // 完成任务
  async completeTask(taskId, userId, variables = {}) {
    const task = await this.taskService.getTask(taskId);
    if (!task) {
      throw new Error(`Task not found: ${taskId}`);
    }

    // 验证任务权限
    if (!this.hasTaskPermission(task, userId)) {
      throw new Error('User does not have permission to complete this task');
    }

    // 更新任务状态
    task.status = 'completed';
    task.completedBy = userId;
    task.completedTime = new Date();
    task.variables = variables;
    await this.taskService.updateTask(task);

    // 更新流程实例变量
    const processInstance = await this.getProcessInstance(task.processInstanceId);
    Object.assign(processInstance.variables, variables);

    // 继续执行流程
    const processDef = this.processDefinitions.get(processInstance.processDefId);
    const currentNode = processDef.getNode(task.nodeId);
    const outgoingFlows = processDef.getOutgoingFlows(currentNode.id);

    for (const flow of outgoingFlows) {
      if (!flow.condition || await this.evaluateCondition(processInstance, flow.condition)) {
        const nextNode = processDef.getNode(flow.targetRef);
        await this.executeNode(processInstance, nextNode);
        return;
      }
    }
  }

  // 条件评估
  async evaluateCondition(processInstance, condition) {
    if (!condition) return true;

    // 简单表达式评估
    const vars = processInstance.variables;
    try {
      // 安全评估:只允许访问预定义的变量
      const safeVars = {
        amount: vars.amount || 0,
        quantity: vars.quantity || 0,
        price: vars.price || 0,
        total: vars.total || 0,
        status: vars.status || '',
        type: vars.type || '',
        customerId: vars.customerId || '',
        userId: vars.userId || '',
        department: vars.department || ''
      };

      // 使用 Function 构造函数(实际生产应使用安全的表达式解析器)
      const fn = new Function('vars', `with(vars) { return ${condition}; }`);
      return fn(safeVars);
    } catch (error) {
      console.error('Condition evaluation failed:', error);
      return false;
    }
  }

  // 获取任务分配
  async resolveCandidateUsers(processInstance, candidateUsers) {
    if (!candidateUsers) return [];

    if (typeof candidateUsers === 'string') {
      return [candidateUsers];
    }

    if (Array.isArray(candidateUsers)) {
      return candidateUsers;
    }

    if (candidateUsers.type === 'initiator') {
      return [processInstance.initiator];
    }

    if (candidateUsers.type === 'variable') {
      return [processInstance.variables[candidateUsers.expression]];
    }

    if (candidateUsers.type === 'script') {
      const fn = new Function('vars', `with(vars) { return ${candidateUsers.expression}; }`);
      return fn(processInstance.variables);
    }

    return [];
  }

  generateId() {
    return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }
}

// 流程定义
class ProcessDefinition {
  constructor(config) {
    this.id = config.id || this.generateId();
    this.name = config.name;
    this.version = config.version || 1;
    this.nodes = new Map();
    this.flows = [];

    // 解析节点
    config.nodes.forEach(node => {
      this.nodes.set(node.id, node);
    });

    // 解析连线
    this.flows = config.flows || [];
  }

  getStartNode() {
    return [...this.nodes.values()].find(n => n.type === 'start');
  }

  getNode(nodeId) {
    return this.nodes.get(nodeId);
  }

  getOutgoingFlows(nodeId) {
    return this.flows.filter(f => f.sourceRef === nodeId);
  }

  generateId() {
    return `process_${Date.now()}`;
  }
}

// 流程实例
class ProcessInstance {
  constructor(config) {
    this.id = this.generateId();
    this.processDefId = config.processDefId;
    this.businessKey = config.businessKey;
    this.initiator = config.initiator;
    this.status = config.status || 'running';
    this.variables = config.variables || {};
    this.currentNode = null;
    this.startTime = new Date();
    this.endTime = null;
  }

  generateId() {
    return `instance_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
  }
}

流程设计器

可视化流程配置:

// 流程设计器数据模型
class ProcessDesigner {
  // 导出流程定义 JSON
  exportProcess(nodes, flows) {
    const processDef = {
      id: `process_${Date.now()}`,
      name: '新流程',
      version: 1,
      nodes: nodes.map(node => this.exportNode(node)),
      flows: flows.map(flow => this.exportFlow(flow))
    };

    return processDef;
  }

  // 导出节点
  exportNode(node) {
    return {
      id: node.id,
      name: node.name,
      type: node.type,
      documentation: node.documentation || '',
      x: node.x,
      y: node.y,
      // 用户任务特定属性
      candidateUsers: node.candidateUsers || null,
      candidateGroups: node.candidateGroups || null,
      dueDate: node.dueDate || null,
      // 服务任务特定属性
      serviceType: node.serviceType || null,
      serviceConfig: node.serviceConfig || null,
      // 网关特定属性
      defaultFlow: node.defaultFlow || null
    };
  }

  // 导出连线
  exportFlow(flow) {
    return {
      id: flow.id,
      sourceRef: flow.source.id,
      targetRef: flow.target.id,
      condition: flow.condition || null,
      name: flow.name || ''
    };
  }

  // 导入流程定义
  importProcess(json) {
    return {
      nodes: json.nodes.map(n => this.importNode(n)),
      flows: json.flows.map(f => this.importFlow(f, json))
    };
  }

  // 验证流程定义
  validateProcess(nodes, flows) {
    const errors = [];

    // 检查是否包含开始节点
    const startNodes = nodes.filter(n => n.type === 'start');
    if (startNodes.length === 0) {
      errors.push('流程必须包含开始节点');
    }

    // 检查是否有未连接的节点
    const connectedNodes = new Set();
    flows.forEach(f => {
      connectedNodes.add(f.source.id);
      connectedNodes.add(f.target.id);
    });

    nodes.forEach(node => {
      if (!connectedNodes.has(node.id) && node.type !== 'start' && node.type !== 'end') {
        errors.push(`节点 "${node.name}" 未连接到流程`);
      }
    });

    // 检查结束节点
    const endNodes = nodes.filter(n => n.type === 'end');
    if (endNodes.length === 0) {
      errors.push('流程必须包含结束节点');
    }

    // 检查死循环
    if (this.hasDeadLoop(nodes, flows)) {
      errors.push('流程存在死循环');
    }

    return {
      valid: errors.length === 0,
      errors
    };
  }

  // 检查死循环
  hasDeadLoop(nodes, flows) {
    // 简化的死循环检测
    const inDegree = new Map();
    const outDegree = new Map();

    nodes.forEach(n => {
      inDegree.set(n.id, 0);
      outDegree.set(n.id, 0);
    });

    flows.forEach(f => {
      inDegree.set(f.target.id, inDegree.get(f.target.id) + 1);
      outDegree.set(f.source.id, outDegree.get(f.source.id) + 1);
    });

    // 检查是否有节点只有出度没有入度(除了开始节点)
    for (const [nodeId, inD] of inDegree) {
      const outD = outDegree.get(nodeId);
      const node = nodes.find(n => n.id === nodeId);
      if (inD === 0 && outD > 0 && node.type !== 'start') {
        return true;
      }
    }

    return false;
  }
}

// 常用流程模板
const ProcessTemplates = {
  // 采购审批流程
  purchaseApproval: {
    name: '采购审批流程',
    nodes: [
      { id: 'start', name: '开始', type: 'start', x: 100, y: 200 },
      { id: 'apply', name: '提交申请', type: 'userTask', x: 250, y: 200 },
      { id: 'gateway1', name: '金额判断', type: 'exclusiveGateway', x: 400, y: 200 },
      { id: 'approve1', name: '部门经理审批', type: 'userTask', x: 550, y: 100 },
      { id: 'approve2', name: '总经理审批', type: 'userTask', x: 550, y: 300 },
      { id: 'execute', name: '执行采购', type: 'userTask', x: 700, y: 200 },
      { id: 'end', name: '结束', type: 'end', x: 850, y: 200 }
    ],
    flows: [
      { source: { id: 'start' }, target: { id: 'apply' } },
      { source: { id: 'apply' }, target: { id: 'gateway1' } },
      { source: { id: 'gateway1' }, target: { id: 'approve1' }, condition: 'amount <= 10000' },
      { source: { id: 'gateway1' }, target: { id: 'approve2' }, condition: 'amount > 10000' },
      { source: { id: 'approve1' }, target: { id: 'execute' } },
      { source: { id: 'approve2' }, target: { id: 'execute' } },
      { source: { id: 'execute' }, target: { id: 'end' } }
    ]
  },

  // 销售审批流程
  salesApproval: {
    name: '销售审批流程',
    nodes: [
      { id: 'start', name: '开始', type: 'start', x: 100, y: 200 },
      { id: 'create', name: '创建订单', type: 'userTask', x: 250, y: 200 },
      { id: 'check', name: '库存检查', type: 'serviceTask', x: 400, y: 200 },
      { id: 'approve', name: '销售审批', type: 'userTask', x: 550, y: 200 },
      { id: 'delivery', name: '发货', type: 'userTask', x: 700, y: 200 },
      { id: 'end', name: '结束', type: 'end', x: 850, y: 200 }
    ],
    flows: [
      { source: { id: 'start' }, target: { id: 'create' } },
      { source: { id: 'create' }, target: { id: 'check' } },
      { source: { id: 'check' }, target: { id: 'approve' } },
      { source: { id: 'approve' }, target: { id: 'delivery' } },
      { source: { id: 'delivery' }, target: { id: 'end' } }
    ]
  }
};

最佳实践建议

总结

PSI 进销存系统的工作流引擎提供了完整的流程自动化能力:

通过工作流引擎,可以显著提升进销存系统的业务处理效率和规范性。

← 下一篇:PSI进销存系统智能报表与经营分析