转帖|其它|编辑:郝浩|2009-02-09 11:33:47.000|阅读 3526 次
概述:工作流是企业开发中不可或缺的一个重要组件。有了工作流,客户需求的实现速度将大大提高,同时兼顾到开发效率,灵活性。Java领域已经有了多个稳定的工作流,成了Java占领企业级开发的有力助手。
# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>
工作流是企业开发中不可或缺的一个重要组件。有了工作流,客户需求的实现速度将大大提高,同时兼顾到开发效率,灵活性。Java领域已经有了多个稳定的工作流,成了Java占领企业级开发的有力助手。但在ROR领域,目前还没有出色的工作流出现。RubyForge上有一些工作流的项目,但仔细看下,都是针对Java工作流的移植,而且达不到可以实用的程度。面对这个现状,我在2006年自己开发了一个小型Ruby工作流,虽然代码量小,但是实用性却不错,对于一些真实的使用案例能够轻松胜任,也有力的支撑着我继续向ROR道路前行。
下面介绍我的工作流是怎么实现的。
用VC写一个工作流设计器,这个小软件功能比较简单,包含一些简单符号的绘图及拖拽,比如开始、结束、状态、流转。对于每个状态可以设置权限,对于每个流转可以设置条件。我在工作流领域研究的不是很深,开发这个设计器就以实用性为原则,没有实现的特别复杂。在能够实现用户需求的基础上怎么简单怎么做。
文件保存为xml格式
Xml代码 
<?xml version="1.0" encoding="gb2312" ?>  
<workflow>  
    <start right="" leave="" enter="@form.a2 = @user.truename
@form.c2 = @user.department.name" x1="97" y1="156" x2="247" y2="279" />  
    <end right="行政归档" x1="969" y1="148" x2="1129" y2="285" enter="" />  
    <state name="部门经理审批" right="领导" enter="" leave="" x1="343" y1="179" x2="453" y2="253" />  
    <state name="总经理审批" right="经理审批" enter="" leave="" x1="566" y1="34" x2="668" y2="98" />  
    <state name="行政审批" right="行政审批" enter="" leave="" x1="717" y1="191" x2="870" y2="244" />  
    <trasit name="" condition="" from="开始" to="部门经理审批" />  
    <trasit name="大于等于3天" condition="@form.b5!=nil && @form.b5 >=3" from="部门经理审批" to="总经理审批" />  
    <trasit name="" condition="@form.b5 == nil || @form.b5 <3" from="部门经理审批" to="行政审批" />  
    <trasit name="" condition="" from="总经理审批" to="行政审批" />  
    <trasit name="" condition="" from="行政审批" to="结束" />  
</workflow> 
<?xml version="1.0" encoding="gb2312" ?>
<workflow>
    <start right="" leave="" enter="@form.a2 = @user.truename
@form.c2 = @user.department.name" x1="97" y1="156" x2="247" y2="279" />
    <end right="行政归档" x1="969" y1="148" x2="1129" y2="285" enter="" />
    <state name="部门经理审批" right="领导" enter="" leave="" x1="343" y1="179" x2="453" y2="253" />
    <state name="总经理审批" right="经理审批" enter="" leave="" x1="566" y1="34" x2="668" y2="98" />
    <state name="行政审批" right="行政审批" enter="" leave="" x1="717" y1="191" x2="870" y2="244" />
    <trasit name="" condition="" from="开始" to="部门经理审批" />
    <trasit name="大于等于3天" condition="@form.b5!=nil && @form.b5 >=3" from="部门经理审批" to="总经理审批" />
    <trasit name="" condition="@form.b5 == nil || @form.b5 <3" from="部门经理审批" to="行政审批" />
    <trasit name="" condition="" from="总经理审批" to="行政审批" />
    <trasit name="" condition="" from="行政审批" to="结束" />
</workflow>
然后将这个文件发布到系统上,由Ruby来解析这个工作流,解析工作流的Ruby代码(放在lib目录下)如下:
Ruby代码 
#Flow.rb   
  
require 'rexml/document'  
require "State"  
require "Trasit"  
require "Flow"  
require "pp"  
  
include REXML   
  
class Flow    
  attr_accessor :name, :publish_time  
  attr_reader :trasits, :states  
  def initialize(name, xmlstr, publish_time)   
    @publish_time = publish_time   
    @name = name   
       
    #存放所有状态,包括开始状态和结束,开始状态放在第一个,结束状态放在最后   
    @states = Array.new  
    @trasits = Array.new  
       
    #载入XML文档   
    doc = Document.new(xmlstr)   
       
    #开始解析doc文档   
    root = doc.root   
       
    #解析开始状态节点   
    root.elements.each("start") {|element|   
      start = State.start   
      start.name = "开始"  
      start.enter = element.attributes["enter"].gbk   
      start.leave = element.attributes["leave"].gbk   
      start.right = element.attributes["right"].gbk   
      start.x1 = element.attributes["x1"].to_i   
      start.x2 = element.attributes["x2"].to_i   
      start.y1 = element.attributes["y1"].to_i   
      start.y2 = element.attributes["y2"].to_i   
      @states << start   
      break  
    }   
       
    #解析所有状态节点   
    root.elements.each("state") {|element|   
      state = State.new  
      state.name = element.attributes["name"].gbk   
      state.right = element.attributes["right"].gbk   
      state.enter = element.attributes["enter"].gbk   
      state.leave = element.attributes["leave"].gbk   
      state.x1 = element.attributes["x1"].to_i   
      state.x2 = element.attributes["x2"].to_i   
      state.y1 = element.attributes["y1"].to_i   
      state.y2 = element.attributes["y2"].to_i   
      @states << state   
    }   
       
    #解析结束状态节点   
    root.elements.each("end") {|element|   
      end_node = State.new  
      end_node.name = "结束"  
      end_node.right = element.attributes["right"].gbk   
      end_node.enter = element.attributes["enter"].gbk   
      end_node.x1 = element.attributes["x1"].to_i   
      end_node.x2 = element.attributes["x2"].to_i   
      end_node.y1 = element.attributes["y1"].to_i   
      end_node.y2 = element.attributes["y2"].to_i   
         
      @states << end_node   
    }   
    #解析所有流转   
    root.elements.each("trasit") {|element|   
      from_name = element.attributes["from"].gbk   
      to_name = element.attributes["to"].gbk   
           
      for state in @states  
        if state.name == from_name   
          from_node = state   
        end  
        if state.name == to_name   
          to_node = state   
        end  
      end          
           
      trasit = Trasit.new(from_node, to_node)   
      trasit.name = element.attributes["name"].gbk   
      trasit.condition = element.attributes["condition"].gbk   
           
      from_node.trasits << trasit   
      to_node.guest_trasits << trasit   
      @trasits << trasit     
    }   
  end  
     
  def start   
    @states[0]   
  end  
     
  def get_state(name)   
    for state in @states  
      return state if state.name == name   
    end  
    nil  
  end  
     
end 
#Flow.rb
require 'rexml/document'
require "State"
require "Trasit"
require "Flow"
require "pp"[SPAN]
include REXML
class Flow 
  attr_accessor :name, :publish_time
  attr_reader :trasits, :states
  def initialize(name, xmlstr, publish_time)
    @publish_time = publish_time
    @name = name
   
    #存放所有状态,包括开始状态和结束,开始状态放在第一个,结束状态放在最后
    @states = Array.new
    @trasits = Array.new
   
    #载入XML文档
    doc = Document.new(xmlstr)
   
    #开始解析doc文档
    root = doc.root
   
    #解析开始状态节点
    root.elements.each("start") {|element|
      start = State.start
      start.name = "开始"
      start.enter = element.attributes["enter"].gbk
      start.leave = element.attributes["leave"].gbk
      start.right = element.attributes["right"].gbk
      start.x1 = element.attributes["x1"].to_i
      start.x2 = element.attributes["x2"].to_i
      start.y1 = element.attributes["y1"].to_i
      start.y2 = element.attributes["y2"].to_i
      @states << start
      break
    }
   
    #解析所有状态节点
    root.elements.each("state") {|element|
      state = State.new
      state.name = element.attributes["name"].gbk
      state.right = element.attributes["right"].gbk
      state.enter = element.attributes["enter"].gbk
      state.leave = element.attributes["leave"].gbk
      state.x1 = element.attributes["x1"].to_i
      state.x2 = element.attributes["x2"].to_i
      state.y1 = element.attributes["y1"].to_i
      state.y2 = element.attributes["y2"].to_i
      @states << state
    }
   
    #解析结束状态节点
    root.elements.each("end") {|element|
      end_node = State.new
      end_node.name = "结束"
      end_node.right = element.attributes["right"].gbk
      end_node.enter = element.attributes["enter"].gbk
      end_node.x1 = element.attributes["x1"].to_i
      end_node.x2 = element.attributes["x2"].to_i
      end_node.y1 = element.attributes["y1"].to_i
      end_node.y2 = element.attributes["y2"].to_i
      
      @states << end_node
    }
    #解析所有流转
    root.elements.each("trasit") {|element|
      from_name = element.attributes["from"].gbk
      to_name = element.attributes["to"].gbk
    
      for state in @states
        if state.name == from_name
          from_node = state
        end
        if state.name == to_name
          to_node = state
        end
      end    
    
      trasit = Trasit.new(from_node, to_node)
      trasit.name = element.attributes["name"].gbk
      trasit.condition = element.attributes["condition"].gbk
    
      from_node.trasits << trasit
      to_node.guest_trasits << trasit
      @trasits << trasit 
    }
  end
  
  def start
    @states[0]
  end
  
  def get_state(name)
    for state in @states
      return state if state.name == name
    end
    nil
  end
  
end[SPAN]
Ruby代码 
#FlowMeta.rb   
  
$LOAD_PATH.unshift(File.dirname(__FILE__))   
  
require "Flow"  
require "EncodeUtil"  
  
class FlowMeta   
  class << self  
    def LoadAllFlows()   
      YtLog.info "loading all workflow..."  
      $Workflows.clear   
      flows = YtwgWorkflow.find(:all)   
      for flow in flows   
        #LoadWorkFlow(flow.name, flow.content.sub!('<?xml version="1.0" encoding="gb2312" ?>', ''))   
        LoadWorkFlow(flow.name, flow.content, flow.publish_time)   
      end  
    end  
           
    def LoadWorkFlow(name, str, publish_time=Time.new)   
      YtLog.info name   
      $Workflows[name] = Flow.new(name, str, publish_time)   
    end  
           
    def Remove(name)   
      $Workflows.delete(name)   
    end  
  end  
end 
#FlowMeta.rb
$LOAD_PATH.unshift(File.dirname(__FILE__))
require "Flow"
require "EncodeUtil"
class FlowMeta
  class << self
    def LoadAllFlows()
      YtLog.info "loading all workflow..."
      $Workflows.clear
      flows = YtwgWorkflow.find(:all)
      for flow in flows
        #LoadWorkFlow(flow.name, flow.content.sub!('<?xml version="1.0" encoding="gb2312" ?>', ''))
        LoadWorkFlow(flow.name, flow.content, flow.publish_time)
      end
    end
  
    def LoadWorkFlow(name, str, publish_time=Time.new)
      YtLog.info name
      $Workflows[name] = Flow.new(name, str, publish_time)
    end
  
    def Remove(name)
      $Workflows.delete(name)
    end
  end
end
Ruby代码 
#State.rb   
  
##工作流中的状态   
  
require "Trasit"  
class State   
  attr_accessor :name, :leave, :enter, :right, :trasits, :guest_trasits  
  attr_accessor :x1, :x2, :y1, :y2  
  def initialize   
    #从此状态出发的流转   
    @trasits = Array.new  
       
    #从其他状态到此状态的流转   
    @guest_trasits = Array.new  
  end  
       
  def trasits   
    @trasits  
  end  
       
  def add_trasit(trasit)   
    @trasits << trasit   
  end  
     
  def add_guest_trasit(trasit)   
    @guest_trasits << trasit   
  end  
       
  class << self  
    def start   
      start = State.new  
      start.name = "开始"  
      start   
    end  
  end  
end 
#State.rb
##工作流中的状态
require "Trasit"
class State
  attr_accessor :name, :leave, :enter, :right, :trasits, :guest_trasits
  attr_accessor :x1, :x2, :y1, :y2
  def initialize
    #从此状态出发的流转
    @trasits = Array.new
    
    #从其他状态到此状态的流转
    @guest_trasits = Array.new
  end
 
  def trasits
    @trasits
  end
 
  def add_trasit(trasit)
    @trasits << trasit
  end
  
  def add_guest_trasit(trasit)
    @guest_trasits << trasit
  end
 
  class << self
    def start
      start = State.new
      start.name = "开始"
      start
    end
  end
end
Ruby代码 
#Trasit.rb   
  
class Trasit   
    attr_accessor :condition, :name, :from, :to  
       
    #新建流转类,from,to均为State类对象   
    def initialize(from, to)   
        @from = from   
        @to = to   
    end  
end 
#Trasit.rb
class Trasit
 attr_accessor :condition, :name, :from, :to
 
 #新建流转类,from,to均为State类对象
 def initialize(from, to)
  @from = from
  @to = to
 end
end
OK,解析工作流的任务就算完成了,250行Ruby代码,一个小型的,可定制化程度高的工作流引擎就算是完成了。下面我们就看怎么使用这个工作流了。
工作流引擎完成以后下面自然而然就会想到用户在每个流程点上看到的表单界面从何而来?对于这个功能,我专门写了表单设计器和表单解析引擎,表单解析引擎可将xml格式的表单翻译为html格式的表单。这个表单组件更为复杂,超出了本讨论的范围,暂且先不说了。[SPAN]
下面说一下数据库表,为了使用这个工作流引擎需要建立3张表:
Sql代码 
//工作流表   
CREATE TABLE `ytwg_workflow` (   
  `id` int(11) NOT NULL auto_increment,   
  `name` varchar(100) default NULL,          //工作流名称   
  `content` longtext,                                   //工作流内容,设计器保存的xml文件   
  `publish_time` datetime default NULL,     //发布时间   
  `formtable` varchar(30) default NULL,      //表单数据存放的表格,每个工作流建立后会单独建立数据库表,存放表单数据   
  `position` int(11) default NULL,                 //排序位置   
  `reserved1` varchar(100) default NULL,       
  `reserved2` varchar(100) default NULL,   
  `reserved3` varchar(100) default NULL,   
  `reserved4` varchar(100) default NULL,   
  `reserved5` varchar(100) default NULL,   
  `reserved6` varchar(100) default NULL,   
  PRIMARY KEY  (`id`)   
)    
  
//工作流状态表单界面表   
CREATE TABLE `ytwg_stateinterface` (   
  `id` int(11) NOT NULL auto_increment,   
  `flowid` int(11) default NULL,                     //工作流id   
  `name` varchar(100) default NULL,            //状态名称   
  `content` longtext,                                     //表单,表单设计器保存的xml文件   
  `publish_time` datetime default NULL,       //发布时间   
  `reserved1` varchar(100) default NULL,        
  `reserved2` varchar(100) default NULL,   
  `reserved3` varchar(100) default NULL,   
  `reserved4` varchar(100) default NULL,   
  `reserved5` varchar(100) default NULL,   
  `reserved6` varchar(100) default NULL,   
  PRIMARY KEY  (`id`)   
)    
  
//表单处理记录表   
CREATE TABLE `ytwg_formhistory` (   
  `id` int(11) NOT NULL auto_increment,   
  `userid` int(11) default NULL,                    //用户id   
  `flowid` int(11) default NULL,                     //工作流id   
  `formid` int(11) default NULL,                     //表单id   
  `process_time` datetime default NULL,      //处理时间   
  PRIMARY KEY  (`id`)   
)  
//工作流表
CREATE TABLE `ytwg_workflow` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(100) default NULL,          //工作流名称
  `content` longtext,                                   //工作流内容,设计器保存的xml文件
  `publish_time` datetime default NULL,     //发布时间
  `formtable` varchar(30) default NULL,      //表单数据存放的表格,每个工作流建立后会单独建立数据库表,存放表单数据
  `position` int(11) default NULL,                 //排序位置
  `reserved1` varchar(100) default NULL,    
  `reserved2` varchar(100) default NULL,
  `reserved3` varchar(100) default NULL,
  `reserved4` varchar(100) default NULL,
  `reserved5` varchar(100) default NULL,
  `reserved6` varchar(100) default NULL,
  PRIMARY KEY  (`id`)
)
//工作流状态表单界面表
CREATE TABLE `ytwg_stateinterface` (
  `id` int(11) NOT NULL auto_increment,
  `flowid` int(11) default NULL,                     //工作流id
  `name` varchar(100) default NULL,            //状态名称
  `content` longtext,                                     //表单,表单设计器保存的xml文件
  `publish_time` datetime default NULL,       //发布时间
  `reserved1` varchar(100) default NULL,     
  `reserved2` varchar(100) default NULL,
  `reserved3` varchar(100) default NULL,
  `reserved4` varchar(100) default NULL,
  `reserved5` varchar(100) default NULL,
  `reserved6` varchar(100) default NULL,
  PRIMARY KEY  (`id`)
)
//表单处理记录表
CREATE TABLE `ytwg_formhistory` (
  `id` int(11) NOT NULL auto_increment,
  `userid` int(11) default NULL,                    //用户id
  `flowid` int(11) default NULL,                     //工作流id
  `formid` int(11) default NULL,                     //表单id
  `process_time` datetime default NULL,      //处理时间
  PRIMARY KEY  (`id`)
)
每发布一个工作流后,跟着要为这个工作流动态创建数据库表,存放表单数据。我是通过向这个工作流发布一个表单模板来动态创建表的。
下面看如何使用工作流:
Ruby代码 
#发布工作流     
def create   
    stream = params[:ytwg_workflow][:content]   
    content = stream.read   
    name = stream.original_filename[0, stream.original_filename.index(".")]   
    if YtwgWorkflow.find(:all, :conditions=>"name='#{name}'").size > 0   
      flash[:error] = "存在同名工作流,上传失败"  
      render :action => 'new'  
      return  
    end  
       
    @ytwg_workflow = YtwgWorkflow.new()   
    @ytwg_workflow.name = name   
    begin  
      @ytwg_workflow.content = content   
    rescue  
      flash[:error] = "上传文件非法"  
      render :action => 'new'  
    end  
    @ytwg_workflow.publish_time = Time.new  
    if @ytwg_workflow.save   
      FlowMeta.LoadWorkFlow(@ytwg_workflow.name, @ytwg_workflow.content.sub!('<?xml version="1.0" encoding="gb2312" ?>', ''))   
      flash[:notice] = '添加工作流成功'  
      redirect_to :action => 'list'  
    else  
      flash[:error] = "添加工作流失败"  
      render :action => 'new'  
    end  
  end  
  
  #上传表定义模板,根据这个表单动态生成数据库表   
  def upload_formtable   
    stream = params[:content]   
    content = stream.read   
    helper = XMLHelper.new  
    helper.ReadFromString(content)   
    formtable = helper.tables[0]   
    if !formtable   
      flash[:notice] = "上传文件格式错误"  
      redirect_to :action=>"listinterface"  
      return  
    end  
    conn = ActiveRecord::Base.connection   
    conn.create_table "ytwg_#{formtable.GetTableID}", :primary_key=>:id do |t|   
      t.column "userid", :integer         #流程发起人的id   
      t.column "flowid", :integer         #工作流的id   
      Integer(0).upto(formtable.GetRowCount()-1) do |row|   
        next if formtable.IsEmptyRow(row)   
        Integer(0).upto(formtable.GetColumnCount()-1) do |col|   
          next if formtable.IsEmptyCol(col)   
          cell = formtable.GetCell(row, col)   
          next if !cell.IsStore || !cell.IsEffective   
          next if formtable.GetCellDBFieldName(row, col).downcase == "id"  
                
          t.column "_state", :string, :limit=>30   
          t.column "_madetime", :datetime  
          t.column "_lastprocesstime", :datetime  
          if cell.GetDataType == 1    #CCell.CtNumeric   
            t.column formtable.GetCellDBFieldName(row, col).downcase, :float  
          elsif cell.GetDataType == 0    #CCell.CtText                  
            if cell.IsCheckWidth()   
              t.column formtable.GetCellDBFieldName(row, col).downcase, :string, {:limit=>cell.GetTextWidth}   
            else  
              t.column formtable.GetCellDBFieldName(row, col).downcase, :string, {:limit=>100}   
            end        
          elsif cell.GetDataType == 3 #CCell.CtDate   
            t.column formtable.GetCellDBFieldName(row, col).downcase, :datetime  
          end               
        end  
      end  
    end  
       
    flow = YtwgWorkflow.find(params[:id])   
    flow.formtable = formtable.GetTableID   
    flow.save   
       
    flash[:notice] = "建表成功"  
    redirect_to :action=>"listinterface"  
  end  
  
  #上传状态节点的表单界面   
  def uploadinterface   
    stream = params[:content]   
    content = stream.read   
       
    interfaces = YtwgStateinterface.find(:all, :conditions=>"flowid = #{params[:id]} and name = '#{params[:name]}'")   
    if interfaces.size > 0   
      interface = interfaces[0]   
      interface.publish_time = Time.new  
    else  
      interface = YtwgStateinterface.new  
      interface.flowid = params[:id]   
      interface.name = params[:name]   
      interface.publish_time = Time.new  
    end  
    interface.content = content  #EncodeUtil.change("UTF-8", "GB2312", content)   
    interface.save   
    flash[:notice] = "上传状态界面成功"  
    redirect_to :action=>"listinterface"  
  end  
  
  #用户点击某一工作流连接后,查看自己已经发起的工作流。   
  def show_form   
    @flow = YtwgWorkflow.find(params[:flowid])   
    YtwgForm.set_table_name("ytwg_" + @flow.formtable)   
    YtwgForm.reset_column_information()    
    form = YtwgForm.find(params[:formid])   
        
    interfaces = YtwgStateinterface.find(:all, :conditions=>"flowid=#{params[:flowid]} and name='#{form._state.split(',')[0]}'")   
    if interfaces.size > 0   
      helper = XMLHelper.new  
      helper.ReadFromString(interfaces[0].content)   
      @style = helper.StyleToHTML(helper.tables[0])   
      @html = helper.TableToEditHTML(helper.tables[0], helper.dictionFactory,    
        {:record=>form, :encoding=>"gb2312"})   
      @historys = YtwgFormhistory.find(:all, :conditions=>"flowid=#{params[:flowid]} and formid = #{params[:formid]}")   
    else  
      render :text=>"没有上传工作流界面"  
    end  
  end  
  
  #用户发起或者审批一个表单   
  def write_form   
    @flow = YtwgWorkflow.find(params[:flowid])   
    YtwgForm.set_table_name("ytwg_" + @flow.formtable)   
    YtwgForm.reset_column_information()    
  
    if params[:formid]   
      form_record = YtwgForm.find(params[:formid])   
      state_name = form_record._state   
    else  
      form_record = YtwgForm.new  
      form_record._state = '开始'  
      state_name = form_record._state   
    end  
       
    states = []   
    for state in form_record._state.split(',')   
      states << state if checkright(state)   
    end  
    if states.size > 0   
      state_name = states[0]   
    else  
      state_name = '开始'  
    end  
  
    process = FlowProcess.new($Workflows[@flow.name], form_record, state_name)   
    process.user = session[:user]   
    process.signal_enter   
       
    interfaces = YtwgStateinterface.find(:all, :conditions=>"flowid=#{@flow.id} and name = '#{state_name}'")   
    if interfaces.size ==0   
      render :text=>"没有上传开始界面"  
      return  
    end  
    @start_interface = interfaces[0]   
    helper = XMLHelper.new  
    helper.ReadFromString(@start_interface.content)   
    @style = helper.StyleToHTML(helper.tables[0])   
    @html = helper.TableToEditHTML(helper.tables[0], helper.dictionFactory,    
      {:record=>form_record,:encoding=>"gb2312", :script=>helper.script})   
    @historys = YtwgFormhistory.find(:all, :conditions=>"flowid=#{params[:flowid]} and formid = #{params[:formid]}") if params[:formid]   
  end  
  
  #用户写完一个表单后点击提交   
  def update_form   
    @flow = YtwgWorkflow.find(params[:id])   
    YtwgForm.set_table_name("ytwg_" + @flow.formtable)   
    YtwgForm.reset_column_information()    
    if params[:formid]   
      form = YtwgForm.find(params[:formid])   
      form.update_attributes(params[@flow.formtable])   
         
      states = []   
      for state in form._state.split(',')   
        states << state if check_state_right(@flow.name, state)   
      end  
      state_name = states[0]   
    else  
      form = YtwgForm.new(params[@flow.formtable])   
      form._madetime = Time.new  
      form._state = '开始'  
      state_name = form._state   
      form.userid = session[:user].id   
      form.flowid = @flow.id   
    end  
  
    form._lastprocesstime = Time.new       
    process = FlowProcess.new($Workflows[@flow.name], form, state_name)   
    process.user = session[:user]   
    process.signal_leave   
       
    history = YtwgFormhistory.new  
    history.userid = session[:user].id   
    history.flowid = @flow.id   
    history.formid = form.id   
    history.process_time = Time.new  
    history.save   
    redirect_to :action=>'myform', :id=>params[:id]   
  end  
  
 #等待我处理的流程   
  def show_waiting_form   
    @forms = get_wait_form(params[:id])   
    render :layout=>false  
  end  
  
 #获得某一种单据中等待当前登陆者审批的   
  def get_wait_form(flowid)   
    forms = []   
    flow = YtwgWorkflow.find(flowid)   
    if !flow.formtable || flow.formtable.size==0   
      return forms   
    end  
    YtwgForm.set_table_name("ytwg_" + flow.formtable)   
    YtwgForm.reset_column_information()    
    for state in $Workflows[flow.name].states   
      next if state.name == "结束"  
         
      conditions = []   
      conditions << "_state='#{state.name}'"  
         
      #如果可以从多个状态转移到这个状态,则等待所有状态都执行完此状态才可以执行   
      if state.guest_trasits.size == 1      #只可以从一个状态转到这里   
        conditions << " _state like '%,#{state.name}'"  
        conditions << "_state like '#{state.name},%'"  
      end  
  
      if state.right == "领导"  
        all_forms = YtwgForm.find(:all, :conditions=>conditions.join(' or '), :order=>"id desc")   
        for form in all_forms   
          forms << form if YtwgUser.find(form.userid).department.leader_id == session[:user].id rescue nil  
        end  
      else  
        for right in state.right.split(',')   
          if checkright(right)   
            forms += YtwgForm.find(:all, :conditions=>conditions.join(' or '), :order=>"id desc")   
          end  
        end  
      end  
    end  
    forms.uniq!   
    return forms   
  end 
#发布工作流  
def create
    stream = params[:ytwg_workflow][:content]
    content = stream.read
    name = stream.original_filename[0, stream.original_filename.index(".")]
    if YtwgWorkflow.find(:all, :conditions=>"name='#{name}'").size > 0
      flash[:error] = "存在同名工作流,上传失败"
      render :action => 'new'
      return
    end
    
    @ytwg_workflow = YtwgWorkflow.new()
    @ytwg_workflow.name = name
    begin
      @ytwg_workflow.content = content
    rescue
      flash[:error] = "上传文件非法"
      render :action => 'new'
    end
    @ytwg_workflow.publish_time = Time.new
    if @ytwg_workflow.save
      FlowMeta.LoadWorkFlow(@ytwg_workflow.name, @ytwg_workflow.content.sub!('<?xml version="1.0" encoding="gb2312" ?>', ''))
      flash[:notice] = '添加工作流成功'
      redirect_to :action => 'list'
    else
      flash[:error] = "添加工作流失败"
      render :action => 'new'
    end
  end
  #上传表定义模板,根据这个表单动态生成数据库表
  def upload_formtable
    stream = params[:content]
    content = stream.read
    helper = XMLHelper.new
    helper.ReadFromString(content)
    formtable = helper.tables[0]
    if !formtable
      flash[:notice] = "上传文件格式错误"
      redirect_to :action=>"listinterface"
      return
    end
    conn = ActiveRecord::Base.connection
    conn.create_table "ytwg_#{formtable.GetTableID}", :primary_key=>:id do |t|
      t.column "userid", :integer         #流程发起人的id
      t.column "flowid", :integer         #工作流的id
      Integer(0).upto(formtable.GetRowCount()-1) do |row|
        next if formtable.IsEmptyRow(row)
        Integer(0).upto(formtable.GetColumnCount()-1) do |col|
          next if formtable.IsEmptyCol(col)
          cell = formtable.GetCell(row, col)
          next if !cell.IsStore || !cell.IsEffective
          next if formtable.GetCellDBFieldName(row, col).downcase == "id"
          
          t.column "_state", :string, :limit=>30
          t.column "_madetime", :datetime
          t.column "_lastprocesstime", :datetime
          if cell.GetDataType == 1    #CCell.CtNumeric
            t.column formtable.GetCellDBFieldName(row, col).downcase, :float
          elsif cell.GetDataType == 0    #CCell.CtText               
            if cell.IsCheckWidth()
              t.column formtable.GetCellDBFieldName(row, col).downcase, :string, {:limit=>cell.GetTextWidth}
            else
              t.column formtable.GetCellDBFieldName(row, col).downcase, :string, {:limit=>100}
            end     
          elsif cell.GetDataType == 3 #CCell.CtDate
            t.column formtable.GetCellDBFieldName(row, col).downcase, :datetime
          end           
        end
      end
    end
    
    flow = YtwgWorkflow.find(params[:id])
    flow.formtable = formtable.GetTableID
    flow.save
    
    flash[:notice] = "建表成功"
    redirect_to :action=>"listinterface"
  end[SPAN]
  #上传状态节点的表单界面
  def uploadinterface
    stream = params[:content]
    content = stream.read
    
    interfaces = YtwgStateinterface.find(:all, :conditions=>"flowid = #{params[:id]} and name = '#{params[:name]}'")
    if interfaces.size > 0
      interface = interfaces[0]
      interface.publish_time = Time.new
    else
      interface = YtwgStateinterface.new
      interface.flowid = params[:id]
      interface.name = params[:name]
      interface.publish_time = Time.new
    end
    interface.content = content  #EncodeUtil.change("UTF-8", "GB2312", content)
    interface.save
    flash[:notice] = "上传状态界面成功"
    redirect_to :action=>"listinterface"
  end
  #用户点击某一工作流连接后,查看自己已经发起的工作流。
  def show_form
    @flow = YtwgWorkflow.find(params[:flowid])
    YtwgForm.set_table_name("ytwg_" + @flow.formtable)
    YtwgForm.reset_column_information() 
    form = YtwgForm.find(params[:formid])
     
    interfaces = YtwgStateinterface.find(:all, :conditions=>"flowid=#{params[:flowid]} and name='#{form._state.split(',')[0]}'")
    if interfaces.size > 0
      helper = XMLHelper.new
      helper.ReadFromString(interfaces[0].content)
      @style = helper.StyleToHTML(helper.tables[0])
      @html = helper.TableToEditHTML(helper.tables[0], helper.dictionFactory, 
        {:record=>form, :encoding=>"gb2312"})
      @historys = YtwgFormhistory.find(:all, :conditions=>"flowid=#{params[:flowid]} and formid = #{params[:formid]}")
    else
      render :text=>"没有上传工作流界面"
    end
  end
  #用户发起或者审批一个表单
  def write_form
    @flow = YtwgWorkflow.find(params[:flowid])
    YtwgForm.set_table_name("ytwg_" + @flow.formtable)
    YtwgForm.reset_column_information()
    if params[:formid]
      form_record = YtwgForm.find(params[:formid])
      state_name = form_record._state
    else
      form_record = YtwgForm.new
      form_record._state = '开始'
      state_name = form_record._state
    end
    
    states = []
    for state in form_record._state.split(',')
      states << state if checkright(state)
    end
    if states.size > 0
      state_name = states[0]
    else
      state_name = '开始'
    end
    process = FlowProcess.new($Workflows[@flow.name], form_record, state_name)
    process.user = session[:user]
    process.signal_enter
    
    interfaces = YtwgStateinterface.find(:all, :conditions=>"flowid=#{@flow.id} and name = '#{state_name}'")
    if interfaces.size ==0
      render :text=>"没有上传开始界面"
      return
    end
    @start_interface = interfaces[0]
    helper = XMLHelper.new
    helper.ReadFromString(@start_interface.content)
    @style = helper.StyleToHTML(helper.tables[0])
    @html = helper.TableToEditHTML(helper.tables[0], helper.dictionFactory, 
      {:record=>form_record,:encoding=>"gb2312", :script=>helper.script})
    @historys = YtwgFormhistory.find(:all, :conditions=>"flowid=#{params[:flowid]} and formid = #{params[:formid]}") if params[:formid]
  end
  #用户写完一个表单后点击提交
  def update_form
    @flow = YtwgWorkflow.find(params[:id])
    YtwgForm.set_table_name("ytwg_" + @flow.formtable)
    YtwgForm.reset_column_information() 
    if params[:formid]
      form = YtwgForm.find(params[:formid])
      form.update_attributes(params[@flow.formtable])
      
      states = []
      for state in form._state.split(',')
        states << state if check_state_right(@flow.name, state)
      end
      state_name = states[0]
    else
      form = YtwgForm.new(params[@flow.formtable])
      form._madetime = Time.new
      form._state = '开始'
      state_name = form._state
      form.userid = session[:user].id
      form.flowid = @flow.id
    end
    form._lastprocesstime = Time.new    
    process = FlowProcess.new($Workflows[@flow.name], form, state_name)
    process.user = session[:user]
    process.signal_leave
    
    history = YtwgFormhistory.new
    history.userid = session[:user].id
    history.flowid = @flow.id
    history.formid = form.id
    history.process_time = Time.new
    history.save
    redirect_to :action=>'myform', :id=>params[:id]
  end
 #等待我处理的流程
  def show_waiting_form
    @forms = get_wait_form(params[:id])
    render :layout=>false
  end
 #获得某一种单据中等待当前登陆者审批的
  def get_wait_form(flowid)
    forms = []
    flow = YtwgWorkflow.find(flowid)
    if !flow.formtable || flow.formtable.size==0
      return forms
    end
    YtwgForm.set_table_name("ytwg_" + flow.formtable)
    YtwgForm.reset_column_information() 
    for state in $Workflows[flow.name].states
      next if state.name == "结束"
      
      conditions = []
      conditions << "_state='#{state.name}'"
      
      #如果可以从多个状态转移到这个状态,则等待所有状态都执行完此状态才可以执行
      if state.guest_trasits.size == 1      #只可以从一个状态转到这里
        conditions << " _state like '%,#{state.name}'"
        conditions << "_state like '#{state.name},%'"
      end
      if state.right == "领导"
        all_forms = YtwgForm.find(:all, :conditions=>conditions.join(' or '), :order=>"id desc")
        for form in all_forms
          forms << form if YtwgUser.find(form.userid).department.leader_id == session[:user].id rescue nil
        end
      else
        for right in state.right.split(',')
          if checkright(right)
            forms += YtwgForm.find(:all, :conditions=>conditions.join(' or '), :order=>"id desc")
          end
        end
      end
    end
    forms.uniq!
    return forms
  end
有了上面这些最核心的函数后,如何使用这个工作流基本就算明白了。除此之外还有许多附加的小功能需要去实现,比如导出Excel,导出PDF,在网页上展示工作流的流程图(我用VML实现)。以下是请假登记表的表单显示界面:
这个工作流的应用现状:
目前这个工作流还没有商用,只有一个应用场景。前几个月我们公司买了一套金和OA,在销售员满嘴跑火车的吹嘘下我们经理花9800买了,后来实施的时候发现金和的工作流根本无法使用。一个简单的请假申请单都无法实现自定义表单和流程,无奈之下我基于我的工作流组件,快速开发了一套OA,几天之后就上线,然后边用边完善,一个月以后就很少再动了。目前公司对这套OA还是比较满意的。虽然我的OA比国内的优秀OA产品还有很大差距,但是这套工作流组件至少还是能够胜任大多数场合,对于尚不能满足的场合还可以灵活扩展。
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@ldacury.cn
文章转载自:JavaEye