jxTMS的HelloWorld

jxTMS已发布docker版本,大家可以下拉使用:
docker版jxTMS使用指南

本文所讲述的是以docker镜像为例,而注册到jxTMS后则可开箱即用了,本文以及本系列文章如和上文有不同,则应注意以新的文章所述为准。

本系列还包括:

1、数据库操作

2、数据库查询

3、多表联合查询

4、类表同步的继承

5、简易流程

6、简易流程的流转

7、流程追溯

8、简易流程的查询

9、导入excel

10、导出excel

11、合规性保证

12、降低合规性检查时的规则复杂度

13、兴趣点

14、角色与权限

15、使用静态web界面

16、和其它系统勾连

jxTMS的Hello world

jxTMS是以低成本快速定制为目标的业务系统开发平台。jxTMS的开发思想是打铁,即随动用户业务发展一点点的进行迭代,是在配合逐步改善的业务管理而提供持续的、微调的定制服务。本文讲述如何用jxTMS编写一个最简单的Hello world程序。

由于jxTMS的helloWorld涉及到web界面的制作、操作入口的制作、相关的处理逻辑,所以相比其它的helloWorld会比较复杂,大家最好是边动手按本示例的说明边做边看。

准备工作

首先,需要下拉jxTMS镜像,拉好后根据说明用该镜像启动一个名为tms的容器:

docker run -itd --name tms --privileged=true -p 10022:22 -p 10018:10018 -p 10119:10119 -p 10873:873 jxandrew/tms

注:启动tms容器时,占用了本机的四个端口,分别是:

  • 10022端口,映射为容器内的22号端口,用于ssh访问和sftp更新代码

  • 10873端口,映射为容器内的873号端口,用于rsync来同步代码,本demo中未用到

  • 10018端口,映射为容器内的10018号端口,用于动态web界面的访问

  • 10119端口,映射为容器内的10119端口,用于系统管理,本demo中未用到

挑选这些端口的目的就是不和其它应用产生端口占用的冲突,一般是没有冲突,但大家最好确认一下这些端口确实没有被占用。

然后,通过本机的10022端口,用ssh【用户名/密码:tms/123456】登录到该容器。然后执行:

cd codeDefine/demo/demo/demo1
cp ~/demoCode/demo1/* .

注:请注意本demo中所有的演示代码的存放目录都是tms容器中的/home/tms/codeDefine/demo/demo/demo1目录中。如果热机刷新后未发现任何变化,请首先检查代码是否保存到了这个目录中

将代码拷贝工作区中后,启动整个系统:

cd
sudo ./startTMS
#会提示输入tms的密码,请输入:123456

注1:由于docker容器没有启动init,所以【docker start tms】启动容器后,相关的服务都不会自动启动,所以需要手动执行startTMS来启动所需的服务和jxTMS系统

注2:jxTMS自身只需要mysql和rabbitMQ两个基本服务,ssh和rsync是为了系统管理方便的需要

注3:jxTMS是用java8开发,所以镜像中的java环境是OpenJDK8。笔者曾测试过,在java11的环境下,jxTMS也是可以正常工作的

注4:本示例手册中所有的代码最好是先将demoCode下的所有文件下载到本地,然后根据本手册各节中的说明修改相关文件后再上传到容器的/home/tms/codeDefine/demo/demo/demo1目录中。笔者建议用fileZilla通过sftp的方式进行代码管理,请参考用sftp管理jxTMS的代码

首次尝试

在容器中执行过startTMS后,打开浏览器,在地址栏中输入127.0.0.1:10018/tms.html,最好将该地址保存下来便于以后的访问。

在弹出的登录框中输入用户名:manager,密码:123456。登录后,系统会显示工作界面。

在左侧的快捷栏中,点击【演示->helloworld】:

jxTMS的HelloWorld_第1张图片

系统弹出helloWorld的界面:

jxTMS的HelloWorld_第2张图片

点击【点我】按钮,系统弹出一个对话框,要求确认是否执行,点击【OK】按钮后,系统在【说点啥:】后显示:Hello world!

注:主界面的左侧为快捷栏。上面为功能菜单栏和系统菜单栏,大家不要去点,包括退出jxTMS,只要重新刷新一下界面就会自动退回到登录界面的。上面的两菜单栏只是给大家看一下的,一定不要去随意点击,否则在不熟悉的情况下执行这些系统层面的操作可能会导致jxTMS不工作了。不过呢,不工作也没事,删掉这个不工作的容器,回到本节的开头重新启动一个新的tms容器就好

代码结构

所有用户代码都保存在codeDefine目录下。这些代码被组织为两个层次,对应于codeDefine目录下的三级目录结构。大家可以进入codeDefine目录下查看一下:

  • manager目录下是部分系统管理的代码,大家现在不要去管它们

  • demo目录中所保存的是demo组织的用户代码,如果大家需要jxTMS支持多个企业,则可以在codeDefine目录下分别为不同的企业创建不同的代码主目录【如还有一个test目录,是test组织的用户代码的主目录】。由于本镜像中的虚拟组织名字是demo,所以其所对应的代码主目录名就是demo

注1:组织名有三种:全称、简称、唯一性简称:

  • 全称名是组织绝不可能重复的唯一名,一般用工商注册名进行区分

  • 由于全称太长,说起来不方便,所以一般在没有歧义的情况下,用户会使用简称

  • 但简称就有可能在全局出现重复,所以系统对此简称创建了一个全局唯一的系统自用的简称来区分不同的组织

即全称是供用户使用的全局唯一名,但用户一般不用而是使用简称,但对于jxTMS来说简称可能无法确保全局唯一,所以jxTMS又创建了一个自用的唯一性简称。

对于我们的演示来说,demo组织的全称是demo,简称也是demo,唯一性简称则是demo_6228。

注2:唯一性简称用来识别数据库中不同组织的私有数据库,以实现不同组织的数据隔离;还被用来作为组织单播消息的接收地址,以实现不同组织间的通信隔离

注3:组织的代码主目录名就是组织的全称名

不同组织的代码主目录下的代码分为两个层级:

  • sales、demo代表的不同的代码模块

  • sales下的order、demo下的demo1等是sales模块中的order功能、demo模块中的demo1功能

每一个功能目录中可能包含5种文件:

  • web:本功能所用到的所有web界面的定义

  • capa.py:本功能的python处理代码

  • op.py:本功能的操作入口【菜单、快捷栏、工具条等】定义

  • data:本功能所使用到的ORM数据类的定义,这些ORM数据类对应了数据库中的数据表,jxTMS会自动根据data文件中的定义创建相应的数据表,并自动完成数据库中的行数据与python代码中的pyORM对象的映射

  • sql:本功能从数据库中查询数据所使用到的类sql定义的查询语句

代码结构图的示意如下:

jxTMS的HelloWorld_第3张图片

代码分为公用代码和组织私有代码,组织私有代码以组织全名为目录名。jxTMS中除必要的内部功能模块之外,用户的所有业务功能模块都是由这五种文件定义的,我们可以修改这些文件中,保存后,点击快捷栏中的【运维管理->重新加载】就可以重新加载这些文件,也就实现了热机更新

比如,大家将demo1中的capa.py打开,在其sayHello函数中:

self.setOutput('outText','Hello world!')

将其修改为:

self.setOutput('outText','你好,世界。'.decode('utf-8'))

保存后,将修改后的capa.py按用sftp管理jxTMS的代码中所述拷贝到tms容器的/home/tms/codeDefine/demo/demo/demo1目录中,然后在web界面中点击【运维管理->重新加载】,并在弹出的对话框中一路点击确认,在系统通知执行完毕后,再次点击【演示->helloworld】进行操作,看看前后有什么变化。

注:由于python和java中的编码不同,所以python代码的中文需用decode(‘utf-8’)进行转码,才能正确的显示出来

web界面的说明

web文件中以一句【以英文分号分隔,如过长可折行】文本定义一个web控件。关于web界面的一般说明请参考:web界面定义、web控件的一般定义、web界面中各控件的定义

一个web界面一般都是一个div的组容器【用来将其它子控件成组显示】,如demo1中的:

web helloWorld type div;

大多数情况下,一个div都会包含几个table的组容器,如demo1中的helloWorldt1:

web helloWorldt1 parent helloWorld type table title="世界,你好",width=900;

helloWorldt1用parent指出自己是helloWorld控件的子控件,然后定义了一个表的标题,并定义了表宽。

表控件是最常用的控件,一般有两种:

  • 用于嵌入各种控件的容器表

  • 显示多行数据的数据表

demo1中所演示的就是容器表:

with helloWorldt1 row 0 col c0 web n type text text='说点啥:',width=200;
with helloWorldt1 row 0 col c1 web n bind outText type text width=700;

with helloWorldt1 row 1 col c0 web n type button width=80,text='点我',motion=cmd,demand=sayHello,confirm='确认要执行吗!!',onlyOnce=false;

即helloWorldt1内嵌了两行两列,分别:

  • 在0行的c0列放置了一个文本控件,用来静态的向用户提示信息

  • 在0行的c1列放置了一个文本控件,用来动态【即如demo1所演示的,可编程改变】的向用户提示信息

  • 在1行的c0列放置了一个按钮,用来执行用户操作

注:大家观看一下0行与1行的c1列,就可以理解为什么在div容器中还要采用容器表了:表容器可以行列对齐,这就避免了痛苦的web界面的控件定位与对齐

大家可以在helloWorldt1的定义中添加一个新的alone的属性定义:

web helloWorldt1 parent helloWorld type table title="世界,你好",width=900,alone=true;

然后热热机刷新下再点击【演示->helloworld】看看是什么效果?

表容器默认会将同名列进行对齐,而alone=true则会取消这个特性,使得表中的每行是独立的。

注1:web控件的属性定义是以英文逗号进行分隔的key=value值对

注2:web控件都需要一个唯一的名字,但复杂的页面,容器表中的控件比较多,所以起名字起的头疼,而且如果修改界面的时候,改名字也很烦,所以笔者对容器表中的控件做了一个简化:如果其名字为n,则系统自动按【表名_行号_列名】的格式给控件起名字

web控件的输入输出

对于可以前后端交互的控件,主要是输入控件,如:文本、输入框、多行输入框、单选框、多选框、下拉选择框、标记输入框、日期时间拾取框、代码编辑器、文本编辑器等,需要额外绑定【bind】一个数据名,如demo1中的:

with helloWorldt1 row 0 col c1 web n bind outText type text width=700;

即绑定了一个outText的数据名。然后在python代码中即可操作其输入输出:

#读取outText的值,这里由于outText是绑在了一个文本控件上,没法输入,可以将type text更换为type input看看能不能读到
v = self.getInput('outText')
#将读到的用户输入显示到日志文件中
jx.log('input:{}',v)
#输出到该控件中
self.setOutput('outText','ttttteeeeesssstttttt')

动作按钮的响应函数

【点我】按钮点击后,会执行capa.py中的一个函数,也就是第48行开始的sayHello函数。大家请注意按钮在web文件中的定义中,有一个motion的属性,和一个demand的属性:

motion=cmd,demand=sayHello,

而capa.py中sayHello函数先进行了一下修饰:

@myModule.event('cmd', 'sayHello')
def sayHello(self, db, ctx):
	self.setOutput('outText','Hello world!')

即,在web中指定了motion和demand的按钮,在点击后,jxTMS就会触发一个event,调用以myModule.event用对应的motion和demand所修饰的事件处理函数。目前的motion有两种:

  • cmd:前端触发的命令响应

  • prepareDisp:某界面显示后的数据装订,即某个div容器被显示后,系统会调用以@myModule.event(‘prepareDisp’, ‘界面名’)所修饰的事件响应函数,以对界面进行初始化,一般来说,主要就是对界面中的各种输入控件进行数据装订

所有的事件响应函数,都有两个参数:

  • db:本组织所对应的数据库操作接口,可以通过db来操作数据库

  • ctx:事件函数执行时的上下文,包括调用者信息、组织信息

注:事件响应函数不可以直接调用。因为被修饰后其已经被jxTMS接管,jxTMS已经将函数名进行了修改以避免被用户代码直接调用

capa.py代码文件结构

32行之前都是导包语句,建议不要动,这些导包语句所导入的都是jxTMS所提供的包,而没有任何python内嵌以及第三方的包,经过笔者长时间使用,对于大多数的功能模块来说,只需要这些包就足够了,而由于目前没有针对性的IDE环境,所以如果缺少相应的导包语句会导致执行时报错,所以建议最好就不要动这些导包语句了。代码的主体是:

class demo1(affairMgr):
	def New(self, con):
		return demo1(con)

	def module(self):
		return 'demo'

	def name(self):
		return 'demo1'

	# 基类可能有自己的op定义,如果不阻止则会重复
	def listOP(self):
		pass

	@myModule.event('cmd', 'sayHello')
	def sayHello(self, db, ctx):
		self.setOutput('outText','Hello world!')

# 一定要有,否则无法顺利注册
sp=demo1(biz)

即声明了一个名为demo1的功能类,该类继承自jxTMS所提供的affairMgr类,并对该类必须重载的几个对象函数进行了重载,最后生成了一个该对象以用于向jxTMS注册该功能类。

注1:capa.py是python代码文件,所以大家一定要牢记缩进、缩进、缩进,所有的缩进一定要以四个空格为单位,而不要用tab键【\t字符】,所以大家请看一下自己python代码的编辑器,是否设置了将tab转换为四个空格,如果没有请一定要做这个设置。而从页面复制过去的代码也可能是tab键,所以请务必注意此问题

注2:除非是直接复制的capa.py,否则从其它地方复制过来的python代码,都要将tab转换为四个空格的处理

用户一定要按此格式从affairMgr派生出一个新的类,并在最后一定要初始化这个类,这才能将自己所开发的功能类注册到系统中。该类有几个要求:

  • 类名无所谓,但为便于识别,最好是写成模块+功能名,如salesOrder。修改了类名后,则一定要把New函数中返回的、以及最后一行所生成的都改为该类名,否则系统不会正确的加载该类

  • module函数返回的就是代码所在组织代码主目录下的模块级目录名

  • name函数返回的就是代码所在的目录名

  • listOP函数一定要这么些

只要说到在capa.py中添加函数,就一定是该类的对象函数,即和New、module等函数是一个缩进级别,同时第一个参数是self。

操作入口

上面,我们在web文件中定义了helloWorld的显示界面,那么,我们该如何显示该界面呢?在jxTMS中,这种开启一个独立的操作序列的称之为操作入口,统一在功能模块的op.py文件中进行定义。

jxTMS的操作入口包括:

  • 左上面的功能菜单入口,暂不介绍,大家也一定不要去点

  • 左侧快捷栏入口

  • 工具条入口,暂不介绍,在后面的演示中用到时再介绍

操作入口的说明可参考:操作入口定义,但现在我们只需要知道如何在最常用的快捷栏中添加入口即可:

@biz.Motion('demo.demo1','disp','helloWorld')
@biz.OPDescr
def op1(json):
	json.setShortcut('演示'.decode('utf-8'),'helloWorld')

系统在加载op.py文件时,就会在快捷栏中增加一个名为演示的一级入口,并在该入口的下面再增加一个名为helloWorld二级入口。当用户点击这个helloWorld的二级入口时,jxTMS就会执行相应的操作。

这个操作是通过@biz.Motion(‘demo.demo1’,‘disp’,'helloWorld’这个是新增加的语法定义的,就是指定了该操作的系统名:demo.demo1.disp.helloWorld。大家看一下上面capa.py代码结构部分,应该会发现这个入口的名字的组成是:

模块名.功能名.motion.demand

所以demo.demo1.disp.helloWorld这个名字的意思就是:显示demo.demo1的helloWorld界面。当用户点击该入口时,web端向jxTMS发送这个系统名时,jxTMS就会查找到我们在capa.py中注册的demo.demo1功能点,然后New出一个新的demo1功能对象,然后请求该对象发送helloWorld的界面描述,jxTMS的web端在收到helloWorld的界面描述后,自动绘制出相应的界面。绘制完毕后,会触发helloWorld的prepareDisp事件,由于我们没有在capa.py中定义helloWorld的prepareDisp事件响应函数,所以该请求被忽略。

现在大家只要知道,点击了某入口,则jxTMS会动态绘制该界面,然后触发该界面的prepareDisp事件,这就是op.py中定义的快捷栏入口的显示工作。

你可能感兴趣的:(jxTMS,系统开发,Python,python,开发平台,业务管理系统)