一 闲叙
运维系统这个项目上回弄了整体的图形界面,没有实现后端逻辑和功能。写完上一篇没多久,我就搭了套Django,实现了从数据库里调取数据,在“工单记录”一栏里的记录显示区展现出来。在进一步添加增删改查功能之前,我想先加个数据压缩加密的功能,这一搞就是俩礼拜,虽然期间工作也影响了进度,但编程的过程着实也费了不少功夫,趟了许多坑。写完代码之后,回头来看其实也不太难,一路走来就是不知庐山真面目,只缘身在此山中。当然你要让我再写一遍,我可能会拒绝,现在的代码量已经上千了,主要是图形界面占得多,逻辑上的代码应该就小几百行。
二 工单系统界面介绍
登陆之后,点击“工单记录”选项卡,有三个区域。
“记录显示区”在主界面生成的同时,从服务端调取已有的数据,不同的用户只能看到自己的数据,表头可以点击进行排序,排的不太理想。
“工单录入区”是用来新添工单的,需要选择日期、事件,填写数量及内容,日期只能是5天之内,为的是工单记录的时效性,事件包括了我们团队日常涉及的大部分工作,有些事件比如开虚机有数量,并且有些事件需要详细说明,所以有了数量和内容栏,这几项都不能为空,不然提交时会有提示窗,正确提交后,显示区会刷新数据。
“工单删改区”用来修改或删除已有工单,需要先按照显示区里的工单号进行搜索,内容会显示在下面的各项控件中,事件只能显示第一项,然后可以对各项进行修改,最后点击“修改”按钮提交数据,如果需要删除,搜索后直接点击“删除”按钮便可,修改或删除后,显示区会刷新数据。
三 程序逻辑讲解
逻辑图
客户端 main.py
客户端入口,加载登陆窗口文件LoginPage.py,保持窗口循环。
客户端 LoginPage.py
加载mi.py和主窗口m.py,创建客户端公钥密钥,接收用户输入的登录信息,用户点击确定后,程序先向服务端发送客户端公钥并接收服务端公钥,然后压缩加密用户名密码,发送服务端等待验证回复,若匹配则跳转主窗口,不匹配则弹出提示窗。
客户端 m.py
此文件包含运维系统主窗口图形界面的代码、各控件事件响应函数及对gdxt.py和xzList.py文件函数的调用,该文件就有小八百行代码,算是整个系统的核心程序。图形界面代码就不介绍了,只说说“工单记录”分页里的控件事件响应和其他文件调用。
此文件会调用xzList.py文件以获取工单内容中“事件”的分类,有大中小三层关系,比如“学习”→“开发语言学习”→“Python”,当在大项下拉选择框选择一项后,会出现中项下拉选择框,选择中项后,会出现小项,由于绑定事件我只找到了对被选择项的操作,所以下拉选择框都必须被点选才能获取选择的值。
日期下拉选择框只有过去五天的选择,“今天”的值是1,往后依次增加。“数量”和“输入工单号”两个输入控件都限制了只接收数字,并且不能超过五位。“事件内容”框里输入什么都行,数据库那边也没有最大字节限制。“搜索”功能并没有去后台搜索数据,而是在显示区里搜索。
调用gdxt.py可以实现四个功能:获取工单列表、新建一条工单、修改一条工单、删除一条工单。当点“提交”按钮时,会获取日期、事件、数量和事件内容四项的值,然后经过数据检查,如果有没选或没填的项,就会弹出相应的提示窗,如果检查都没问题,就会交给gdxt.py,然后等待消息回复,如果成功,就重新调取工单列表。在前面登陆成功后,会自动执行一次获取工单列表。修改和删除功能也是同样的流程,要说明的是,修改不用选日期,当修改成功后,系统会自动在“更新日期”一栏加上时间戳,另外删除工单后,工单号不会自动不齐,所以每个工单都有唯一不变的工单号。
客户端 xzList.py
该文件负责从Excel中获取数据,并简单处理,供m.py调用,提供事件大中小种类间的关系,Excel的形式如图:
处理关系我分了三部分,一个函数返回大项的列表,第二个函数返回大项为key,中项为值的字典,第三个也如是,中项为key小项为值。当窗口中选完大项后,对应字典的值就会给中项,选完中项再创建对应字典中的小项。xzList.py的代码:
# -*- coding: utf-8 -*-
import pandas as pd
df = pd.read_excel('考核细则.xlsx')
df1 = df.fillna(method = 'ffill', axis = 0)
dd1 = df1.drop_duplicates(['中项'])
gd1 = dd1['中项'].groupby(df1['大项'])
gd2 = df1['小项'].groupby(df1['中项'])
a = {}
for x,y in gd1:
a[x]=[]
b = list(y)
a[x] = b
c = {}
for x,y in gd2:
c[x]=[]
d = list(y)
c[x] = d
def xzd():
return list(a.keys())
def xzz():
return a
def xzx():
return c
我用的pandas模块处理Excel,很方便,可以看出Python的简约,感觉不可思议。
客户端 gdxt.py
正如上面所说,为m.py提供增删改查四个功能,并且负责与服务端通信。看看代码:
# -*- coding: utf-8 -*-
import requests
import json
from datetime import date,datetime,timedelta
from mi import handle_data as hd
url = '127.0.0.1:8000'
requestHeaders = {
'Content-Encoding': 'deflate',
'Content-Type':'application/json;charset=UTF-8'
}
def GetDate(prkc):
rg = requests.get('http://%s/gdlist/'%url,headers=requestHeaders)
rrg = (rg.content).decode()
rrrg = hd().unzip_decrypt(rrg,prkc)
rrrrg = json.loads(rrrg)
return rrrrg
def Post_create(Data,puks):
Data[0] = (date.today()-timedelta(days=int(Data[0])-1)).isoformat()
d = '#'.join(Data)
data = hd().zip_encrypt(d,puks)
rc = requests.post('http://%s/gdcreate/'%url, data=data ,headers=requestHeaders)
rrc = rc.json()
return rrc['msg']
def Post_change(Data,puks):
d = '#'.join(Data)
data = hd().zip_encrypt(d,puks)
rc = requests.get('http://%s/gdchange/'%url, data=data ,headers=requestHeaders)
rrc = rc.json()
return rrc['msg']
def Post_delete(Data,puks):
data = hd().zip_encrypt(Data,puks)
rd = requests.get('http://%s/gddelete/'%url, data=data ,headers=requestHeaders)
rrd = rd.json()
return rrd['msg']
四个函数传入的参数有数据,还有公钥密钥,从服务端取数据需要解密,并返回解密后的数据给客户端,从客户端发数据需要加密,然后返回服务端发回的消息。
客户端 服务端 mi.py
比较高端的部分,对于我来说,因为搞它搞了很长时间。有两个调用函数,一个负责压缩加密,一个负责解密解压,客户端和服务端各有一个mi.py文件,里面只有公钥密钥的命名不一样,功能是完全一样的。这块坑最多,str和bytes相互转换,encode和decode字符,客户端服务端互传公钥会改变格式,所以还得先把公钥拆了放到列表里,传完之后再用密码包的函数把列表组成公钥,后来还遇到加密内容超过秘钥长度的问题,又上网找了个分段加解密的代码,坎坷之后突然成功了,我感觉很惊讶和欣喜。
这里涉及到写代码和测试之间的衡量问题,以前都是写一点测一点,但当工程大了,一个是这样做很费时,一个是容易钻牛角尖,把整体构思忘了。所以我尝试先按思路把代码铺开来写,完成一项功能的编写后再测,虽然并没减多少测试量,但改起来能稍微从大视角看问题,有时会更容易找到问题关键(说的有点梦幻,可能实际并不是这么回事)。
服务端 urls.py
这里就是url的定义,没什么可介绍的,只不过是服务端的入口。
服务端 views.py
这里有用于交换公钥的函数,还有接收登陆信息,匹配用户名密码并返回消息的函数。对应gdxt.py,此文件里也有增删改查四个功能。网上找了一段检查是否处于登陆状态的代码,为了防止客户端跳过登陆直接操作,但存在问题,就把它注释掉了。
服务端 models.py
定义了两个表,一个工单表,一个用户表。Django自带的数据库不知什么原因,编程期间出过问题,我以为是代码的事,直到实在解决不了,我重新建了个Django环境,把代码考过去,才正常运行,这也坑了我一两天。估计是我家台式机装的是Python3.6,我笔记本装的是3.7,并且我还经常传到跳板机里,我看执行后的pyc文件两个版本都有,而且我还更新过数据库表结构,所以导致数据库出问题。
服务端 admin.py
里面就几行,注册了工单表和用户表,从Django后台可以看见数据。我没有写用户注册的程序,打算从后台直接写入。
四 安全防护
关于安全目前我就想到以下几点:
数据加密。加密我用的RSA,对密码学不了解,我就选了非对称加密,感觉和HTTPS有些像。
密码录入。在数据库里应该把密码转成如MD5格式的,目前还没做。
防绕。我不知道我的代码逻辑防不防绕,客户端公钥密钥是由LoginPage.py生成的,登陆成功后会传给m.py,m.py调用gdxt.py时会传给它,它再给mi.py进行处理。所以不登录就没有公钥传递,后面的操作也就无法进行。服务端的防绕就交给中间件了。
后台仅内网访问。Django的admin可以限制ip访问,仅管理员可以从内网登陆,不过加完用户之后,也就没有什么需求了,可以直接关闭后台管理。
五 后续工作
后续工作我也想到几点:
C/S分离。差不多是时候分离客户端和服务端了,之后在虚机上搭套Nginx+Django+MySQL,把服务端部上,测试客户端和服务端的通信。
算分系统。工单系统除了记录工单事件之外,还是用来统计和考核员工工作的,并计算绩效成绩,所以还得开发一套算分的程序。
其他运维功能。刚开发完一个分页,还有四个没弄,之前接触了QtPy,它也是图形化界面包,看介绍它能加载html文件,也就是说它可以加载动态echarts图表,而且它有可视化编辑工具,可以编排窗口控件,并自动生成代码,而而且它好像还能支持远程交互,就像xshell或远程桌面那样,所以我在考虑转它。
小结
兴趣推动进步,学Python也一年多了,不能说有什么成就,但一直坚持下来还是学到些东西。现在做总结有点早,还是聊大业吧,“从底层到前端”系列由于技艺不精,卡在一处后来就没搞,至于想展示什么样的前端我也还没想好,可能会把“运维系统”系列当做一个前端吧,这就是说从底层向上卡主了,现在是从前端到底层走,两头汇合在被卡处。。。
监控、安全两方面即涉及前端又渗透底层。监控因为公司有同事搞,我就一直没怎么接触,之间关于zabbix的文章也只是考虑展现监控数据,而不是监控开发;安全这块虽然后来有意识地把它考虑进来,但本身安全是一大块内容,我其实啥也不懂,真要学还不得三五年功夫。
先走着吧,眼下还好多能搞的事呢。