非常不错的文章!收藏了!原文作者: WXWinter(冬)
最近有不少朋友写信问我一些关于团队开发的问题,由于 这段时间有些忙,没有回复.今天写一篇这方面的文章向大家介绍一下我是如何带领团队开发工作流项目的
关于团队建设,项目管理的文章网上已经有很多了,在这里我就不谈这些理论了,直接给大家展示一个我在 项目开发方,后台服务开发方式,前台UI开发方式,后台服务与前台UI对接方式,代码文档,页面的开发文档,源码管理,单元测试,以及单元测试文档,实现 思路设计文档,数据库文档,数据库设计规范,编码规范,操做数据的方法命名规则 方面的一些片断,这是一个为期6个月的工作流平台开发项目,是今年3月份启动的,现在已完成,比计划时间多出25天.核心开发人员(不包括美工,需求,黑 盒测试)共有12人(编号从114到125)
补充一点:在UI草图设计上,这次想用绘图板,但最后还是使用铅笔绘制+扫描的方式制做的.
项目开发方式说明图
后台服务开发方式说明图
前台UI开发方式说明图
后台服务与前台UI对接方式说明图
代码文档(片断节选)
√ | 方法签名 | public int? addBaseEnumeration(string powerID, List list) |
√ | 返回值 |
|
√ | 参数 |
|
√ | 约束 | <1> 调用[security.checkPowerID方法]判断[powerID],如果[security.checkPowerID方法]返回[false],返回[401] <2> 如果参数[list]为[null],返回[-1] <3> 如果参数[list]中的[baseEnumeration.rowID]为[Guid.Empty],返回[-2] <4> 如果参数[list]中的[baseEnumeration.entity]为[null]或[string.Empty], 返回[-3] <5> 如果参数[list]中的[baseEnumeration.field]为[null]或[string.Empty], 返回[-4] <6> 如果参数[list]中的[baseEnumeration.entityType]为[null]或[string.Empty], 返回[-5] <7> 如果参数[list]中的[baseEnumeration.title]为[null]或[string.Empty], 返回[-6] <8> 如果参数[list]中的[baseEnumeration.value]为[null]或[string.Empty], 返回[-7] <9> 如果参数[list]中存在[rowID]重复的记录,返回[-8] <10>如果参数[list]中存在[entity][field][value]重复的记录,返回[-9] <11>如果参数[list]中的[baseEnumeration.rowID]在数据库中已存在, 返回[3] <12>如果联合唯一索引[entity][field][value]在数据库 中已存在, 返回[4] <13>如果参数[entity]为["baseEnumeration"],[field]为[entitytype]的数据,返回[7] <14>如果参数[entityType]的传入值不是枚举表[entitytype]的基础枚举数据, 返回[5] <15>如果插入时数据库异常, 返回[6] |
√ | 说明 | [Entity][field][value] 联合唯一索引 <14>获取[Entitytype]基础枚举数据时,使用[23 selectBaseEnumerationTypeName]得到枚举类型名称 |
√ | 单元测试 | (组合测试),(自动判断返回状态),(自动判断返回结果) WFServiceTestProject. manageServiceTest. addBaseEnumerationTest() |
//[26] 批 量添加枚举 public int? addBaseEnumeration(string powerID, List list) { //<1> if (!security.checkPowerID(powerID)) { return 401; } //<2> if (list == null) { return -1; } using (wxwinterDBDataContext db = new wxwinterDBDataContext()) { //<3> if (list.Count(p => p.rowID == Guid.Empty) > 0) { return -2; } //<4> if (list.Count(p => string.IsNullOrEmpty(p.entity)) > 0) { return -3; } //<5> if (list.Count(p => string.IsNullOrEmpty(p.field)) > 0) { return -4; } //<6> if (list.Count(p => string.IsNullOrEmpty(p.entityType)) > 0) { return -5; } ............................... |
页面的开发文档(片断节选)
模块编号 | Wxwinter.Index.Power.manageDutyControl | |
模块需要调用的其它UI模块列表 | Wxwinter.Index.Power.insertDutyControl Wxwinter.Index.Power.changeDutyControl | |
模块的调用入口UI | Wxwinter.Index.Power.navigationOrganizationControl | |
UI类型 | [ V ] 中控件 700 * 500 | |
工具栏按钮的调用路径 | [ V ] 无工具栏 | |
模块调用方式 | [ V ] 模式化弹出框 | |
action说明 | 不需要action | |
| ||
|
源码管理
单元测试,以及单元测试文档(片断节选)
文档
× | 方法签名 | public int? transactComplete(string powerID , Guid instanceID , Guid stateID , string transactResult , status status) |
× | 返回值 |
|
× | 参数 |
|
× | 约束 | <1>调用[security.checkPowerID方法]判断 [powerID],如果[security.checkPowerID 方法]返回[false],返回[401] <2>调用 [checkInstanceState()]方法对[instanceID][stateID]指定的实例状态进行验证,返回值不为[null],返回 [checkInstanceState()]的返回值 <3>调用 [checkStatus()]方法对[status]进行验证,返回值不为[null],返回[checkStatus()]的返回值 <4>得到 [wfStateTransactTask]表中 [ wfStateTransactTask.instanceID = instanceID && wfStateTransactTask.stateID = stateID && wfStateTransactTask.departmentNo = status.departmentNo && wfStateTransactTask.dutyNo = status.dutyNo && wfStateTransactTask.personNo = status.personNo ] 的记录并赋给变量[taskInfo],如果不 存在,返回[1] <5>如果 [taskInfo.runState != runState.wait],返回[2] <6>修改 [ taskInfo.runState=runState.end taskInfo.completeTime=System.DateTime.Now taskInfo.transactResult=transactResult ] 用[taskInfo]修改 [wfStateTransactTask]表中记录 <7>向[wfStepList] 中插入数据 [ flowID = taskInfo.flowID flowName = taskInfo.flowName nodeID = taskInfo.nodeID nodeName = taskInfo.nodeName departmentNo = status.departmentNo departmentName = status.departmentName dutyNo = status.dutyNo dutyName = status.dutyName personNo = status.personNo personName = status.personName instanceID = taskInfo.instanceID stateID = taskInfo.stateID processID = taskInfo.processID processName = taskInfo.processName stepAction = stepAction.办理 stepTime = System.DateTime.Now taskID = stepAction.办理 ] <8>如果数据库提交失败,返回 [3],成功,返回[null] |
× | 说明 | 调用 [checkInstanceState()]方法对实例状态进行验证 |
× | 单元测试 |
单元测试选项
范围 | 判断 | 影响 |
(无)
(不需要)
(全路径)
(正常路径)
(简单调用)
(组合测试)
(已在外部调试通过) | (自动判断返回状态)
(自动判断返回结果)
(人工判断返回结果,结果控制台输出)
(人工判断返回结果,结果存入磁盘)
(自动判断操作结果)
(人工判断操作结果,结果存入数据库)
(人工判断操作结果,结果存入数据库)
(不出异常即可)
(说明…) | (操作数据库,完成测试后已复原)
(操作数据库,已做state标记)
(操作数据库,需要手工复原)
(操作临时数据库) |
实现思路设计文档(片断节选)
例1:工作流结构的解析
例2:删除部门职能人员的约束
例3:查询用户的模块权限
public List<viewRelationModel> searchModelPowerOfPerson(string powerID,string personNo)
searchModelPowerOfPerson("","user1")
step1 | 使用 searchStatusList("", "user1") 得到[得到身份列表]
| |||||||||||||||||||||||||
step2 | 用得到的身份与[powerRelationModel]对比,并返回如下算法的集合
|
数据库文档
wfFlow 流程表
表说明:存储流程模板的属性信息,该表内容是将 xoml存入时,解析xoml后一次性生成的,不能修改
flowID | 流程编号 | f1 | 来自iFlow |
flowName | 流程名称 | f2 | |
flowType | 流程类型 | f3 | |
flowDescription | 流程说明 | f4 | |
businessType | 业务类型 | f5 | |
startWindow | 启动窗体 | f6 | |
dataFormList | 表单列表 | f7 | |
startDataForm | 启动时填写的表单 | f8 | |
inputFormItems | 传入表单 | f9 | |
inputFormItemsExpandData | 传入表单扩展数据 | f10 | |
commandOption | 命令选项 | f11 | |
commandOptionExpandData | 命令选项扩展数据 | f12 | |
ownedType | 流程归属类型 | f13 | |
timelimitUnit | 时限单位 | l1 | 来自iTimelimit |
timelimit | 时限 | l2 | |
overtimeOperate | 超时操作 | l3 | |
calendar | 日历 | l4 | |
residualTimelimit | 剩余时限 | l5 | |
createDepartmentNo | 创建部门编号 | 创建该流程模板的人员所在部门的编号 | |
createDepartmentName | 创建部门名称 | ||
createDutyNo | 创建职能编号 | 创建该流程模板的人员所担任的职能的编号 | |
createDutyName | 创建职能名称 | ||
createPersonNo | 创建人员编号 | 创建该流程模板的人员的编号 | |
createPersonName | 创建人员名称 | ||
createTime | 创建时间 | ||
isCheckout | 是否签出 | 如果为真,签出人可以对流程模板进行修改,非签 出人不能再将该模板签出进行修改,如果为假,就可以将该流程签出进行修改 | |
checkoutPersonNo | 签出人员编号 | ||
checkoutPersonName | 签出人员名称 | ||
checkoutTime | 签出时间 | ||
checkinTime | 签入时间 | ||
isFreeze | 是否冻结 | 如果为真,流程模板处于冻结状态,不能被启动 如果为假,流程模板可以正常启动 |
wfFlow 流程表 表结构
数据库设计规范(片断节选)
SQL Server 类型 | C# 类型 | 说明 |
nvarchar(50) | string |
对应的UI为单行行文本框,标签,提示文字 |
nvarchar(255) | string |
对应的UI为垂直滚动条的多行文本框,或多段的标签 |
nvarchar(MAX) | string |
对应的UI为双滚动条的多行文本框 |
编码规范(片断节选)
操做数据的方法命名规则
前缀 | 含义 | 传入 | 返回 |
get | 得到单条记录 | rowID | 单条记录对象 |
唯一键 | |||
search | 查询多条记录 | 唯一键 | List<记录对象> |
非唯一键 | |||
多个参数, [and/or]关系组合 | |||
select | 查询多条记录 | 唯一键,[字段名] | List 字段名所对应的[表.字段.ToString()] |
非唯一键,[字段名] | |||
多个参数, [and/or]关系组合,[字段名] | |||
change | 修改单条 | 记录对像 | 执行状态 |
stringResult.state 执行状态 stringResult.value 出问题的字段 | |||
set | 修改单条 | 唯一键,[字段名],[值] | 执行状态 |
rowID,[字段名],[值] | |||
update | 修改多条 (事物) | List<记录对象> | 执行状态 |
modify | 修改多条 (无事物) | List<记录对象> | 无法完成修改的List<记录对象> |
insert | 添加单条 | 记录对象 | 执行状态 |
add | 批量添加 (事物) | List<记录对象> | 执行状态 |
stringResult.state 执行状态 stringResult.value 出问题的字段 | |||
append | 批量添加 (无事物) | List<记录对象> | 无法完成添加的List<记录对象> |
remove | 删除单条 | rowID | 执行状态 |
唯一键 | |||
delete | 删除多条 (事物) | List<记录对象> | 执行状态 |
clear | 删除多条 (事物) | 多个参数, [and/or]关系组合 | 执行状态 |
wipe | 删除多条 (无事物) | List<记录对象> | 无法完成删除的List<记录对象> |
execute | 执行sql | SQL 字串
存储过程 | 执行状态 |
stringResult.state 执行状态 stringResult.value 问题 | |||
stringResult.state 执行状态 stringResult.value 返回对象的XML | |||
binding | 添加单条(关系表) | 记录对象 | 执行状态 |
unbinding | 删除单条(关系表) | rowID | 执行状态 |