联系方式: 微信:biyebang QQ: 629001810
摘 要
工作流是指整个或部分业务流程在计算机支持下的全自动或半自动化。在计算机网络环境下,工作任务在多个人或单位之间的流转实际上将表现为信息或数据在多个人之间的传送。使用可视化的OA工作流设计工具,用户不需要编程就可以定义设计出满足要求的收发文流程,实际使用效果良好。论文以可视化的OA工作流设计工具的开发为基础,主要阐述五个方面的内容。第一部分介绍了工作流和工作流设计工具的相关概念与开发背景;第二部分对工作相关基础理论技术、系统环境与平台基础、以及开发技术进行了描述;第三部分对系统功能进行了分析;第四部分详细介绍了系统流程、流程可视化设计的实际开发过程中使用到的关键技术;最后一部分在总结了系统开发心得的同时,提出了目前系统存在的不足和有待改进的地方。
系统设计 3.1系统需求分析 在最近几年的各级单位的信息化改造建设中,随着信息化基础设施建设的逐渐完备,各类应用系统开始受到各级单位的重视,开发重点主要侧重于人事信息数字化、文书信息化、公文流转、交互式审批等信息化可行性高的工作领域。因此,改革传统办公模式,开展电子办公,使业务办公、公文流转和管理过程电子化、信息化,并通过统一办公规范,提高工作效率降低办公成本,实现办公自动化已势在必行。 而上述所介绍的公文流转的一个重要特征就是流程复杂,难以用标准的程序化语言解释,因而公文流转系统必须有与之相适应的工作流程定义系统,使操作人员能够自主地设定公文流转流程。为此我们设计了一个可视化流程设计工具,该工具可以作为工作流管理系统里的一个模块。它具有图形用户界面,用户定义与任务执行完全分离,其体系结构基本上符合WFMC 标准结构。用户不需要程序员的支持就可以定义和设计出工作流程模型,根据该模型生成的文件可以非常方便地被解释为上层接口所需的工作流。 3.2 系统功能介绍 可视化OA工作流程设计工具针对用户的要求对工作流程进行定制和维护,主要包括:对接点、接点连接线可自由拖放。具备删除节点、节点之间连接线功能。接点之间采用箭头连接。接点采用矩形表示。图形内容利用XML形式保存于的Access数据库中,可查询,编辑,保存,更多自定义属性。 通过对用户需求的分析,要求本流程设计工具具有以下功能: (1)可视化的操作界面; (2)可以对流程节点和弧线进行填加、修改、删除操作; (3)工作流程支持串签、会签和分支流程; (4)可以将图形内容保存于access数据库或(xml)中,可查询,编辑,保存; (5)可以通过流程模型解释接口和引擎主程序提供给上层程序使用 3.3 系统模块功能和设计思想 1994年11月,工作流管理联盟发布了工作流管理系统的参考模型,该模型定义了一个基本的工作流管理系统所需要的6个基本模块:过程定义工具,工作流执行服务,其他工作流执行服务,客户应用程序,被调应用程序,管理及监控工具;并制定了个模块之间的接口标准。 可视化流程设计工具各模块的功能描述和设计思想介绍: (1)绘图模块:可将需要设计的流程用此模块画出,利用java.awt来绘制节点和弧线; (2)图象关系序列化模块:可将已画出流程中的节点和弧线序列化,利用接口java.io.Serializable就能实现; (3)图象关系保存模块:可将序列化后的节点和弧线关系保存起来供上层流程定义解释器使用。 3.4数据库设计 3.4.1 E-R图设计 实体关系图(E-R图)将系统中的数据模型以一种更加清楚、具体的方式展现出来。图中方框代表实体,椭圆代表实体的属性,实体和实体之间的关系用菱形来表示。 在工作流管理系统中,上层程序需要通过读取保存在数据库里流程的XML文件来实现流程的解释和实例化。在对工作流进行定义的工作中用到的E-R如下图所示: 3.4.2表的构建 表1 流程节点表(flow_Node) 对象名称 数据类型 功能描述 Node_ID 数字 节点编号 Node_Nane 文本 节点名称 Node_prev 文本 节点前驱名称 Node_prevID 数字 前驱节点号 Node_next 文本 节点后继 Node_nextID 数字 后继节点号 Flow_ID 数字 节点所属的流程号 表2 流程表(flowdata) 对象名称 数据类型 功能描述 fileName 文本 保存流程的文件名 id 数字 流程号 type 文本 Node 类型还是 line 类型 3.4.3数据库连接实现 public Connection getConnection(){ // now we can get the connection from the DriverManager Connection con = null; try { Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); //驱动 set this to a MS Access DB you have on your machine String filename = "/fileData.mdb"; String database ="jdbc:odbc:Driver={Microsoft Access Driver (*.mdb)};DBQ="; database+= filename.trim() + ";DriverID=22;READONLY=true}"; // add on to the end con = DriverManager.getConnection("jdbc:odbc:ccos" ,"",""); //数据源 } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } return con; } 系统实现 4.1系统流程及图示 一个流程的设计是先通过绘图来对实际业务过程进行分析、建模,然后通过一定的技术生成所建立流程模型的可被上层接口处理的形式化描述。其流程如下图: 4.2流程可视化设计实现 4.2.1界面可视化 这是工具开始运行出现的第一个界面,由Internet Explorer浏览器打开,可以适应B/S结构的OA系统。 界面的布局由BorderLayout来实现,分北部、西部、中部三部分。 包括: 北部控制板(ArcControls); 西部绘图工具板(ToolBlank); 中部绘图画板(ArcCanvas)。 具体代码实现如下: this.setLayout(borderLayout1); this.add(ArcControls,BorderLayout.NORTH); this.add(ArcCanvas,BorderLayout.CENTER); this.add(ToolBlank, BorderLayout.WEST); 4.2.2绘图功能设计 当设计一个流程时,用户点击绘图工作板中的绘制节点和绘制弧线两个按钮结合着选择环节名,将指定的可视化部件拖拉绘制在工作平台上,每一个可视化部件代表一个角色,用流向箭头将各个部件关联起来。还可以对节点和弧线进行修改和删除。 一个矩形节点的绘制,需要对它的各项属性进行定义,包括对它的起点坐标、矩形的宽和高、矩形的边框坐标。以下是节点定义的简单描述: publicclass Node extends BaseObject{ int LeftUpNodeX;//定义顶点坐标 int LeftUpNodeY; private int starx; //定义起始点,高,宽 private int stary; private int Heigh; private int Width; private int MinX; //定义边框 private int MinY; private int MaxX; private int MaxY; Color color; //定义外框颜色 String NodeName="";//节点名称 } 绘制节点: g.drawRect(this.starx,this.stary,this.Width,this.Height);//画矩形 g.drawString(this.NodeName,this.starx+this.Width/3,this.stary+Height/2);//画节点名 拖动节点: publicvoid SetNodeMoved(int x,int y){ this.starx = x; this.stary = y; this.MinX = starx; this.MinY = stary; this.MaxX = starx + Width; this.MaxY = stary + Height; } 拖动节点时鼠标动态: publicvoid mouseDragged(MouseEvent e) { //鼠标拖动 int x = e.getX(); int y = e.getY(); switch(status){ case1: if(this.NodeIFinit == true) { node= new Node(e.getX(), e.getY(),CurrentNodeName); this.NodeObject.add(node); this.IfDrag = true; this.NodeIFinit = false; repaint(); } else { if (IfDrag == true) { if (node != null) { node.SetHeightAndWidth(x, y); repaint(); } } else { if (SelectNodeDrag == true) { node.SetNodeMoved(x, y); node.DrawMyself(this.getGraphics()); repaint(); } } } break; } } 节点与节点之间需要用弧线进行连接。一段弧线需要对它的起点坐标、终点坐标等属性进行定义。以下是弧线定义的简单描述: 弧线定义: publicclass Arc { private int endx; //结束坐标 private int endy; private int startx;//起点坐标 private int starty; private int[] SelectedNode = new int[2];//选择节点 private Vector Point;//点元素 private Color color; private boolean ArcEnd;//判断弧段是否结束增加节点 public Arc(int x,int y){ Point = new Vector(); int[] star = new int[2];//点由2元素(x,y)组成 star[0] = x; star[1] = y; this.startx = x; this.starty = y; this.Point.addElement(star); this.color = Color.BLACK; this.ArcEnd = false; } 绘制弧线: public void DrawMyself(Graphics g){ int length= Point.size(); g.setColor(color); if (length> 0) { System.out.println("Arc node size:" + length); if(length == 1) { } else { for(int i = 0; i < length - 1; i++) { int[] Spoint = (int[]) Point.elementAt(i); int[] Epoint = (int[]) Point.elementAt(i + 1); g.drawLine(Spoint[0], Spoint[1],Epoint[0], Epoint[1]); } 弧线拖动: public void mouseDragged(MouseEvent e) { //鼠标拖动 int x = e.getX(); int y =e.getY(); switch(status){ case 3: if (SelectArcDrag == true) { if ((arc != null) && (arcNodeindex != -1)) { arc.SetSelectedNode(arcNodeindex, x, y); repaint(); }elseif((arc != null)&&(arcNodeindex == -1)){//选中的是最后一个不再Vetor里面的点 arc.SetSelectedNode(x, y); repaint(); } } break; } } 画箭头: int[] lastpoint = (int[]) Point.elementAt(length -1); g.drawLine(lastpoint[0],lastpoint[1], this.endx, this.endy); //画箭头 if((lastpoint[0]== endx)&&(lastpoint[1]<endy))//开口向下 { g.drawLine(endx,endy,endx+3,endy-3); g.drawLine(endx,endy,endx-3,endy-3); }elseif((lastpoint[0] == endx)&&(lastpoint[1]>endy)){ //开口向上 g.drawLine(endx,endy,endx+3,endy+3); g.drawLine(endx,endy,endx-3,endy+3); }elseif((lastpoint[1] == endy)&&(lastpoint[0]>endx)){//开口向左 g.drawLine(endx,endy,endx+3,endy-3); g.drawLine(endx,endy,endx+3,endy+3); }elseif((lastpoint[1] == endy)&&(lastpoint[0]<endx)){//开口向右 g.drawLine(endx,endy,endx-3,endy-3); g.drawLine(endx,endy,endx-3,endy+3); }else{//任意方向用点代替 g.fillOval(endx-2,endy-2,5,5); } if ((SelectedNode[0] != 0) && (SelectedNode[1] != 0)) { g.setColor(Color.BLUE); g.fillOval(SelectedNode[0]-2,SelectedNode[1]-2,5,5); } 删除节点: public void DeleteSelectedNode() { if(haveSelectedNode == true) { this.NodeObject.removeElementAt(nodeindex); this.haveSelectedNode = false; repaint(); } } 删除弧线: public void DeleteSelectedArc() { if(haveSelectedArc == true) { this.ArcObject.removeElementAt(arcindex); this.haveSelectedArc = false; repaint(); } } 下图为一设计好了的流程图: 4.2.3流程图保存和读取方法设计 将序列化后的节点与弧线的对象保存为xml文件,易于保存和重绘。 序列化是将对象写到流中,序列化需要对象实现Serializable接口,序列化后能将对象转换成一系列字节,并可在以后完全复原。序列化可以实现“有限持久化”。 在保存前先要对已经绘出的节点以及弧线进行属性采集,我们用一个NodeBean以及ArcBean方法来实现其功能,NodeBean的代码描述如下: public class NodeBeanimplements java.io.Serializable{} public NodeBean(){ } String LeftUpNodeX; //定义顶点坐标 String LeftUpNodeY; private String MinX;//定义边框 private String MinY; private String MaxX; private String MaxY; Color color; //定义外框颜色 String NodeName="";//节点名称 public String getNodeName() { return NodeName; } public void setNodeName(StringnodeName) { NodeName = nodeName; } public Color getColor() { return color; } public void setColor(Colorcolor) { this.color = color; } public String getLeftUpNodeX(){ return LeftUpNodeX; } public voidsetLeftUpNodeX(String leftUpNodeX) { LeftUpNodeX = leftUpNodeX; } public String getLeftUpNodeY(){ return LeftUpNodeY; } public voidsetLeftUpNodeY(String leftUpNodeY) { LeftUpNodeY = leftUpNodeY; } public String getMaxX() { return MaxX; } public void setMaxX(StringmaxX) { MaxX = maxX; } public String getMaxY() { return MaxY; } public void setMaxY(StringmaxY) { MaxY = maxY; } public String getMinX() { return MinX; } public void setMinX(StringminX) { MinX = minX; } public String getMinY() { return MinY; } public void setMinY(StringminY) { MinY = minY; } 将流程图中节点的属性对象进行采集以后,通过调用 ObjectToXMLUtil.objectXmlEncoder方法可以对节点和弧段进行保存,以下代码是对节点的保存,描述如下: Vector v = new Vector(); Vector temp = this.canvas.getNodeObject(); for(int i=0;i<temp.size();i++){//通过NODEBEAN存储 Node o =(Node)temp.elementAt(i); NodeBean bean = newNodeBean(); bean.setNodeName(o.NodeName); bean.setLeftUpNodeX(o.getLeftUpNodeX()+""); bean.setLeftUpNodeY(o.getLeftUpNodeY()+""); bean.setMaxX(o.GetMaxX()+""); bean.setMaxY(o.GetMaxY()+""); bean.setMinX(o.GetMinX()+""); bean.setMinY(o.GetMinY()+""); v.add(bean); } String nodeFile ="nodeData"+System.currentTimeMillis()+".xml"; ObjectToXMLUtil.objectXmlEncoder( v ,nodeFile ); 对弧线的属性采集类ArcBean相关的代码描述如下: public class ArcBean implements java.io.Serializable{} private String endx; private String endy; private String startx; private String starty; public ArcBean(){//返回值 } public String getEndx() { return endx; } public void setEndx(String endx) { this.endx = endx; } public String getEndy() { return endy; } public void setEndy(String endy) { this.endy = endy; } public String getStartx() { return startx; } public void setStartx(String startx) { this.startx = startx; } public String getStarty() { return starty; } public void setStarty(String starty) { this.starty = starty; } 将流程图中弧线的属性对象进行采集以后,通过调用 ObjectToXMLUtil.objectXmlEncoder方法可以对节点和弧段进行保存,以下代码是对节点的保存,描述如下: Vector line = new Vector(); Vector lineTemp = this.canvas.getArcObject(); for(int i=0;i<lineTemp.size();i++){ Arc o = (Arc)lineTemp.elementAt(i); ArcBean bean = new ArcBean(); bean.setStartx(o.getStartx()+""); bean.setStarty(o.getStarty()+""); bean.setEndx(o.getEndx()+""); bean.setEndy(o.getEndy()+""); line.add(bean); } String lineFile = "lineData"+System.currentTimeMillis()+".xml"; ObjectToXMLUtil.objectXmlEncoder(line , lineFile); 流程图的读取,也就是将保存的xml文件中的节点以及弧线的属性读取出来,然后调用各个的显示方法,重新绘制在屏幕上。 对节点的读取: String fileName = oper.getFile("node"); ArrayList list = null; if(null != fileName && !fileName.equals("")){ ist = (ArrayList)ObjectToXMLUtil.objectXmlDecoder(fileName); }else{ list=(ArrayList)ObjectToXMLUtil.objectXmlDecoder("nodeData.xml"); } if(list != null &&list.size()>0){ Vector v = new Vector(); for(int i= 0;i<list.size();i++){ v =(Vector)list.get(i); } Vector nodeVector = new Vector(); for(int j = 0;j<v.size();j++){ NodeBean bean =(NodeBean)v.elementAt(j); Node node = new Node(Integer.parseInt(bean.getLeftUpNodeX()), Integer.parseInt(bean.getLeftUpNodeY()),bean.getNodeName()); node.setMaxX(Integer.parseInt(bean.getMaxX())); node.setMaxY(Integer.parseInt(bean.getMaxY())); node.setMinX(Integer.parseInt(bean.getMinX())); node.setMinY(Integer.parseInt(bean.getMinY())); node.SetHeightAndWidth(node.GetMaxX(), node.GetMaxY()); node.getLeftUpNodeY()); nodeVector.add(node); } this.canvas.setNodeObject(nodeVector); } 对弧线的读取: String fileLineName = oper.getFile("line"); ArrayList listLine = null; if(null != fileLineName && !fileLineName.equals("")){ System.out.println("line:"+ fileLineName); listLine =(ArrayList)ObjectToXMLUtil.objectXmlDecoder(fileLineName); }else{ listLine =(ArrayList)ObjectToXMLUtil.objectXmlDecoder("lineData.xml"); } System.out.println("lisrlineL"+ listLine); if(listLine != null &&listLine.size()>0){ Vector lineVector = newVector(); for(int i= 0;i<listLine.size();i++) lineVector =(Vector)listLine.get(i); Vector line = new Vector(); for(int j = 0;j<lineVector.size();j++){ ArcBean bean = (ArcBean)lineVector.elementAt(j); Arcnode=new Arc(Integer.parseInt(bean.getStartx()),Integer.parseInt(bean.getStarty())); node.setEndx(Integer.parseInt(bean.getEndx())); node.setEndy(Integer.parseInt(bean.getEndy())); node.AddPoint(new int[]{node.getEndx(),node.getEndy()}); node.SetArcEnd(); line.add(node); } this.canvas.setArcObject(line); } 4.2.4节点间关系保存的实现 要设计一个流程,必须用一个算法将流程各个节点与节点之间的关系表示出来。我们采用矩形来表示节点实例,用带箭头的弧线表示节点实例间的前驱后继关系,如果节点实例A是节点实例B的前驱,则有一条由A出发到B结束的箭头。这样就必须保证任意两条连接线都不重叠。 节点和弧线的关系通过运算之后生成一份xml文档,同时把这份xml文档序列化后保存在数据库中。通过解析这样的xml文档,上层应用程序能了解节点之间的先后关系,并应用到上层程序逻辑。下面是图6中流程所保存的节点xml文件,其中包括对节点位置的信息,也包括对节点间关系的信息。 public class Node extends BaseObject { publicnode() { publicstring Node_Name;// 节点名 public intNode_ID;//节点号 public string Node_next;// 节点后继节点 public intNode_nextID;// 后继节点号 publicstring Node_prev;// 节点前驱节点名 public intNode_prevID;// 前驱节点号 } } 在XML中保存的节点信息: <java version="1.5.0_09" class="java.beans.XMLDecoder"> -<object class="java.util.Vector"> - <void method="add"> - <object class="applettest.NodeBean"> - <void property="leftUpNodeX"><string>225</string></void> - <void property="leftUpNodeY"><string>20</string></void> - <void property="maxX"><string>320</string></void> - <void property="maxY"><string>59</string></void> - <void property="minX"><string>225</string></void> - <void property="minY"><string>20</string></void> - <void property="nodeName"><string>开始</string></void> - <void property=" Node_ID "><string>1</string></void> -<void property="Node_next"><string>拟稿</string></void> -<void property=" Node_nextID "><string>2</string></void> -<void property="Node_prev"><string>0</string></void> -<void property=" Node_prevID "><string>0</string></void> </object></void> - <void method="add"> - <object class="applettest.NodeBean"> - <void property="leftUpNodeX"><string>224</string></void> - <void property="leftUpNodeY"><string>93</string></void> - <void property="maxX"><string>321</string></void> - <void property="maxY"><string>135</string></void> - <void property="minX"><string>224</string></void> - <void property="minY"><string>93</string></void> - <void property="nodeName"><string>拟稿</string></void> -<void property="Node_prevID "><string>2</string></void> -<void property="Node_next"><string>处室审核</string></void> -<void property="Node_nextID "><string>3</string></void> -<void property="Node_next"><string>处室审核</string></void> -<void property="Node_nextID "><string>4</string></void> -<void property="Node_prev"><string>开始</string></void> -<void property="Node_prevID "><string>1</string></void> </object></void> - <void method="add"> - <object class="applettest.NodeBean"> - <void property="leftUpNodeX"><string>127</string></void> - <void property="leftUpNodeY"><string>178</string></void> - <void property="maxX"><string>242</string></void> - <void property="maxY"><string>224</string></void> - <void property="minX"><string>137</string></void> - <void property="minY"><string>174</string></void> - <void property="nodeName"><string>处室审核</string> - <void property=" Node_ID "><string>3</string></void> -<void property="Node_next"><string>主任审核</string></void> -<void property="Node_nextID "><string>5</string></void> -<void property="Node_prev"><string>拟稿</string></void> -<void property="Node_prevID "><string>2</string></void> </object></void> - <void method="add"> - <object class="applettest.NodeBean"> - <void property="leftUpNodeX"><string>296</string></void> - <void property="leftUpNodeY"><string>180</string></void> -<void property="maxX"><string>412</string></void> -<void property="maxY"><string>229</string></void> - <void property="minX"><string>285</string></void> - <void property="minY"><string>173</string></void> - <void property="nodeName"><string>处室审核</string> - <void property=" Node_ID "><string>4</string></void> -<void property="Node_next"><string>主任审核</string></void> -<void property="Node_nextID "><string>5</string></void> -<void property="Node_prev"><string>拟稿</string></void> -<void property="Node_prevID "><string>2</string></void> </object></void> - <void method="add"> - <object class="applettest.NodeBean"> - <void property="leftUpNodeX"><string>218</string></void> - <void property="leftUpNodeY"><string>262</string></void> -<void property="maxX"><string>316</string></void> - <void property="maxY"><string>317</string></void> - <void property="minX"><string>218</string></void> - <void property="minY"><string>262</string></void> - <void property="nodeName"><string>主任审核</string> - <void property=" Node_ID "><string>5</string></void> -<void property="Node_next"><string>局长审核</string></void> -<void property="Node_nextID "><string>6</string></void> -<void property="Node_prev"><string>处室审核</string></void> -<void property="Node_prevID "><string>3</string></void> -<void property="Node_prev"><string>处室审核</string></void> -<void property="Node_prevID "><string>4</string></void> </object></void> - <void method="add"> - <object class="applettest.NodeBean"> - <void property="leftUpNodeX"><string>214</string></void> - <void property="leftUpNodeY"><string>362</string></void> - <void property="maxX"><string>321</string></void> -<void property="maxY"><string>414</string></void> - <void property="minX"><string>214</string></void> - <void property="minY"><string>362</string></void> - <void property="nodeName"><string>局长审核</string> - <void property=" Node_ID "><string>6</string></void> -<void property="Node_next"><string>签发</string></void> -<void property="Node_nextID "><string>7</string></void> -<void property="Node_prev"><string>主任审核</string></void> -<void property="Node_prevID "><string>5</string></void> </object></void> - <void method="add"> - <object class="applettest.NodeBean"> - <void property="leftUpNodeX"><string>212</string></void> -<void property="leftUpNodeY"><string>450</string></void> - <void property="maxX"><string>325</string></void> - <void property="maxY"><string>496</string></void> - <void property="minX"><string>212</string></void> - <void property="minY"><string>450</string></void> - <void property="nodeName"><string>签发</string> - <void property=" Node_ID "><string>7</string></void> -<void property="Node_next"><string>null</string></void> -<void property="Node_nextID "><string>255</string></void> -<void property="Node_prev"><string>局长审核</string></void> -<void property="Node_prevID "><string>6</string></void> </object></java> 源文件
版权所有© 帮我毕业网 并保留所有权利