转帖|其它|编辑:郝浩|2009-02-09 11:33:47.000|阅读 3483 次
概述:工作流是企业开发中不可或缺的一个重要组件。有了工作流,客户需求的实现速度将大大提高,同时兼顾到开发效率,灵活性。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@pclwef.cn
文章转载自:JavaEye