From 89c79bdaa1023a2ecdd195e6ed0a37ad5fb96b06 Mon Sep 17 00:00:00 2001 From: bwstudio <7469504@qq.com> Date: Tue, 23 Sep 2025 08:17:10 +0800 Subject: [PATCH] home0923 --- add_user_id_to_query_history.py | 115 +++++++++ api.py | 6 +- app.py | 124 ++-------- car_info.db | Bin 40960 -> 40960 bytes ewcc2.py | 252 +++++++++++++++++++ query_processor.py | 163 +++++++++++++ remove_department_from_query_history.py | 69 ++++++ requirements.txt | 5 + run.ps1 | 15 -- start_server.py | 14 -- templates/query.html | 311 ++++++++++-------------- 11 files changed, 768 insertions(+), 306 deletions(-) create mode 100644 add_user_id_to_query_history.py create mode 100644 ewcc2.py create mode 100644 query_processor.py create mode 100644 remove_department_from_query_history.py create mode 100644 requirements.txt delete mode 100644 run.ps1 delete mode 100644 start_server.py diff --git a/add_user_id_to_query_history.py b/add_user_id_to_query_history.py new file mode 100644 index 0000000..b722209 --- /dev/null +++ b/add_user_id_to_query_history.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +为query_history表添加user_id字段,并设置为users表id字段的外键 +""" + +import sqlite3 +import logging + +# 配置日志 +handlers = [logging.FileHandler('db_update.log'), logging.StreamHandler()] +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=handlers +) +logger = logging.getLogger(__name__) + +# 数据库文件路径 +db_path = 'car_info.db' + + +def main(): + """ + 主函数:连接数据库并为query_history表添加user_id字段 + """ + logger.info("开始为query_history表添加user_id字段") + + try: + # 连接数据库 + conn = sqlite3.connect(db_path) + cursor = conn.cursor() + + # 检查query_history表是否已经存在user_id字段 + cursor.execute("PRAGMA table_info(query_history)") + columns = cursor.fetchall() + + has_user_id = any(column[1] == 'user_id' for column in columns) + + if has_user_id: + logger.info("query_history表已存在user_id字段,无需添加") + else: + # 添加user_id字段(先不添加外键约束,确保字段可以添加成功) + logger.info("开始添加user_id字段") + cursor.execute("ALTER TABLE query_history ADD COLUMN user_id INTEGER") + conn.commit() + logger.info("user_id字段添加成功") + + # 尝试添加外键约束 + try: + logger.info("开始添加外键约束") + cursor.execute(""" + ALTER TABLE query_history + ADD CONSTRAINT fk_query_history_user_id + FOREIGN KEY (user_id) REFERENCES users(id) + """) + conn.commit() + logger.info("外键约束添加成功") + except sqlite3.Error as e: + # 在SQLite中,某些版本不支持通过ALTER TABLE直接添加外键约束 + # 如果添加外键约束失败,记录警告信息并继续 + logger.warning(f"添加外键约束失败: {str(e)}") + logger.warning("将创建临时表并重新创建query_history表来添加外键约束") + + # 创建临时表 + cursor.execute(""" + CREATE TABLE query_history_temp ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + department TEXT NOT NULL, + query_date TEXT NOT NULL, + plate_number TEXT, + phone TEXT, + results_count INTEGER NOT NULL, + query_ip TEXT NOT NULL, + user_id INTEGER, + FOREIGN KEY (user_id) REFERENCES users(id) + ) + """) + + # 复制数据到临时表 + cursor.execute(""" + INSERT INTO query_history_temp ( + id, department, query_date, plate_number, + phone, results_count, query_ip + ) + SELECT + id, department, query_date, plate_number, + phone, results_count, query_ip + FROM query_history + """) + + # 删除原表 + cursor.execute("DROP TABLE query_history") + + # 重命名临时表 + cursor.execute("ALTER TABLE query_history_temp RENAME TO query_history") + + # 提交更改 + conn.commit() + logger.info("通过重新创建表的方式成功添加外键约束") + + # 关闭连接 + conn.close() + logger.info("操作完成") + + except Exception as e: + logger.error(f"操作过程中发生错误: {str(e)}") + if 'conn' in locals() and conn: + conn.rollback() + conn.close() + + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/api.py b/api.py index 5ebbc61..bd1e734 100644 --- a/api.py +++ b/api.py @@ -73,7 +73,7 @@ def get_pending_queries(): # 查询query_history表中results_count=-1的所有数据 cursor.execute(""" - SELECT id, department, query_date, plate_number, phone, results_count, query_ip + SELECT id, query_date, plate_number, phone, results_count, query_ip, user_id FROM query_history WHERE results_count = -1 ORDER BY query_date DESC @@ -90,12 +90,12 @@ def get_pending_queries(): for row in rows: pending_query = { 'id': row['id'], - 'department': row['department'], 'query_date': row['query_date'], 'plate_number': row['plate_number'], 'phone': row['phone'], 'results_count': row['results_count'], - 'query_ip': row['query_ip'] + 'query_ip': row['query_ip'], + 'user_id': row['user_id'] if 'user_id' in row.keys() else None } pending_queries.append(pending_query) diff --git a/app.py b/app.py index 830b192..ebd60ed 100644 --- a/app.py +++ b/app.py @@ -1,7 +1,6 @@ from flask import Flask, render_template, request, redirect, url_for, session, jsonify from datetime import datetime, timedelta import os -import random from flask import Flask, render_template, request, redirect, url_for, session # 导入用户模块 @@ -58,87 +57,6 @@ def init_db(): ) ''') - # 插入一些测试车辆数据 - try: - test_cars = [ - ('京A12345', '13800138001', '110101199001011234', '张三', 'zhangsan@example.com', '北京市朝阳区'), - ('沪B54321', '13900139001', '310101199001012345', '李四', 'lisi@example.com', '上海市静安区'), - ('粤C67890', '13700137001', '440401199001013456', '王五', 'wangwu@example.com', '广东省珠海市'), - ('苏D12345', '13600136001', '320401199001014567', '赵六', 'zhaoliu@example.com', '江苏省常州市'), - ('浙E54321', '13500135001', '330501199001015678', '钱七', 'qianqi@example.com', '浙江省湖州市') - ] - cursor.executemany("INSERT INTO car_owners (plate_number, phone, id_card, name, email, address) VALUES (?, ?, ?, ?, ?, ?)", test_cars) - except Exception as e: - # 数据已存在或其他错误,忽略 - print(f"插入测试数据时出错: {e}") - - # 单独处理查询历史演示数据,确保每次都能插入20条记录 - try: - # 先删除现有的演示数据 - cursor.execute("DELETE FROM query_history WHERE department IN ('IT部', '财务部', '人事部', '市场部', '行政部')") - - # 生成最近20天的随机查询历史 - provinces = ['京', '沪', '粤', '苏', '浙', '鲁', '豫', '川', '湘', '鄂'] - cities = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K'] - departments = ['IT部', '财务部', '人事部', '市场部', '行政部'] - - # 获取现有的车辆信息,用于生成能匹配的查询历史 - cursor.execute("SELECT plate_number, phone FROM car_owners") - existing_cars = cursor.fetchall() - - for i in range(20): - # 生成随机日期(最近20天内) - days_ago = random.randint(0, 19) - date = (datetime.now() - timedelta(days=days_ago)).strftime('%Y-%m-%d %H:%M:%S') - - # 70%的概率使用已存在的车辆信息,30%的概率生成随机信息 - use_existing = random.random() < 0.7 - - if use_existing and existing_cars: - # 使用已存在的车辆信息 - car_info = random.choice(existing_cars) - if random.choice([True, False]): - plate_number = car_info[0] - phone = None - else: - plate_number = None - phone = car_info[1] - # 确保结果数量正确 - if plate_number: - cursor.execute("SELECT COUNT(*) FROM car_owners WHERE plate_number = ?", (plate_number,)) - results_count = cursor.fetchone()[0] - else: - cursor.execute("SELECT COUNT(*) FROM car_owners WHERE phone = ?", (phone,)) - results_count = cursor.fetchone()[0] - else: - # 随机生成车牌号或手机号 - use_plate = random.choice([True, False]) - if use_plate: - plate_number = f"{random.choice(provinces)}{random.choice(cities)}{random.randint(100000, 999999)}" - phone = None - else: - plate_number = None - phone = f"1{random.randint(3000000000, 9999999999)}" - # 检查是否存在匹配结果 - if plate_number: - cursor.execute("SELECT COUNT(*) FROM car_owners WHERE plate_number = ?", (plate_number,)) - results_count = cursor.fetchone()[0] - else: - cursor.execute("SELECT COUNT(*) FROM car_owners WHERE phone = ?", (phone,)) - results_count = cursor.fetchone()[0] - - # 随机部门 - department = random.choice(departments) - - # 生成随机IP地址 - query_ip = f"192.168.{random.randint(0, 255)}.{random.randint(1, 254)}" - - cursor.execute( - "INSERT INTO query_history (department, query_date, plate_number, phone, results_count, query_ip) VALUES (?, ?, ?, ?, ?, ?)", - (department, date, plate_number, phone, results_count, query_ip) - ) - except Exception as e: - print(f"插入查询历史数据时出错: {e}") try: conn.commit() except Exception as e: @@ -184,15 +102,16 @@ def query(): # 获取客户端IP地址 query_ip = request.remote_addr - # 从用户信息表获取部门信息 + # 从用户信息表获取用户ID + user_id = None try: # 可以复用同一个连接,不需要创建新连接 - cursor.execute("SELECT department FROM users WHERE username = ?", (session['username'],)) - user_dept = cursor.fetchone() - if user_dept: - department = user_dept[0] + cursor.execute("SELECT id FROM users WHERE username = ?", (session['username'],)) + user_info = cursor.fetchone() + if user_info: + user_id = user_info[0] except Exception as e: - print(f"获取部门信息时出错: {e}") + print(f"获取用户信息时出错: {e}") # 记录查询历史到数据库 try: @@ -207,8 +126,8 @@ def query(): results_count = len(results) cursor.execute( - "INSERT INTO query_history (department, query_date, plate_number, phone, results_count, query_ip) VALUES (?, ?, ?, ?, ?, ?)", - (department, query_date, plate_number if plate_number else None, phone if phone else None, results_count, query_ip) + "INSERT INTO query_history (query_date, plate_number, phone, results_count, query_ip, user_id) VALUES (?, ?, ?, ?, ?, ?)", + (query_date, plate_number if plate_number else None, phone if phone else None, results_count, query_ip, user_id) ) conn.commit() except Exception as e: @@ -300,7 +219,14 @@ def get_recent_queries(): cursor = conn.cursor() try: - cursor.execute("SELECT id, department, query_date, plate_number, phone, results_count, query_ip FROM query_history ORDER BY query_date DESC LIMIT 20") + # 使用JOIN操作获取用户姓名和部门信息 + cursor.execute(""" + SELECT qh.id, qh.query_date, qh.plate_number, qh.phone, qh.results_count, qh.query_ip, qh.user_id, + u.name AS user_name, u.department AS user_department + FROM query_history qh + LEFT JOIN users u ON qh.user_id = u.id + ORDER BY qh.query_date DESC LIMIT 20 + """) recent_queries = cursor.fetchall() # 格式化查询历史数据 @@ -308,12 +234,14 @@ def get_recent_queries(): for q in recent_queries: formatted_recent_queries.append({ 'id': q[0], - 'department': q[1], - 'query_date': q[2], - 'plate_number': q[3] if q[3] else '未提供', - 'phone': q[4] if q[4] else '未提供', - 'results_count': q[5], - 'query_ip': q[6] + 'query_date': q[1], + 'plate_number': q[2] if q[2] else '未提供', + 'phone': q[3] if q[3] else '未提供', + 'results_count': q[4], + 'query_ip': q[5], + 'user_id': q[6] if len(q) > 6 else None, + 'user_name': q[7] if len(q) > 7 else '未知', + 'user_department': q[8] if len(q) > 8 else '未知' }) return formatted_recent_queries @@ -340,7 +268,7 @@ if __name__ == '__main__': try: init_db() - app.run(debug=True) + app.run(debug=True,host="0.0.0.0",port=99) finally: # 确保在程序退出时关闭数据库连接 close_global_connection() \ No newline at end of file diff --git a/car_info.db b/car_info.db index 280554fbee4cee1288edc7af77ed0228bb932085..b2de3dcc336fee66fae0e2797b1618213cd701b9 100644 GIT binary patch literal 40960 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 Br1by* literal 40960 zcmeI5U2NON9l$BcvL%0rNgP*mfI#$&!ZA{7Jbs9z90zeyCGh4rsUr``Hb7u3B@t>% zjwCPbx=+VjktRsHty$Q{X_}xIieU|qc0pZtK!LR{!&YE#+r#=YSa$rf0vm?zZTlZ7 zl9FY)OCOeW`(GmJ$m9Rxe*Zhl;@wflo*2!RGTfy32kqCzxA26%Ww!25gDv5K0nfZ#(oarhX7qwSohfAssmXlqRQB}x{ni8HiQ!~|OAhZJO>pZ= zxm~SXRm!Hhk+EdrP-2`rIzDoEc>DzSmBa~dcp`aZWDFD?PK+fVtC27j5-yoIzEOeD zs6cE~AT=tGHz=5$N$aIds+671aLJLwiDSv(!$-Nt69J3pU&n|+1#nTn+w!%bB|XMyFXg9#Y`awp)%{ijU7pHV-usJ+{D<( z6B7v+>Wp42p34`~wWqn|`O*wTI=xPfTYm7!cw*$xm>IWSw#|NSJaI4qG!7(=ar9u3 zbIVLD{bHawe5l96WOM1vQ^m7W@TYl7pDpFh{ghk(6mj#xInvw`?(6f+_m~{d&SnZv zr_N-HrF`M(nw^#!nl(kA8EBfe{nB(MS8Bw{vP0WQqcdivXama4PM^#a%yV3jGiUNS zOU^x3$P{O%N<|33ELgR>iXBV$Jj-x)rq-+ldTvLP)%Lz;GMm&3sr@z%&! zdXjTpc_0^r&7iw&c!OYmdjBbw^wO*q9_Omsr%#r$-^}zm3a!BPdz-^U;YODa6I6ClWvcNB{{S0VIF~kN^@u0!RP}Ac2RKz#dP4 z2}dHmk9O1@v25J8kyZ{J*f;YZ~k8$?uj-rVq&W7lr288@si`cNySb7yDg)^1(b zshE8BTn4g`(rNb;Y9QZiw&z;Tg%RHq| z&rD^allf_oUb%B)b>Rmq@BFO%_ABMZxf4y6W|7>x`26GMjCUf&sAh8`@RF-p0@e5K z-Yx&|jc=XN^HbT`8r64hfCf-qUR<?CdICNH^4k9h8a(!`jJYu3V~1+ zyb>3*0!{xSf)eH7ABkQ0&ZdGqCkSy#1lv#2dW)4 zaLfpAYL#$=k4ti#4t+s#1qA`BFUF(`C#igA1LH*F2ndt7DBpYi`>u5Cg1(tqM~sf( z$uZd#RGytN?R9R9*AgUBTvAPcs~nEa7cI%ahJhg}N3^&k#R&|@(c}b1oG(~aY%Hk6 z2?T~O-&m@W8iD6viQV0S2&{dz!%wXUaaoRwFb1e9ib}Vt;z$Wso}sq3G8@|x%veGL zl0?yU5PIJ>CbIYH^j?Dw+CkghYjjze;O=`jFRi@zUR5LJ6}J&PkN<)ZQ>zgZO^pgf z9Wu686!h0AAX1eAXAIL;ppYn0#UZ29?nTQ>OUp|as~mtzIK4;}6d1h~MZxJs;D;b7 zdyH*VKEiBJ0b+I`vllwwJ#6bFCPj%T#f_~M@xHoF%xjK$=gW#CE|aLZ$JkP-dtaUJ ze9RGcpPg0%FE(VbRJ?}?Y|0)+Yrh=hNlesi;{v^asPdo@s`y@i0pdl+yz^zs7T2^W z5wtx%Km|46dRGW2S@#khmq_Ei@yotnDW2v(}SyRO2hN;18{TFI(dZPZj$DXI(_ z%{J{iuX*>g#`(T&`z!%%8Y?;z!lYF%?bf-jp%7k*irSz-XFHhEb*;Rrht*3iPhl?^jeo7Gk#RnjDoyI{)9tE;2Cs{|9)(ganWP5|gO003MEcse#Th>}K pB@ :') + time.sleep(5) + except Exception as e: + print('已经登录') + print(f'{e}') + cur_login = True + + if cur_login: + break + i += 1 + if not cur_login: + print('登录 失败,强制退出。') + return cur_login + + def login(self,username=None,userpass=None,otp=None): + self.tab.get(self.url) + self.tab.wait.eles_loaded('xpath://*[@id="authen1Form"]/button') + time.sleep(5) + + try: + ele_div=self.tab.ele('#tab3Con') + ele_div.ele('#fs3_username').set.value(self.ewcc_config.get('username','pengf332.hnsy')) + time.sleep(1) + ele_div.ele('#fs3_password').set.value(self.ewcc_config.get('password','Pf7842158c')) + time.sleep(1) + ele_div.ele('#fs3_otpOrSms').set.value(otp) + time.sleep(1) + ele_div.ele('@type=submit').click() + except Exception as e: + print(e) + time.sleep(3) + + ### 判断是否有仍然发生的界面 + try: + ele_process = self.tab.ele('#proceed-button') + ele_process.click() + + except Exception as e: + print(e) + + time.sleep(3) + return True + + + def show_login(self): + self.tab.get(self.url) + self.tab.wait.eles_loaded('xpath://*[@id="authen1Form"]/button') + time.sleep(3) + + try: + ele_div=self.tab.ele('#tab3Con') + ele_div.ele('#fs3_username').set.value(self.ewcc_config.get('username','pengf332.hnsy')) + ele_div.ele('#fs3_password').set.value(self.ewcc_config.get('password','Pf7842158b')) + ele_div.ele('#fs3_otpOrSms').set.value(self.ewcc_config.get('otpOrSms','129727')) + # ele_div.ele('@type=submit').click() + except Exception as e: + print(e) + + time.sleep(60) + return True + + + def send_message(self): + pass + + def query_car_info(self,plate_number=None,phone_number=None,additional_info=None): + self.tab.ele("@@tag()=li@@title=订单中心").click() + time.sleep(2) + self.tab.ele("@@tag()=a@@name=客户信息查询").click() + time.sleep(2) + myframe = self.tab.get_frame("iframeSOC_CSR_DETAIL") + ### print(myframe.inner_html) + time.sleep(2) + + ### print("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa") + ele = myframe.ele("#searchType") + ### print(ele.inner_html) + + myframe.ele("@@tag()=select@@id=searchType").select.by_text("车牌号") + myframe.ele('#searchValue').input(plate_number, clear=True) + + myframe.listen.start('acc/getAccCsrInfo') + myframe.ele("@text()=查询").click() + DataPacket = myframe.listen.wait() + ### print(DataPacket.response.body) + + ###print("ccccccccccccccccccccccccccccccccccccccc") + rtn={} + if DataPacket.response.body['success']: + total_num = DataPacket.response.body['data']['total'] + # rtn['length']=total_num + # rtn['test']='aaa' + rtn=[] + eles = myframe.eles("@@name=客户信息详情界面@@text()=详情") + for ele in eles: + sele = self.tab.ele("@@text()^客户信息查询@@class:J_menuTab") + if sele: + sele.click() + self.tab.listen.start('getAccCsrInfoByCsrid') + ele.click() + DataPacket = self.tab.listen.wait() + ### print(DataPacket.response.body) + rtn.append(DataPacket.response.body['data']) + self.tab.listen.stop() + ### print(ele.inner_html) + + return rtn + +if __name__ == "__main__": + ewcc = Sinopec_ewcc() + + # 1 查找现有的标签 + print('<1> 查找 现有的标签') + ewcc.find_tab() # 新增:查找标签页 + + # 2 如果没有登录 ,则使用cookie登录 + if not ewcc.is_login(): + print('<2> 找不到对应的网站,使用cookies尝试登录') + ewcc.load_cookies() + ewcc.new_tab() + + # 3 判断是否登录,如果登录不成功,则使用帐号和密码登录 + if not ewcc.is_login(): + print('<3> 找不对应的网站,使用帐号密码登录') + # 这里要注意12小时才可以登录一次。 + # 这里显示登录界面,并等待输入一分钟 + ewcc.show_login() + + # 4 判断是否登录 ,如果失败,则发送信息到微信上 + if ewcc.is_login(): + ewcc.tab.refresh() + ewcc.save_cookies() + print(" 保存cookies") + else: + ewcc.send_message() + + # ewcc.query_car_info() + + ewcc.quit() + + + + + + diff --git a/query_processor.py b/query_processor.py new file mode 100644 index 0000000..eceadcd --- /dev/null +++ b/query_processor.py @@ -0,0 +1,163 @@ +import requests +import time +import logging + +# 配置日志 +handlers = [logging.FileHandler('query_processor.log'), logging.StreamHandler()] +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(levelname)s - %(message)s', + handlers=handlers +) +logger = logging.getLogger(__name__) + +# API基础URL +BASE_URL = 'http://localhost:99' + +# 演示数据处理函数 +def process_query_data(query_data, parameters): + """ + 处理查询数据的演示函数 + 这里使用演示代码代替实际的数据处理逻辑 + """ + # 获取用户ID,如果存在 + user_id = query_data.get('user_id', '未知') + logger.info(f"处理查询数据: ID={query_data['id']}, 车牌号={query_data['plate_number']}, 用户ID={user_id}") + + # 这里是演示处理逻辑 + # 在实际应用中,这里应该是真实的数据处理代码 + logger.info(f"使用参数进行处理: {parameters}") + + # 模拟处理过程 + time.sleep(2) # 模拟处理耗时 + + # 返回模拟的处理结果(简单地将results_count设置为随机数1-5) + # 实际应用中,应该根据真实处理结果返回相应的值 + results_count = 3 # 演示用的固定结果数 + logger.info(f"处理完成,结果数量: {results_count}") + + return results_count + +# 读取参数配置 +def get_parameters(): + """ + 从后端API读取参数配置 + """ + try: + url = f'{BASE_URL}/api/parameters' + response = requests.get(url) + response.raise_for_status() + data = response.json() + + if data['success']: + logger.info(f"成功获取参数配置: {data['data']}") + return data['data'] + else: + logger.error(f"获取参数配置失败: {data['error']}") + return None + except Exception as e: + logger.error(f"获取参数配置时发生错误: {str(e)}") + return None + +# 获取待处理查询 +def get_pending_queries(): + """ + 从后端API获取待处理查询 + """ + try: + url = f'{BASE_URL}/api/pending-queries' + response = requests.get(url) + response.raise_for_status() + data = response.json() + + if data['success']: + logger.info(f"成功获取待处理查询,共 {data['count']} 条") + return data['data'] + else: + logger.error(f"获取待处理查询失败: {data['error']}") + return [] + except Exception as e: + logger.error(f"获取待处理查询时发生错误: {str(e)}") + return [] + +# 更新查询结果 +def update_query_result(query_id, results_count): + """ + 更新查询结果到后端API + """ + try: + url = f'{BASE_URL}/api/update-query-result' + data = { + 'id': query_id, + 'results_count': results_count + } + + response = requests.post(url, json=data) + response.raise_for_status() + result = response.json() + + if result['success']: + logger.info(f"成功更新查询结果: ID={query_id}, 结果数量={results_count}") + return True + else: + logger.error(f"更新查询结果失败: {result['error']}") + return False + except Exception as e: + logger.error(f"更新查询结果时发生错误: {str(e)}") + return False + +# 主处理函数 +def main(): + logger.info("查询处理代码启动") + + # 获取参数配置 + parameters = get_parameters() + if not parameters: + logger.warning("无法获取参数配置,使用默认参数继续") + # 使用默认参数继续 + parameters = { + 'param1': 'default1', + 'param2': 'default2', + 'param3': 'default3', + 'param4': 'default4', + 'param5': 'default5' + } + + # 主循环 + while True: + try: + # 获取待处理查询 + pending_queries = get_pending_queries() + + # 处理每条查询 + for query in pending_queries: + # 处理查询数据 + results_count = process_query_data(query, parameters) + + # 更新查询结果 + update_query_result(query['id'], results_count) + + # 等待一段时间后再次检查 + wait_time = 60 # 默认等待60秒 + # 如果有parameters参数,可以根据参数调整等待时间 + if parameters and 'polling_interval' in parameters: + try: + wait_time = int(parameters['polling_interval']) + except (ValueError, TypeError): + logger.warning(f"无效的轮询间隔参数: {parameters['polling_interval']}") + + logger.info(f"本次处理完成,等待 {wait_time} 秒后再次检查") + time.sleep(wait_time) + + except KeyboardInterrupt: + logger.info("查询处理器被用户中断") + break + except Exception as e: + logger.error(f"处理过程中发生错误: {str(e)}") + # 发生错误时,等待更短的时间后重试 + time.sleep(30) + + logger.info("查询处理器停止") + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/remove_department_from_query_history.py b/remove_department_from_query_history.py new file mode 100644 index 0000000..ba29b39 --- /dev/null +++ b/remove_department_from_query_history.py @@ -0,0 +1,69 @@ +import sqlite3 +from db_utils import get_db_connection, close_db_connection + + +def remove_department_from_query_history(): + """ + 从query_history表中删除department字段 + SQLite不支持直接删除列,采用以下步骤: + 1. 创建一个不包含department字段的新表 + 2. 将原始表中的数据(除department外)复制到新表 + 3. 删除原始表 + 4. 重命名新表为原始表名 + 5. 重新创建索引和外键约束 + """ + conn = None + try: + # 获取数据库连接 + conn = get_db_connection() + cursor = conn.cursor() + + print("开始从query_history表中删除department字段...") + + # 1. 创建临时表,不包含department字段 + print("1. 创建临时表query_history_new...") + cursor.execute(''' + CREATE TABLE IF NOT EXISTS query_history_new ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + query_date TEXT NOT NULL, + plate_number TEXT, + phone TEXT, + results_count INTEGER NOT NULL, + query_ip TEXT NOT NULL, + user_id INTEGER, + FOREIGN KEY (user_id) REFERENCES users (id) + ) + ''') + + # 2. 复制数据到新表,排除department字段 + print("2. 复制数据到新表,排除department字段...") + cursor.execute(''' + INSERT INTO query_history_new (id, query_date, plate_number, phone, results_count, query_ip, user_id) + SELECT id, query_date, plate_number, phone, results_count, query_ip, user_id + FROM query_history + ''') + + # 3. 删除原表 + print("3. 删除原始表query_history...") + cursor.execute('DROP TABLE IF EXISTS query_history') + + # 4. 重命名新表为原表名 + print("4. 重命名新表为query_history...") + cursor.execute('ALTER TABLE query_history_new RENAME TO query_history') + + # 5. 提交事务 + conn.commit() + print("操作完成:department字段已成功从query_history表中删除!") + + except sqlite3.Error as e: + print(f"数据库操作出错:{e}") + if conn: + conn.rollback() + raise + finally: + if conn: + close_db_connection(conn) + + +if __name__ == "__main__": + remove_department_from_query_history() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b1676e1 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,5 @@ +# Flask应用依赖 +flask + +# 查询处理器依赖 +requests \ No newline at end of file diff --git a/run.ps1 b/run.ps1 deleted file mode 100644 index f5d7870..0000000 --- a/run.ps1 +++ /dev/null @@ -1,15 +0,0 @@ -# 设置执行策略以允许运行脚本(仅在首次运行时需要取消注释) -# Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process - -# 激活虚拟环境 -& '.\venv\Scripts\Activate.ps1' - -# 设置Flask应用程序环境变量 -$env:FLASK_APP = "app.py" -$env:FLASK_ENV = "development" - -# 启动Flask服务器,允许外部访问(使用python -m flask确保命令可用) -python -m flask run --host=0.0.0.0 - -# 保持窗口打开 -Read-Host "Press Enter to exit" \ No newline at end of file diff --git a/start_server.py b/start_server.py deleted file mode 100644 index f72275e..0000000 --- a/start_server.py +++ /dev/null @@ -1,14 +0,0 @@ -import os -import subprocess - -# 设置Flask应用程序环境变量 -os.environ['FLASK_APP'] = 'app.py' -os.environ['FLASK_ENV'] = 'development' - -print("Starting Flask server...") -print("Server will be available at http://localhost:5000/") -print("To allow external access, it's listening on all interfaces (0.0.0.0)") -print("Press Ctrl+C to stop the server") - -# 启动Flask服务器,允许外部访问 -subprocess.run(['python', '-m', 'flask', 'run', '--host=0.0.0.0']) \ No newline at end of file diff --git a/templates/query.html b/templates/query.html index cc2d26f..5db38a6 100644 --- a/templates/query.html +++ b/templates/query.html @@ -91,6 +91,7 @@ 车牌号 手机号 状态 + 登录用户 登录部门 登录IP @@ -118,7 +119,8 @@ {% endif %} - {{ query.department }} + {{ query.user_name }} + {{ query.user_department }} {{ query.query_ip }} {% endfor %} @@ -136,19 +138,7 @@ - - + {% include 'footer.html' %} @@ -158,25 +148,23 @@ document.addEventListener('DOMContentLoaded', function() { // 获取DOM元素 var addQueryBtn = document.getElementById('addQueryBtn'); - var addQueryModal = document.getElementById('addQueryModal'); - var closeAddModal = document.getElementById('closeAddModal'); - var cancelAddBtn = document.getElementById('cancelAddBtn'); - var addQueryForm = document.getElementById('addQueryForm'); - var addQueryLoading = document.getElementById('addQueryLoading'); - var detailModal = document.getElementById('detailModal'); - var closeModal = document.getElementById('closeModal'); - var loadingState = document.getElementById('loadingState'); - var noResultState = document.getElementById('noResultState'); - var resultList = document.getElementById('resultList'); - var carOwnerCards = document.getElementById('carOwnerCards'); - // 为查询历史记录行添加点击事件 + // 为查询历史记录行添加单击事件 var rows = document.querySelectorAll('tr[data-history-id]'); for (var i = 0; i < rows.length; i++) { rows[i].addEventListener('click', function() { var historyId = this.getAttribute('data-history-id'); loadHistoryDetail(historyId); }); + // 为了提升用户体验,添加鼠标悬停效果 + rows[i].style.cursor = 'pointer'; + rows[i].style.transition = 'background-color 0.2s ease'; + rows[i].addEventListener('mouseenter', function() { + this.style.backgroundColor = '#f5f7fa'; + }); + rows[i].addEventListener('mouseleave', function() { + this.style.backgroundColor = ''; + }); } // 增加查询按钮点击事件 @@ -396,48 +384,21 @@ }); }); - // 移除对已删除元素的引用 + - // 点击详情模态框背景关闭 - detailModal.addEventListener('click', function(e) { - if (e.target === detailModal) { - // 兼容classList API - if (detailModal.classList) { - detailModal.classList.add('hidden'); - } else { - detailModal.className += ' hidden'; - } - } - }); + // 详情弹窗已使用layui实现,点击背景关闭功能由layui自动处理 // 表单提交逻辑已整合到layui弹窗中,不需要单独的表单提交事件 // 加载查询历史详情 function loadHistoryDetail(historyId) { - // 显示模态框和加载状态 - // 兼容classList API - if (detailModal.classList) { - detailModal.classList.remove('hidden'); - } else { - detailModal.className = detailModal.className.replace('hidden', ''); - } - if (loadingState.classList) { - loadingState.classList.remove('hidden'); - } else { - loadingState.className = loadingState.className.replace('hidden', ''); - } - if (noResultState.classList) { - noResultState.classList.add('hidden'); - } else { - noResultState.className += ' hidden'; - } - if (resultList.classList) { - resultList.classList.add('hidden'); - } else { - resultList.className += ' hidden'; - } - carOwnerCards.innerHTML = ''; + // 创建加载中的弹窗 + const loadingIndex = layer.load(2, { + shade: [0.3, '#fff'], + content: '
正在加载详情...
', + shadeClose: false + }); // 发送AJAX请求获取详情 fetch('/query_history_detail/' + historyId) @@ -445,99 +406,135 @@ return response.json(); }) .then(function(data) { - // 隐藏加载状态 - // 兼容classList API - if (loadingState.classList) { - loadingState.classList.add('hidden'); - } else { - loadingState.className += ' hidden'; + // 关闭加载弹窗 + layer.close(loadingIndex); + + // 创建详情弹窗内容 + let content = ''; + + // 转义特殊字符以防止XSS攻击 + function escapeHtml(text) { + const div = document.createElement('div'); + div.textContent = text || ''; + return div.innerHTML; } if (data.error) { // 显示错误信息 - noResultState.querySelector('p').textContent = data.error; - // 兼容classList API - if (noResultState.classList) { - noResultState.classList.remove('hidden'); - } else { - noResultState.className = noResultState.className.replace('hidden', ''); - } + content = ` +
+
+ +
+

${data.error}

+
+ `; } else if (data.car_owners && data.car_owners.length > 0) { - // 显示结果列表 - // 兼容classList API - if (resultList.classList) { - resultList.classList.remove('hidden'); - } else { - resultList.className = resultList.className.replace('hidden', ''); - } + // 添加查询信息标题 + content = ` +
+
+ +
+
+ `; // 添加车辆信息卡片 for (var i = 0; i < data.car_owners.length; i++) { var carOwner = data.car_owners[i]; - var card = document.createElement('div'); - card.className = 'bg-white border border-gray-200 rounded-lg p-5 shadow-sm hover:shadow-md transition-shadow duration-200'; - // 使用字符串拼接替代模板字符串 - var cardHtml = ''; - cardHtml += '

'; - cardHtml += ' '; - cardHtml += ' 车辆信息'; - cardHtml += '

'; - cardHtml += '
'; - cardHtml += '
'; - cardHtml += ' 车牌号:'; - cardHtml += ' ' + (carOwner.plate_number || '') + ''; - cardHtml += '
'; - cardHtml += '
'; - cardHtml += ' 手机号:'; - cardHtml += ' ' + (carOwner.phone || '') + ''; - cardHtml += '
'; - cardHtml += '
'; - cardHtml += ' 车主姓名:'; - cardHtml += ' ' + (carOwner.name || '') + ''; - cardHtml += '
'; - cardHtml += '
'; - cardHtml += ' 身份证号:'; - cardHtml += ' ' + (carOwner.id_card || '') + ''; - cardHtml += '
'; - cardHtml += '
'; - cardHtml += ' 电子邮箱:'; - cardHtml += ' ' + (carOwner.email || '未提供') + ''; - cardHtml += '
'; - cardHtml += '
'; - cardHtml += ' 居住地址:'; - cardHtml += ' ' + (carOwner.address || '未提供') + ''; - cardHtml += '
'; - cardHtml += '
'; - - card.innerHTML = cardHtml; - carOwnerCards.appendChild(card); + content += ` +
+
+
+ + 车牌号: + ${escapeHtml(carOwner.plate_number)} +
+
+ + 手机号: + ${escapeHtml(carOwner.phone)} +
+
+ + 车主姓名: + ${escapeHtml(carOwner.name)} +
+
+ + 身份证号: + ${escapeHtml(carOwner.id_card)} +
+
+
+ `; } + + content += ` +
+
+ `; } else { // 显示无结果状态 - // 兼容classList API - if (noResultState.classList) { - noResultState.classList.remove('hidden'); - } else { - noResultState.className = noResultState.className.replace('hidden', ''); - } + content = ` +
+
+ +
+

暂无车辆信息

+

该查询条件下未找到相关车辆信息

+
+ `; } + + // 打开详情弹窗 - 优化布局和样式 + layer.open({ + type: 1, + title: ' 查询详情', + area: ['700px', 'auto'], // 使用自适应高度 + offset: '10%', + shade: [0.3, '#fff'], + shadeClose: true, + anim: 2, + content: content, + btn: ['关闭'], + btnAlign: 'c', + btnClass: ['layui-btn layui-btn-primary'], + skin: 'layui-layer-molv', + success: function(layero, index) { + + } + }); }) .catch(function(error) { - // 隐藏加载状态,显示错误信息 - // 兼容classList API - if (loadingState.classList) { - loadingState.classList.add('hidden'); - } else { - loadingState.className += ' hidden'; - } - noResultState.querySelector('p').textContent = '加载详情时发生错误'; - // 兼容classList API - if (noResultState.classList) { - noResultState.classList.remove('hidden'); - } else { - noResultState.className = noResultState.className.replace('hidden', ''); - } + // 关闭加载弹窗 + layer.close(loadingIndex); + + // 显示错误弹窗 + layer.open({ + type: 1, + title: ' 加载失败', + area: ['360px', 'auto'], + offset: '10%', + shade: 0.3, + shadeClose: false, + anim: 2, + content: ` +
+
+ +
+

加载详情时发生错误

+

请稍后重试

+
+ `, + btn: ['确定'], + btnAlign: 'c', + btnClass: ['layui-btn layui-btn-primary'], + skin: 'layui-layer-molv' + }); + console.error('Error loading history detail:', error); }); } @@ -561,44 +558,6 @@ }); }); - - - - - - - - - \ No newline at end of file