目录
银行流水账单俗称银行卡存取款交易对账单,也称银行账户交易对账单。指的是客户在一段时间内与银行发生的存取款业务交易清单。一般而言,在申请贷款或者信用卡时,银行或其他金融机构会要求借款人提供申请者本人最近三个月或者以上的银行账单流水,以便银行在审核和授信时进行参考。
银行流水账单查询系统为个人客户提供在线查询和打印服务。而本项目为整个系统提供数据库部分的支撑。
需求如下
查询编号 | 查询需求 | 发生频率 | 要求返回时间 | 最大并发数 |
1 | 按照交易流水号查询交易细节。 | 高 | <=100ms | 100 |
2 | 按照账号(不包括对方账户)查询交易流水,优先显示距离查询时间较近的交易。每页最多返回10条记录。
| 高 | <=500ms | 100 |
3 | 查询某月内交易总数 | 低 | 无 | 1 |
4 | 查询某月内总交易额 | 低 | 无 | 1 |
架构图
采用Vue3.0+Element-plus 开发,已部署至front_end。web服务器使用nginx,解决跨域问题
查询一实现:
- const handleSearch=()=>{
- let param={
- txID:data.searchValue
- }
- console.log(param)
- FindByTxID(param).then(res=>{
- data.tableData=res.data.data
- ElMessage.success('查询耗时:'+res.data.cost)
- }).catch(e=>{
- console.log(e)
- })
- }
查询2实现
实现了异步分页功能
- const handleClick=(e)=>{
- console.log(e)
- data.curPage=Number(e.target.innerText)
- let param={
- accountID:data.searchValue,
- pageNum:data.curPage
- }
-
- FindHisory(param).then(res=>{
- data.tableData=res.data.data.data
- data.totalPage=res.data.data.count
- }).catch(e=>{
- console.log(e)
- })
- }
- const handleSearch=()=>{
- data.curPage=1
- let param={
- accountID:data.searchValue,
- pageNum:data.curPage
- }
-
- FindHisory(param).then(res=>{
- data.tableData=res.data.data.data
- data.totalPage=res.data.data.count
- ElMessage.success('查询耗时:'+res.data.cost)
- }).catch(e=>{
- console.log(e)
- })
-
- }
查询3 查询4实现
前端选择年-月后,将计算出该年-月的起始日期和终止日期传给后端
- export function getMonthScope(date) {
- const odd=['01','03','05','07','08','10','12']
- const even=['04','06','09','11']
- let mid=date.split('-')
- if (odd.indexOf(mid[1])!=-1) {
- mid[2] = "31";
- }
- else if (even.indexOf(mid[1])!=-1) {
- mid[2] = "30";
- }
- else if (mid[1]==='02'){
- let year=Number(mid[0])
- if ((year%4==0 && year%100!=0) || (year % 400==0)) mid[2]='29'
- else mid[2]='28'
- }
- return {
- ldate:date,
- rdate:mid[0] + "-" + mid[1] + "-" + mid[2]
- };
- }
- const handleDateChange=()=>{
- let param=getMonthScope(data.selectValue)
- if (data.searchType==0){
- FindTxCount(param).then(res=>{
- data.result=res.data.data
- ElMessage.success('查询耗时:'+res.data.cost)
- })
- }else if (data.searchType==1){
- FindTxAmount(param).then(res=>{
- data.result=res.data.data
- ElMessage.success('查询耗时:'+res.data.cost)
- })
- }
-
- }
后端使用flask框架,并自己简单实现了连接池,不需要每次查询都新建连接,减少了开销。
对于查询1和查询2,在tx_id和account_id上建立索引
对于查询3和查询四,在tx_time上建立倒序索引,因为查询2需要查询时间最近的项
- class ConnectionPool(object):
- def __init__(self, **kwargs):
- self.size = kwargs.get('size', 100)
- self.kwargs = kwargs
- self.host='localhost'
- self.port=11810
- self.conn_queue = queue.Queue(maxsize=self.size)
- for i in range(self.size):
- self.conn_queue.put(self._create_new_conn())
-
- def _create_new_conn(self):
- return client(self.host, self.port)
-
- def put_conn(self, conn):
- self.conn_queue.put(conn)
-
- def get_conn(self):
- conn = self.conn_queue.get()
- if conn is None:
- conn = self._create_new_conn()
- return conn
- pool = ConnectionPool()
查询一和查询二的测试代码和测试结果
- import time
-
- import requests
- import random
- import sys
- from concurrent.futures import ThreadPoolExecutor
- import threading
- tPool=ThreadPoolExecutor(max_workers=100)
- BaseUrl="http://121.36.229.172:8899"
-
- def testFind1():
- response=requests.get(BaseUrl+'/findByTxID',params={
- 'txID':random.randint(10000,2009999)
- })
- response=response.json()
- return response
-
-
- def testFind2():
- response=requests.get(BaseUrl+'/findHistory',params={
- 'accountID':random.randint(622700000001,622700000030),
- 'pageNum':random.randint(1,4)
- })
- response=response.json()
- return response
-
- def callback(res):
- cost.append(res.result()['cost'])
- if len(cost)==iter:
- print(sum(cost)/len(cost))
-
- if __name__ == "__main__":
- iter=100
- if len(sys.argv) == 1:
- sys.exit(-1)
- elif sys.argv[1]=="test1":
- cost = []
- for i in range(iter):
- future = tPool.submit(testFind1)
- future.add_done_callback(callback)
-
- elif sys.argv[1]=="test2":
- cost = []
- for i in range(iter):
- future = tPool.submit(testFind2)
- future.add_done_callback(callback)
-
- else:
- print('error')
-