公司主營短信,工作中數據庫查詢占了很大比例。
我們的操作是,通過navicat連接十多臺機器的庫,然后連接數據庫-輸入sql-修改查詢條件(如一長串的日期)進行各種查詢。
遇上高峰期,客服頻頻轉發(fā)問題給我們,例如查下客戶為什么沒收到短信啦,查詢發(fā)送記錄啦,某個短信通道的發(fā)送量,簽名統(tǒng)計等等。

最讓我郁悶的地方,每次手機號、日期、短信通道等條件挨個修改一遍,鍵盤敲得噼里啪啦,鼠標點的不亦樂乎,別人還以為你有多忙,結果一頓操作猛于虎就查了條數據。
都說打工人苦打工人累。

我們就不要把工作耗費在這無意義的機械重復中了,是的,即使快一點也好。
思路
我想到總結常用的sql,寫入配置文件。

通過網頁點擊執(zhí)行這些語句。
在前端,好不容易靠試錯發(fā)現(xiàn),可以通過JavaScript的splice函數來對接紅框內的查詢條件:

利用splice實現(xiàn)搜索條件動態(tài)添加,這個用法是我蒙的,不知道專業(yè)的前端MM怎么做的。
上述內容就是我最初的想法。
雖然研發(fā)已經給客服MM做了查詢后臺,不過不適合我們這種“查詢工程師”。
手工DIY一個?
想法挺好,怎么實現(xiàn)呢。
這個不急,騎著毛驢看唱本——走著瞧
摸到一條魚
日月輪轉,上班閑敲棋子,忙改文字,敲敲打打的,在沒有其它技術人員支持的情況下,前后端居然調通了。
忽然間,大部分的查詢工作,我都可以通過自己的筆記本輕描淡寫的“指點江山”了:

不管多少個數據庫,多少條語句,我都可以寫到配置文件中。
前端呢,可以用element-ui的“Cascader 級聯(lián)選擇器”分門別類來添加要執(zhí)行的sql

下圖紅框內就是“Cascader 級聯(lián)選擇器”效果,選中后會動態(tài)出現(xiàn)相關的搜索選項:

我們看下展示效果:

大體情況就是這樣。
怎么實現(xiàn)的,主要介紹2點:
文末上傳了所有源碼到gitee,有興趣的同學可以看看
如何連接不同的數據庫
1. 怎么在前端看到數據庫
如圖,怎么讓數據庫信息在下拉框中顯示呢?

首先,每個數據庫取個英文名字,寫在配置文件里,通過configparser模塊讀取

from rest_framework.views import APIView
import configparser
def read_cfg(name):
cfg = configparser.RawConfigParser()
cfg.read(settings.CONFIG_PATH, encoding='utf-8')
return cfg.items(name)
class get_dbs(APIView):
def get(self, request, *args, **kwargs):
dbs = read_cfg('db')
dbs_list = []
d = {}
for k,v in dbs:
d['label'] = k
d['value'] = v
dbs_list.append(d)
d = {}
return JsonResponse({'code': 1, 'data': dbs_list})
然后配置前端,一打開頁面就獲取數據庫信息
export default {
created() {
this.get_dbs()
},
}
async get_dbs() {
//this.$http.get('dbs'),dbs為獲取數據庫信息的接口地址
const { data: res } = await this.$http.get('dbs')
if (res.code !== 1) {
return this.$message.error('獲取dbs失敗')
}
this.operateFormLabel[0].children = res.data
},
2. 讓django處理前端傳來的數據庫信息
公司數據庫為oracle,并且經過堡壘機,所以用到cx_Oracle和sshtunnel模塊
from cx_Oracle import Connection
from sshtunnel import SSHTunnelForwarder
from rest_framework.views import APIView
class get_messages(APIView):
def post(self, request, *args, **kwargs):
data = json.loads(request.body.decode('utf-8'))
db = data['db']
#根據下拉框中選擇的數據庫名字,配置要連接的數據庫
if not db:
return JsonResponse({'code':0,'msg':"請選擇數據庫"})
if db == 'lt1':
conn = ('192.168.2.1','ms','sgate;Normal;sms')
elif db == 'lt2':
conn = ('192.168.2.6','ms2','sgate;Normal;sms2')
#sshtunnel建立客戶端與堡壘機的隧道
with SSHTunnelForwarder(
('堡壘機地址', port),
ssh_username="ssw",
ssh_password="1223456",
remote_bind_address=(conn, 1521)) as server:
conn = (conn[1], '123456', '127.0.0.1:%d/%s' % (server.local_bind_port,conn[2]))
xconn = Connection(*conn)
cursor = xconn.cursor() # 新建游標
cursor.execute(sql) # 執(zhí)行sql語句
ret = cursor.fetchall()
cursor.close()
xconn.close()
這樣就可以在頁面下拉框中,自由的進行數據庫的連接啦。接下來就是如何添加sql了,請看下面的例子。
如何添加一條sql查詢
前端操作
1. 怎么在前端看到它
如圖,怎么讓這條sql在前端顯示呢?

只需一步, 在messages.vue中添加級聯(lián)菜單的一級菜單巡檢和二級菜單服務器即可
casecadeFormLabel: [
{
model: 'weekly_check',
label: '巡檢',
children: [
{
label: '服務器',
value: 'inspect'
},
]
}
],
只是顯示還不夠,每條語句要顯示的字段不一樣,我們需要單獨定義它們。

2. 單獨定義要顯示的表頭字段
如上所述,在messages.vue中添加inspect的表頭字段,并且設置this.tableLabel = this.inspectLabel。為每條sql語句設置不同的表頭字段,賦值給this.tableLabel,這樣可以讓不同的sql顯示不同的字段
inspectLabel: [
{ prop: 'ip', label: '服務器', width: 120},
{ prop: 'cpu', label: 'CPU占用率', width: 70},
{ prop: 'mem', label: '內存占用率', width: 70},
{ prop: 'disk', label: '磁盤使用情況', width: 230 },
{ prop: 'vda1', label: '/dev/vda1使用率', width: 100},
{ prop: 'vdb1', label: '/dev/vdb1使用率', width: 100},
{ prop: 'network', label: '網絡連接', width: 60},
{ prop: 'service', label: '服務檢查', width: 165 },
{ prop: 'url', label: '站點檢查', width: 165 },
{ prop: 'create_time', label: '日期', width: 70}
],
...中間略
else if (this.operateForm.sql === 'inspect') {
this.tableLabel = this.inspectLabel
}
3. 動態(tài)添加搜索條件
如圖,框內的3個搜索條件是通過JavaScript的splice函數生成的。通過它,我們可以為每條語句定義不同的搜索條件。

在commonForm.vue中編輯:
// 如果點擊的的sql是“inspect”,就在頁面上添加3個搜索條件,分別是網絡連接、服務檢查、時間范圍
export default {
data() {
return {
network: {
model: 'network',
label: '網絡連接'
},
service: {
model: 'service',
label: '服務檢查'
},
timerange: {
model: 'timerange',
label: '時段',
type: 'date'
},
}
},
methods: {
handleChange() {
if (this.selectedKeys === 'inspect') {
this.formLabel.splice(1, 1, this.network)
this.formLabel.splice(2, 1, this.service)
this.formLabel.splice(3, 1, this.timerange)
}
}
}
}
接下來是后端操作。
后端操作
1. 讀取sql
首先,sql取名為“inspect”。config.cfg-[sql] 填寫要執(zhí)行的sql語句

old_views.py-read_cfg函數,get_sql函數讀取config.cfg中的sql語句,比如讀到名為“inspect”的語句,返回的結果是這樣的 ['select project,ip,cpu,mem,disk,vda1,vdb1,network,service,url,create_time\nfrom weekly_check\nwhere create_time BETWEEN {1} AND {2}']
import configparser
def read_cfg(name):
cfg = configparser.RawConfigParser()
cfg.read(settings.CONFIG_PATH, encoding='utf-8')
return cfg.items(name)
#讀取config.cfg中的sql語句,比如讀到名為“inspect”的語句,返回的結果是這樣的
#['select project,ip,cpu,mem,disk,vda1,vdb1,network,service,url,create_time\nfrom weekly_check\nwhere create_time BETWEEN {1} AND {2}']
def get_sql(sqlname):
data = read_cfg('sql')
sql = [item[1] for item in data if sqlname == item[0]]
return sql[0]
2. 對頁面提交過來的sql語句進行處理
然后,對頁面提交過來的sql語句進行處理:如修改日期,修改where條件,加上搜索條件等
old_views.py-get_messages()
if sqlname in ('inspect'):
inputLabel = []
field = []
condition = []
field_dict = {'network': network, 'service': service}
for key, value in field_dict.items():
print('len(value)',len(value))
field.append(key)
condition.append("{0} like '%{1}%' and".format(key, value))
if sqlname == 'inspect':
print('get_sql(sqlname)',get_sql(sqlname).format(','.join(field), start, end))
print('condition',condition)
sql = get_sql(sqlname).format(','.join(field), start, end).replace('where','where {0}').format(' '.join(condition))
3. 數據轉成字典
(('農林牧漁', '172.16.1.6', '2.05', '24.59', '/dev/vda1 used: 7.9G nouse: 30G', '19.75', '74.11', '異常', 'Bootlog: OK'),)
這是從數據庫返回的數據,類型為元組,需要轉成字典并給value加上key,方便前端識別。如{'項目名稱': '農林牧漁', 'ip': '172.16.1.6'}
old_views.py-foo函數,bar函數
#把參數轉成字典
def foo(**kwargs):
#對字典中的datetime時間格式數據轉為字符串
if isinstance(kwargs['create_time'],datetime.datetime):
kwargs['create_time'] = kwargs['create_time'].strftime(('%Y-%m-%d %X'))
return kwargs
#field是選取哪些字段的數據返回給前端
def bar(ret,sqlname,field=None,user_id=None):
for item in ret:
if sqlname == 'inspect':
obj = foo(project=item[0], ip=item[1], cpu=item[2],mem=item[3],disk=item[4],
vda1=item[5],
vdb1=item[6],
network=item[7],
service=item[8],
url=item[9],
create_time=item[10])
yield obj
上一步bar()函數主要作用是把從數據庫查到的數據轉成字典,并給value加上一個key,大概過程:
import datetime
ret = (('農林牧漁', '172.16.1.6', '2.05', '24.59', '/dev/vda1 used: 7.9G nouse: 30G', '19.75', '74.11', '異常', 'Bootlog: OK', '', datetime.datetime(2022, 8, 19, 16, 17, 12)),)
obj = {}
field = ['network', 'service']
for item in ret:
for i, v in enumerate(field):
obj[v] = item[i]
print(obj)
輸出為:
{'network': '農林牧漁', 'service': '172.16.1.6'}
到這里,一條sql查詢就添加完了。