在开始总结这个数据监控系统开发项目之前,
必须要感叹一下,真是不容易啊。完成开发任务的时候,还要去完成毕业的事情,两地奔波,几乎没有休息。
最后弄得自己大病一场,连续几日的高烧不退,在医院打点滴的时候,不停地问自己,这么拼值得么。
我想,我已经付出了代价了,如果不能收获点什么,有所成长,那才是最不值得的事情。
数据监控web系统开发项目 是我参加工作以来,第一个正式的开发项目。前后端分离的web项目。
而我主要负责的是后端的开发,用的是 Django 2.1.8 的后端开发框架,由另一个同事负责前端的开发工作,用的是vue.js
项目的背景:在公司的业务中,常需要用到几张数据表格,但之前都是人工维护,因此会导致几张表格之中会出现数据不统一,并且难发现的情况。(共有5表格,如A B C D E)其中B 对应为A的每日数据,C为B 与 D的对比数据结果。E(用户表)
项目总需求:数据表格入库,入库后对数据进行提取对比生成监控数据,对有问题数据进行问题标注。
项目后端需求:编写 A数据表格,单条添加,批量导入。编辑,查询接口。
B数据表格,批量导入。编辑,查询接口。
C表格,查询接口
-----------------------------------------------------------------背景介绍完毕-----------------------------------------------------------------------------------------
第一天开始开发时,和同事了解过几遍,每个需求的具体实现,还有数据表的具体设计。
首先是常规的 django-admin.py startproject project_name
创建项目
python manage.py startapp xxx
创建项目应用
由于我们先建好了数据库,因此修改好同名文件夹中setting.py的设置后 python manage.py inspectdb >models.py
反向生成
接着就可以开始写业务代码了。
由于编辑,添加,各个表之间存在业务逻辑,因此我就先写了各个表的查询接口。
res_msg= {'recode':1,'errorMessage':None,'content':[]}
def A_get(request):
error_logging.error('station_arrange_get的请求参数:' + '||' + str(request.POST))
res = res_msg
try:
res = res_msg
query_data_dict = {}
station_month = request.POST.get("station_month", None)
if station_month:
error_code = judge_month(station_month)
if error_code:
res['recode'] = 0
res['errorMessage'] = 'station_month错误'
res['content'] = []
error_logging.error('error_code:'+str(res['recode']) + '||' + res['errorMessage'])
res = json.dumps(res, cls=DjangoJSONEncoder, ensure_ascii=False)
return HttpResponse(res)
else:
query_data_dict["station_month"] = station_month
# 项目名称
brand = request.POST.get("brand", None)
if brand:
error_code = judge_string(brand)
if error_code:
res['recode'] = 0
res['errorMessage'] = 'brand有误'
res['content'] = []
error_logging.error('error_code:' + str(res['recode']) + '||' + res['errorMessage'])
res = json.dumps(res, cls=DjangoJSONEncoder, ensure_ascii=False)
return HttpResponse(res)
else:
query_data_dict['brand__icontains'] = brand
。。。。。
这里一共有30多个参数,参数的代码结构均一致,因此就不一一展示了。
order_by = request.POST.get("order_by", 'station_id')
if order_by:
error_code = judge_string(order_by)
if error_code:
res['recode'] = 0
res['errorMessage'] = 'order_by' + '有误'
res['content'] = []
error_logging.error('error_code:' + str(res['recode']) + '||' + res['errorMessage'])
res = json.dumps(res, cls=DjangoJSONEncoder, ensure_ascii=False)
return HttpResponse(res)
else:
order_by='station_id'
is_ascend = request.POST.get("is_ascend", '1')
最后这里是排序的参数,由于前端排序过程中会传""这样的空字符,因此另外对 order_by='station_id' 进行单独赋值
if len(query_data_dict)>0:
# 根据查询条件查询
#显示筛选数据
A_obj = models.A.objects.filter(**query_data_dict)
# 将查询到数据对象转换成字典,添加到data_list中
for a_obj in A_obj :
a_obj_dict = model_to_dict(a_obj )
a_obj_dict ['update_tms'] = a_obj .update_tms
a_obj_dict ['user_name'] = a_obj .uid.name
data_list.append(a_obj_dict )
else: # 默认显示数据
A_obj =models.A.objects.all()
for a_obj in A_obj :
a_obj_dict = model_to_dict(a_obj )
a_obj_dict ['update_tms'] = a_obj .update_tms
a_obj_dict ['user_name'] = a_obj .uid.name
data_list.append(a_obj_dict )
if is_ascend == '1':
reverse = False
elif is_ascend == '0':
reverse = True
data_list = sorted(data_list, key=lambda x: x[order_by], reverse=reverse)
res['recode'] = 1
res['errorMessage']=None
res['content']=data_list
res = json.dumps(res, cls=DjangoJSONEncoder, ensure_ascii=False)
return HttpResponse(res)
比如我们之前讨论过的A表中有一个起始时间段和结束时间段,B表中则是对应,该时间段的A表的每日数据的相应说明。同时查询时,需要返回关于该条数据在A中的部分字段,其中涉及到了联合查询。
当初,在讨论时,同事表示数据表结构设计不使用关联,因此使用orm多次查询导致性能特别差,后来反复商榷后改成关联关系,A表作外键关联至B表。
B_obj = models.B.objects.filter(**query_data_dict).values(
'station_id',
'station__station_name',
'station__station_month',
'station__executive_supplier',
'station__edition',
'station__brand',
'date',
'date_type',
'conclusion_abnormal',
'output_sign',
'estimated_entry',
'end_sign',
'camera_abnormal',
'mbox_abnormal',
'remark',
'abnormal_sign',
'update_tms',
'insert_tms',
'uid__name',
)
# import pdb
# pdb.set_trace()
for b_obj in B_obj :
b_obj ["station_name"] = b_obj .pop("station__station_name")
b_obj ["station_month"] = b_obj .pop("station__station_month")
b_obj ["brand"] = b_obj .pop("station__brand")
b_obj ["executive_supplier"] = b_obj .pop("station__executive_supplier")
b_obj ["edition"] = b_obj .pop("station__edition")
b_obj ["user_name"] = b_obj .pop("uid__name")
data_list.append(b_obj )
if is_ascend == '1':
reverse = False
elif is_ascend == '0':
reverse = True
data_list = sorted(data_list, key=lambda x: x[order_by], reverse=reverse)
res['recode'] = 1
res['errorMessage'] = ''
res['content'] = data_list
整个查询接口的业务代码,大致上就是这样。还有一些异常捕获处理,就没有补上了。包括B表,C表的查询接口也是大同小异,只是需要返回的参数不同。
当查询接口都写完之后,就开始着手,新增的业务逻辑了。
A | B |
---|---|
新增一条数据,时间为2019-6-1到2019-6-3 | B中新增,与A相应的3条数据,分别为2019-6-1,2019-6-2,2019-6-3 |
因此A表 新增接口 从前端获取完参数之后,利用Django的orm 新增数据记录
同时将时间提取处理,自动新增B表中的数据记录
为B表记录添加默认参数
query_data_dict = {
"station_id": station_id,
"output_sign": 1,
"end_sign": 0,
"abnormal_sign": '0',
"uid": uid,
}
start_date = datetime.datetime.strptime(start_date, "%Y-%m-%d").date()
end_date = datetime.datetime.strptime(end_date, "%Y-%m-%d").date()
B_create(start_date, end_date, **query_data_dict)
B表中,创建数据记录的方法如下
def B_create(start_date, end_date, **query_data_dict):
len_day = abs((end_date - start_date).days)
for i in range(len_day + 1):
date = start_date + datetime.timedelta(days=i)
query_data_dict["date"] = date
if date.weekday() < 5:
query_data_dict["date_type"] = "工作日"
else:
query_data_dict["date_type"] = "休息日"
new_B = models.B.objects.create(**query_data_dict)
然后就开始写 A,B 的编辑接口。
同样的,也是接受前端发送过来的参数,然后根据参数去修改数据库中相应的数据内容。是不是一开始听到,觉得很简单。但其中是由几个小坑的。修改的字段中,起始时间,和结束时间字段,一但修改了A的时间字段,那么B中的数据也要进行相应的调整,多的时间段数据就要删除,少的时间段就要补齐。
为此我整理所有的可能性。
# A--B 为修改后的时间段 a--b为修改前的时间段
# ----A---B---a---b-- (1)
# ----A---B(a)---b-- (1-1)相等
# 或 ----a---b-A----B--- (2) 全删除重建
# ----a---b(A)----B---- (2-2)相等
# 或---A--a----B---b--- (3) 删除并新建
# 或 ----a-A---B--b-- (4) 删除前后多余的
# 或 ----a-A--b--B---- (5) 删除并新建
# 修改的时间出现相同时
# 或 ----a(A)--B---b-- (7) 删除多余
# 或 ----a(A)--b---B- (8) 新建
# 或 ----a--A--b(B)- (9) 删除多余
# 或 ----A--a----b(B)-- (10) 新建
# 或 ----(A)a----b(B)-- (11) 无操作
# 补充情况
# 或 --A--a---b--B---- (12) 新建
并对每一种情况作相应的业务处理。
来到这里的我,曾天真地认为把导入接口完成了,就大功告成了。
实则,还有很多业务上的坑,在等着我。
比如 C表中 会出现 即B表无该天数据,但D表有该天数据的情况 或者 即D表无该天数据,但B表有该天数据的情况
这两种特殊情况都要生成相应的监控数据。
在部门老大的梳理下,把各种异常情况都总结了出来,其实就是用0 1 -1 分别代表不出数,出数,和无数据,以此来对监控数据进行填充。
最后就剩下导入功能的实现了