客户的原始需求
收到H公司的需求信息:他们现在有三个系统:HR系统、OA系统、一卡通系统,各个系统的主要优势如下:
- HR:有强大的组织架构和人事信息的管理
- OA:有优秀的流程支持,可以让数据根据既定规则在不同的人员之间流转处理,或做数据自动处理
- 一卡通:有良好的工卡、饭卡统一管理,充值管理,考勤管理等的功能
此三个系统需要做数据同步,同步需求如下:
- HR跟OA:OA从HR单向获取组织架构、人事数据,当HR有数据变更,OA把变更数据拿过来,更新OA的相应数据
- HR跟一卡通:如上,类似HR跟OA
- OA跟一卡通:OA上有请假、加班、出差的审批流程,审批通过的请假、加班、出差数据写入一卡通。一卡通结合从OA获取的请假、加班、出差数据,以及自身功能采集的打卡数据,进行考勤计算。对于异常的数据送到OA,在OA走考勤异常申请流程,审批完毕后(通过或不通过),把结果写回一卡通。
下图是同步的数据流图,箭头方向是数据流的方向。我方是OA厂商,橙色部分所示。
H公司的要求是用Web Service方式同步,一个月内上线,各厂商先给出同步方案。
需求调研
第一次调研
按约定时间三方厂商赶到客户现场,到场人员角色如下
- H公司:项目总负责人、技术人员、其他人员
- OA:1位实施工程师,1位开发工程师
- HR:1位实施工程师
- 一卡通:1位开发(兼实施)工程师
三方系统结构
- HR:Java + SQL Server 2000 + Tomcat,BS/CS双结构
- OA:ASP.Net + SQL Server 2005 + IIS,BS结构
- 一卡通:C# + SQL Server 2005,CS结构
根据各系统的结构,OA跟一卡通用数据库同步,不建议用Web Service 方式;OA跟HR可用用数据库或者Web Service方式。我们各厂商都希望完全用数据库方式同步,可以省事。
对比各方的数据表。确认各方必须的字段,以保证系统能正常运行。确认同步所需要的业务字段,这是客户的需要。
我方跟一卡通公司的同步相对简单,双方都有开发人员在,经过两三个小时的讨论与确认,双方的接口以及同步模型基本敲定。
HR公司来的是实施人员,对系统的底层结构不甚了解,很多问题不能深入讨论。HR系统的技术文档里有相关的Web Service接口介绍,但是限于对HR系统自身数据的增删改查。现在H公司数百个部门、数万个人员,不可能每次同步都把所有部门、所有人员的数据全给OA,再对比哪些数据有变更,然后处理,这样性能太差了。如果要他们提供差异数据,他们的接口必须要重新开发。
此次调研我方给出的同步方式是:OA跟一卡通用数据库同步。OA跟HR若用数据库同步可以在一个月内上线,如果用Web Service方式,则需要两个月。
客户最终接受的同步方式是OA跟一卡通用数据库同步,OA跟HR、一卡通跟HR都用Web Service 的方式,可以接受推迟一个月上线。
后来,H公司转发来HR公司的一份同步方案。同步原理是:HR把差异数据放到数据库的中间表,定时扫描中间表,发现有差异数据,就发送HR的数据库链接方式给外部系统(OA ),外部系统(OA)直接读取HR的数据库中间表。
我看了之后,心里面暗笑,这样的方式,Web Service的同步只是一个幌子,实际还是数据库的同步。但考虑到用户的紧急性,技术我放行了,"只要能尽快上线,功能与时间第一,其它次之"。
第二次调研
一卡通公司在上海,其他各方都在广州,这时一卡通的开发人员已经回上海。此次调研的出席人员如下:
- H公司:项目总负责人、技术人员、其他人员
- HR:1位技术顾问、1位开发人员
- OA:1位开发工程师
HR公司的两位人员明显事前没做什么准备,没有一个完整的同步模型。我带着事前准备的同步方案,这样我显得比较主动。我的同步模型是:首先做手工同步,OA已经被H公司应用多年,数据肯定不能够清空,然后从HR系统来拿全新的数据。这样我要做中间表,存放HR系统与OA系统的主键对应关系,手工同步的作用就是填充双方系统的既有对应关系。其次是差异同步,HR新增数据,OA也跟着新增数据,并在OA中间表插入对应关系;若HR修改数据,OA从中间表找到相应记录,更改之;若HR删除数据,OA从中间表找到相应记录,只做记录,但不删除。
系统相关结构如下:
- OA的部门与机构分开在两张表,HR的部门与机构在同一张表,用不同的状态区别。
- HR有三个完全独立的数据库,每个数据库有自己的机构、部门、人员。三套机构、部门略有差异,大部分数据是冗余的;而H公司的人员分布在三个数据库中。
此次调研确定了:
- HR通知OA有数据变更的格式,包含OA连接HR数据库的格式;
- HR中间表的名称、表列及数据格式;
- OA接收HR通知的接口;
- OA返回同步结果给HR的格式;
- OA按严格的时间顺序 模拟HRP的数据变化来更新OA的数据,组织、人员、人员兼任部门职位信息,HRP给我们的必须是按更新时间排序的,OA用归并算法来同步这些数据;
- 数据约束:HR的某些部门不传给OA;HR目前有两个部门,"总部"与"分部",二者在HR是并列的机构,但在OA中只有一个机构"总部","分部"降了一级,作为总部的一个子部门;
- 若OA同步过程出现错误,则停止同步,避免同步的错误越来越大;
- 同步模型就上图的方式。
从我的主动与HR的被动,我想:到客户现场务必要事先做好准备,否则丢失的是客户对自己能力的信任和对公司能力的信任。事前我曾花了近三天时间写了同步方案,有些是实用的,有些是华而不实的,不实用的东西尽量不写,否则只是徒劳,而有用的东西尽量详实。
流水帐差异还是全表差异
流水帐差异 是指主表与中间表是一对多的关系,每发生一次数据变更,就往中间表写入一条数据,中间表的数据行多于主表的数据行,例如组织表organization的部门"人力资源部"的ID为11,则11这条记录会在中间表出现多次,出现一次代表"人力资源部"的一次数据变更。
全表差异 指主表与中间表是一对一关系,每发生一次数据变更,就覆盖中间表的对应数据。
HR提供的方案是组织、人员中间表都用全表差异的方式。我第一印象是不妥,可能会导致我们OA数据混乱,我一时举不出反例,但是潜意识里认为这样的数据不安全。冥思苦想很久,如果HR在10点新增了部门A,11点在A下增加了部门subA,12点修改部门A的名字为AA,这个12点覆盖了之前的10点。此时,OA从HR同步数据,发现11点增加了部门subA,但是找不到它的父部门,因为HR标识其父部门在12点新增(修改)。
所以,组织只能用流水帐差异的方式。HR的开发人员似乎特钟情与全表差异的方式,人员中间表还希望用这种方式。但是,他们的人员没有主键,只能用工号充当,而工号又后变更,全表差异也给推翻了,还得用流水帐差异的方式。
别人提供的技术方案未必是正确的,可能是误导,甚至错误的。还需自己进行技术论证。这个同步项目本身的确存在较多这种似是而非的陷阱,需要格外小心,稍不慎则犯错。
第三次调研
经过之前的调研,OA跟HR的同步正风风火火地开发,大概是此同步项目启动后的一个月,一卡通的开发人员才来到广州。
OA跟一卡通的同步方案跟第一次调研的一致,本次调研确认每个接口的签名,主要体现是存储过程与视图,包括存储过程的名称、参数的名称及参数类型,视图的名称、其列名称、列类型。
HR系统的人员不能提供唯一键,唯一可做标识的是工号,然而工号又可能会变更,还好H公司的工号不会重复利用,否则数据就可能混乱。
由于工号勉强做唯一标志,而工号又会变更,我抛出了一个问题:HR改工号后,若OA从HR同步成新工号,而一卡通未同步成功还是旧工号。此时OA与一卡通同步,就会找不到人员匹配。H公司决定:OA先从HR同步数据,OA同步成功后,一卡通才从HR同步数据。OA向一卡通发数据时需发新工号与旧工号,若一卡通匹配不了新工号就匹配旧工号;一卡通向OA发数据时,只发一个工号,OA用新工号与旧工号匹配之。
现在回想,人员似乎可以补用GUID来唯一标识之。
HR、OA、一卡通三个厂商之间互相讨论,到方案敲定,时间已是晚上六点。各有一份自己与其他两个厂商的同步方案文档,各写各的。H公司要求我们三方把三份文档整合成一份,由于一卡通的开发人员第二天要赶回上海的公司,整合文档一定要在当天出来。文档整合使用"Ctrl-C" "Ctrl-V"很容易,麻烦的是文档的排版,折磨到将近十点才完毕,更名为《H公司三方系统同步方案》。
开发与测试
经过第二次调研后,OA跟HR的同步开发工作就紧锣密鼓地进行。我跟HR的开发人员一个星期几乎有两三天在H公司上班。H公司也很重视此项目,专门分配了两个人员来协助我们,搭了三套虚拟机,各安装一个厂商的程序,模拟他们的现实环境。
HR公司的艰难
HR系统只能用一个"怪异"来形容:
- 数据库不能用SQL Server的备份还原功能来加载到虚拟机,而要用程序来恢复,数据库上10G,程序创建一次数据库将近1小时,然后报错,如是折腾了三天都失败,最后好像是从正式环境下复制数据库文件到虚拟机的相应目录,数据库的部署才成功。
- 数据散布在三个数据库中,同样是人力资源部,在库1的编号是0105,在库2的编号可能是0106,在库3中可能是0207。OA只与HR的库1对应,若同属于人力资源部的杨过与郭靖,库1中杨过的部门编码是0105是正确的,库2中郭靖的部门编码给OA的不应当是0106,而应该是0105,否则会造成OA数据混乱。我想光是将数据整合成一致的对外接口都够他们受了,并且维护也不是简单的事情,这不是我方的任务,管他的。
- 所有表都没有主键。组织表organization 的唯一标识是组织编码,但是部门(组织)移动后这个编码是会变更的。我建议HR在组织表新增一个自增列,来创建唯一ID。人员散布在三个库中,无法用自增列,因为自增列在不同的库中会重复,只有将就着用工号。
- HR公司是个代理商,开发人员没有HR系统的源代码,只能在系统外围做处理
开发初期,接口频繁变动,一个月后,接口基本稳定,HR与OA同步的某些模块已经初步实现。此时一卡通公司开始参与进来,经过上面的"第三次调研",一卡通的开发人员把电子文档发回公司。第二天一大早我接到H公司项目负责人Z的电话,问我实现方案是否怎么变了,何时变了。我告诉她在第一次调研后HR发来的文档就已经变了。接着我收到H公司转发来的一卡通的Email。Email内容痛陈HR与OA的同步方案是非数据库非Web Service 同步的四不象,复杂有加,实用不足,逻辑存缺陷,安全有隐患,并提供了他们的同步方案,请H公司重新做技术审核云云。洋洋洒洒两三千字,图文并茂,措辞犀利,我打从心底里面佩服作者的文笔功力。
文章不是专门针对我的,我没有太大歉意,但是我没有特意指出这个问题给H公司,我有连带责任。我原先是希望尽早实现功能,让客户尽早上线。如此看来,我的算盘打错了,欲速则不达,现在推翻之前的工作重来,项目进展更慢了。
我反思:客户不是傻子,不要忽悠客户,到头来只是忽悠自己。我同时也纳闷,早在H公司转发HR公司邮件的时候,他们也收到邮件,怎么当时不指出此问题的严重性,而留待至今。
OA接口的开发
HR的同步方案被一卡通推翻后,H公司跟HR公司重新确认了同步方案,如上图所示。我花了5个工作日来重新实现被推翻的功能。同时我重构了程序,原先很多工作都放在程序上做,程序写得很长,后来我又把大部分工作挪到数据库级别来做,数据库密集的工作,应该让数据库来直接处理。程序变短了,逻辑更清晰:
- 调用HR的Web Service接口获取差异数据XML,
- 把XML转换成DataTable,
- 迭代DataTable来做同步操作
- 把同步结果返回给HR系统
然而存储过程变长了:
- 根据HR发来的数据,检索对应的OA数据
- 数据容错校验
- 若容错校验通过,同步数据到OA
这样的结果是,程序的责任减轻,只是做了数据获取与数据传递(到数据库)和记录日志的作用,很容易保证程序的正确。而存储过程的责任加重,问题的出现也基本在此。存储过程写得够长的,我出来,逻辑结构也很清晰,该注释的也注释了,人员的同步与兼任部门职位的同步也容易调试。但是组织的同步一直折磨着我,特别是HR系统中"总部"与"分部"是并列关系,在OA变为机构与子部门之间的父子关系,我翻来覆去改动了多次,依然无法保证数据的正确对应关系。SQL脚本的不易调试,如何验证我的存储过程是正确的,或者说如何证明我的存储过程是错误的。最后我写存储过程来测试存储过程,这也不失是一种很好的单元测试方法。
OA跟一卡通的开发工作大概用了3个工作日便完成,运用链接服务器从OA数据库链接到一卡通的数据,调用它们的存储过程和视图。唯一困扰的是在Sql ServerManagement Studio中直接远程调用一卡通的数据库对象时很顺利,但是放在作业中让Sql Agent自动调度时出现诸如"安全上下文"的错误,折腾了两天没解决,在公司经理的指导下,配置一个Windows帐号来启用Sql Agent服务后,并给该帐号Sql Server的相应权限,问题得以解决。
部署
客户的机房里面运行着重要的程序、存放着重要的数据,稍不小心就有可能把程序搞垮,或把数据弄坏,尽管客户事前已经做过备份,一旦出现不可回退的误操作,还原现场也需要耗费大量的时间。所以,一个好的部署方案应该有序、不出差错,这需要事前充足的准备,列一个清晰的部署计划列表,第一步做什么、第二部做什么,井然有序。先把此部署列表给客户,好让他们清楚整个部署过程,消除心中疑虑,知道部署人员在干什么,将要干什么,这样客户会更好地支持部署人员的工作。这些我们都基本做到了。
结语
如何对待不合理需求
H公司最初给的上线时间要求是一个月,当时李经理指导我把此同步项目当作正常的二次开发项目对待, 调研、需求分析、详细设计、代码开发、测试、试运行、部署,但是这样的项目周期远超过客户的时间要求一个月,我自身也疑惑,到底以哪个时间为参考;后来一卡通也提到,延迟上线造成的不便可以通过其它途径来弥补。客户的要求的时间限定只是一个参考,否则软件不能交付、或者交付了有严重问题的软件,你客户敢用吗?客户的时间需求也是一项需求(应该算非功能性需求吧),当需求不合理时,尝试说服客户,否则后果还是客户自己承受。
文档的规范
H公司和一卡通的文档很规范,尤其是一卡通,常常图文并茂,图形很漂亮。
工作范围
接口是静态的,怎么处理接口是动态的,OA跟一卡通双方技术讨论的时候,都只提接口的事,没有任一方提出谁来处理这接口,谁来创建SQL Server作业调度对方接口。而我的心态是我来做,毕竟做过了就是经验,对我自己或对公司都是一种经验,以后可以借鉴。
- The end