背景
十几年前,为一家工厂开发过一套供应仓储系统。适合工厂用的仓储系统市场上有很多,这家工厂的规模还是可以的,有2500人左右,之所以要定制开发,是因为要把供应计划、询价、报价、合同等供应整个流程中的业务全部内容纳入系统,进行管理,不单是入库、出库和成本算。市场上成熟的erp系统太大,仓储系统太小,定制开发也是合理的选择。当时开发工具用的是上古神器delphi
,开发团队对整个业务流程比较熟悉,简单调研了一下需求,3个月左右时间完成开发并投入运行,又用了2个多月的时间修复bug和维护,就正常运行了,一直平稳运行了十几年,其间因为把成本计算方法由计划价改为移动平均价,做过一次小的升级。
2020年10月,这家工厂准备对系统做一次大的升级,主要的思路是强化各业务的关联性,达到全流程追踪物资供应的目的。比如谁报的计划、哪个厂家供的货,哪天入库的,哪天领出的,还剩多少没领出等等,全部一对一串联起来,每一笔业务都可追溯。由于原系统设计各业务流程间的数据并不是一一对应,如:出库时并不会记录这一物资是和哪条入库记录对应的,出库也不会反映到计划中,升级后要实现入库记录上要动态反映结存数据,以及申报计划人和领用人是否匹配,数量是否相等。要把整个系统串联起来,系统的改动还是非常大的,每个业务模块都需要重新规划设计,数据结构变化很大,用户要求保留原来的数据。
由于之前的良好合作关系,客户非常信任,要把这个升级项目交给我来做,事先沟通过,只能用业余时间来做,工期上给的也比较宽松。
技术选型
多数客户并不关心项目采用什么技术,主要是实现功能。旧系统是delphi开发的(有源码),继续用delphi,效率高,风险小,自己也比较擅长。但这样有两个问题:一是从用户的角度考虑,采用传统的c/s结构,扩展性差,如果系统的生命周期是接下来的10年,手机、平板、互联网远程办公等未来可能都是需要的;二是缺乏挑战性,自身没有成长,码农又卖了一次苦力而已。
最终方案:
- 用户服务器是windows的,数据库是sqlserver,要保留历史数据,这个没的说,不能变。
- 系统架构采用前后端分离,便于将来扩展,windows上后端
.net
比较合理了,考虑了一下,决定使用.net core 3.1
做后台api服务。 - 前端用
React+Antd
,Angular也是选项,只是antd官方维护的React版更有信心。
开发过程
开发过程比较顺利,主要是业务流程非常熟悉,对客户的需求把握比较准确,一边学React,一边学Antd,用了五个月时间,基本完成了项目的升级工作。说是升级,其实是重新开发,只有数据库结构保留了大部分原设计,因为要保留原系统的数据。在这之前,没用过React,既然是新学,用的hooks
,简单好用。这种管理系统全是增删改查,没什么技术难度,复制粘贴,然后修修补补,重点是如何从用户的角度考虑把业务需求转变成增删改查的方法去实现,其中有几个技术性的细节,绕了一些弯子:
四舍五入
这本来是一个很简单的问题,平时很少遇到,系统涉及到成本核算,要求保留6位小数。但由于系统涉及到了多种语言,舍入处理方法不一致,造成计算结果不一致,还是遇到一些坑的:
- javascript 的浮点数运算,是使用一种国际标准,直接计算是有误差的。
toFixed()
和Math.round()
都不能实现四舍五入。 - c# 浮点数运算可以使用参数控制使用哪个舍入方法,默认的算法不是传统的四舍五入。
- SqlServer的浮点运算6位小数以上有精度问题,
round()
计算是平时理解的四舍五入。 - Excel的
round()
计算是平时理解的四舍五入。
由于涉及界面计算、后端计算、导出excel后计算,要求系统中的计算数据一致,因此需要统一浮点运算结果和舍入方法。
操作习惯
- 单据结构。业务系统肯定会涉及大量单据,单据一般都是主细表的结构,要放到一个页面上,控制起来比较复杂,为了简化界面控制复杂度,把单据改成了列表/明细结构,在工厂这种特定的场景下,问题也不大,很快用户就适应了这种界面。
- 录入时回车移动到下一行。传统界面都是可以使用回车移动光标的,但浏览器中使用TAB是默认的方式,由于用户需要一只手翻单据,另一只手录入数据,使用TAB不方便,专门对录入界面进行了操作的优化。
- 分页。传统c/s结构,列表都是显示全部数据的,用户可以一直向下拉着看,由于浏览器的渲染性能问题,只能分页,用户非常不习惯,要求一次显示全部数据,由于业务数据非常多,全部显示非常慢,每个用户的电脑性能也不一样,最后采取折衷的办法,用户可自定义页面行数,最大可以一万行一页,如果电脑性能允许,或者用户愿意等,也是可以的。
- 列表录入。有些列表需要一次录入所有行整列的数据,如果用弹出窗口做,操作过程太过繁琐,就改用在界面渲染出所有行的input,录入数据后,回车换行,自动保存的方法。
打印
管理系统的打印是必不可少的环节,打印格式要求能随时修改,打印出的文档还要规范。浏览器直接打印不行,后台输出pdf,再到前台打印,体验也不好,最终采用了一个迂回的办法:
- 原系统中有一个设计好的打印组件,可以设计模板和使用模板渲染数据进行打印。仍然利用原系统中的这个打印组件(DLL)和原来设计好的打印模板。
- 使用delphi做一个简单的
Web Server
,接收http
指令来调用打印组件实现打印。 - 打印的
http
连接在页面中发起,连接到Web Server
上,这个服务器安装在用户的电脑上,使用localhost
连接即可。过程是:需要打印的用户,要安装一个小小的打印服务器软件,安装一次即可,安装后自动启动。 - 打印服务器是无状态的,从页面通过
http
协议传递模板文件、要打印的数据,打印服务器只是按一定的规则读取传递过来的数据,进行打印即可。
部署
.net core 3.1
的部署还是非常简单的,直接使用dotnet 项目.dll
启动项目在指定端口,再通过nginx反向代理一下就可以了,性能不错。不需要iis。
结语
项目已经成功运行几个月了,最初两个月修复了大量bug,非常感谢系统的核心用户,他们对系统的细枝末节非常熟悉,总能发现自己测试中忽视的问题。不要放过用户提出的任何疑问,要仔细思考,问题背后可能是你忽视的业务规则。
用了半年多时间升级这个项目,也算有所收获。