工作流的实现(在Ruby on Rails环境下)

转帖|其它|编辑:郝浩|2009-02-09 11:33:47.000|阅读 3288 次

概述:工作流是企业开发中不可或缺的一个重要组件。有了工作流,客户需求的实现速度将大大提高,同时兼顾到开发效率,灵活性。Java领域已经有了多个稳定的工作流,成了Java占领企业级开发的有力助手。

  工作流是企业开发中不可或缺的一个重要组件。有了工作流,客户需求的实现速度将大大提高,同时兼顾到开发效率,灵活性。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&#x0D;&#x0A;@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 &amp;&amp; @form.b5 &gt;=3" from="部门经理审批" to="总经理审批" /> 
    <trasit name="" condition="@form.b5 == nil || @form.b5 &lt;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&#x0D;&#x0A;@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 &amp;&amp; @form.b5 &gt;=3" from="部门经理审批" to="总经理审批" />
    <trasit name="" condition="@form.b5 == nil || @form.b5 &lt;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产品还有很大差距,但是这套工作流组件至少还是能够胜任大多数场合,对于尚不能满足的场合还可以灵活扩展。



标签:

本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处,尊重他人劳动成果

文章转载自:JavaEye

登录慧都网发表评论登录


暂无评论...

为你推荐

  • 推荐视频
  • 推荐活动
  • 推荐产品
  • 推荐文章
  • 慧都慧问
相关厂商
相关产品
Aspose.Workflow

提供了一个功能强大的工作流引擎以及一整套符合业界标准的工作流对象

Skelta Workflow.NET

建立在.NET、XML以及Web services技术之上的业务流程管理工作流软件,同时也是世界上第一个可嵌入的工作流引擎

TMS Workflow Studio

一个Delphi/ C ++ Builder VCL框架的业务流程管理(BPM)控件,为你的应用程序添加工作流和BPM功能

Wonderware Skelta BPM

全球第一且功能强大的.NET企业级业务流程管理和高级工作流解决方案

在线客服 在线QQ 电话咨询
400-700-1020
反馈
在线客服系统
live chat