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

最讓我郁悶的地方,每次手機(jī)號(hào)、日期、短信通道等條件挨個(gè)修改一遍,鍵盤(pán)敲得噼里啪啦,鼠標(biāo)點(diǎn)的不亦樂(lè)乎,別人還以為你有多忙,結(jié)果一頓操作猛于虎就查了條數(shù)據(jù)。
都說(shuō)打工人苦打工人累。

我們就不要把工作耗費(fèi)在這無(wú)意義的機(jī)械重復(fù)中了,是的,即使快一點(diǎn)也好。
思路
我想到總結(jié)常用的sql,寫(xiě)入配置文件。

通過(guò)網(wǎng)頁(yè)點(diǎn)擊執(zhí)行這些語(yǔ)句。
在前端,好不容易靠試錯(cuò)發(fā)現(xiàn),可以通過(guò)JavaScript的splice函數(shù)來(lái)對(duì)接紅框內(nèi)的查詢(xún)條件:

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

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

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

我們看下展示效果:

大體情況就是這樣。
怎么實(shí)現(xiàn)的,主要介紹2點(diǎn):
- 如何連接不同的數(shù)據(jù)庫(kù)
- 如何添加一條sql查詢(xún)
文末上傳了所有源碼到gitee,有興趣的同學(xué)可以看看
如何連接不同的數(shù)據(jù)庫(kù)
1. 怎么在前端看到數(shù)據(jù)庫(kù)
如圖,怎么讓數(shù)據(jù)庫(kù)信息在下拉框中顯示呢?

首先,每個(gè)數(shù)據(jù)庫(kù)取個(gè)英文名字,寫(xiě)在配置文件里,通過(guò)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})
然后配置前端,一打開(kāi)頁(yè)面就獲取數(shù)據(jù)庫(kù)信息
export default {
created() {
this.get_dbs()
},
}
async get_dbs() {
//this.$http.get('dbs'),dbs為獲取數(shù)據(jù)庫(kù)信息的接口地址
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處理前端傳來(lái)的數(shù)據(jù)庫(kù)信息
公司數(shù)據(jù)庫(kù)為oracle,并且經(jīng)過(guò)堡壘機(jī),所以用到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']
#根據(jù)下拉框中選擇的數(shù)據(jù)庫(kù)名字,配置要連接的數(shù)據(jù)庫(kù)
if not db:
return JsonResponse({'code':0,'msg':"請(qǐng)選擇數(shù)據(jù)庫(kù)"})
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建立客戶(hù)端與堡壘機(jī)的隧道
with SSHTunnelForwarder(
('堡壘機(jī)地址', 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() # 新建游標(biāo)
cursor.execute(sql) # 執(zhí)行sql語(yǔ)句
ret = cursor.fetchall()
cursor.close()
xconn.close()
這樣就可以在頁(yè)面下拉框中,自由的進(jìn)行數(shù)據(jù)庫(kù)的連接啦。接下來(lái)就是如何添加sql了,請(qǐng)看下面的例子。
如何添加一條sql查詢(xún)
前端操作
1. 怎么在前端看到它
如圖,怎么讓這條sql在前端顯示呢?

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

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

在commonForm.vue中編輯:
// 如果點(diǎn)擊的的sql是“inspect”,就在頁(yè)面上添加3個(gè)搜索條件,分別是網(wǎng)絡(luò)連接、服務(wù)檢查、時(shí)間范圍
export default {
data() {
return {
network: {
model: 'network',
label: '網(wǎng)絡(luò)連接'
},
service: {
model: 'service',
label: '服務(wù)檢查'
},
timerange: {
model: 'timerange',
label: '時(shí)段',
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)
}
}
}
}
接下來(lái)是后端操作。
后端操作
1. 讀取sql
首先,sql取名為“inspect”。config.cfg-[sql] 填寫(xiě)要執(zhí)行的sql語(yǔ)句

old_views.py-read_cfg函數(shù),get_sql函數(shù)讀取config.cfg中的sql語(yǔ)句,比如讀到名為“inspect”的語(yǔ)句,返回的結(jié)果是這樣的 ['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語(yǔ)句,比如讀到名為“inspect”的語(yǔ)句,返回的結(jié)果是這樣的
#['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. 對(duì)頁(yè)面提交過(guò)來(lái)的sql語(yǔ)句進(jìn)行處理
然后,對(duì)頁(yè)面提交過(guò)來(lái)的sql語(yǔ)句進(jìn)行處理:如修改日期,修改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. 數(shù)據(jù)轉(zhuǎn)成字典
(('農(nóng)林牧漁', '172.16.1.6', '2.05', '24.59', '/dev/vda1 used: 7.9G nouse: 30G', '19.75', '74.11', '異常', 'Bootlog: OK'),)
這是從數(shù)據(jù)庫(kù)返回的數(shù)據(jù),類(lèi)型為元組,需要轉(zhuǎn)成字典并給value加上key,方便前端識(shí)別。如{'項(xiàng)目名稱(chēng)': '農(nóng)林牧漁', 'ip': '172.16.1.6'}
old_views.py-foo函數(shù),bar函數(shù)
#把參數(shù)轉(zhuǎn)成字典
def foo(**kwargs):
#對(duì)字典中的datetime時(shí)間格式數(shù)據(jù)轉(zhuǎn)為字符串
if isinstance(kwargs['create_time'],datetime.datetime):
kwargs['create_time'] = kwargs['create_time'].strftime(('%Y-%m-%d %X'))
return kwargs
#field是選取哪些字段的數(shù)據(jù)返回給前端
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()函數(shù)主要作用是把從數(shù)據(jù)庫(kù)查到的數(shù)據(jù)轉(zhuǎn)成字典,并給value加上一個(gè)key,大概過(guò)程:
import datetime
ret = (('農(nóng)林牧漁', '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': '農(nóng)林牧漁', 'service': '172.16.1.6'}
到這里,一條sql查詢(xún)就添加完了。