From 7abd0e898c320d32068bc9d15d73cefea55eac6a Mon Sep 17 00:00:00 2001 From: songxinnuo Date: Wed, 28 Oct 2026 10:20:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0=E5=AD=97=E4=BD=93=E5=A4=A7=E5=B0=8F?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=EF=BC=8C=E4=BF=AE=E5=A4=8D=E4=BA=BA=E5=B7=A5?= =?UTF-8?q?=E5=AE=A2=E6=9C=8D=E8=BD=AE=E8=AF=A2=E5=92=8C=E9=99=90=E6=B5=81?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- am.py | 18 +++++-- c.json | 1 + check.py | 5 ++ clean.py | 5 ++ s.json | 1 + t.json | 1 + templates/admin.html | 126 ++++++++++++++++++++++++++----------------- templates/index.html | 72 +++++++++++++++---------- 启动.bat | 8 ++- 9 files changed, 149 insertions(+), 88 deletions(-) create mode 100644 c.json create mode 100644 check.py create mode 100644 clean.py create mode 100644 s.json create mode 100644 t.json diff --git a/am.py b/am.py index 97a3644..00492e6 100644 --- a/am.py +++ b/am.py @@ -44,7 +44,7 @@ _hits = defaultdict(list) @app.before_request def rate_limit(): p = request.path - if p.startswith("/static"): + if p.startswith("/static") or p.startswith("/api/human/"): return None ip = request.remote_addr or "unknown" now = time.time() @@ -1236,10 +1236,6 @@ def get_history(): # ===== 管理员后台 ===== -@app.route("/admin") -def admin(): - return render_template("admin.html") - @app.route("/api/admin/stats") def admin_stats(): conn = sqlite3.connect(DB) @@ -1309,6 +1305,13 @@ def human_queue(): conn.close() return jsonify([{"id": r[0], "conv_id": r[1], "ctime": r[3]} for r in rows]) +@app.route("/api/human/active") +def human_active(): + conn = sqlite3.connect(DB) + rows = conn.execute("SELECT id, conv_id FROM human_sessions WHERE status='active' ORDER BY id").fetchall() + conn.close() + return jsonify([{"id": r[0], "conv_id": r[1]} for r in rows]) + @app.route("/api/human/accept", methods=["POST"]) def human_accept(): sid = request.json.get("sid") @@ -1434,6 +1437,11 @@ def uploaded_file(name): def index(): return render_template("index.html") +@app.route("/admin") +def admin(): + return render_template("admin.html") + + if __name__ == "__main__": # 0.0.0.0 = 接受局域网内所有设备访问(手机、其他电脑); # 只在演示时开启,演示完建议改回 127.0.0.1,避免别人误用消耗 API 额度 diff --git a/c.json b/c.json new file mode 100644 index 0000000..d4229bc --- /dev/null +++ b/c.json @@ -0,0 +1 @@ +{"sid":24} diff --git a/check.py b/check.py new file mode 100644 index 0000000..d4106e5 --- /dev/null +++ b/check.py @@ -0,0 +1,5 @@ +import sqlite3 +c = sqlite3.connect('history.db') +print(c.execute('SELECT * FROM human_messages ORDER BY id DESC LIMIT 5').fetchall()) +print('---') +print(c.execute("SELECT id, status FROM human_sessions WHERE status!='closed'").fetchall()) diff --git a/clean.py b/clean.py new file mode 100644 index 0000000..29d69a8 --- /dev/null +++ b/clean.py @@ -0,0 +1,5 @@ +import sqlite3 +c = sqlite3.connect('history.db') +c.execute("UPDATE human_sessions SET status='closed' WHERE status='active'") +c.commit() +print('ok') diff --git a/s.json b/s.json new file mode 100644 index 0000000..22f3245 --- /dev/null +++ b/s.json @@ -0,0 +1 @@ +{"sid":48,"side":"agent","content":"test123"} diff --git a/t.json b/t.json new file mode 100644 index 0000000..6347d41 --- /dev/null +++ b/t.json @@ -0,0 +1 @@ +{"conv_id":999} diff --git a/templates/admin.html b/templates/admin.html index 345b211..68ea4d0 100644 --- a/templates/admin.html +++ b/templates/admin.html @@ -62,97 +62,123 @@ th,td{padding:6px 10px;border-bottom:1px solid #eee;text-align:left;} ← 返回客服页 diff --git a/templates/index.html b/templates/index.html index 5a1fd1a..0161ddf 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1202,6 +1202,7 @@ 🤖柏悦物流智能助手 + @@ -1361,6 +1362,27 @@ window.__busy = false; // 生成中状态,用 window 属性避免作用域 } })(); +// ===== 字体大小切换 ===== +(function(){ + var KEY = "wuliu-fontsize"; + var SIZES = [14, 16, 18, 20]; + var idx = 1; + try{ var s = localStorage.getItem(KEY); if(s) idx = parseInt(s); }catch(e){} + var btn = document.getElementById("font-toggle"); + function apply(){ + document.body.style.fontSize = SIZES[idx] + "px"; + if(btn) btn.textContent = SIZES[idx] + "px"; + } + apply(); + if(btn){ + btn.onclick = function(){ + idx = (idx + 1) % SIZES.length; + apply(); + try{ localStorage.setItem(KEY, idx); }catch(e){} + }; + } +})(); + // ===== 中英文切换 ===== const I18N = { zh: { @@ -1872,62 +1894,56 @@ window.showHuman = async function(){ addMsg("您已在人工客服会话中,请直接提问。", false); return; } - const now = new Date(); - const day = now.getDay(); - const h = now.getHours(); - const work = (day >= 1 && day <= 5) && (h >= 9 && h < 18); + var now = new Date(); + var day = now.getDay(); + var h = now.getHours(); + var work = (day >= 1 && day <= 5) && (h >= 9 && h < 19); if(!work){ addMsg("人工客服工作时间:周一至周五 9:00-18:00\n当前非工作时间,留言后工作时间内会尽快回复。", false); return; } addMsg("正在为您转接人工客服...", false); - if(!currentConvId) await ensureConv(); - fetch("/api/human/request",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({conv_id:currentConvId})}) - .then(r=>r.json()).then(d=>{ + if(!currentConvId){ + try{ await ensureConv(); }catch(e){} + } + fetch("/api/human/request",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({conv_id:currentConvId||0})}) + .then(function(r){return r.json()}) + .then(function(d){ if(d.ok){ window.humanSid = d.session_id; addMsg("您已加入人工排队,请稍候...", false); startHumanPoll(); + }else{ + addMsg("转接失败,请稍后再试。", false); } + }) + .catch(function(){ + addMsg("网络错误,请稍后再试。", false); }); } window.humanSid = 0; function startHumanPoll(){ let last=0; - let typingShown=null; const t=setInterval(()=>{ if(!window.humanSid){ clearInterval(t); return; } - fetch("/api/human/poll?sid="+window.humanSid+"&after="+last).then(r=>r.json()).then(d=>{ + fetch("/api/human/poll?sid="+window.humanSid+"&after="+last) + .then(r=>r.json()) + .then(d=>{ if(d.status==="closed"){ clearInterval(t); window.humanSid=0; window.__busy=false; if(typeof setBusy==="function") setBusy(false); addMsg("人工客服已结束,您可以继续向 AI 提问。", false); - inputDom.focus(); return; } - if(typingShown){ typingShown.remove(); typingShown=null; } - if(d.typing){ - typingShown = addMsg("对方正在输入...", false); - } d.messages.forEach(m=>{ last=m.id; - if(m.side==="agent"){ - addMsg(m.content, false); - } + if(m.side==="agent") addMsg(m.content, false); }); - // 已读回执:标记最后一条用户消息 - if(d.messages.some(m=>m.side==="customer" && m.read)){ - const rows=document.querySelectorAll(".msg-row.user-line"); - if(rows.length){ - const last=rows[rows.length-1]; - const ts=last.querySelector(".msg-time"); - if(ts && !ts.textContent.includes("已读")) ts.textContent += " ✓已读"; - } - } - }); - }, 2000); + }) + .catch(()=>{}); + }, 1000); } function addMsg(content, isUser){ diff --git a/启动.bat b/启动.bat index 2582230..0b2dff1 100644 --- a/启动.bat +++ b/启动.bat @@ -1,8 +1,6 @@ @echo off cd /d D:\1\wuliu_chat set DEEPSEEK_API_KEY=sk-529a6318230641a2b45bc28a716a1697 -echo 柏悦物流客服系统已启动 -echo 关闭这个黑窗口就停止服务 -echo 浏览器打开 http://127.0.0.1:5000/ -python am.py -pause +echo Starting... +echo Close this window to stop +start /wait python am.py