frontend.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744
  1. import logging
  2. import os
  3. import sqlite3
  4. import uuid
  5. from io import BytesIO
  6. import pandas as pd
  7. from flask import Blueprint, request, jsonify, send_from_directory, current_app, send_file, session
  8. from sqlalchemy import text, select, MetaData, Table
  9. from sqlalchemy.orm import sessionmaker
  10. from werkzeug.security import check_password_hash, generate_password_hash
  11. from werkzeug.utils import secure_filename
  12. from app import db
  13. from .database_models import Models
  14. # 配置日志
  15. logging.basicConfig(level=logging.DEBUG)
  16. logger = logging.getLogger(__name__)
  17. # 创建蓝图 (Blueprint),用于分离路由
  18. bp = Blueprint('frontend', __name__)
  19. # 封装数据库连接函数
  20. def get_db_connection():
  21. return sqlite3.connect('software_intro.db')
  22. # 密码加密
  23. def hash_password(password):
  24. return generate_password_hash(password)
  25. def get_db():
  26. """ 获取数据库连接 """
  27. return sqlite3.connect(current_app.config['DATABASE'])
  28. # 定义添加数据库记录的 API 接口
  29. @bp.route('/add_item', methods=['POST'])
  30. def add_item():
  31. """
  32. 接收 JSON 格式的请求体,包含表名和要插入的数据。
  33. 尝试将数据插入到指定的表中,并进行字段查重。
  34. :return:
  35. """
  36. try:
  37. # 确保请求体是 JSON 格式
  38. data = request.get_json()
  39. if not data:
  40. raise ValueError("No JSON data provided")
  41. table_name = data.get('table')
  42. item_data = data.get('item')
  43. if not table_name or not item_data:
  44. return jsonify({'error': 'Missing table name or item data'}), 400
  45. # 定义各个表的字段查重规则
  46. duplicate_check_rules = {
  47. 'users': ['email', 'username'],
  48. 'products': ['product_code'],
  49. 'current_reduce': ['Q_over_b', 'pH', 'OM', 'CL', 'H', 'Al'],
  50. 'current_reflux': ['OM', 'CL', 'CEC', 'H_plus', 'N', 'Al3_plus', 'Delta_pH'],
  51. # 其他表和规则
  52. }
  53. # 获取该表的查重字段
  54. duplicate_columns = duplicate_check_rules.get(table_name)
  55. if not duplicate_columns:
  56. return jsonify({'error': 'No duplicate check rule for this table'}), 400
  57. # 动态构建查询条件,逐一检查是否有重复数据
  58. condition = ' AND '.join([f"{column} = :{column}" for column in duplicate_columns])
  59. duplicate_query = f"SELECT 1 FROM {table_name} WHERE {condition} LIMIT 1"
  60. result = db.session.execute(text(duplicate_query), item_data).fetchone()
  61. if result:
  62. return jsonify({'error': '重复数据,已有相同的数据项存在。'}), 409
  63. # 动态构建 SQL 语句,进行插入操作
  64. columns = ', '.join(item_data.keys())
  65. placeholders = ', '.join([f":{key}" for key in item_data.keys()])
  66. sql = f"INSERT INTO {table_name} ({columns}) VALUES ({placeholders})"
  67. # 直接执行插入操作,无需显式的事务管理
  68. db.session.execute(text(sql), item_data)
  69. # 提交事务
  70. db.session.commit()
  71. # 返回成功响应
  72. return jsonify({'success': True, 'message': 'Item added successfully'}), 201
  73. except ValueError as e:
  74. return jsonify({'error': str(e)}), 400
  75. except KeyError as e:
  76. return jsonify({'error': f'Missing data field: {e}'}), 400
  77. except sqlite3.IntegrityError as e:
  78. return jsonify({'error': '数据库完整性错误', 'details': str(e)}), 409
  79. except sqlite3.Error as e:
  80. return jsonify({'error': '数据库错误', 'details': str(e)}), 500
  81. @bp.route('/delete_item', methods=['POST'])
  82. def delete_item():
  83. """
  84. 删除数据库记录的 API 接口
  85. """
  86. data = request.get_json()
  87. table_name = data.get('table')
  88. condition = data.get('condition')
  89. # 检查表名和条件是否提供
  90. if not table_name or not condition:
  91. return jsonify({
  92. "success": False,
  93. "message": "缺少表名或条件参数"
  94. }), 400
  95. # 尝试从条件字符串中解析键和值
  96. try:
  97. key, value = condition.split('=')
  98. key = key.strip() # 去除多余的空格
  99. value = value.strip().strip("'\"") # 去除多余的空格和引号
  100. except ValueError:
  101. return jsonify({
  102. "success": False,
  103. "message": "条件格式错误,应为 'key=value'"
  104. }), 400
  105. # 准备 SQL 删除语句
  106. sql = f"DELETE FROM {table_name} WHERE {key} = :value"
  107. try:
  108. # 使用 SQLAlchemy 执行删除
  109. with db.session.begin():
  110. result = db.session.execute(text(sql), {"value": value})
  111. # 检查是否有记录被删除
  112. if result.rowcount == 0:
  113. return jsonify({
  114. "success": False,
  115. "message": "未找到符合条件的记录"
  116. }), 404
  117. return jsonify({
  118. "success": True,
  119. "message": "记录删除成功"
  120. }), 200
  121. except Exception as e:
  122. return jsonify({
  123. "success": False,
  124. "message": f"删除失败: {e}"
  125. }), 500
  126. # 定义修改数据库记录的 API 接口
  127. @bp.route('/update_item', methods=['PUT'])
  128. def update_record():
  129. """
  130. 接收 JSON 格式的请求体,包含表名和更新的数据。
  131. 尝试更新指定的记录。
  132. """
  133. data = request.get_json()
  134. # 检查必要的数据是否提供
  135. if not data or 'table' not in data or 'item' not in data:
  136. return jsonify({
  137. "success": False,
  138. "message": "请求数据不完整"
  139. }), 400
  140. table_name = data['table']
  141. item = data['item']
  142. # 假设 item 的第一个键是 ID
  143. id_key = next(iter(item.keys())) # 获取第一个键
  144. record_id = item.get(id_key)
  145. if not record_id:
  146. return jsonify({
  147. "success": False,
  148. "message": "缺少记录 ID"
  149. }), 400
  150. # 获取更新的字段和值
  151. updates = {key: value for key, value in item.items() if key != id_key}
  152. if not updates:
  153. return jsonify({
  154. "success": False,
  155. "message": "没有提供需要更新的字段"
  156. }), 400
  157. # 动态构建 SQL
  158. set_clause = ', '.join([f"{key} = :{key}" for key in updates.keys()])
  159. sql = f"UPDATE {table_name} SET {set_clause} WHERE {id_key} = :id_value"
  160. # 添加 ID 到参数
  161. updates['id_value'] = record_id
  162. try:
  163. # 使用 SQLAlchemy 执行更新
  164. with db.session.begin():
  165. result = db.session.execute(text(sql), updates)
  166. # 检查是否有更新的记录
  167. if result.rowcount == 0:
  168. return jsonify({
  169. "success": False,
  170. "message": "未找到要更新的记录"
  171. }), 404
  172. return jsonify({
  173. "success": True,
  174. "message": "数据更新成功"
  175. }), 200
  176. except Exception as e:
  177. # 捕获所有异常并返回
  178. return jsonify({
  179. "success": False,
  180. "message": f"更新失败: {str(e)}"
  181. }), 500
  182. # 定义查询数据库记录的 API 接口
  183. @bp.route('/search/record', methods=['GET'])
  184. def sql_search():
  185. """
  186. 接收 JSON 格式的请求体,包含表名和要查询的 ID。
  187. 尝试查询指定 ID 的记录并返回结果。
  188. :return:
  189. """
  190. try:
  191. data = request.get_json()
  192. # 表名
  193. sql_table = data['table']
  194. # 要搜索的 ID
  195. Id = data['id']
  196. # 连接到数据库
  197. cur = db.cursor()
  198. # 构造查询语句
  199. sql = f"SELECT * FROM {sql_table} WHERE id = ?"
  200. # 执行查询
  201. cur.execute(sql, (Id,))
  202. # 获取查询结果
  203. rows = cur.fetchall()
  204. column_names = [desc[0] for desc in cur.description]
  205. # 检查是否有结果
  206. if not rows:
  207. return jsonify({'error': '未查找到对应数据。'}), 400
  208. # 构造响应数据
  209. results = []
  210. for row in rows:
  211. result = {column_names[i]: row[i] for i in range(len(row))}
  212. results.append(result)
  213. # 关闭游标和数据库连接
  214. cur.close()
  215. db.close()
  216. # 返回 JSON 响应
  217. return jsonify(results), 200
  218. except sqlite3.Error as e:
  219. # 如果发生数据库错误,返回错误信息
  220. return jsonify({'error': str(e)}), 400
  221. except KeyError as e:
  222. # 如果请求数据中缺少必要的键,返回错误信息
  223. return jsonify({'error': f'缺少必要的数据字段: {e}'}), 400
  224. # 更新用户信息接口
  225. @bp.route('/update_user', methods=['POST'])
  226. def update_user():
  227. # 获取前端传来的数据
  228. data = request.get_json()
  229. # 打印收到的请求数据
  230. current_app.logger.info(f"Received data: {data}")
  231. user_id = data.get('userId') # 用户ID
  232. name = data.get('name') # 用户名
  233. old_password = data.get('oldPassword') # 旧密码
  234. new_password = data.get('newPassword') # 新密码
  235. logger.info(f"Update request received: user_id={user_id}, name={name}")
  236. # 校验传入的用户名和密码是否为空
  237. if not name or not old_password:
  238. logger.warning("用户名和旧密码不能为空")
  239. return jsonify({"success": False, "message": "用户名和旧密码不能为空"}), 400
  240. # 新密码和旧密码不能相同
  241. if new_password and old_password == new_password:
  242. logger.warning(f"新密码与旧密码相同:{name}")
  243. return jsonify({"success": False, "message": "新密码与旧密码不能相同"}), 400
  244. try:
  245. # 查询数据库验证用户ID
  246. query = "SELECT * FROM users WHERE id = :user_id"
  247. conn = get_db()
  248. user = conn.execute(query, {"user_id": user_id}).fetchone()
  249. if not user:
  250. logger.warning(f"用户ID '{user_id}' 不存在")
  251. return jsonify({"success": False, "message": "用户不存在"}), 400
  252. # 获取数据库中存储的密码(假设密码是哈希存储的)
  253. stored_password = user[2] # 假设密码存储在数据库的第三列
  254. # 校验旧密码是否正确
  255. if not check_password_hash(stored_password, old_password):
  256. logger.warning(f"旧密码错误:{name}")
  257. return jsonify({"success": False, "message": "旧密码错误"}), 400
  258. # 如果新密码非空,则更新新密码
  259. if new_password:
  260. hashed_new_password = hash_password(new_password)
  261. update_query = "UPDATE users SET password = :new_password WHERE id = :user_id"
  262. conn.execute(update_query, {"new_password": hashed_new_password, "user_id": user_id})
  263. conn.commit()
  264. logger.info(f"User ID '{user_id}' password updated successfully.")
  265. # 如果用户名发生更改,则更新用户名
  266. if name != user[1]:
  267. update_name_query = "UPDATE users SET name = :new_name WHERE id = :user_id"
  268. conn.execute(update_name_query, {"new_name": name, "user_id": user_id})
  269. conn.commit()
  270. logger.info(f"User ID '{user_id}' name updated to '{name}' successfully.")
  271. return jsonify({"success": True, "message": "用户信息更新成功"})
  272. except Exception as e:
  273. # 记录错误日志并返回错误信息
  274. logger.error(f"Error updating user: {e}", exc_info=True)
  275. return jsonify({"success": False, "message": "更新失败"}), 500
  276. # 注册用户
  277. @bp.route('/register', methods=['POST'])
  278. def register_user():
  279. # 获取前端传来的数据
  280. data = request.get_json()
  281. name = data.get('name') # 用户名
  282. password = data.get('password') # 密码
  283. logger.info(f"Register request received: name={name}")
  284. # 检查用户名和密码是否为空
  285. if not name or not password:
  286. logger.warning("用户名和密码不能为空")
  287. return jsonify({"success": False, "message": "用户名和密码不能为空"}), 400
  288. # 动态获取数据库表的列名
  289. columns = get_column_names('users')
  290. logger.info(f"Database columns for 'users' table: {columns}")
  291. # 检查前端传来的数据是否包含数据库表中所有的必填字段
  292. for column in ['name', 'password']:
  293. if column not in columns:
  294. logger.error(f"缺少必填字段:{column}")
  295. return jsonify({"success": False, "message": f"缺少必填字段:{column}"}), 400
  296. # 对密码进行哈希处理
  297. hashed_password = hash_password(password)
  298. logger.info(f"Password hashed for user: {name}")
  299. # 插入到数据库
  300. try:
  301. # 检查用户是否已经存在
  302. query = "SELECT * FROM users WHERE name = :name"
  303. conn = get_db()
  304. user = conn.execute(query, {"name": name}).fetchone()
  305. if user:
  306. logger.warning(f"用户名 '{name}' 已存在")
  307. return jsonify({"success": False, "message": "用户名已存在"}), 400
  308. # 向数据库插入数据
  309. query = "INSERT INTO users (name, password) VALUES (:name, :password)"
  310. conn.execute(query, {"name": name, "password": hashed_password})
  311. conn.commit()
  312. logger.info(f"User '{name}' registered successfully.")
  313. return jsonify({"success": True, "message": "注册成功"})
  314. except Exception as e:
  315. # 记录错误日志并返回错误信息
  316. logger.error(f"Error registering user: {e}", exc_info=True)
  317. return jsonify({"success": False, "message": "注册失败"}), 500
  318. def get_column_names(table_name):
  319. """
  320. 动态获取数据库表的列名。
  321. """
  322. try:
  323. conn = get_db()
  324. query = f"PRAGMA table_info({table_name});"
  325. result = conn.execute(query).fetchall()
  326. conn.close()
  327. return [row[1] for row in result] # 第二列是列名
  328. except Exception as e:
  329. logger.error(f"Error getting column names for table {table_name}: {e}", exc_info=True)
  330. return []
  331. # 导出数据
  332. @bp.route('/export_data', methods=['GET'])
  333. def export_data():
  334. table_name = request.args.get('table')
  335. file_format = request.args.get('format', 'excel').lower()
  336. if not table_name:
  337. return jsonify({'error': '缺少表名参数'}), 400
  338. if not table_name.isidentifier():
  339. return jsonify({'error': '无效的表名'}), 400
  340. try:
  341. conn = get_db()
  342. query = "SELECT name FROM sqlite_master WHERE type='table' AND name=?;"
  343. table_exists = conn.execute(query, (table_name,)).fetchone()
  344. if not table_exists:
  345. return jsonify({'error': f"表 {table_name} 不存在"}), 404
  346. query = f"SELECT * FROM {table_name};"
  347. df = pd.read_sql(query, conn)
  348. output = BytesIO()
  349. if file_format == 'csv':
  350. df.to_csv(output, index=False, encoding='utf-8')
  351. output.seek(0)
  352. return send_file(output, as_attachment=True, download_name=f'{table_name}_data.csv', mimetype='text/csv')
  353. elif file_format == 'excel':
  354. df.to_excel(output, index=False, engine='openpyxl')
  355. output.seek(0)
  356. return send_file(output, as_attachment=True, download_name=f'{table_name}_data.xlsx',
  357. mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
  358. else:
  359. return jsonify({'error': '不支持的文件格式,仅支持 CSV 和 Excel'}), 400
  360. except Exception as e:
  361. logger.error(f"Error in export_data: {e}", exc_info=True)
  362. return jsonify({'error': str(e)}), 500
  363. # 导入数据接口
  364. @bp.route('/import_data', methods=['POST'])
  365. def import_data():
  366. logger.debug("Import data endpoint accessed.")
  367. if 'file' not in request.files:
  368. logger.error("No file in request.")
  369. return jsonify({'success': False, 'message': '文件缺失'}), 400
  370. file = request.files['file']
  371. table_name = request.form.get('table')
  372. if not table_name:
  373. logger.error("Missing table name parameter.")
  374. return jsonify({'success': False, 'message': '缺少表名参数'}), 400
  375. if file.filename == '':
  376. logger.error("No file selected.")
  377. return jsonify({'success': False, 'message': '未选择文件'}), 400
  378. try:
  379. # 保存文件到临时路径
  380. temp_path = os.path.join(current_app.config['UPLOAD_FOLDER'], secure_filename(file.filename))
  381. file.save(temp_path)
  382. logger.debug(f"File saved to temporary path: {temp_path}")
  383. # 根据文件类型读取文件
  384. if file.filename.endswith('.xlsx'):
  385. df = pd.read_excel(temp_path)
  386. elif file.filename.endswith('.csv'):
  387. df = pd.read_csv(temp_path)
  388. else:
  389. logger.error("Unsupported file format.")
  390. return jsonify({'success': False, 'message': '仅支持 Excel 和 CSV 文件'}), 400
  391. # 获取数据库列名
  392. db_columns = get_column_names(table_name)
  393. if 'id' in db_columns:
  394. db_columns.remove('id') # 假设 id 列是自增的,不需要处理
  395. if not set(db_columns).issubset(set(df.columns)):
  396. logger.error(f"File columns do not match database columns. File columns: {df.columns.tolist()}, Expected: {db_columns}")
  397. return jsonify({'success': False, 'message': '文件列名与数据库表不匹配'}), 400
  398. # 清洗数据并删除空值行
  399. df_cleaned = df[db_columns].dropna()
  400. # 统一数据类型,避免 int 和 float 合并问题
  401. df_cleaned[db_columns] = df_cleaned[db_columns].apply(pd.to_numeric, errors='coerce')
  402. # 获取现有的数据
  403. conn = get_db()
  404. with conn:
  405. existing_data = pd.read_sql(f"SELECT * FROM {table_name}", conn)
  406. # 查找重复数据
  407. duplicates = df_cleaned.merge(existing_data, on=db_columns, how='inner')
  408. # 如果有重复数据,删除它们
  409. df_cleaned = df_cleaned[~df_cleaned.index.isin(duplicates.index)]
  410. logger.warning(f"Duplicate data detected and removed: {duplicates}")
  411. # 获取导入前后的数据量
  412. total_data = len(df_cleaned) + len(duplicates)
  413. new_data = len(df_cleaned)
  414. duplicate_data = len(duplicates)
  415. # 导入不重复的数据
  416. df_cleaned.to_sql(table_name, conn, if_exists='append', index=False)
  417. logger.debug(f"Imported {new_data} new records into the database.")
  418. # 删除临时文件
  419. os.remove(temp_path)
  420. logger.debug(f"Temporary file removed: {temp_path}")
  421. # 返回结果
  422. return jsonify({
  423. 'success': True,
  424. 'message': '数据导入成功',
  425. 'total_data': total_data,
  426. 'new_data': new_data,
  427. 'duplicate_data': duplicate_data
  428. }), 200
  429. except Exception as e:
  430. logger.error(f"Import failed: {e}", exc_info=True)
  431. return jsonify({'success': False, 'message': f'导入失败: {str(e)}'}), 500
  432. # 模板下载接口
  433. @bp.route('/download_template', methods=['GET'])
  434. def download_template():
  435. """
  436. 根据给定的表名,下载表的模板(如 CSV 或 Excel 格式)。
  437. """
  438. table_name = request.args.get('table')
  439. if not table_name:
  440. return jsonify({'error': '表名参数缺失'}), 400
  441. columns = get_column_names(table_name)
  442. if not columns:
  443. return jsonify({'error': f"Table '{table_name}' not found or empty."}), 404
  444. # 不包括 ID 列
  445. if 'id' in columns:
  446. columns.remove('id')
  447. df = pd.DataFrame(columns=columns)
  448. file_format = request.args.get('format', 'excel').lower()
  449. try:
  450. if file_format == 'csv':
  451. output = BytesIO()
  452. df.to_csv(output, index=False, encoding='utf-8')
  453. output.seek(0)
  454. return send_file(output, as_attachment=True, download_name=f'{table_name}_template.csv',
  455. mimetype='text/csv')
  456. else:
  457. output = BytesIO()
  458. df.to_excel(output, index=False, engine='openpyxl')
  459. output.seek(0)
  460. return send_file(output, as_attachment=True, download_name=f'{table_name}_template.xlsx',
  461. mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
  462. except Exception as e:
  463. logger.error(f"Failed to generate template: {e}", exc_info=True)
  464. return jsonify({'error': '生成模板文件失败'}), 500
  465. # 切换模型接口
  466. @bp.route('/switch-model', methods=['POST'])
  467. def switch_model():
  468. session = None
  469. try:
  470. data = request.get_json()
  471. model_id = data.get('model_id')
  472. model_name = data.get('model_name')
  473. # 创建 session
  474. Session = sessionmaker(bind=db.engine)
  475. session = Session()
  476. # 查找模型
  477. model = session.query(Models).filter_by(ModelID=model_id).first()
  478. if not model:
  479. return jsonify({'error': 'Model not found'}), 404
  480. # 更新模型状态(或其他切换逻辑)
  481. # 假设此处是更新模型的某些字段来进行切换
  482. model.status = 'active' # 假设有一个字段记录模型状态
  483. session.commit()
  484. # 记录切换日志
  485. logger.info(f'Model {model_name} (ID: {model_id}) switched successfully.')
  486. return jsonify({'success': True, 'message': f'Model {model_name} switched successfully!'}), 200
  487. except Exception as e:
  488. logger.error('Failed to switch model:', exc_info=True)
  489. return jsonify({'error': str(e)}), 400
  490. finally:
  491. if session:
  492. session.close()
  493. # 修改了一下登录·接口
  494. @bp.route('/login', methods=['POST'])
  495. def login_user():
  496. data = request.get_json()
  497. logger.debug(f"Received login data: {data}") # 增加调试日志
  498. name = data.get('name')
  499. password = data.get('password')
  500. logger.info(f"Login request received for user: {name}")
  501. if not isinstance(name, str) or not isinstance(password, str):
  502. logger.warning("Username and password must be strings")
  503. return jsonify({"success": False, "message": "用户名和密码必须为字符串"}), 400
  504. if not name or not password:
  505. logger.warning("Username and password cannot be empty")
  506. return jsonify({"success": False, "message": "用户名和密码不能为空"}), 400
  507. query = "SELECT id, name, password FROM users WHERE name = ?"
  508. try:
  509. with get_db() as conn:
  510. user = conn.execute(query, (name,)).fetchone()
  511. if not user:
  512. logger.warning(f"User '{name}' does not exist")
  513. return jsonify({"success": False, "message": "用户名不存在"}), 400
  514. stored_password = user[2] # 假设 'password' 是第三个字段
  515. user_id = user[0] # 假设 'id' 是第一个字段
  516. if check_password_hash(stored_password, password):
  517. session['name'] = name
  518. logger.info(f"User '{name}' logged in successfully.")
  519. return jsonify({
  520. "success": True,
  521. "message": "登录成功",
  522. "userId": user_id,
  523. "name": name
  524. })
  525. else:
  526. logger.warning(f"Incorrect password for user '{name}'")
  527. return jsonify({"success": False, "message": "用户名或密码错误"}), 400
  528. except sqlite3.DatabaseError as db_err:
  529. logger.error(f"Database error during login process: {db_err}", exc_info=True)
  530. return jsonify({"success": False, "message": "数据库错误"}), 500
  531. except Exception as e:
  532. logger.error(f"Unexpected error during login process: {e}", exc_info=True)
  533. return jsonify({"success": False, "message": "登录失败"}), 500
  534. # 添加退出登录状态接口
  535. @bp.route('/logout', methods=['GET', 'POST'])
  536. def logout_user():
  537. try:
  538. session.clear()
  539. return jsonify({"msg": "退出成功"}), 200
  540. except Exception as e:
  541. logger.error(f"Error during logout process: {e}", exc_info=True)
  542. return jsonify({"msg": "退出失败"}), 500
  543. # 获取软件介绍信息的路由
  544. @bp.route('/software-intro/<int:id>', methods=['GET'])
  545. def get_software_intro(id):
  546. try:
  547. conn = get_db_connection()
  548. cursor = conn.cursor()
  549. cursor.execute('SELECT title, intro FROM software_intro WHERE id = ?', (id,))
  550. result = cursor.fetchone()
  551. conn.close()
  552. if result:
  553. title, intro = result
  554. return jsonify({
  555. 'title': title,
  556. 'intro': intro
  557. })
  558. return jsonify({}), 404
  559. except sqlite3.Error as e:
  560. print(f"数据库错误: {e}")
  561. return jsonify({"error": f"数据库错误: {str(e)}"}), 500
  562. # 更新软件介绍信息的路由
  563. @bp.route('/software-intro/<int:id>', methods=['PUT'])
  564. def update_software_intro(id):
  565. try:
  566. data = request.get_json()
  567. title = data.get('title')
  568. intro = data.get('intro')
  569. conn = get_db_connection()
  570. cursor = conn.cursor()
  571. cursor.execute('UPDATE software_intro SET title =?, intro =? WHERE id = ?', (title, intro, id))
  572. conn.commit()
  573. conn.close()
  574. return jsonify({'message': '软件介绍更新成功'})
  575. except sqlite3.Error as e:
  576. print(f"数据库错误: {e}")
  577. return jsonify({"error": f"数据库错误: {str(e)}"}), 500
  578. # 处理图片上传的路由
  579. @bp.route('/upload-image', methods=['POST'])
  580. def upload_image():
  581. file = request.files['image']
  582. if file:
  583. filename = str(uuid.uuid4()) + '.' + file.filename.rsplit('.', 1)[1].lower()
  584. file.save(os.path.join(os.getcwd(), 'uploads', filename))
  585. imageUrl = f'http://127.0.0.1:5000/uploads/{filename}'
  586. return jsonify({'imageUrl': imageUrl})
  587. return jsonify({'error': '未找到图片文件'}), 400
  588. # 配置静态资源服务
  589. @bp.route('/uploads/<path:filename>')
  590. def serve_image(filename):
  591. uploads_folder = os.path.join(os.getcwd(), 'uploads')
  592. return send_from_directory(uploads_folder, filename)