From c4ac4f513cee499ff5c0dddd21d9ab5cc8b78795 Mon Sep 17 00:00:00 2001 From: songxinnuo Date: Wed, 28 Oct 2026 14:05:46 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=90=8E=E5=8F=B0=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E3=80=81=E5=9B=BE=E7=89=87=E9=A2=84=E8=A7=88=E5=8F=91=E9=80=81?= =?UTF-8?q?=E3=80=81=E8=87=AA=E5=8A=A8=E6=B8=85=E7=90=86=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- am.py | 30 ++++++++++++++++++++++++++++++ templates/admin.html | 7 ++++++- templates/index.html | 35 +++++++++++++++++++++++++---------- 3 files changed, 61 insertions(+), 11 deletions(-) diff --git a/am.py b/am.py index 00492e6..186613d 100644 --- a/am.py +++ b/am.py @@ -269,6 +269,21 @@ def backup_db(): print(f"[数据库备份失败] {e}") +def cleanup_uploads(): + """删除 uploads/ 里 7 天前的图片""" + try: + udir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "uploads") + if not os.path.isdir(udir): + return + cutoff = time.time() - 7 * 86400 + for f in os.listdir(udir): + fp = os.path.join(udir, f) + if os.path.isfile(fp) and os.path.getmtime(fp) < cutoff: + os.remove(fp) + except Exception as e: + print(f"[清理上传文件失败] {e}") + + def cleanup_old_conversations(): """自动清理 30 天前的旧对话,防止数据库无限膨胀""" try: @@ -290,6 +305,7 @@ def cleanup_old_conversations(): backup_db() +cleanup_uploads() # 自动清理已关闭,不删旧对话 @@ -919,6 +935,9 @@ def stream_chat(): conv_id = request.args.get("conv_id", type=int) if not msg: return Response(sse({"e": "问题不能为空"}), mimetype="text/event-stream") + # 纯图片消息:不调AI + if "[图片]" in msg and len(msg) < 300: + return Response(sse({"t": "收到您的图片,请描述一下您的问题。"}), mimetype="text/event-stream") # 运单号:直接给轨迹卡片,不让模型编 track_no = extract_tracking_no(msg) @@ -1437,8 +1456,19 @@ def uploaded_file(name): def index(): return render_template("index.html") +@app.route("/admin/login", methods=["POST"]) +def admin_login(): + pwd = request.json.get("password", "") + if pwd == "123": + return jsonify({"ok": True}) + return jsonify({"ok": False}), 401 + @app.route("/admin") def admin(): + return render_template("admin_login.html") + +@app.route("/admin/dashboard") +def admin_dash(): return render_template("admin.html") diff --git a/templates/admin.html b/templates/admin.html index f0315f5..80567ca 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -149,7 +149,12 @@ function switchSession(sid){ function appendMsg(m){ var div=document.createElement("div"); div.className="msg "+(m.side==="customer"?"cust":"agent"); - div.innerHTML='
'+m.content.replace(/
'+(m.side==="customer"?"客户":"客服")+'
'; + var c = m.content.replace(/'); + } + div.innerHTML='
'+c+'
'+(m.side==="customer"?"客户":"客服")+'
'; document.getElementById("chatBox").appendChild(div); document.getElementById("chatBox").scrollTop=999999; } diff --git a/templates/index.html b/templates/index.html index 0161ddf..5a9323f 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1321,9 +1321,15 @@ uploadInput.onchange = function(){ .then(data=>{ uploadBtn.textContent = "📎"; if(data.ok){ - let text = "[附件] " + data.url; - if(data.is_image) text = "[图片] " + data.url; - inputDom.value = text; + window.__pendingImg = data.url; + var old = document.getElementById("imgPreview"); + if(old) old.remove(); + var prev = document.createElement("div"); + prev.id = "imgPreview"; + prev.style.cssText="margin:0 0 8px 50px;"; + prev.innerHTML = ''; + var area = document.querySelector(".input-area"); + area.insertBefore(prev, area.firstChild); inputDom.focus(); }else{ alert(data.error || "上传失败"); @@ -1961,7 +1967,8 @@ function addMsg(content, isUser){ const userMsg = document.createElement('div'); userMsg.className = "user-message"; if(imgUrl){ - userMsg.innerHTML = ''; + var txt = content.replace(/\[图片\]\/uploads\/\S+\.(?:jpg|jpeg|png|gif|webp)/i, "").trim(); + userMsg.innerHTML = '' + (txt ? '
'+txt+'
' : ''); }else{ userMsg.innerText = content; } @@ -2121,17 +2128,21 @@ function renderFollowups(bubble){ async function doSend(retryText){ const isRetry = typeof retryText === "string"; const sendWord = (isRetry ? retryText : inputDom.value).trim(); - if(!sendWord){ + var imgUrl = window.__pendingImg || ""; + if(!sendWord && !imgUrl){ inputDom.focus(); return; } - // 人工客服模式:直接发给客服,不走AI if(window.humanSid){ - if(!isRetry) addMsg(sendWord, true); + var content = sendWord; + if(imgUrl) content += (content ? " " : "") + "[图片]" + imgUrl; + if(!isRetry) addMsg(content, true); inputDom.value = ""; + window.__pendingImg = ""; + var p = document.getElementById("imgPreview"); if(p) p.remove(); inputDom.focus(); - fetch("/api/human/send",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sid:window.humanSid,side:"customer",content:sendWord})}); + fetch("/api/human/send",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({sid:window.humanSid,side:"customer",content:content})}); return; } @@ -2139,8 +2150,12 @@ async function doSend(retryText){ if(cancelActive){ cancelActive(); cancelActive = null; } if(!isRetry){ - addMsg(sendWord, true); + var fullContent = sendWord; + if(imgUrl) fullContent += (fullContent ? " " : "") + "[图片]" + imgUrl; + addMsg(fullContent, true); inputDom.value = ""; + window.__pendingImg = ""; + var p = document.getElementById("imgPreview"); if(p) p.remove(); inputDom.focus(); } @@ -2294,7 +2309,7 @@ async function doSend(retryText){ fetch("/stream_chat", { method: "POST", headers: {"Content-Type": "application/json"}, - body: JSON.stringify({msg: sendWord, conv_id: currentConvId}), + body: JSON.stringify({msg: fullContent, conv_id: currentConvId}), signal: controller.signal }).then(function(res){ if(!res.ok){ throw new Error("HTTP " + res.status); }