轉帖|其它|編輯:郝浩|2009-02-09 11:33:47.000|閱讀 3515 次
概述:工作流是企業開發中不可或缺的一個重要組件。有了工作流,客戶需求的實現速度將大大提高,同時兼顧到開發效率,靈活性。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@fc6vip.cn
文章轉載自:JavaEye