You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

296 lines
7.3 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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/<filename>")
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/<task_id>")
def mail_progress(task_id):
return mail_notify_progress(task_id)
@app.route("/tool/<name>", 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/<filename>")
def temp_download(filename):
return download_temp_file(filename)
# =========================
# smart data download
# =========================
@app.route("/tool/smart_data_v2/download/<filename>")
def smart_data_v2_download(filename):
return smart_download_v2(filename)
# =========================
# file convert download
# =========================
@app.route("/download/file_convert/<filename>")
def file_convert_download_route(filename):
return file_convert_download(filename)
# =========================
# image compress download
# =========================
@app.route("/tool/image_compress/download/<filename>")
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)