jxTMS架构

41 篇文章 0 订阅

jxTMS架构

jxTMS架构

jxTMS自带了一个web服务,10018端口用于提供动态web界面的操作,10119端口用于提供系统管理。

系统需要一个commonDB,用于用户信息、组织信息、消息这三者以及其它必要数据的保存。所有部件通过rabbitMQ消息总线进行通信。

针对每个组织,jxTMS会创建一个ORG对象来提供app服务来监听消息总线对其访问,并代理该组织私有数据库的管理,缓存组织内的各种数据以降低数据库的访问开销。

由于采取了消息总线进行访问,所以理论上,web服务、ORG的app服务、第三方微服务都是可以独立的、多机负载均衡的,也没有数量上的限制。只是目前所开放的jxTMS版本,采取了如下的限制:

  • web服务和ORG的app服务合并在一台服务器上,第三方微服务目前只有前端

  • 每个jxTMS系统,目前有一个demo的app,额外预留了5个组织可供开发者使用,每个组织在创建时,jxTMS同时自动创建一个正式组织的app服务、一个Test组织的app服务,所以目前每台服务器可使用的ORG共11个,可同时为5个组织提供服务

commonDB

jxTMS设计思想就是平台化,就是要支持用户在多个企业之间协作、入职离职等。所以就必然需要一个公共的数据库用来保存所有用户的一致性信息并保留平台的登录信息。同时还需要保存所有组织的相关信息、个人的消息等等。

目前,jxTMS对这些信息只具备最基本的使用,尚不支持:

  • 用户在各组织的经历、工作评价等档案信息

  • 个人消息是基于组织内各容器的,尚不支持个人之间的私信,同时考虑到目前也不太会有太多通过jxTMS进行消息交互的需求,所以消息功能目前暂不提供前端访问接口

注1:所谓的容器是指部门、班级、大赛这样有多个参与者的事物,其消息发送逻辑是发出一条消息,所有参与者在轮询接收消息时会检查该容器是否有新消息。参与者加入与退出该容器时,绑定或解绑这个消息查询点。所有消息是统一增加消息号的,用户各自记录自己已经查看过的最老的消息号,然后以此消息号为基础,检查自己当前绑定的所有容器的最新消息
注2:消息是全站的,即如果用户同时加入了多个组织,这些组织所发送的消息他都可以查看的到而不需切换到这些组织才能查看

app

jxTMS为每个组织创建一个ORG对象,该对象在加载时自动启动一个app服务。其主要完成:

  • 监听消息总线上发送给本ORG的消息

  • 处理用户在本组织的登录,为其创建上下文,包括准备web访问的关联、用户信息、ORG信息等

  • 接收web服务等前端发送过来的数据,将其关联到相应的上下文中

  • 接收web服务等前端发送过来的用户操作,核验用户是否有执行该操作的权限

  • 为其加载相应的功能模块,并关联上下文,然后调用该操作所对应的函数

  • 根据用户请求,发送web界面描述,并根据用户提交的参数,对该界面进行初始化,包括数据装定、工具条准备、权限审查准备

  • 为该函数准备数据库连接,并将整个函数的数据库操作打包为数据库事务,在函数执行完毕统一提交

  • 处理函数异常,将其保存到系统日志并发送到web服务端通知用户

  • 将事件处理函数的输出转换后发送到相应的web服务

注:上述所有处理,开发者不需关注与介入,jxTMS完全根据开发者的定义完成上述处理。如根据入口中定义的role以及人员所映射的role进行匹配来完成权限审查;根据web文件,将其转换为json发送到web服务;根据python代码进行初始化等等

app的web数据访问

jxTMS目前支持两种前端的数据交互方式:

  • 异步模式:jxTMS设计用来实现web界面、后端业务处理的一人式开发,从而大幅度的降低开发成本。所以原生的web界面都是动态的,由后台根据用户请求发送json描述到前端,由前端动态生成各种web控件。用户的输入则是触发相应的change事件,统一收集,以异步轮询的方式发送到后台并捎带回后台的输出来更新各控件的显示

  • 同步模式:由于动态web界面都是系统固化的控件,其风格等比较单一,而且大小、对齐等控制就难以精细化,所以难以做到美学上的优化,只能提供一个入门级的用户界面,这对于部分对美学要求较高的项目,客户难以接受。所以,jxTMS增加了支持静态web界面的同步模式。即开发者需自行开发web界面及其交互管理与数据读写,而jxTMS只在进行页面初始化时也之执行数据装定功能

注1:两种模式的切换,通过送入staticWeb=true的参数实现,默认即动态
注2:同步模式还用于自动化测试
注3:tms目录下的webRoot是动态web界面的根目录,staticWeb是静态web的根目录,但考虑到目前精力需主要集中于接受开发者提交的bug修复和文档编写,所以暂不开放静态web界面的访问接口

在异步模式下,setOutput会将输出放入到上下文中的数据cache中。浏览器会定时调用pollData函数提交当前界面中各输入控件的change,同时将后台cache中的数据捎带回前端并更新各控件的显示。

pollData函数的调用:

  • 用户每次操作前【点击各种按钮、工具条、快捷入口、菜单等】会触发一次

  • 用户操作完,pollData函数按每隔一秒的频率进行调度,每执行5次,调度间隔加倍,直到调度间隔超过一分钟,即长时间无操作后是每64秒调度一次

注1:原生采用异步模式的原因乃在于,有些操作,如统计、分析、智能计算等非常耗时,那么同步模式下为了避免用户失去耐心就需要分成多个接口,将其中的部分操作以异步的方式后台执行,而javascript本身又是不支持异步编程的,这就会导致编程模型的复杂化。违背了简化编程、降低编程门槛的初衷。而这种情况下,异步模式就非常简单了,耗时操作执行前,对某个web控件输出【执行中,请耐心等待】,执行完毕输出【执行完毕】,然后将执行结果输出即可。用户是否想执行其它操作完全不需考虑
注2:此外,如果有后台通知、告警等等,如果是同步模式,就需要开发者额外考虑整个界面中相应操作的管理,然后启动定时器来轮询。而异步模式则还是完全不需要开发者考虑,有了相关的消息等,后台代码直接输出就可以了,前端最迟一分钟后就会自动显示出来

功能点capability

capability是jxTMS最核心的概念之一,所以的业务代码都必须继承自capability或其子类。jxTMS的用户代码组织是以组织为最大容器的,然后分为不同的空间,空间中又分为不同的模块,每个模块包括5种用户自定义的文件【不需要则可忽略】:

  • web:文本文件。用来逐个定义动态web界面中的控件

  • data:文本文件。用来定义数据库中的数据表。jxTMS内置了部分通用数据表,并为访问这些数据表提供了预定义的接口,用户可以直接使用。但由于这些表是通用的,所以其字段也自然无法反映业务,这样其他人员阅读源码就有一定的困难。所以jxTMS开放了ORM定义,使得开发者可以直接用文本文件来定义数据表,这样就更容易反映业务,其他人员的阅读也自然更容易,但这就需要开发者自行编写相关的数据访问接口。所以开发者需要自己权衡这两者的利弊

  • sql:文本文件。用来定义如何从数据库中查询业务所需要的数据。即便是jxTMS内置的数据表,根据业务需要也是必须由开发者在必要时定义专用的数据查询语句的,jxTMS将这种sql语句称之数据源。jxTMS提供了一种类似sql语句的语法让开发者可以简单的编写数据查询语句,然后在调用时直接引用就可以了

  • op.py:python代码文件。用来定义用户的操作入口。jxTMS动态界面中共有四种操作入口:菜单项、快捷栏项、界面中的按钮【即button】、界面中的工具条【即a】。这四种控件,点击后即会发送给后台一个操作指令,这个操作指令即被称为一个入口。如果该入口未被显示定义,则jxTMS会拒绝执行。该入口定义中最重要的就是role,其指定了可执行该入口的角色,如果当前用户未映射该角色,则jxTMS会报告无权执行该操作

注:如果是动态web界面,未映射相应的role则不会显示相应的入口控件

  • capa.py:python代码文件。用来定义业务处理代码的capability。用户定义的入口,有两种类型,一种是打开一个页面进行显示的,对于这种入口,capability将其映射为和页面同名的prepareDisp事件,开发者要在自定义的capa中绑定这个同名的prepareDisp事件进行数据装定;一种是用户在该页面中的点击操作,对于这种入口,capability将其映射为和操作同名的cmd事件,开发者要在自定义的capa中绑定这个同名的cmd事件来执行用户期望的操作

capability在发射这两种事件到执行开发者自定义的python代码时,会为其准备好上下文并打开一个本ORG私有数据库的连接并准备好数据库事务,这些代码如果执行出现异常,capability也会收集异常,然后将其记入系统日志并发给用户一个弹窗消息通知其出现了问题,好让其联络现场维护人员进行处理。

注1:capability如何编写,请阅读服务器tms目录下codeDefine目录中各模块中的capa.py源文件中的注释【先读manager、msg下的源文件,最后读demo下的源文件】
注2:codeDefine目录下msg、manager这样的两级子目录是系统通用模块,即每个ORG都有的公共模块;而demo这样的三级子目录,是ORG自定义的模块,以组织全名【创建该组织时所给出的组织全名,最好是工商注册名,以避免重名】为目录名,其下一层子目录即空间名,再下一层即模块名

需要注意的是,capability的资源名,如web控件、sql数据源都是以空间名为前缀的,即在同一个空间中,这两样资源不得同名;而操作入口的命名格式形如:空间名.模块名.disp或cmd.操作名。所有的权限检查、模块加载、工具条链接等都是以这种形式的全名为准的。

只要知道了各资源或入口的全名,在模块导入后,即可在ORG范围内通过该全名加以引用,系统会自动加载相应的资源或capa来提供开发者所需的服务,当然,前提是开发者为其指定了相应的角色并为用户映射了该角色。

capability还集成了其它一些常用功能:如简易流程、业务规则、统计等。请阅读源码和后继的专项说明。

affairMgr类

affairMgr类继承自capability,主要用于提供对事的管理。其提供了:

  • 流程管理,功能完备而强大的流程定义与驱动

  • 简单流程管理,流程管理对大多数业务来说都比较复杂,所以affairMgr类特意提供了一个简化的流程管理能力,以更快更简便的开发各种简单的小流程

注:由于目前精力不够,而流程又比较复杂,所以目前暂不开放流程功能,请开发者先集中尝试简易流程的使用

  • 现场保存,流程操作需要保存前一个业务点用户填写的流程数据以及审批动作、处理情况等,如果这些信息全部由开发者自行保管则较为繁琐而且是完全重复性的,所以affairMgr类提供了一个对这些信息的现场保存与自动复现功能

  • 快照,由于流程可能被打回重做,则相关的业务数据就存在多次修改的情况,为了追溯流程的执行情况,affairMgr类提供了快照功能,即每次现场保存的信息发生变化时即加以保存,同时affairMgr类还提供了流程流转的日志记录、数据变动记录等功能。以在审计或事后追责时可用于明确责任、固定证据

  • 合规性检查,业务管理需要对业务操作是否合规进行检查。这种检查包括两个层面:一是用户输入的数据是否符合要求,这被放入了web端进行检查,以降低系统开销并提高用户感知到的反应速度;二是业务逻辑的合规性审查,比如,销售给出的折扣太深,超出了其权限,则自动将订单审批流程流转到更高层级的领导处进行审批,并将超权限部分标红以做提醒。为实现这一功能,jxTMS提供了业务规则编写与应用能力。业务规则当然也可以直接编码来实现,但绝大多数的用户都对代码看起来头痛,而文本样式的如果…则…,其就能接受,也就是开发者和用户有了一个讨论业务逻辑或规则的桥梁

  • 自动计算,提供类似excel的行累加、折扣计算等功能,为了提供效率,此部分也是推到了web端执行【所以严格来说,自动计算和数据项检查都属于jxTMS固有能力而不是affairMgr类提供的】

  • 列表查询页面的标准化操作,针对大量的列表查询类页面,提供了基于分页控件的工作逻辑。原则上,用户只要用文本定义了该页面的表格,并定义了查询该表格的数据源【sql文件中定义】,如果有条件查询则再编写条件查询的代码,则该查询页面即可自动工作。这种自动化表格输出的工作逻辑即是由affairMgr提供的

python中继承affairMgr类的示例源码

 一般来说,开发者需要编写的python代码都是继承自affairMgr类,然后简单修改并增加业务处理代码即完成了python的代码编写工作。其代码结构为:

#导包,原则上直接将demo中这一部分直接copy过来即可,一般有30个左右
from data import ...

#自定义的名为testDemo的capa
class testDemo(affairMgr):
	#系统在用户请求执行某操作时发现需要加载testDemo,则将调用本函数生成一个新的capa对象来提供服务
    def New(self, con):
	return testDemo(con)

	#本capa所属的空间名
    def module(self):
	return 'test'

	#模块名
    def name(self):
	return 'demo'

	#本capa操作的数据对象的类型【或者是jxTMS内置的或者是开发者在data文件中定义的数据表名】,主要是用于查询时自动将查询出来的数据转换为相应的对象
    def joType(self):
	return 'demo'

	#在列表本capa所操作的数据对象时,自动生成的【查看】入口所对应的disp入口的界面名称
    def viewWebInterface(self):
	return 'viewDemo'

	#如果希望自动生成流水号,则该流水号的模板格式。此处为:Demo-000001、Demo-000002等等
    def SerialNumberModel(self):
	return 'Demo-{SNT6}'

	#定义一个cmd入口,用户点击某按钮以请求访问test.deom.cmd.testOp1入口,将调用本函数提供服务
	#db:数据库接口
	#ctx:上下文
    @myModule.event('testDemo','cmd', 'testOp1')
    def testOp1(self,db,ctx):
    	#相应的业务处理代码

	#列表时,从数据查询出来的数据如何显示到前端【因为有数据格式以及改名的可能】
	#如数据库为了节省空间,将状态用1/2/3等等进行保存,但要显示给人看,就得转换成:未开始/进行中/已结束
	#又如,开发者为了减少代码编写,不定义专用的数据表,而是直接采用了jxTMS内嵌的数据表,但这些表的
	#属性名是通用的,如Number,无法反应业务情境,所以就需要在这里改为【库存数量】这样的名字
    def dispAffairInfo(self,db,ctx,json, jo):
	json.set("demoID", jo.ID)
	json.set("demoName", jo.Name)
	json.set("demoState", tools.transState(jo.State))
	json.set("storeNumber", jo.Number)
	#其它显示

	#用户请求test.deom.disp.viewSubject时,执行本函数对界面进行初始化以及数据装定
    @myModule.event('testDemo','prepareDisp', 'viewSubject')
    def prepare_viewSubject(self,db,ctx, page):
	#相应的页面初始化以及数据装定
	
	#列表查询时设置动态查询变量值
    def setSearchConditionVarValue(self, db, ctx):
	if self.dataSource == 'test.sqlListDemo':
	    self.infoSearch.setVar('demoID',self.getInput('demoID'))

	#列表查询时根据用户输入增加查询条件,一般用于用户按条件搜索
	#
	#setSearchCondition是动态的给列表查询增加一个筛选条件,如根据用户输入的销售名查询其名下的销售订单
	#根据用户输入的时间段,查询那个时间段内的销售订单
	#
	#setSearchConditionVarValue则是根据上下文的不同,对一个已经确定的查询条件设置当前的取值,如所有的销售都有
	#【我的订单】这一个查询入口,但是在真正执行时就要根据查询者的不同来设置不同的用户ID进行查询的
	#
    def setSearchCondition(self, db, ctx):
	if self.dataSource == 'test.sqlListDemo':
		#添加查询条件
	    
# 一定要有,否则无法顺利注册
# 由于testDemo是一个类,所以在此生成一个testDemo对象,以将testDemo这个capa注册到系统中
sp=testDemo(biz)

目前,jxTMS已经打包为云服务器镜像,开发者开箱即用:
jxTMS-腾讯云市场​

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值