自定义工作流相关思路
本工作流以一套金融软件业务处理流程为例,实现功能包括:流程自定义、步骤自定义、步骤重复次数、步骤类型(顺序/并行)、定义排序功能,完全使用数据库实现,本文将详细分析业务流程、系统设计及实现细节。
术语:
工作流(Workflow)[1],是对工作流程及其各操作步骤之间业务规则的抽象、概括、描述。工作流建模,即将工作流程中的工作如何前后组织在一起的逻辑和规则在计算机中以恰当的模型进行表示并对其实施计算。工作流要解决的主要问题是:为实现某个业务目标,在多个参与者之间,利用计算机,按某种预定规则自动传递文档、信息或者任务。工作流管理系统(Workflow Management System, WfMS)的主要功能是通过计算机技术的支持去定义、执行和管理工作流,协调工作流执行过程中工作之间以及群体成员之间的信息交互。工作流需要依靠工作流管理系统来实现。
流程:工作流包含多个工作流程,处理时可任选一种流程进行处理,其包含步骤信息;
步骤:流程中每一环节的名称,某一流程将包含多个步骤(其他工作流中也称为节点)。
正文:
第一部分、业务逻辑分析
1、自定义工作流是指工作流各个环节及其参数完全自定义,常用于公文处理、业务流程签批处理等。本系统来源于本人参与开发的一套金融管理软件,业务处理人分不同角色拥有不同权限进行业务处理,将贷款数据库从贷款调查一直到贷款签批的完整流程。其中由于软件功能要求,需要将贷款调查固定置为第一步骤,将贷款签批置为最后一步骤。
其中数字表示当前步骤重复次数。
2、用户业务处理部分包括:
1)、通过:当前步骤处理通过,(选择下一处理人)进入当前流程中下一步骤,若为末步骤,则流程完成;
2)、退回:将步骤退回至上一步骤,即返回至上一处理人处,若为首步骤,则不可进行退回;
3)、否决:将步骤直接结束,不可再进行操作,或者回退至第一步骤;本系统中采用第二种方式;
4)、撤回:若当前步骤已处理,且在下一处理人未进行处理的情况下可进行撤回操作。
3、顺序与并行
顺序是指上一处理人指定某一处理人时,其他拥有此步骤权限的操作员不可进行查看和操作,必须当前处理人处理完毕后,流程才能继续;并行是由上一处理人指定固定多个处理人时,由任一员工处理即可,不分前后顺序,全部处理完成,进入下一步骤,此处理人数目由当前步骤重复次数确定。
两者之间对应关系如下。
第二部分、系统设计
数据库设计如下:
1)、流程信息表:S_flow_info(flow_id,flow_name)
2)、步骤信息表:S_action_info(action_id,action_name)
3)、流程-步骤信息表:S_step_info(step_id,action_id,flow_id,step_repeat_no,step_order_no,step_type)
(其中step_repeat_no为重复次数,step_order_no为排序号,step_type为类型:0为顺序,1为并行)
4)、流程处理明细表:L_tranct_proc(proc_id,loan_id,step_id,step_action,step_emp_id)
(其中loan_id为数据主表主键,step_id关联S_action_info,step_action存储处理结果:0--不通过,1--通过,2--退回,3--否决,4--撤回,step_emp_id为当前处理员工编号)
其中流程表、步骤表、流程步骤关系表为核心数据表,它们三者确定工作流的完全自定义。流程处理明细表为重要数据表,查询数据主要通过此表进行连接查询。
其他相关表包括:
1)、数据主表L_loan_info(loan_id,loan_name,flow_id,step_id,...)
(flow_id为流程编号,step_id关联S_action_info)
2)、操作员工表E_emp_info(emp_id,emp_name,..)
3)、角色信息表E_role_info(role_id,role_name)
4)、员工-角色关系表E_emp_role(emp_role_id,emp_id,role_id)
(关联角色表与员工表)
5)、步骤角色关系表S_action_role(action_role_id,action_id,role_id)
(关联角色表与步骤表)
6)、下一处理人表L_loan_next_emp(loan_next_emp_id,loan_id,next_emp_id,step_id)
(其中next_emp_id关联操作员工表E_emp_info,step_id关联S_action_info)
其中数据流向如下:
1)业务最开始发生时,数据主表L_loan_info插入数据,其step_id为其所在流程的第一个步骤的编号,可根据下一步骤的重复次数来去将下一处理人的操作员编号插入到下一处理人表L_loan_next_emp中;插入流程处理明细表中数据,step_id为当前步骤编号;更新主表L_loan_info的step_id为下一步骤编号;
2)流程进入下一步骤;
3)下一处理人可查看当前待处理数据(以及本环节待处理数据),选定进行处理,将处理结果(0:不通过,1:通过,2:退回,3:否决)插入到流程处理明细表中,若为通过由更新主表L_loan_info的step_id为下一步骤编号,退回更新为上一步骤编号,否决则更新到第一个步骤编号;
4)在本人已处理数据中可查看已处理过的数据,若下一步骤中操作员还没有进行操作,则可对数据操作进行撤回,撤回时将处理结果(4:撤回)插入到流程处理明细表中,其中的next_emp_id为本人操作员工编号);更新主表L_loan_info的step_id为流程的第一步骤的编号;
5)下一处理人继续3)、4)的循环,直至流程的结束;
6)流程最后一个步骤,将处理结果中step_id值为0插入至流程处理明细中。
第三部分、实现细节
1、第一步骤中获得数据列表代码如下:
SELECT * FROM L_loan_info WHERE step_id=@step_id
其中@step_id使用代码得到当前数据的第一步骤编号
2、第一步骤获得已处理数据列表代码:
SELECT * FROM L_loan_info WHERE loan_id IN (SELECT DISTINCT loan_id FROM l_tranct_proc WHERE step_id=@step_id)
3、获得任一步骤数据列表(含未处理和已处理)
/// <summary>
/// 方法:获得审批(或签批)数据列表
/// 开发:王洪剑http://www.cnblogs.com/walkingp http://www.51obj.cn/
/// 时间:2010-6-29
/// 最后修改时间:2010-6-29
/// 修改详情:
/// </summary>
/// <param name="step_emp_id">签批人(默认为当前操作员):0、本环节 其他、当前处理人</param>
/// <param name="action_id">步骤</param>
/// <param name="step_action">操作值:0:待审批/签批 1:已审批/签批 _:所有</param>
/// <param name="version">版本:3.0</param>
/// <returns></returns>
public List<Hope.Model.L_loan_info> GetModelByProcess(int step_emp_id, int action_id, string step_action, int version)
{#regionstring sql = "SELECT COUNT(*) FROM s_flow_info WHERE del_sign='1' and flow_id in(select flow_id from s_step_info where action_id=@action_id)";SqlParameter[] para ={ new SqlParameter("@action_id", SqlDbType.Int, 4) };para[0].Value = action_id;int count = int.Parse(DbHelperSQL.GetSingle(sql, para).ToString());string[] arrFlowId = new string[count];//流程信息sql = "SELECT flow_id FROM s_flow_info WHERE del_sign='1' and flow_id in(select flow_id from s_step_info where action_id=@action_id)";DataTable dt = DbHelperSQL.Query(sql, para).Tables[0];for (int i = 0; i < count; i++){arrFlowId[i] = dt.Rows[i]["flow_id"].ToString();}string[] pre_action_id = new string[count];//当前流程中上一流程idstring[] next_action_id = new string[count];//当前流程中下一流程idfor (int i = 0; i < count; i++){sql = "SELECT TOP 1 action_id FROM s_step_info WHERE step_order_no<(SELECT order_no FROM S_action_info WHERE action_id=@action_id) AND flow_id=" + arrFlowId[i] + " ORDER BY s_step_info.step_order_no DESC";SqlParameter[] paras ={ new SqlParameter("@action_id", SqlDbType.Int, 4) };paras[0].Value = action_id;dt.Clear();dt = DbHelperSQL.Query(sql, paras).Tables[0];if (dt.Rows.Count > 0)pre_action_id[i] = dt.Rows[0][0].ToString();sql = "SELECT TOP 1 action_id FROM s_step_info,l_loan_info WHERE step_order_no>(SELECT order_no FROM S_action_info WHERE action_id=@action_id) AND s_step_info.flow_id=" + arrFlowId[i] + " ORDER BY s_step_info.step_order_no";SqlParameter[] paras_ ={new SqlParameter("@action_id",SqlDbType.Int,4)};paras_[0].Value = action_id;dt.Clear();dt = DbHelperSQL.Query(sql, paras).Tables[0];if (dt.Rows.Count > 0)next_action_id[i] = dt.Rows[0][0].ToString();}DataSet ds = new DataSet();for (int k = 0; k < count; k++){StringBuilder sbTmp = new StringBuilder();if (!string.IsNullOrEmpty(pre_action_id[k]))sbTmp.Append("(step_id=" + pre_action_id[k] + " AND step_action='1')");if (!string.IsNullOrEmpty(pre_action_id[k]) && !string.IsNullOrEmpty(next_action_id[k]))sbTmp.Append(" OR ");if (!string.IsNullOrEmpty(next_action_id[k]))sbTmp.Append("(step_id=" + next_action_id[k] + " AND step_action='2')");string strTemp = "1=1";if (!string.IsNullOrEmpty(sbTmp.ToString()))strTemp += " AND " + sbTmp.ToString();sql = "SELECT * FROM l_loan_info WHERE ";sql += "1=1";if (step_action == "1")//已审批(签批){sql += " AND loan_id IN (SELECT loan_id FROM l_tranct_proc WHERE step_id=@action_id AND step_action='1'";if (step_emp_id == 0)sql += ")";elsesql += " AND step_emp_id=@step_emp_id)";}else if (step_action == "0")//待审批(签批){sql += " AND step_id=@action_id AND loan_id IN (SELECT loan_id FROM l_tranct_proc WHERE " + strTemp + ")";if (step_emp_id == 0)sql += "";elsesql += " AND loan_id IN (select loan_id from l_loan_next_emp where next_emp_id=@step_emp_id)";}else if (step_action == "")//所有{sql += " AND loan_id IN (SELECT loan_id FROM l_tranct_proc WHERE step_id=@action_id AND step_action='1'";if (step_emp_id == 0)sql += ")";elsesql += " AND step_emp_id=@step_emp_id)";//已审批(签批)sql += " UNION ";sql += " AND step_id=@action_id AND loan_id IN (SELECT loan_id FROM l_tranct_proc WHERE " + strTemp + ")";if (step_emp_id == 0)sql += "";elsesql += " AND loan_id IN (select loan_id from l_loan_next_emp where next_emp_id=@step_emp_id)";//待审批(签批)}sql += " ORDER BY loan_id DESC";SqlParameter[] parameters ={new SqlParameter("@step_emp_id",SqlDbType.Int,4),new SqlParameter("@action_id",SqlDbType.Int,4)};parameters[0].Value = step_emp_id;parameters[1].Value = action_id;if (ds.Tables.Count == 0)ds = DbHelperSQL.Query(sql, parameters);else{ds.Merge(DbHelperSQL.Query(sql, parameters), true, MissingSchemaAction.AddWithKey);}}dt = ds.Tables[0];/*去除重复*/DataView dv = new DataView(dt);string[] strCol ={ "loan_id" };dt = dv.ToTable(true, strCol);Hope.Model.L_loan_info model;List<Hope.Model.L_loan_info> modelList = new List<Hope.Model.L_loan_info>();if (dt.Rows.Count > 0){#regionfor (int i = 0; i < dt.Rows.Count; i++){model = new Hope.Model.L_loan_info();model = GetModel(int.Parse(dt.Rows[i]["loan_id"].ToString()));modelList.Add(model);}#endregionreturn modelList;}else{return null;}
}
以上为核心代码,当然由于工作流的具体需求不同,可调整相应代码。
第四部分、运行结果
1、系统参数配置
2、待处理列表
3、处理页面
(注:当前为最后一处理,下一处理人选择不可见)
4、撤回页面
结语:
完整自定义工作流由于应用广泛且业务逻辑复杂,要实现真正意义上通用的工作流还需要去做更多的分析和研究。
另本文不提供代码及其他资料下载,请勿留言索取。
抛砖引玉,欢迎拍砖!
自定义工作流相关思路相关推荐
- android如何做工作流,JIRA 知多少:聊一聊 Android Studio 、工作流相关设置
Android Studio 相关 配置 JIRA 服务器 配置 JIRA 服务器 配置服务器地址和用户账号密码 如果细心的话会发现有一个选项卡:Commit Message.这一段代码是不是有点熟悉 ...
- sql自定义函数学习思路_学习SQL:用户定义的函数
sql自定义函数学习思路 You can create several user-defined objects in a database. One of these is definitely u ...
- 实现用户自定义表单,自定义工作流
最近参与一个项目,客户要求提供自定义表单的功能.主要的要求是:能够对表单的字段进行增删改,对显示表单的格式可以灵活定制.由于客户的表单变动可能比较频繁,所以决定实现自定义表单功能.初步设想出以下两种自 ...
- JIRA 知多少:聊一聊 Android Studio 、工作流相关设置
Android Studio 相关 配置 JIRA 服务器 如果细心的话会发现有一个选项卡:Commit Message.这一段代码是不是有点熟悉呢?你没有猜错,这段代码就是 commit 模板,当你 ...
- 自定义工作流任务控件
读moss sdk中的自定义工作流任务控件. 自定义工作流任务控件:任务的创建,修改,删除,完成于一体,同时定义了这四个动作的历史纪录. 自定义时封装属性: 1. 封装任务属性 IsTaskComp ...
- DL之GRU:GRU算法相关论文、建立过程(基于TF)、相关思路配图集合、TF代码实现
DL之GRU:GRU算法相关论文.建立过程(基于TF).相关思路配图集合.TF代码实现 目录 GRU算法相关论文 GRU算法建立过程(基于TF) GRU算法的TF代码实现 GRU算法相关论文 GRU是 ...
- ML之GB:GB算法相关论文、相关思路、关键步骤、代码实现、配图集合、案例应用之详细攻略
ML之GB:GB算法相关论文.相关思路.关键步骤.代码实现.配图集合.案例应用之详细攻略 目录 GB算法相关文献.论文 GB算法关键步骤 GB算法代码实现 GB案例应用 1.GB用于回归 2.GB用于 ...
- CV之CycleGAN:CycleGAN算法相关思路配图、论文集合
CV之CycleGAN:CycleGAN算法相关思路配图.论文集合 目录 CycleGAN算法相关思路配图 CycleGAN算法相关论文集合 改进的StarGAN CycleGAN算法相关思路配图 1 ...
- CV之MTCNN:MTCNN算法过程及其相关思路配图集合
MTCNN算法过程及其相关思路配图集合 目录 MTCNN思路结构框图 MTCNN关键步骤 MTCNN思路结构框图 MTCNN关键步骤 1.Proposal Net 2.Refine Net 3.Out ...
最新文章
- SAP SD 客户信贷管理解析
- VS 2019要来了,是时候了解一下C# 8.0新功能
- python怎么导入包-如何理解Python中包的引入
- struts2自动接收表单数据
- 【全真互联网下音视频技术创新应用】
- C++ 数据结构 线性链表
- word20161206
- 深入理解定时器系列第三篇——定时器应用(时钟、倒计时、秒表和闹钟)
- dubbo 使用学习五(dubbo开发中使用到的一些服务配置方式)
- 白云区五措施扶持服务外包及呼叫中心产业发展
- ApplePay 支付开发
- SmileMiner:国人李海峰开发的开源Java机器学习库
- 用纯JavaScript实现的微信二维码图片生成器
- 【游戏开发实战】2D游戏摄像机镜头跟随,屏幕边缘限制镜头移动(使用Cinemachine组件)
- 为什么要用vue,他解决了什么问题,如何使用它?
- QT常用函数总结(全)
- Z-Wave技术的五大协议介绍(ZWave中心)
- 自然语言中corpora.Dictionary的理解
- java 批量执行 sql_JDBC批量执行SQL
- 成长部落# 编辑推荐 Python 入门学习内容系列
热门文章
- java计算器 控制台_java控制台实现的简单小系统,支持计算器功能,日历、乘法表算法...
- jstree获得节点的值
- 角点检测的几种基本方法
- Android fastboot 基本操作命令(Android 刷机)
- 四大运营商频段最新划分情况
- android 图文弹幕重叠,Android双重SurfaceView实现弹幕效果
- js中的定时器和计时器使用
- docker quick start
- AD(Altium Designer)PCB布线中的“格式刷”,助力快速布局布线
- IntelliJ idea——》JSON字符串,自动转义双引号