rewrite
This commit is contained in:
1 parent
0603c473fa
commit
03d7b07bf6
5 files changed
+334
-231
No files matched your search
+135
-41
@@ -1,57 +1,151 @@
|
||||
import sqlite3
|
||||
import os
|
||||
from flask import Flask, request, jsonify, g
|
||||
from sqlalchemy import (
|
||||
create_engine, MetaData, Table, select, insert, update, delete, text
|
||||
)
|
||||
from sqlalchemy.exc import SQLAlchemyError
|
||||
from waitress import serve
|
||||
|
||||
app = Flask(__name__)
|
||||
app.config['DATABASE'] = 'database.db'
|
||||
|
||||
def get_db():
|
||||
if 'db' not in g:
|
||||
conn = sqlite3.connect(app.config['DATABASE'])
|
||||
conn.row_factory = sqlite3.Row
|
||||
g.db = conn
|
||||
return g.db
|
||||
# 数据库配置
|
||||
DATABASE_URL = os.environ.get('DATABASE_URL', 'sqlite:///database.db')
|
||||
engine = create_engine(DATABASE_URL)
|
||||
|
||||
@app.teardown_appcontext
|
||||
def close_db(exception):
|
||||
db = g.pop('db', None)
|
||||
if db is not None:
|
||||
db.close()
|
||||
# 缓存反射的元数据(避免每次请求都反射)
|
||||
metadata = MetaData()
|
||||
|
||||
def get_table(table_name):
|
||||
"""反射表对象(若已缓存则直接返回)"""
|
||||
if table_name not in metadata.tables:
|
||||
Table(table_name, metadata, autoload_with=engine)
|
||||
return metadata.tables[table_name]
|
||||
|
||||
def build_where(table, conditions):
|
||||
"""将条件字典转换为 SQLAlchemy 条件表达式"""
|
||||
if not conditions:
|
||||
return None
|
||||
clauses = []
|
||||
for col, val in conditions.items():
|
||||
column = table.columns[col]
|
||||
if isinstance(val, dict):
|
||||
# 支持多种比较操作
|
||||
if 'eq' in val:
|
||||
clauses.append(column == val['eq'])
|
||||
if 'ne' in val:
|
||||
clauses.append(column != val['ne'])
|
||||
if 'gt' in val:
|
||||
clauses.append(column > val['gt'])
|
||||
if 'lt' in val:
|
||||
clauses.append(column < val['lt'])
|
||||
if 'ge' in val:
|
||||
clauses.append(column >= val['ge'])
|
||||
if 'le' in val:
|
||||
clauses.append(column <= val['le'])
|
||||
if 'like' in val:
|
||||
clauses.append(column.like(val['like']))
|
||||
if 'in' in val:
|
||||
clauses.append(column.in_(val['in']))
|
||||
else:
|
||||
# 直接等值比较
|
||||
clauses.append(column == val)
|
||||
from sqlalchemy import and_
|
||||
return and_(*clauses)
|
||||
|
||||
@app.route('/execute', methods=['POST'])
|
||||
def execute_sql():
|
||||
def execute_structured():
|
||||
data = request.get_json()
|
||||
if not data or 'sql' not in data:
|
||||
return jsonify({'success': False, 'error': '缺少 sql 字段'}), 400
|
||||
if not data:
|
||||
return jsonify({'success': False, 'error': '请求体不能为空'}), 400
|
||||
|
||||
sql = data['sql'].strip()
|
||||
params = data.get('params', [])
|
||||
|
||||
# 参数必须是列表、元组或字典
|
||||
if not isinstance(params, (list, tuple, dict)):
|
||||
params = []
|
||||
action = data.get('action', 'select')
|
||||
table_name = data.get('table')
|
||||
if not table_name:
|
||||
return jsonify({'success': False, 'error': '缺少 table 字段'}), 400
|
||||
|
||||
try:
|
||||
db = get_db()
|
||||
cur = db.execute(sql, params)
|
||||
db.commit()
|
||||
|
||||
try:
|
||||
rows = cur.fetchall()
|
||||
result = [dict(row) for row in rows]
|
||||
except Exception:
|
||||
result = []
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'data': result
|
||||
})
|
||||
table = get_table(table_name)
|
||||
except Exception as e:
|
||||
db = g.get('db')
|
||||
if db:
|
||||
db.rollback()
|
||||
return jsonify({'success': False, 'error': f'表加载失败: {str(e)}'}), 400
|
||||
|
||||
conn = None
|
||||
try:
|
||||
conn = engine.connect()
|
||||
result = []
|
||||
if action == 'select':
|
||||
columns = data.get('columns', None)
|
||||
if columns:
|
||||
cols = [table.columns[c] for c in columns]
|
||||
else:
|
||||
cols = [table] # select *
|
||||
|
||||
stmt = select(*cols)
|
||||
where_cond = build_where(table, data.get('where'))
|
||||
if where_cond is not None:
|
||||
stmt = stmt.where(where_cond)
|
||||
|
||||
# 排序
|
||||
order_by = data.get('order_by')
|
||||
if order_by:
|
||||
for item in order_by:
|
||||
col = table.columns[item['column']]
|
||||
stmt = stmt.order_by(col.desc() if item.get('desc') else col.asc())
|
||||
|
||||
# 分页
|
||||
limit = data.get('limit')
|
||||
if limit is not None:
|
||||
stmt = stmt.limit(limit)
|
||||
offset = data.get('offset')
|
||||
if offset is not None:
|
||||
stmt = stmt.offset(offset)
|
||||
|
||||
res = conn.execute(stmt)
|
||||
rows = res.fetchall()
|
||||
result = [dict(row._mapping) for row in rows]
|
||||
|
||||
elif action == 'insert':
|
||||
values = data.get('values', {})
|
||||
stmt = insert(table).values(**values)
|
||||
res = conn.execute(stmt)
|
||||
conn.commit()
|
||||
result = [{'lastrowid': res.lastrowid}] if hasattr(res, 'lastrowid') else []
|
||||
|
||||
elif action == 'update':
|
||||
values = data.get('values', {})
|
||||
where_cond = build_where(table, data.get('where'))
|
||||
stmt = update(table).values(**values)
|
||||
if where_cond is not None:
|
||||
stmt = stmt.where(where_cond)
|
||||
res = conn.execute(stmt)
|
||||
conn.commit()
|
||||
result = [{'rowcount': res.rowcount}]
|
||||
|
||||
elif action == 'delete':
|
||||
where_cond = build_where(table, data.get('where'))
|
||||
stmt = delete(table)
|
||||
if where_cond is not None:
|
||||
stmt = stmt.where(where_cond)
|
||||
res = conn.execute(stmt)
|
||||
conn.commit()
|
||||
result = [{'rowcount': res.rowcount}]
|
||||
|
||||
else:
|
||||
return jsonify({'success': False, 'error': f'不支持的操作: {action}'}), 400
|
||||
|
||||
return jsonify({'success': True, 'data': result})
|
||||
|
||||
except SQLAlchemyError as e:
|
||||
if conn:
|
||||
conn.rollback()
|
||||
return jsonify({'success': False, 'error': str(e)}), 400
|
||||
except Exception as e:
|
||||
if conn:
|
||||
conn.rollback()
|
||||
return jsonify({'success': False, 'error': str(e)}), 400
|
||||
finally:
|
||||
if conn:
|
||||
conn.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
# 单线程单进程,绑定 127.0.0.1:8005
|
||||
serve(app, host='127.0.0.1', port=8005, threads=1)
|
||||
# 单线程单进程,绑定 127.0.0.1
|
||||
serve(app, host='127.0.0.1', port=8005, threads=1)
|
||||
Reference in new issue
Block a user