import subprocess from flask import Flask, render_template, request, send_file, session, redirect, url_for from datetime import datetime from common.logger import tool_logger, error_logger, system_logger import os, uuid # ========================= # 工具模块导入 # ========================= from tools.file_convert import file_convert_page, file_convert_download from tools.temp_upload import ( temp_upload_page, download_temp_file ) from tools.excel_merge import ( read_file, merge_df, export_excel, build_preview ) from tools.smart_data_v2 import ( smart_data_page_v2, smart_download_v2 ) from tools.weekly_permission import weekly_permission_page # ⭐ 新增:邮件通知工具 from tools.mail_notify import mail_notify_page, mail_notify_progress # ⭐ 新增:Base64 编解码工具 from tools.base64_codec import page as base64_page # ⭐ 新增:JSON 格式化工具 from tools.json_tool import page as json_page # ⭐ 新增:URL 编解码工具 from tools.url_codec import page as url_page # ⭐ 新增:图片压缩工具 from tools.image_compress import page as image_compress_page # ⭐ 新增:文本差异对比工具 from tools.text_diff import page as text_diff_page # ========================= # Flask app # ========================= app = Flask(__name__) app.secret_key = "excel_merge_secret_key" UPLOAD_FOLDER = "uploads" OUTPUT_FOLDER = "output" os.makedirs(UPLOAD_FOLDER, exist_ok=True) os.makedirs(OUTPUT_FOLDER, exist_ok=True) # ========================= # reset # ========================= @app.route("/reset") def reset(): session.clear() return redirect(url_for("index")) # ========================= # 首页 # ========================= @app.route("/") def index(): return render_template("index.html") # ========================= # Excel merge(原逻辑不变) # ========================= def handle_excel_merge(): preview_data = None download_file = None message = None if request.method == "POST": try: file1 = request.files.get("file1") file2 = request.files.get("file2") if file1 and file1.filename: p1 = os.path.join(UPLOAD_FOLDER, f"{uuid.uuid4()}_{file1.filename}") file1.save(p1) session["file1_path"] = p1 session["file1_name"] = file1.filename if file2 and file2.filename: p2 = os.path.join(UPLOAD_FOLDER, f"{uuid.uuid4()}_{file2.filename}") file2.save(p2) session["file2_path"] = p2 session["file2_name"] = file2.filename if not session.get("file1_path") or not session.get("file2_path"): raise Exception("请上传两个文件") key1 = request.form.get("key1", "").strip() key2 = request.form.get("key2", "").strip() join_type = request.form.get("join_type", "left") session["key1"] = key1 session["key2"] = key2 session["join_type"] = join_type df1 = read_file(session["file1_path"]) df2 = read_file(session["file2_path"]) merged_df = merge_df(df1, df2, key1, key2, join_type) out_name = f"merged_{uuid.uuid4().hex}.xlsx" out_path = os.path.join(OUTPUT_FOLDER, out_name) export_excel(merged_df, out_path) preview_data = build_preview(merged_df) download_file = out_name tool_logger.info( f"[ExcelMerge] file1={session.get('file1_name')} " f"file2={session.get('file2_name')} rows={len(merged_df)}" ) message = ( f"整合成功,共 {len(merged_df)} 条记录 | " f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')}" ) except Exception as e: error_logger.exception("[ExcelMergeError]") message = f"错误:{e}" return render_template( "excel.html", preview_data=preview_data, download_file=download_file, message=message, file1_name=session.get("file1_name", ""), file2_name=session.get("file2_name", ""), key1=session.get("key1", ""), key2=session.get("key2", ""), join_type=session.get("join_type", "left"), ) # ========================= # download # ========================= @app.route("/download/") def download(filename): return send_file( os.path.join(OUTPUT_FOLDER, filename), as_attachment=True ) # ========================= # TOOL HUB(统一入口) # ========================= TOOL_ROUTES = {} def tool_dispatch(name): handler = TOOL_ROUTES.get(name) if handler: return handler() return render_template(f"{name}.html") @app.route("/tool/mail_notify/progress/") def mail_progress(task_id): return mail_notify_progress(task_id) @app.route("/tool/", methods=["GET", "POST"]) def tool(name): return tool_dispatch(name) # ========================= # 工具注册(核心扩展点) # ========================= TOOL_ROUTES.update({ "excel": handle_excel_merge, "smart_data_v2": smart_data_page_v2, "file_convert": file_convert_page, "weekly_permission": weekly_permission_page, # ⭐ 新增邮件通知工具 "mail_notify": mail_notify_page, # ⭐ 新增 Base64 编解码工具 "base64": base64_page, # ⭐ 新增 JSON 格式化工具 "json": json_page, # ⭐ 新增 URL 编解码工具 "url": url_page, # ⭐ 新增图片压缩工具 "image_compress": image_compress_page, # ⭐ 新增文本差异对比工具 "text_diff": text_diff_page, }) # ========================= # temp upload # ========================= @app.route("/tool/temp_upload", methods=["GET", "POST"]) def temp_upload(): return temp_upload_page() @app.route("/temp/download/") def temp_download(filename): return download_temp_file(filename) # ========================= # smart data download # ========================= @app.route("/tool/smart_data_v2/download/") def smart_data_v2_download(filename): return smart_download_v2(filename) # ========================= # file convert download # ========================= @app.route("/download/file_convert/") def file_convert_download_route(filename): return file_convert_download(filename) # ========================= # image compress download # ========================= @app.route("/tool/image_compress/download/") def image_compress_download(filename): return send_file(os.path.join(OUTPUT_FOLDER, filename), as_attachment=True) cleanup_process = None def start_cleanup_task(): """ 启动定时清理任务 """ global cleanup_process cleanup_path = os.path.join( os.path.dirname(__file__), "cleanup_upload.py" ) cleanup_process = subprocess.Popen( ["python3", cleanup_path] ) system_logger.info( f"[cleanup] started pid=" f"{cleanup_process.pid}" ) print( f"[cleanup] started pid=" f"{cleanup_process.pid}" ) # ========================= # main # ========================= if __name__ == "__main__": # 防止 Flask debug 启动两次 if os.environ.get("WERKZEUG_RUN_MAIN") == "true": start_cleanup_task() app.run(host="0.0.0.0", port=8209, debug=True)