Here are my workflow how the implementation.
VC Writing with a workflow designer, this little software is relatively simple, including some simple drag and drop graphics and symbols, such as the beginning and end, a state of flow. For each state can set the permissions can be set for each flow condition. At my research in the field of workflow is not very deep, development of the designer on the principle of practicality, there is no special complexity of implementation. Be able to achieve at the basis of user needs how how easy to do.
Save the file to xml format
<?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>
And this document to the system, from Ruby to resolve the job flow
Analytical Workflow Ruby code (on the lib directory) as follows:
#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 #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 #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 #Trasit.rb class Trasit attr_accessor :condition, :name, :from, :to #新建流转类,from,to均为State类对象 def initialize(from, to) @from = from @to = to end end
OK, analytic workflow even completed the mission, 250 line Ruby code, a small, customizable and high even if the workflow engine are completed. Here we look how to use this work flow.
Workflow engine automatically after the completion of the following users will think of flow at each point to see form interface come from? For this feature, I wrote a specialized form design and form analytic engine, analytic engine form xml format can be translated into html form format forms. This form more complex components beyond the scope of this discussion, let us not refer to the time being.
Say what the following database tables, in order to use the workflow engine required three tables set up
//工作流表 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`) )
Each release of a job stream, the following should work for the dynamic creation of database tables, stored form data. Me through to this workflow template to publish a form to dynamically create the table.
Look below how to use workflow
#发布工作流
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 With the above function of the core, how to use the basic job flow even understand. In addition there are many additional features required to achieve small, such as export Excel, export PDF, at page display workflow flow chart (I use the VML implementation). Following is a registration form leave the form shows the interface:
The workflow application status:
The current workflow has not yet commercial, there is only one application scenario. The first few months of our company and bought a set of OA, at train salesmen run mouthed puff flower Manager 9800, we bought, and later found the implementation of workflow and simply can not use. Leave a simple application form can not be realized in a custom form and flow, but under me based on my workflow components, rapid development of a set of OA, after a few days on the last line, then side by side perfect, one-month After very little moved. At present, this company is quite satisfied with the OA. Although OA of my outstanding OA than domestic products have a big gap, but this set of workflow components, or at least be able to do the majority of occasions, can not meet for the occasion can also be flexible expansion.
Welcome to everyone for their valuable opinions, the field has at ROR Workflow friends worry everyone can explore.







