From b0e8a682048bbd8d2c3b3bddad4983255b448572 Mon Sep 17 00:00:00 2001 From: bwadmin <7469504@qq.com> Date: Fri, 19 Sep 2025 16:47:15 +0800 Subject: [PATCH] 20250819corp --- api.py | 197 +++++++++++--- app.py | 196 ++++++++------ car_info.db | Bin 40960 -> 40960 bytes check_db.py | 82 +++--- db_utils.py | 90 +++++++ static/js/utils.js | 247 +++++++++++++----- templates/js/utils.js | 214 --------------- templates/query.html | 310 +++++++++++++++------- templates/users_management.html | 13 +- update_database.py | 75 +++++- user.py | 447 ++++++++++++++------------------ verify_database.py | 86 +++--- 12 files changed, 1148 insertions(+), 809 deletions(-) create mode 100644 db_utils.py delete mode 100644 templates/js/utils.js diff --git a/api.py b/api.py index 0cdd625..5ebbc61 100644 --- a/api.py +++ b/api.py @@ -1,75 +1,188 @@ -from flask import Flask, jsonify -import sqlite3 from datetime import datetime +from flask import Blueprint, jsonify, request + +# 导入数据库工具 +from db_utils import get_db_connection, close_db_connection # 创建Flask蓝图 -api_bp = Flask(__name__) +api_bp = Blueprint('api', __name__) -# 连接数据库的辅助函数 -def get_db_connection(): - conn = sqlite3.connect('car_info.db') - conn.row_factory = sqlite3.Row # 使返回的数据可以通过列名访问 - return conn - -# API函数1: 返回所有的参数信息 +# API函数1: 返回最新的参数配置记录 @api_bp.route('/api/parameters', methods=['GET']) -def get_all_parameters(): +def get_parameters(): try: # 连接数据库 conn = get_db_connection() cursor = conn.cursor() - # 查询所有参数配置记录 + # 查询所有参数配置记录并只返回第一条(最新的一条) cursor.execute(""" SELECT id, param1, param2, param3, param4, param5, update_time FROM parameter_config ORDER BY update_time DESC + LIMIT 1 + """) + + # 获取第一条查询结果 + row = cursor.fetchone() + + # 关闭数据库连接 + close_db_connection(conn) + + # 如果没有查询到数据 + if not row: + return jsonify({ + 'success': False, + 'error': '未找到参数配置记录', + 'data': None + }), 404 + + # 格式化查询结果 + parameter = { + 'id': row[0], + 'param1': row[1], + 'param2': row[2], + 'param3': row[3], + 'param4': row[4], + 'param5': row[5], + 'update_time': row[6] + } + + # 返回JSON格式的数据 + return jsonify({ + 'success': True, + 'data': parameter + }) + except Exception as e: + # 确保在异常情况下也关闭连接 + if 'conn' in locals() and conn: + close_db_connection(conn) + return jsonify({ + 'success': False, + 'error': str(e), + 'data': [] + }), 500 + +# API函数2: 返回query_history表中所有results_count=-1的数据 +@api_bp.route('/api/pending-queries', methods=['GET']) +def get_pending_queries(): + try: + # 连接数据库 + conn = get_db_connection() + cursor = conn.cursor() + + # 查询query_history表中results_count=-1的所有数据 + cursor.execute(""" + SELECT id, department, query_date, plate_number, phone, results_count, query_ip + FROM query_history + WHERE results_count = -1 + ORDER BY query_date DESC """) # 获取所有查询结果 rows = cursor.fetchall() # 关闭数据库连接 - conn.close() - - # 如果没有查询到数据 - if not rows: - return jsonify({ - 'success': False, - 'error': '未找到参数配置记录', - 'data': [] - }), 404 + close_db_connection(conn) # 格式化查询结果 - parameters_list = [] + pending_queries = [] for row in rows: - parameters_list.append({ + pending_query = { 'id': row['id'], - 'param1': row['param1'], - 'param2': row['param2'], - 'param3': row['param3'], - 'param4': row['param4'], - 'param5': row['param5'], - 'update_time': row['update_time'] - }) + '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'] + } + pending_queries.append(pending_query) - # 返回JSON格式的参数信息 + # 返回JSON格式的数据 return jsonify({ 'success': True, - 'message': '参数信息获取成功', - 'data': parameters_list, - 'total': len(parameters_list) - }), 200 - + 'data': pending_queries, + 'count': len(pending_queries) + }) except Exception as e: - # 处理异常情况 + # 确保在异常情况下也关闭连接 + if 'conn' in locals() and conn: + close_db_connection(conn) return jsonify({ 'success': False, - 'error': f'获取参数信息失败: {str(e)}', + 'error': str(e), 'data': [] }), 500 -# 如果直接运行此文件(用于测试) -if __name__ == '__main__': - # 在生产环境中不应使用debug=True - api_bp.run(debug=True) \ No newline at end of file +# API函数3: 更新query_history表中对应记录的results_count值 +@api_bp.route('/api/update-query-result', methods=['POST']) +def update_query_result(): + try: + # 获取前端提交的数据 + data = request.json + if not data: + return jsonify({ + 'success': False, + 'error': '请求数据不能为空', + 'data': None + }), 400 + + # 检查必要的字段是否存在 + if 'id' not in data or 'results_count' not in data: + return jsonify({ + 'success': False, + 'error': '缺少必要的字段: id 或 results_count', + 'data': None + }), 400 + + # 获取id和results_count值 + query_id = data['id'] + results_count = data['results_count'] + + # 连接数据库 + conn = get_db_connection() + cursor = conn.cursor() + + # 更新query_history表中的对应记录 + cursor.execute(""" + UPDATE query_history + SET results_count = ? + WHERE id = ? + """, (results_count, query_id)) + + # 提交更改 + conn.commit() + + # 获取受影响的行数 + affected_rows = cursor.rowcount + + # 关闭数据库连接 + close_db_connection(conn) + + # 检查是否有记录被更新 + if affected_rows == 0: + return jsonify({ + 'success': False, + 'error': '未找到id为 {} 的记录'.format(query_id), + 'data': None + }), 404 + + # 返回成功响应 + return jsonify({ + 'success': True, + 'message': '记录更新成功', + 'data': { + 'id': query_id, + 'results_count': results_count + } + }) + except Exception as e: + # 确保在异常情况下也关闭连接 + if 'conn' in locals() and conn: + close_db_connection(conn) + return jsonify({ + 'success': False, + 'error': str(e), + 'data': None + }), 500 \ No newline at end of file diff --git a/app.py b/app.py index 1592485..830b192 100644 --- a/app.py +++ b/app.py @@ -1,13 +1,15 @@ from flask import Flask, render_template, request, redirect, url_for, session, jsonify from datetime import datetime, timedelta -import sqlite3 import os import random +from flask import Flask, render_template, request, redirect, url_for, session # 导入用户模块 from user import bp as user_bp, init_user_db # 导入API模块 from api import api_bp +# 导入数据库工具 +from db_utils import get_db_connection, close_db_connection, close_global_connection app = Flask(__name__) app.secret_key = 'your_secret_key' # 生产环境中应该使用更安全的密钥 @@ -27,7 +29,7 @@ def init_db(): # 初始化用户相关的数据库 init_user_db() - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() # 创建车辆信息表 @@ -66,9 +68,9 @@ def init_db(): ('浙E54321', '13500135001', '330501199001015678', '钱七', 'qianqi@example.com', '浙江省湖州市') ] cursor.executemany("INSERT INTO car_owners (plate_number, phone, id_card, name, email, address) VALUES (?, ?, ?, ?, ?, ?)", test_cars) - except sqlite3.IntegrityError: - # 数据已存在,忽略 - pass + except Exception as e: + # 数据已存在或其他错误,忽略 + print(f"插入测试数据时出错: {e}") # 单独处理查询历史演示数据,确保每次都能插入20条记录 try: @@ -137,8 +139,12 @@ def init_db(): ) except Exception as e: print(f"插入查询历史数据时出错: {e}") - conn.commit() - conn.close() + try: + conn.commit() + except Exception as e: + print(f"提交数据库更改时出错: {e}") + finally: + close_db_connection(conn) # 首页重定向到登录页 @app.route('/') @@ -161,7 +167,7 @@ def query(): if not plate_number and not phone: return render_template('query.html', error='请至少输入车牌号或手机号', recent_queries=formatted_recent_queries) - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() query_date = datetime.now().strftime('%Y-%m-%d %H:%M:%S') @@ -180,32 +186,35 @@ def query(): # 从用户信息表获取部门信息 try: - conn_dept = sqlite3.connect('car_info.db') - cursor_dept = conn_dept.cursor() - cursor_dept.execute("SELECT department FROM users WHERE username = ?", (session['username'],)) - user_dept = cursor_dept.fetchone() - department = user_dept[0] if user_dept else '未知部门' + # 可以复用同一个连接,不需要创建新连接 + cursor.execute("SELECT department FROM users WHERE username = ?", (session['username'],)) + user_dept = cursor.fetchone() + if user_dept: + department = user_dept[0] except Exception as e: print(f"获取部门信息时出错: {e}") - department = '未知部门' - finally: - if 'conn_dept' in locals(): - conn_dept.close() # 记录查询历史到数据库 try: - conn_history = sqlite3.connect('car_info.db') - cursor_history = conn_history.cursor() - cursor_history.execute( + # 继续使用同一个连接 + # 检查是否是从"增加"按钮提交的请求 + is_add_query = request.form.get('is_add_query', 'false').lower() == 'true' + + # 如果是增加按钮提交的请求,将results_count设置为-1 + if is_add_query: + results_count = -1 + else: + 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, len(results), query_ip) + (department, query_date, plate_number if plate_number else None, phone if phone else None, results_count, query_ip) ) - conn_history.commit() + conn.commit() except Exception as e: print(f"记录查询历史时出错: {e}") finally: - if 'conn_history' in locals(): - conn_history.close() + close_db_connection(conn) # 处理查询结果 car_owners = [] @@ -241,68 +250,97 @@ def query_history_detail(history_id): if 'username' not in session: return jsonify({'error': '未登录'}), 401 - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() - # 获取查询历史记录 - cursor.execute("SELECT plate_number, phone FROM query_history WHERE id = ?", (history_id,)) - history = cursor.fetchone() - - if not history: - conn.close() - return jsonify({'error': '查询历史不存在'}), 404 - - plate_number, phone = history - - # 根据车牌号和手机号查询车辆信息 - if plate_number and phone: - cursor.execute("SELECT * FROM car_owners WHERE plate_number = ? AND phone = ?", (plate_number, phone)) - elif plate_number: - cursor.execute("SELECT * FROM car_owners WHERE plate_number = ?", (plate_number,)) - else: - cursor.execute("SELECT * FROM car_owners WHERE phone = ?", (phone,)) - - results = cursor.fetchall() - conn.close() - - # 格式化结果 - car_owners = [] - for result in results: - car_owners.append({ - 'id': result[0], - 'plate_number': result[1], - 'phone': result[2], - 'id_card': result[3], - 'name': result[4], - 'email': result[5], - 'address': result[6] - }) - - return jsonify({'car_owners': car_owners}) + try: + # 获取查询历史记录 + cursor.execute("SELECT plate_number, phone FROM query_history WHERE id = ?", (history_id,)) + history = cursor.fetchone() + + if not history: + return jsonify({'error': '查询历史不存在'}), 404 + + plate_number, phone = history + + # 根据车牌号和手机号查询车辆信息 + if plate_number and phone: + cursor.execute("SELECT * FROM car_owners WHERE plate_number = ? AND phone = ?", (plate_number, phone)) + elif plate_number: + cursor.execute("SELECT * FROM car_owners WHERE plate_number = ?", (plate_number,)) + else: + cursor.execute("SELECT * FROM car_owners WHERE phone = ?", (phone,)) + + results = cursor.fetchall() + + # 格式化结果 + car_owners = [] + for result in results: + car_owners.append({ + 'id': result[0], + 'plate_number': result[1], + 'phone': result[2], + 'id_card': result[3], + 'name': result[4], + 'email': result[5], + 'address': result[6] + }) + + return jsonify({'car_owners': car_owners}) + except Exception as e: + print(f"查询历史详情时出错: {e}") + return jsonify({'error': '查询失败'}), 500 + finally: + close_db_connection(conn) # 获取最近查询历史的辅助函数 def get_recent_queries(): # 获取最近20条查询历史记录,按查询日期降序排列 - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() - cursor.execute("SELECT id, department, query_date, plate_number, phone, results_count, query_ip FROM query_history ORDER BY query_date DESC LIMIT 20") - recent_queries = cursor.fetchall() - conn.close() - # 格式化查询历史数据 - formatted_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] - }) - return formatted_recent_queries + 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") + recent_queries = cursor.fetchall() + + # 格式化查询历史数据 + formatted_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] + }) + + return formatted_recent_queries + except Exception as e: + print(f"获取最近查询历史时出错: {e}") + return [] + finally: + close_db_connection(conn) + +import signal +import sys + +def signal_handler(sig, frame): + """处理终止信号,确保在程序退出时关闭数据库连接""" + print('\n接收到终止信号,正在关闭数据库连接...') + close_global_connection() + print('数据库连接已关闭') + sys.exit(0) if __name__ == '__main__': - init_db() - app.run(debug=True) \ No newline at end of file + # 注册信号处理程序 + signal.signal(signal.SIGINT, signal_handler) # 处理Ctrl+C + signal.signal(signal.SIGTERM, signal_handler) # 处理终止信号 + + try: + init_db() + app.run(debug=True) + finally: + # 确保在程序退出时关闭数据库连接 + close_global_connection() \ No newline at end of file diff --git a/car_info.db b/car_info.db index 69db6b22aa20dca88f84a10f5fa6d2b910ae81b9..311f0bed381df7acd9401cd75b6bae7d3b1215bb 100644 GIT binary patch delta 1665 zcmaJ>TWeHT6h4{pGBcTE8^a@dqR^{s5nK?=$C;cx?AD8)jJFx7S+Vx7PH0 zYI;8P`_{I~AKNC@R}Ax~+Wl{Cex08+(0kNC3+Ne|ZFCiDsAtRi^k`dUvO0O`{Ez3W zr_W9OaN)ae)Ah!l!PJJq#=@SjierXh^krWdxy9VQ+~wR@E|qoCUsPAD{3f=0xm2rcpPW_R`J;iK3kt32h3$Q_$LmmU%q%N?a0CU zneV$jAUaKIxePBq{Chv=m{Ag@NeDLyvCDEmmuU=TMiOOW5#d2F&WI(5#Xry49+OF;uNTZG5A+Vz)hg4 zf*k>TWU<*?Y}UdA>cy#bp0AYO0-#*u^%dd)R1X*{$?Z`B38_`DgB}7Enm}J60T(70 z<8oU7A6|O=%hEs1FahP1OJM&W-omN@q9nI!0&TUm6(|B6s?a?FHZRF+`kjTvK<`869!uIartGKa8h%=D&IuI7&XDhw)`Sv^y&vk4jx-o zEKr5@0>jPS*~hir!neUuez%m^z5iJ&#BVP@nc1hN0Q1PNj&d92W!ck8{d0EpTjjOH zwrxxtCAJ;;m0&G4Sh71vet2#2vC|ftclQFJEHbL2dn!5T?x@z`=~YO-lwk%VFGq#SA`g! zFVPG-r8hJxyJ;n3_x7OzWeloS#)$gTYH)aP)X!*X`&*NP+L|O)e|3F#PW?e$LR`%1 HFR!zY6z##n delta 1332 zcmaKs&r4KM6vv;IHNRgXB3a;=l$gxxo_p{6HI7PIP>Y~Nv@PRa=X>AmdgbhT<-=nYiATq~ zsuGIUU-2%vdM322sK3>Wx~YCumowGT1+})O>Ojmt)i>QYK5%DXx~G3~VsLmUo#{ML zS$R9N*?Bsu2kVu(z>E^esh`!m>P7W%z6D4}|c*f9c8 z;BksybNj>61)VbrU{ehYO9KL$VI;&ww`8-8ch-I%7K}L;8%> z$KHvn-d=lsu5?aA17Nlo^C@+#QSr52y6P&CkhQ05mWZ(Li%1S^tKdtK5twE|j5^lo zmC>3M^UVH+N-5JQ)#GBs5j5wr*<5zM$PwuNa&!aMA%^pue68gRMan5>qoA7v86zod z+1V)&4LDY~nXlk)Eh9~I*B+Q5LCeD}U=t0iwTmIAKudXnUT=P2maHI;LIqpLbP-bw zO58G(CAj~W5(=hf(u5dDf1jvNd6dfHgger3__#>!3%O{@T=vabpp5HohnQp@7yYt9 zrKuzj-YrOC9xyeSVvE{%RSJMDhEayNxRF=;OVb2i4*!v2irYc!4$80Edy$;UiQk>p0%kH;jAgl+uq)=eC=SZK y=yR@tMH1pVuH$Hvb5G { - (isSuccess ? successMessage : errorMessage).classList.add('hidden'); - messageContainer.classList.add('hidden'); + setTimeout(function() { + if (isSuccess) { + if (successMessage.classList) { + successMessage.classList.add('hidden'); + } else { + successMessage.className += ' hidden'; + } + } else { + if (errorMessage.classList) { + errorMessage.classList.add('hidden'); + } else { + errorMessage.className += ' hidden'; + } + } + if (messageContainer.classList) { + messageContainer.classList.add('hidden'); + } else { + messageContainer.className += ' hidden'; + } }, duration); } @@ -76,22 +126,32 @@ function showMessage(message, isSuccess = true, duration = 5000) { * @param {string} message - 错误消息内容 * @param {string} successId - 成功消息容器的ID */ -function showFormError(errorId, messageId, message, successId = '') { - const errorContainer = document.getElementById(errorId); - const errorMessage = document.getElementById(messageId); - const successContainer = successId ? document.getElementById(successId) : null; +function showFormError(errorId, messageId, message, successId) { + successId = successId || ''; + + var errorContainer = document.getElementById(errorId); + var errorMessage = document.getElementById(messageId); + var successContainer = successId ? document.getElementById(successId) : null; if (!errorContainer || !errorMessage) return; errorMessage.textContent = message; - errorContainer.classList.remove('hidden'); - - if (successContainer) { - successContainer.classList.add('hidden'); + if (errorContainer.classList) { + errorContainer.classList.remove('hidden'); + } else { + errorContainer.className = errorContainer.className.replace('hidden', ''); } - // 滚动到错误信息 - errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); + if (successContainer) { + if (successContainer.classList) { + successContainer.classList.add('hidden'); + } else { + successContainer.className += ' hidden'; + } + } + + // 滚动到错误信息,使用简单滚动方式替代smooth behavior + errorContainer.scrollIntoView(false); } /** @@ -101,41 +161,71 @@ function showFormError(errorId, messageId, message, successId = '') { * @param {string} message - 成功消息内容 * @param {string} errorId - 错误消息容器的ID */ -function showFormSuccess(successId, messageId, message, errorId = '') { - const successContainer = document.getElementById(successId); - const successMessage = document.getElementById(messageId); - const errorContainer = errorId ? document.getElementById(errorId) : null; +function showFormSuccess(successId, messageId, message, errorId) { + errorId = errorId || ''; + + var successContainer = document.getElementById(successId); + var successMessage = document.getElementById(messageId); + var errorContainer = errorId ? document.getElementById(errorId) : null; if (!successContainer || !successMessage) return; successMessage.textContent = message; - successContainer.classList.remove('hidden'); - - if (errorContainer) { - errorContainer.classList.add('hidden'); + if (successContainer.classList) { + successContainer.classList.remove('hidden'); + } else { + successContainer.className = successContainer.className.replace('hidden', ''); } - // 滚动到成功信息 - successContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); + if (errorContainer) { + if (errorContainer.classList) { + errorContainer.classList.add('hidden'); + } else { + errorContainer.className += ' hidden'; + } + } + + // 滚动到成功信息,使用简单滚动方式替代smooth behavior + successContainer.scrollIntoView(false); } /** - * 发起API请求的通用函数 + * 发起API请求的通用函数 - 使用Promise替代async/await以提高兼容性 * @param {string} url - 请求URL * @param {string} method - 请求方法 * @param {object} data - 请求数据 * @param {object} options - 附加选项 * @returns {Promise} - 返回Promise对象 */ -async function apiRequest(url, method = 'GET', data = null, options = {}) { - const defaultOptions = { +function apiRequest(url, method, data, options) { + // 为参数设置默认值,不使用ES6默认参数 + method = method || 'GET'; + data = data || null; + options = options || {}; + + // 手动合并选项,不使用扩展运算符 + var defaultOptions = { headers: { 'Content-Type': 'application/json' }, - method, - ...options + method: method }; + // 手动合并options + for (var key in options) { + if (options.hasOwnProperty(key)) { + if (key === 'headers' && options.headers) { + for (var headerKey in options.headers) { + if (options.headers.hasOwnProperty(headerKey)) { + defaultOptions.headers[headerKey] = options.headers[headerKey]; + } + } + } else { + defaultOptions[key] = options[key]; + } + } + } + if (data) { // 根据Content-Type处理数据 if (defaultOptions.headers['Content-Type'] === 'application/json') { @@ -145,13 +235,23 @@ async function apiRequest(url, method = 'GET', data = null, options = {}) { } } - try { - const response = await fetch(url, defaultOptions); - return await response.json(); - } catch (error) { - console.error(`API请求错误 [${url}]:`, error); - throw error; - } + // 使用Promise替代async/await + return new Promise(function(resolve, reject) { + fetch(url, defaultOptions) + .then(function(response) { + if (!response.ok) { + throw new Error('网络错误'); + } + return response.json(); + }) + .then(function(data) { + resolve(data); + }) + .catch(function(error) { + console.error('API请求错误 [' + url + ']:', error); + reject(error); + }); + }); } /** @@ -159,7 +259,7 @@ async function apiRequest(url, method = 'GET', data = null, options = {}) { * @param {string} formId - 表单ID */ function resetForm(formId) { - const form = document.getElementById(formId); + var form = document.getElementById(formId); if (form) { form.reset(); } @@ -171,44 +271,61 @@ function resetForm(formId) { * @param {object} data - 表单数据对象 */ function fillForm(formId, data) { - const form = document.getElementById(formId); + var form = document.getElementById(formId); if (!form || !data) return; - Object.keys(data).forEach(key => { - const element = form.elements[key] || document.getElementById(key); - if (element) { - element.value = data[key] || ''; + // 使用传统的for-in循环替代forEach + for (var key in data) { + if (data.hasOwnProperty(key)) { + var element = form.elements[key] || document.getElementById(key); + if (element) { + element.value = data[key] || ''; + } } - }); + } } /** - * 防抖函数 + * 防抖函数 - 使用传统函数替代箭头函数 * @param {Function} func - 要防抖的函数 * @param {number} delay - 延迟时间(毫秒) * @returns {Function} - 返回防抖后的函数 */ function debounce(func, delay) { - let timeoutId; - return function(...args) { + var timeoutId; + var that = this; + + return function() { + var context = this; + var args = arguments; + clearTimeout(timeoutId); - timeoutId = setTimeout(() => func.apply(this, args), delay); + timeoutId = setTimeout(function() { + func.apply(context || that, args); + }, delay); }; } /** - * 节流函数 + * 节流函数 - 使用传统函数替代箭头函数 * @param {Function} func - 要节流的函数 * @param {number} limit - 时间限制(毫秒) * @returns {Function} - 返回节流后的函数 */ function throttle(func, limit) { - let inThrottle; - return function(...args) { + var inThrottle; + var that = this; + + return function() { + var context = this; + var args = arguments; + if (!inThrottle) { - func.apply(this, args); + func.apply(context || that, args); inThrottle = true; - setTimeout(() => inThrottle = false, limit); + setTimeout(function() { + inThrottle = false; + }, limit); } }; } \ No newline at end of file diff --git a/templates/js/utils.js b/templates/js/utils.js deleted file mode 100644 index efee893..0000000 --- a/templates/js/utils.js +++ /dev/null @@ -1,214 +0,0 @@ -// 通用工具函数 - -/** - * 显示模态框 - * @param {string} modalId - 模态框的ID - */ -function showModal(modalId) { - const modal = document.getElementById(modalId); - if (modal) { - modal.classList.remove('hidden'); - } -} - -/** - * 关闭模态框 - * @param {string} modalId - 模态框的ID - */ -function closeModal(modalId) { - const modal = document.getElementById(modalId); - if (modal) { - modal.classList.add('hidden'); - } -} - -/** - * 绑定模态框点击外部关闭事件 - * @param {string} modalId - 模态框的ID - */ -function bindModalOutsideClick(modalId) { - const modal = document.getElementById(modalId); - if (modal) { - modal.addEventListener('click', function(e) { - if (e.target === this) { - closeModal(modalId); - } - }); - } -} - -/** - * 显示消息提示 - * @param {string} message - 消息内容 - * @param {boolean} isSuccess - 是否为成功消息 - * @param {number} duration - 消息显示时长(毫秒) - */ -function showMessage(message, isSuccess = true, duration = 5000) { - const messageContainer = document.getElementById('messageContainer'); - const successMessage = document.getElementById('successMessage'); - const errorMessage = document.getElementById('errorMessage'); - - if (!messageContainer || !successMessage || !errorMessage) return; - - if (isSuccess) { - successMessage.querySelector('span').textContent = message; - successMessage.classList.remove('hidden'); - errorMessage.classList.add('hidden'); - } else { - errorMessage.querySelector('span').textContent = message; - errorMessage.classList.remove('hidden'); - successMessage.classList.add('hidden'); - } - - messageContainer.classList.remove('hidden'); - - // 自动隐藏 - setTimeout(() => { - (isSuccess ? successMessage : errorMessage).classList.add('hidden'); - messageContainer.classList.add('hidden'); - }, duration); -} - -/** - * 显示表单错误消息 - * @param {string} errorId - 错误容器的ID - * @param {string} messageId - 错误消息的ID - * @param {string} message - 错误消息内容 - * @param {string} successId - 成功消息容器的ID - */ -function showFormError(errorId, messageId, message, successId = '') { - const errorContainer = document.getElementById(errorId); - const errorMessage = document.getElementById(messageId); - const successContainer = successId ? document.getElementById(successId) : null; - - if (!errorContainer || !errorMessage) return; - - errorMessage.textContent = message; - errorContainer.classList.remove('hidden'); - - if (successContainer) { - successContainer.classList.add('hidden'); - } - - // 滚动到错误信息 - errorContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); -} - -/** - * 显示表单成功消息 - * @param {string} successId - 成功容器的ID - * @param {string} messageId - 成功消息的ID - * @param {string} message - 成功消息内容 - * @param {string} errorId - 错误消息容器的ID - */ -function showFormSuccess(successId, messageId, message, errorId = '') { - const successContainer = document.getElementById(successId); - const successMessage = document.getElementById(messageId); - const errorContainer = errorId ? document.getElementById(errorId) : null; - - if (!successContainer || !successMessage) return; - - successMessage.textContent = message; - successContainer.classList.remove('hidden'); - - if (errorContainer) { - errorContainer.classList.add('hidden'); - } - - // 滚动到成功信息 - successContainer.scrollIntoView({ behavior: 'smooth', block: 'start' }); -} - -/** - * 发起API请求的通用函数 - * @param {string} url - 请求URL - * @param {string} method - 请求方法 - * @param {object} data - 请求数据 - * @param {object} options - 附加选项 - * @returns {Promise} - 返回Promise对象 - */ -async function apiRequest(url, method = 'GET', data = null, options = {}) { - const defaultOptions = { - headers: { - 'Content-Type': 'application/json' - }, - method, - ...options - }; - - if (data) { - // 根据Content-Type处理数据 - if (defaultOptions.headers['Content-Type'] === 'application/json') { - defaultOptions.body = JSON.stringify(data); - } else if (defaultOptions.headers['Content-Type'] === 'application/x-www-form-urlencoded') { - defaultOptions.body = new URLSearchParams(data); - } - } - - try { - const response = await fetch(url, defaultOptions); - return await response.json(); - } catch (error) { - console.error(`API请求错误 [${url}]:`, error); - throw error; - } -} - -/** - * 重置表单 - * @param {string} formId - 表单ID - */ -function resetForm(formId) { - const form = document.getElementById(formId); - if (form) { - form.reset(); - } -} - -/** - * 填充表单数据 - * @param {string} formId - 表单ID - * @param {object} data - 表单数据对象 - */ -function fillForm(formId, data) { - const form = document.getElementById(formId); - if (!form || !data) return; - - Object.keys(data).forEach(key => { - const element = form.elements[key] || document.getElementById(key); - if (element) { - element.value = data[key] || ''; - } - }); -} - -/** - * 防抖函数 - * @param {Function} func - 要防抖的函数 - * @param {number} delay - 延迟时间(毫秒) - * @returns {Function} - 返回防抖后的函数 - */ -function debounce(func, delay) { - let timeoutId; - return function(...args) { - clearTimeout(timeoutId); - timeoutId = setTimeout(() => func.apply(this, args), delay); - }; -} - -/** - * 节流函数 - * @param {Function} func - 要节流的函数 - * @param {number} limit - 时间限制(毫秒) - * @returns {Function} - 返回节流后的函数 - */ -function throttle(func, limit) { - let inThrottle; - return function(...args) { - if (!inThrottle) { - func.apply(this, args); - inThrottle = true; - setTimeout(() => inThrottle = false, limit); - } - }; -} \ No newline at end of file diff --git a/templates/query.html b/templates/query.html index 707c453..2f62df2 100644 --- a/templates/query.html +++ b/templates/query.html @@ -105,7 +105,7 @@ 查询日期 车牌号 手机号 - 查询结果 + 状态 登录部门 登录IP @@ -119,12 +119,18 @@ {{ query.phone }} - {{ query.results_count }} 条记录 + {% if query.results_count == -1 %} + 待查询 + {% else %} + {{ query.results_count }} 条记录 + {% endif %} {{ query.department }} @@ -219,59 +225,90 @@ // 等待DOM加载完成 document.addEventListener('DOMContentLoaded', function() { // 获取DOM元素 - const addQueryBtn = document.getElementById('addQueryBtn'); - const addQueryModal = document.getElementById('addQueryModal'); - const closeAddModal = document.getElementById('closeAddModal'); - const cancelAddBtn = document.getElementById('cancelAddBtn'); - const addQueryForm = document.getElementById('addQueryForm'); - const addQueryLoading = document.getElementById('addQueryLoading'); - const detailModal = document.getElementById('detailModal'); - const closeModal = document.getElementById('closeModal'); - const loadingState = document.getElementById('loadingState'); - const noResultState = document.getElementById('noResultState'); - const resultList = document.getElementById('resultList'); - const carOwnerCards = document.getElementById('carOwnerCards'); + 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'); // 为查询历史记录行添加点击事件 - document.querySelectorAll('tr[data-history-id]').forEach(row => { - row.addEventListener('click', function() { - const historyId = this.getAttribute('data-history-id'); + 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); }); - }); + } // 增加查询按钮点击事件 addQueryBtn.addEventListener('click', function() { - addQueryModal.classList.remove('hidden'); + // 兼容classList API + if (addQueryModal.classList) { + addQueryModal.classList.remove('hidden'); + } else { + addQueryModal.className = addQueryModal.className.replace('hidden', ''); + } addQueryForm.reset(); }); // 关闭增加查询模态框 closeAddModal.addEventListener('click', function() { - addQueryModal.classList.add('hidden'); + // 兼容classList API + if (addQueryModal.classList) { + addQueryModal.classList.add('hidden'); + } else { + addQueryModal.className += ' hidden'; + } }); // 取消增加查询 cancelAddBtn.addEventListener('click', function() { - addQueryModal.classList.add('hidden'); + // 兼容classList API + if (addQueryModal.classList) { + addQueryModal.classList.add('hidden'); + } else { + addQueryModal.className += ' hidden'; + } }); // 点击增加查询模态框背景关闭 addQueryModal.addEventListener('click', function(e) { if (e.target === addQueryModal) { - addQueryModal.classList.add('hidden'); + // 兼容classList API + if (addQueryModal.classList) { + addQueryModal.classList.add('hidden'); + } else { + addQueryModal.className += ' hidden'; + } } }); // 关闭详情模态框 closeModal.addEventListener('click', function() { - detailModal.classList.add('hidden'); + // 兼容classList API + if (detailModal.classList) { + detailModal.classList.add('hidden'); + } else { + detailModal.className += ' hidden'; + } }); // 点击详情模态框背景关闭 detailModal.addEventListener('click', function(e) { if (e.target === detailModal) { - detailModal.classList.add('hidden'); + // 兼容classList API + if (detailModal.classList) { + detailModal.classList.add('hidden'); + } else { + detailModal.className += ' hidden'; + } } }); @@ -279,8 +316,8 @@ addQueryForm.addEventListener('submit', function(e) { e.preventDefault(); - const plateNumber = document.getElementById('add_plate_number').value.trim(); - const phone = document.getElementById('add_phone').value.trim(); + var plateNumber = document.getElementById('add_plate_number').value.trim(); + var phone = document.getElementById('add_phone').value.trim(); if (!plateNumber && !phone) { alert('请至少输入车牌号或手机号'); @@ -288,107 +325,179 @@ } // 显示加载状态 - addQueryForm.classList.add('hidden'); - addQueryLoading.classList.remove('hidden'); + // 兼容classList API + if (addQueryForm.classList) { + addQueryForm.classList.add('hidden'); + } else { + addQueryForm.className += ' hidden'; + } + if (addQueryLoading.classList) { + addQueryLoading.classList.remove('hidden'); + } else { + addQueryLoading.className = addQueryLoading.className.replace('hidden', ''); + } // 构建表单数据 - const formData = new FormData(); + var formData = new FormData(); formData.append('plate_number', plateNumber); formData.append('phone', phone); + formData.append('is_add_query', 'true'); // 发送请求到查询路由 fetch('/query', { method: 'POST', body: formData }) - .then(response => { + .then(function(response) { if (!response.ok) { throw new Error('网络错误'); } // 重新加载页面以显示新的查询记录 window.location.reload(); }) - .catch(error => { + .catch(function(error) { console.error('Error submitting query:', error); alert('提交失败,请重试'); // 恢复表单显示 - addQueryForm.classList.remove('hidden'); - addQueryLoading.classList.add('hidden'); + // 兼容classList API + if (addQueryForm.classList) { + addQueryForm.classList.remove('hidden'); + } else { + addQueryForm.className = addQueryForm.className.replace('hidden', ''); + } + if (addQueryLoading.classList) { + addQueryLoading.classList.add('hidden'); + } else { + addQueryLoading.className += ' hidden'; + } }); }); // 加载查询历史详情 function loadHistoryDetail(historyId) { // 显示模态框和加载状态 - detailModal.classList.remove('hidden'); - loadingState.classList.remove('hidden'); - noResultState.classList.add('hidden'); - resultList.classList.add('hidden'); + // 兼容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 = ''; // 发送AJAX请求获取详情 - fetch(`/query_history_detail/${historyId}`) - .then(response => response.json()) - .then(data => { + fetch('/query_history_detail/' + historyId) + .then(function(response) { + return response.json(); + }) + .then(function(data) { // 隐藏加载状态 - loadingState.classList.add('hidden'); + // 兼容classList API + if (loadingState.classList) { + loadingState.classList.add('hidden'); + } else { + loadingState.className += ' hidden'; + } if (data.error) { // 显示错误信息 noResultState.querySelector('p').textContent = data.error; - noResultState.classList.remove('hidden'); + // 兼容classList API + if (noResultState.classList) { + noResultState.classList.remove('hidden'); + } else { + noResultState.className = noResultState.className.replace('hidden', ''); + } } else if (data.car_owners && data.car_owners.length > 0) { // 显示结果列表 - resultList.classList.remove('hidden'); + // 兼容classList API + if (resultList.classList) { + resultList.classList.remove('hidden'); + } else { + resultList.className = resultList.className.replace('hidden', ''); + } // 添加车辆信息卡片 - data.car_owners.forEach(carOwner => { - const card = document.createElement('div'); + 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'; - card.innerHTML = ` -

- - 车辆信息 -

-
-
- 车牌号: - ${carOwner.plate_number} -
-
- 手机号: - ${carOwner.phone} -
-
- 车主姓名: - ${carOwner.name} -
-
- 身份证号: - ${carOwner.id_card} -
-
- 电子邮箱: - ${carOwner.email || '未提供'} -
-
- 居住地址: - ${carOwner.address || '未提供'} -
-
- `; + + // 使用字符串拼接替代模板字符串 + 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); - }); + } } else { // 显示无结果状态 - noResultState.classList.remove('hidden'); + // 兼容classList API + if (noResultState.classList) { + noResultState.classList.remove('hidden'); + } else { + noResultState.className = noResultState.className.replace('hidden', ''); + } } }) - .catch(error => { + .catch(function(error) { // 隐藏加载状态,显示错误信息 - loadingState.classList.add('hidden'); + // 兼容classList API + if (loadingState.classList) { + loadingState.classList.add('hidden'); + } else { + loadingState.className += ' hidden'; + } noResultState.querySelector('p').textContent = '加载详情时发生错误'; - noResultState.classList.remove('hidden'); + // 兼容classList API + if (noResultState.classList) { + noResultState.classList.remove('hidden'); + } else { + noResultState.className = noResultState.className.replace('hidden', ''); + } console.error('Error loading history detail:', error); }); } @@ -396,17 +505,19 @@ // 响应式处理:当窗口大小改变时,重新绑定事件(因为表格可能会重新渲染) window.addEventListener('resize', function() { // 重新绑定事件到查询历史记录行 - document.querySelectorAll('tr[data-history-id]').forEach(row => { + var rows = document.querySelectorAll('tr[data-history-id]'); + for (var i = 0; i < rows.length; i++) { + var row = rows[i]; // 先移除旧的事件监听器 - const newRow = row.cloneNode(true); + var newRow = row.cloneNode(true); row.parentNode.replaceChild(newRow, row); // 添加新的事件监听器 newRow.addEventListener('click', function() { - const historyId = this.getAttribute('data-history-id'); + var historyId = this.getAttribute('data-history-id'); loadHistoryDetail(historyId); }); - }); + } }); }); @@ -416,14 +527,33 @@ diff --git a/templates/users_management.html b/templates/users_management.html index ecf6aeb..34efb59 100644 --- a/templates/users_management.html +++ b/templates/users_management.html @@ -286,11 +286,11 @@ // 加载用户列表 function loadUsers() { - apiRequest('{{ url_for('user.get_users') }}') + apiRequest('/api/users') .then(data => { if (data.success) { - usersData = data.users; - totalUsers = usersData.length; + usersData = data.data; + totalUsers = data.total; renderUsersTable(); } else { showMessage('加载用户列表失败:' + (data.error || '未知错误'), false); @@ -377,19 +377,18 @@ const filterDepartment = document.getElementById('filterDepartment').value; const filterStatus = document.getElementById('filterStatus').value; - fetch('{{ url_for('user.get_users') }}') - .then(response => response.json()) + apiRequest('/api/users') .then(data => { if (data.success) { // 应用过滤条件 - usersData = data.users.filter(user => { + usersData = data.data.filter(user => { const matchesUsername = user.username.toLowerCase().includes(searchUsername); const matchesDepartment = !filterDepartment || user.department === filterDepartment; const matchesStatus = !filterStatus || user.status === filterStatus; return matchesUsername && matchesDepartment && matchesStatus; }); - totalUsers = usersData.length; + totalUsers = data.total; currentPage = 1; // 重置到第一页 renderUsersTable(); } else { diff --git a/update_database.py b/update_database.py index 5a55f2c..227fb2b 100644 --- a/update_database.py +++ b/update_database.py @@ -1,10 +1,13 @@ -import sqlite3 +import datetime + +# 导入数据库工具 +from db_utils import get_db_connection, close_db_connection # 连接到数据库并确保users表结构正确 def update_users_table(): try: # 连接数据库 - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() # 检查users表的当前结构 @@ -42,7 +45,10 @@ def update_users_table(): print("已更新admin用户信息") # 提交更改 - conn.commit() + try: + conn.commit() + except Exception as e: + print(f"提交更改时出错: {e}") # 再次验证表结构 cursor.execute("PRAGMA table_info(users)") @@ -59,14 +65,69 @@ def update_users_table(): print(f"ID: {admin_user[0]}, 用户名: {admin_user[1]}, 姓名: {admin_user[2]}, 电话: {admin_user[3]}, 部门: {admin_user[4]}") # 关闭连接 - conn.close() + close_db_connection(conn) print("\n数据库更新完成") except Exception as e: print(f"更新数据库时出错: {str(e)}") # 确保在异常情况下也关闭连接 - if 'conn' in locals(): - conn.close() + if 'conn' in locals() and conn: + close_db_connection(conn) +# 更新数据库中的数据 +def update_database(): + try: + # 连接数据库 + conn = get_db_connection() + cursor = conn.cursor() + + # 检查是否存在测试数据表 + cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='test_data'") + if not cursor.fetchone(): + # 如果不存在,创建测试数据表 + cursor.execute(''' + CREATE TABLE test_data ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + value TEXT, + create_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + update_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP + ) + ''') + conn.commit() + print("测试数据表创建成功") + + # 检查是否有测试数据,如果没有则插入 + cursor.execute("SELECT COUNT(*) FROM test_data") + count = cursor.fetchone()[0] + + if count == 0: + # 插入测试数据 + test_data = [ + ('测试数据1', 'value1', datetime.datetime.now()), + ('测试数据2', 'value2', datetime.datetime.now()), + ('测试数据3', 'value3', datetime.datetime.now()) + ] + + for data in test_data: + cursor.execute( + "INSERT INTO test_data (name, value, create_time, update_time) VALUES (?, ?, ?, ?)", + data + ) + conn.commit() + print("测试数据插入成功") + + # 关闭数据库连接 + close_db_connection(conn) + return True, "数据库更新成功" + + except Exception as e: + # 确保在异常情况下也关闭连接 + if 'conn' in locals() and conn: + close_db_connection(conn) + return False, f"数据库更新失败:{str(e)}" + +# 如果直接运行此脚本,则执行更新操作 if __name__ == "__main__": - update_users_table() \ No newline at end of file + success, message = update_database() + print(message) \ No newline at end of file diff --git a/user.py b/user.py index 54ef1bb..d57374e 100644 --- a/user.py +++ b/user.py @@ -1,13 +1,15 @@ -import sqlite3 from datetime import datetime from flask import Blueprint, render_template, request, jsonify, redirect, url_for, session +# 导入数据库工具 +from db_utils import get_db_connection, close_db_connection + # 创建用户相关的蓝图 bp = Blueprint('user', __name__) # 初始化数据库(用户相关部分) def init_user_db(): - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() # 创建用户表 @@ -48,8 +50,12 @@ def init_user_db(): user ) - conn.commit() - conn.close() + try: + conn.commit() + except Exception as e: + print(f"初始化用户数据库时出错: {e}") + finally: + close_db_connection(conn) # 登录路由 @bp.route('/login', methods=['GET', 'POST']) @@ -59,7 +65,7 @@ def login(): password = request.form.get('password') # 连接数据库 - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() # 验证用户名和密码 @@ -69,7 +75,7 @@ def login(): if user: # 检查用户状态 if user[6] != 'active': - conn.close() + close_db_connection(conn) return render_template('login.html', error='用户已被禁用') # 设置会话信息 @@ -78,14 +84,12 @@ def login(): session['department'] = user[5] # 部门 session['login_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') - - - conn.close() + close_db_connection(conn) # 登录成功,跳转到查询页面 return redirect(url_for('query')) else: - conn.close() + close_db_connection(conn) return render_template('login.html', error='用户名或密码错误') return render_template('login.html') @@ -114,72 +118,31 @@ def register(): return render_template('register.html', error='密码长度至少8位,且必须包含字母和数字') # 连接数据库,检查用户名是否已存在 - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() cursor.execute("SELECT * FROM users WHERE username = ?", (username,)) if cursor.fetchone(): - conn.close() + close_db_connection(conn) return render_template('register.html', error='用户名已存在') try: + # 插入新用户 cursor.execute( "INSERT INTO users (username, password, name, phone, department) VALUES (?, ?, ?, ?, ?)", (username, password, name, phone, department) ) - conn.commit() - conn.close() - # 注册成功,跳转到登录页并显示成功消息 - return render_template('login.html', success='注册成功,请登录') + close_db_connection(conn) + + # 注册成功,跳转到登录页面 + return redirect(url_for('user.login', success='注册成功,请登录')) except Exception as e: - conn.close() - return render_template('register.html', error=f'注册失败,请重试。错误:{str(e)}') + close_db_connection(conn) + return render_template('register.html', error=f'注册失败:{str(e)}') return render_template('register.html') -# 修改密码路由 -@bp.route('/change_password', methods=['POST']) -def change_password(): - if 'username' not in session: - return jsonify({'success': False, 'error': '未登录'}), 401 - - old_password = request.form.get('old_password') - new_password = request.form.get('new_password') - confirm_password = request.form.get('confirm_password') - - # 验证参数 - if not old_password or not new_password or not confirm_password: - return jsonify({'success': False, 'error': '请填写所有必填字段'}) - - # 验证新密码和确认密码是否一致 - if new_password != confirm_password: - return jsonify({'success': False, 'error': '两次输入的新密码不一致'}) - - # 验证新密码复杂度 - if len(new_password) < 8 or not any(c.isalpha() for c in new_password) or not any(c.isdigit() for c in new_password): - return jsonify({'success': False, 'error': '新密码长度至少8位,且必须包含字母和数字'}) - - # 连接数据库,验证旧密码并更新新密码 - conn = sqlite3.connect('car_info.db') - cursor = conn.cursor() - - # 验证旧密码是否正确 - cursor.execute("SELECT password FROM users WHERE username = ?", (session['username'],)) - result = cursor.fetchone() - - if not result or result[0] != old_password: - conn.close() - return jsonify({'success': False, 'error': '旧密码不正确'}) - - # 更新密码 - cursor.execute("UPDATE users SET password = ? WHERE username = ?", (new_password, session['username'])) - - conn.commit() - conn.close() - - return jsonify({'success': True, 'message': '修改密码成功,请重新登录'}) - # 获取个人信息路由 @bp.route('/get_user_profile', methods=['GET']) def get_user_profile(): @@ -187,12 +150,12 @@ def get_user_profile(): return jsonify({'success': False, 'error': '未登录'}), 401 # 连接数据库,获取用户信息 - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],)) user = cursor.fetchone() - conn.close() + close_db_connection(conn) if not user: return jsonify({'success': False, 'error': '用户不存在'}) @@ -207,81 +170,8 @@ def get_user_profile(): return jsonify({'success': True, 'user': user_data}) -# 更新个人信息路由 -@bp.route('/update_user_profile', methods=['POST']) -def update_user_profile(): - if 'username' not in session: - return jsonify({'success': False, 'error': '未登录'}), 401 - - name = request.form.get('name', '').strip() - phone = request.form.get('phone', '').strip() - department = request.form.get('department', '').strip() - - # 验证参数 - if not name or not phone or not department: - return jsonify({'success': False, 'error': '请填写所有必填字段'}) - - # 连接数据库,更新用户信息 - conn = sqlite3.connect('car_info.db') - cursor = conn.cursor() - - try: - cursor.execute( - "UPDATE users SET name = ?, phone = ?, department = ? WHERE username = ?", - (name, phone, department, session['username']) - ) - conn.commit() - - # 获取更新后的用户信息 - cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],)) - updated_user = cursor.fetchone() - conn.close() - - # 更新会话中的部门信息 - session['department'] = department - - # 构造返回数据 - user_data = { - 'username': updated_user[0], - 'name': updated_user[1], - 'phone': updated_user[2], - 'department': updated_user[3] - } - - return jsonify({'success': True, 'message': '个人信息更新成功', 'user': user_data}) - except Exception as e: - conn.close() - return jsonify({'success': False, 'error': f'更新失败,请重试。错误:{str(e)}'}) - - - -# 登出路由 -@bp.route('/logout') -def logout(): - session.clear() - return redirect(url_for('user.login')) - -# 用户管理页面路由 -@bp.route('/users_management') -def users_management(): - if 'username' not in session: - return redirect(url_for('user.login')) - - # 只有admin用户才能访问用户管理页面 - if session['username'] != 'admin': - return redirect(url_for('query')) - - # 获取所有部门列表(用于筛选和修改) - conn = sqlite3.connect('car_info.db') - cursor = conn.cursor() - cursor.execute("SELECT DISTINCT department FROM users") - departments = [dept[0] for dept in cursor.fetchall()] - conn.close() - - return render_template('users_management.html', departments=departments) - -# 获取用户列表路由 -@bp.route('/get_users', methods=['GET']) +# 获取所有用户列表的API +@bp.route('/api/users', methods=['GET']) def get_users(): if 'username' not in session: return jsonify({'success': False, 'error': '未登录'}), 401 @@ -290,26 +180,86 @@ def get_users(): if session['username'] != 'admin': return jsonify({'success': False, 'error': '无权限访问'}), 403 - conn = sqlite3.connect('car_info.db') - cursor = conn.cursor() + # 获取URL参数中的页码、每页数量、排序字段和排序方式 + page = request.args.get('page', 1, type=int) + page_size = request.args.get('pageSize', 10, type=int) + sort_field = request.args.get('sortField', 'id') + sort_order = request.args.get('sortOrder', 'asc') + department = request.args.get('department', '') + search = request.args.get('search', '') - # 获取所有用户信息 - cursor.execute("SELECT id, username, name, department, status FROM users") - users = cursor.fetchall() - conn.close() + # 验证排序字段是否有效 + valid_sort_fields = ['id', 'username', 'name', 'phone', 'department', 'status'] + if sort_field not in valid_sort_fields: + sort_field = 'id' - # 格式化用户数据 - formatted_users = [] - for user in users: - formatted_users.append({ - 'id': user[0], - 'username': user[1], - 'name': user[2], - 'department': user[3], - 'status': user[4] + # 验证排序方式是否有效 + if sort_order not in ['asc', 'desc']: + sort_order = 'asc' + + # 计算偏移量 + offset = (page - 1) * page_size + + try: + # 连接数据库 + conn = get_db_connection() + cursor = conn.cursor() + + # 构建查询条件 + conditions = [] + params = [] + + if department: + conditions.append("department = ?") + params.append(department) + + if search: + conditions.append("(username LIKE ? OR name LIKE ? OR phone LIKE ?)") + params.extend([f'%{search}%', f'%{search}%', f'%{search}%']) + + # 构建SQL查询语句 + where_clause = " AND ".join(conditions) if conditions else "" + if where_clause: + where_clause = "WHERE " + where_clause + + # 查询用户列表 + cursor.execute( + f"SELECT id, username, name, phone, department, status FROM users {where_clause} ORDER BY {sort_field} {sort_order} LIMIT ? OFFSET ?", + params + [page_size, offset] + ) + users = cursor.fetchall() + + # 查询总记录数 + cursor.execute(f"SELECT COUNT(*) FROM users {where_clause}", params) + total = cursor.fetchone()[0] + + # 关闭数据库连接 + close_db_connection(conn) + + # 格式化用户数据 + formatted_users = [] + for user in users: + formatted_users.append({ + 'id': user[0], + 'username': user[1], + 'name': user[2], + 'phone': user[3], + 'department': user[4], + 'status': user[5] + }) + + # 返回分页结果 + return jsonify({ + 'success': True, + 'data': formatted_users, + 'total': total, + 'page': page, + 'pageSize': page_size }) - - return jsonify({'success': True, 'users': formatted_users}) + except Exception as e: + # 确保在异常情况下也关闭连接 + close_db_connection(conn) + return jsonify({'success': False, 'error': str(e)}), 500 # 更新用户状态路由 @bp.route('/update_user_status', methods=['POST']) @@ -329,29 +279,21 @@ def update_user_status(): if not user_id or not status: return jsonify({'success': False, 'error': '缺少必要参数'}) - # 验证状态值 - if status not in ['active', 'inactive']: - return jsonify({'success': False, 'error': '状态值无效'}) - # 不能禁用admin用户 - conn = sqlite3.connect('car_info.db') - cursor = conn.cursor() - cursor.execute("SELECT username FROM users WHERE id = ?", (user_id,)) - user = cursor.fetchone() - - if user and user[0] == 'admin' and status == 'inactive': - conn.close() - return jsonify({'success': False, 'error': '不能禁用管理员用户'}) + if user_id == 1: + return jsonify({'success': False, 'error': '不能修改管理员用户的状态'}) # 更新用户状态 try: + conn = get_db_connection() + cursor = conn.cursor() cursor.execute("UPDATE users SET status = ? WHERE id = ?", (status, user_id)) conn.commit() - conn.close() + close_db_connection(conn) return jsonify({'success': True, 'message': '用户状态更新成功'}) except Exception as e: - conn.close() + close_db_connection(conn) return jsonify({'success': False, 'error': f'更新失败:{str(e)}'}) # 更新用户部门路由 @@ -374,15 +316,15 @@ def update_user_department(): # 更新用户部门 try: - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() cursor.execute("UPDATE users SET department = ? WHERE id = ?", (department, user_id)) conn.commit() - conn.close() + close_db_connection(conn) return jsonify({'success': True, 'message': '部门信息更新成功'}) except Exception as e: - conn.close() + close_db_connection(conn) return jsonify({'success': False, 'error': f'更新失败:{str(e)}'}) # 重置用户密码路由 @@ -407,7 +349,7 @@ def reset_user_password(): default_password = 'default123' try: - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() # 检查用户ID和用户名是否匹配 @@ -415,26 +357,48 @@ def reset_user_password(): db_user_id = cursor.fetchone() if not db_user_id or db_user_id[0] != user_id: - conn.close() + close_db_connection(conn) return jsonify({'success': False, 'error': '用户信息不匹配'}) # 更新users表中的密码 cursor.execute("UPDATE users SET password = ? WHERE id = ?", (default_password, user_id)) - - conn.commit() - conn.close() + close_db_connection(conn) return jsonify({'success': True, 'message': '密码已初始化为:' + default_password}) except Exception as e: - conn.close() + close_db_connection(conn) return jsonify({'success': False, 'error': f'密码重置失败:{str(e)}'}) +# 登出路由 +@bp.route('/logout') +def logout(): + session.clear() + return redirect(url_for('user.login')) + +# 用户管理页面路由 +@bp.route('/users_management') +def users_management(): + if 'username' not in session: + return redirect(url_for('user.login')) + + # 只有admin用户才能访问用户管理页面 + if session['username'] != 'admin': + return redirect(url_for('query')) + + # 获取所有部门列表(用于筛选和修改) + conn = get_db_connection() + cursor = conn.cursor() + cursor.execute("SELECT DISTINCT department FROM users") + departments = [dept[0] for dept in cursor.fetchall()] + close_db_connection(conn) + + return render_template('users_management.html', departments=departments) # 创建参数配置表(如果不存在) def create_parameter_config_table(): - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() # 创建参数配置表 @@ -459,10 +423,12 @@ def create_parameter_config_table(): "INSERT INTO parameter_config (param1, param2, param3, param4, param5, update_time) VALUES (?, ?, ?, ?, ?, ?)", ('默认参数1', '默认参数2', '默认参数3', '默认参数4', '默认参数5', datetime.now().strftime('%Y-%m-%d %H:%M:%S')) ) - conn.commit() + try: + conn.commit() + except Exception as e: + print(f"插入参数配置数据时出错: {e}") - conn.close() - + close_db_connection(conn) # 获取参数配置路由 @bp.route('/get_parameter_config', methods=['GET']) @@ -470,32 +436,38 @@ def get_parameter_config(): if 'username' not in session: return jsonify({'success': False, 'error': '未登录'}), 401 + # 确保参数配置表存在 + create_parameter_config_table() + try: - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() - # 查询参数配置 - cursor.execute("SELECT param1, param2, param3, param4, param5, update_time FROM parameter_config ORDER BY update_time DESC LIMIT 1") + # 获取最新的参数配置 + cursor.execute("SELECT param1, param2, param3, param4, param5 FROM parameter_config ORDER BY update_time DESC LIMIT 1") config = cursor.fetchone() - conn.close() + close_db_connection(conn) if not config: return jsonify({'success': False, 'error': '未找到参数配置'}) - # 构造返回数据 + # 构建符合前端期望格式的响应 parameters = { 'param1': config[0], 'param2': config[1], 'param3': config[2], 'param4': config[3], 'param5': config[4], - 'update_time': config[5] + 'update_time': datetime.now().strftime('%Y-%m-%d %H:%M:%S') } - return jsonify({'success': True, 'parameters': parameters}) + return jsonify({ + 'success': True, + 'parameters': parameters + }) except Exception as e: - return jsonify({'success': False, 'error': f'获取参数配置失败:{str(e)}'}) - + close_db_connection(conn) + return jsonify({'success': False, 'error': str(e)}) # 保存参数配置路由 @bp.route('/save_parameter_config', methods=['POST']) @@ -503,64 +475,40 @@ def save_parameter_config(): if 'username' not in session: return jsonify({'success': False, 'error': '未登录'}), 401 - # 只有管理员可以修改参数配置 - if session['username'] != 'admin': - return jsonify({'success': False, 'error': '无权限修改参数配置'}), 403 + # 确保参数配置表存在 + create_parameter_config_table() - # 获取请求数据 data = request.get_json() - if not data: - return jsonify({'success': False, 'error': '请求数据格式错误'}), 400 - - param1 = data.get('param1', '').strip() - param2 = data.get('param2', '').strip() - param3 = data.get('param3', '').strip() - param4 = data.get('param4', '').strip() - param5 = data.get('param5', '').strip() - - # 验证参数 - if not param1: - return jsonify({'success': False, 'error': '参数1不能为空'}) + param1 = data.get('param1') + param2 = data.get('param2') + param3 = data.get('param3') + param4 = data.get('param4') + param5 = data.get('param5') try: - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() - # 更新参数配置 + # 插入新的参数配置记录(不更新旧记录,而是插入新记录) cursor.execute( - "UPDATE parameter_config SET param1 = ?, param2 = ?, param3 = ?, param4 = ?, param5 = ?, update_time = ? WHERE id = 1", + "INSERT INTO parameter_config (param1, param2, param3, param4, param5, update_time) VALUES (?, ?, ?, ?, ?, ?)", (param1, param2, param3, param4, param5, datetime.now().strftime('%Y-%m-%d %H:%M:%S')) ) - - # 如果没有更新任何记录,插入新记录 - if cursor.rowcount == 0: - cursor.execute( - "INSERT INTO parameter_config (param1, param2, param3, param4, param5, update_time) VALUES (?, ?, ?, ?, ?, ?)", - (param1, param2, param3, param4, param5, datetime.now().strftime('%Y-%m-%d %H:%M:%S')) - ) - conn.commit() - conn.close() + close_db_connection(conn) return jsonify({'success': True, 'message': '参数配置保存成功'}) except Exception as e: - return jsonify({'success': False, 'error': f'保存参数配置失败:{str(e)}'}) - + close_db_connection(conn) + return jsonify({'success': False, 'error': str(e)}) # 参数配置页面路由 -@bp.route('/parameter_config_page', methods=['GET', 'POST']) +@bp.route('/parameter_config_page') def parameter_config_page(): if 'username' not in session: return redirect(url_for('user.login')) - # 只有管理员可以访问参数配置页面 - if session['username'] != 'admin': - return redirect(url_for('user.user_profile_page', error='无权限访问参数配置页面')) - - # 渲染参数配置页面 - return render_template('parameter_config.html', current_year=datetime.now().year) - - + return render_template('parameter_config.html') # 修改密码页面路由 @bp.route('/change_password_page', methods=['GET', 'POST']) @@ -586,7 +534,7 @@ def change_password_page(): return render_template('change_password.html', error='新密码长度至少8位,且必须包含字母和数字') # 连接数据库,验证旧密码并更新新密码 - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() # 验证旧密码是否正确 @@ -594,20 +542,25 @@ def change_password_page(): result = cursor.fetchone() if not result or result[0] != old_password: - conn.close() + close_db_connection(conn) return render_template('change_password.html', error='旧密码不正确') # 更新密码 - cursor.execute("UPDATE users SET password = ? WHERE username = ?", (new_password, session['username'])) - conn.commit() - conn.close() - - # 密码修改成功,先执行退出功能,再跳转到登录页面 - session.clear() - return redirect(url_for('user.login', success='修改密码成功,请重新登录')) + try: + cursor.execute( + "UPDATE users SET password = ? WHERE username = ?", + (new_password, session['username']) + ) + conn.commit() + close_db_connection(conn) + + # 密码修改成功,跳转到登录页面 + return redirect(url_for('user.login', success='密码修改成功,请重新登录')) + except Exception as e: + close_db_connection(conn) + return render_template('change_password.html', error=f'密码修改失败:{str(e)}') - # GET请求,渲染修改密码页面 - return render_template('change_password.html', current_year=datetime.now().year) + return render_template('change_password.html') # 个人信息页面路由 @bp.route('/user_profile_page', methods=['GET', 'POST']) @@ -616,11 +569,11 @@ def user_profile_page(): return redirect(url_for('user.login')) # 连接数据库,获取用户信息 - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],)) user_tuple = cursor.fetchone() - conn.close() + close_db_connection(conn) if not user_tuple: return redirect(url_for('user.login', error='用户不存在')) @@ -643,7 +596,7 @@ def user_profile_page(): return render_template('user_profile.html', user=user, error='请填写所有必填字段', current_year=datetime.now().year) # 连接数据库,更新用户信息 - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() try: @@ -657,14 +610,14 @@ def user_profile_page(): session['department'] = department session['name'] = name - conn.close() + close_db_connection(conn) # 重新获取用户信息 - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() cursor.execute("SELECT username, name, phone, department FROM users WHERE username = ?", (session['username'],)) updated_user_tuple = cursor.fetchone() - conn.close() + close_db_connection(conn) # 将元组转换为字典 updated_user = { @@ -676,7 +629,7 @@ def user_profile_page(): return render_template('user_profile.html', user=updated_user, success='个人信息更新成功', current_year=datetime.now().year) except Exception as e: - conn.close() + close_db_connection(conn) return render_template('user_profile.html', user=user, error=f'更新失败,请重试。错误:{str(e)}', current_year=datetime.now().year) # GET请求,渲染个人信息页面 diff --git a/verify_database.py b/verify_database.py index ee8d24d..203a6e2 100644 --- a/verify_database.py +++ b/verify_database.py @@ -1,41 +1,69 @@ -import sqlite3 +# 导入数据库工具 +from db_utils import get_db_connection, close_db_connection -# 连接到数据库并验证users表结构 -def verify_users_table(): +# 验证数据库结构是否完整 +def verify_database_structure(): try: # 连接数据库 - conn = sqlite3.connect('car_info.db') + conn = get_db_connection() cursor = conn.cursor() - # 检查users表的当前结构 - print("验证users表结构:") - cursor.execute("PRAGMA table_info(users)") - columns = cursor.fetchall() - for column in columns: - print(f"字段名: {column[1]}, 类型: {column[2]}, 是否可空: {column[3]}") + # 需要检查的表 + tables_to_check = ['users', 'query_records'] + verification_results = [] - # 检查admin用户的信息 - print("\n验证admin用户信息:") - cursor.execute("SELECT id, username, name, phone, department FROM users WHERE username = ?", ('admin',)) - admin_user = cursor.fetchone() - if admin_user: - print(f"ID: {admin_user[0]}") - print(f"用户名: {admin_user[1]}") - print(f"姓名: {admin_user[2]}") - print(f"电话: {admin_user[3]}") - print(f"部门: {admin_user[4]}") - else: - print("未找到admin用户") + for table in tables_to_check: + # 检查表是否存在 + cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name=?", (table,)) + table_exists = cursor.fetchone() is not None + + if table_exists: + # 检查表的列结构 + cursor.execute(f"PRAGMA table_info({table})") + columns = cursor.fetchall() + column_names = [col[1] for col in columns] + + verification_results.append({ + "table": table, + "exists": True, + "column_count": len(columns), + "columns": column_names + }) + else: + verification_results.append({ + "table": table, + "exists": False, + "column_count": 0, + "columns": [] + }) - # 关闭连接 - conn.close() - print("\n数据库验证完成") + # 关闭数据库连接 + close_db_connection(conn) + # 构造返回结果 + all_tables_exist = all(result['exists'] for result in verification_results) + + return { + "success": all_tables_exist, + "results": verification_results + } except Exception as e: - print(f"验证数据库时出错: {str(e)}") # 确保在异常情况下也关闭连接 - if 'conn' in locals(): - conn.close() + if 'conn' in locals() and conn: + close_db_connection(conn) + + return { + "success": False, + "error": str(e) + } +# 如果直接运行此脚本,则执行验证操作 if __name__ == "__main__": - verify_users_table() \ No newline at end of file + result = verify_database_structure() + print(f"数据库结构验证: {'通过' if result['success'] else '失败'}") + if result['success']: + print("验证详情:") + for table_result in result['results']: + print(f" - 表 {table_result['table']}: 存在,包含 {table_result['column_count']} 个字段") + else: + print(f"错误信息: {result['error']}") \ No newline at end of file