From ca66b03af590d75c4edfcf2a1fcca70935860fa7 Mon Sep 17 00:00:00 2001 From: selfrelease Date: Wed, 15 Jul 2026 14:09:53 +0800 Subject: [PATCH] =?UTF-8?q?deploy:=202026-07-15=2014:09=20=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- demo/web/server.py | 43 ++++++++++--- demo/web/static/index.html | 123 ++++++++++++++++++++++++++++++++----- 2 files changed, 143 insertions(+), 23 deletions(-) diff --git a/demo/web/server.py b/demo/web/server.py index d2ae96d..6e9dd87 100644 --- a/demo/web/server.py +++ b/demo/web/server.py @@ -712,15 +712,21 @@ def list_conversations( try: with conn.cursor() as cur: sql = """ - SELECT cu.id, cu.customer_name, s.name AS salesperson_name, + SELECT DISTINCT cu.id, cu.customer_name, s.name AS salesperson_name, cu.industry, cu.intent_level, cu.stage, cu.summary, cu.key_points, cu.objections, cu.next_action, cu.key_needs, cu.last_analysis, (SELECT count(*) FROM message m JOIN conversation conv ON conv.id = m.conversation_id - WHERE conv.contact_id = cu.contact_id) AS msg_count + WHERE conv.contact_id = cu.contact_id) AS msg_count, + string_agg(DISTINCT p.category, ', ') AS deal_categories, + string_agg(m2.normalized_content, ' ') AS all_messages FROM customer cu JOIN salesperson s ON s.id = cu.salesperson_id + LEFT JOIN deal d ON d.customer_id = cu.id + LEFT JOIN product p ON p.id = d.product_id + LEFT JOIN conversation conv2 ON conv2.contact_id = cu.contact_id + LEFT JOIN message m2 ON m2.conversation_id = conv2.id """ conditions = [] params = [] @@ -732,11 +738,32 @@ def list_conversations( params.append(stage) if conditions: sql += " WHERE " + " AND ".join(conditions) - sql += " ORDER BY cu.salesperson_id, cu.id" + sql += """ + GROUP BY cu.id, cu.customer_name, s.name, + cu.industry, cu.intent_level, cu.stage, + cu.summary, cu.key_points, cu.objections, cu.next_action, + cu.key_needs, cu.last_analysis + ORDER BY cu.salesperson_id, cu.id + """ cur.execute(sql, params) - return [ - { + diet_keywords = ["减肥", "减脂", "瘦身", "B420", "纤益", "代餐", "体脂", "产后减肥", "节食"] + child_keywords = ["湿疹", "过敏", "鼻炎", "腹泻", "便秘", "免疫力", "胀气", + "拉肚子", "打喷嚏", "流鼻涕", "感冒", "食物过敏", "牛奶蛋白", + "宝宝", "宝妈", "儿童", "丹麦", "鼠李糖", "LGG", "合生元"] + results = [] + for r in cur.fetchall(): + deal_cats = r[13] + all_msgs = r[14] or "" + if deal_cats: + product_categories = deal_cats + elif any(kw in all_msgs for kw in diet_keywords): + product_categories = "女性减肥益生菌" + elif any(kw in all_msgs for kw in child_keywords): + product_categories = "儿童益生菌" + else: + product_categories = None + results.append({ "id": r[0], "customer_name": r[1], "salesperson_name": r[2], "industry": r[3], "intent_level": r[4], "stage": r[5], @@ -747,9 +774,9 @@ def list_conversations( "key_needs": parse_pg_array(r[10]), "last_analysis": r[11].isoformat() if r[11] else None, "msg_count": r[12], - } - for r in cur.fetchall() - ] + "product_categories": product_categories, + }) + return results finally: put_conn(conn) diff --git a/demo/web/static/index.html b/demo/web/static/index.html index ca6269c..9956d3c 100644 --- a/demo/web/static/index.html +++ b/demo/web/static/index.html @@ -230,14 +230,15 @@ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-

选择客户和产品,基于产品详情和真实对话生成标准范式对话模板

- -
+

@@ -976,29 +977,121 @@ function renderAnalysisList() { `).join(''); } +let paradigmCustomers = []; +let paradigmProducts = []; + function populateParadigmSelect() { const sel = document.getElementById('paradigm-customer-select'); if (!sel) return; - const currentVal = sel.value; + paradigmCustomers = conversationsData; + // 填充客户下拉 sel.innerHTML = ''; - conversationsData.forEach(c => { + paradigmCustomers.forEach(c => { const opt = document.createElement('option'); opt.value = c.id; opt.textContent = `${c.customer_name} (${c.salesperson_name}, ${c.stage})`; sel.appendChild(opt); }); - if (currentVal) sel.value = currentVal; - // 填充产品选择 + // 填充产品下拉 const productSel = document.getElementById('paradigm-product-select'); - if (productSel && productSel.options.length <= 1) { - api('/products?status=active').then(products => { - products.forEach(p => { - const opt = document.createElement('option'); - opt.value = p.id; - opt.textContent = `${p.name} ${p.spec || ''}`; - productSel.appendChild(opt); - }); - }).catch(() => {}); + productSel.innerHTML = ''; + api('/products?status=active').then(products => { + paradigmProducts = products; + products.forEach(p => { + const opt = document.createElement('option'); + opt.value = p.id; + opt.textContent = `${p.name} ${p.spec || ''}`; + productSel.appendChild(opt); + }); + }).catch(() => {}); +} + +/** 选择客户时,自动筛选匹配的产品 */ +function onParadigmCustomerChange() { + const customerId = document.getElementById('paradigm-customer-select').value; + const productSel = document.getElementById('paradigm-product-select'); + const hint = document.getElementById('paradigm-match-hint'); + if (!customerId) { + // 恢复全部产品 + productSel.innerHTML = ''; + paradigmProducts.forEach(p => { + const opt = document.createElement('option'); + opt.value = p.id; + opt.textContent = `${p.name} ${p.spec || ''}`; + productSel.appendChild(opt); + }); + hint.textContent = ''; + return; + } + const customer = paradigmCustomers.find(c => String(c.id) === customerId); + if (!customer) return; + const cats = (customer.product_categories || '').split(', ').filter(x => x); + if (cats.length > 0) { + // 筛选匹配产品线的产品 + const matched = paradigmProducts.filter(p => cats.includes(p.category)); + productSel.innerHTML = ''; + matched.forEach(p => { + const opt = document.createElement('option'); + opt.value = p.id; + opt.textContent = `${p.name} ${p.spec || ''}`; + productSel.appendChild(opt); + }); + hint.textContent = `客户关联产品线: ${cats.join('、')},已筛选 ${matched.length} 个匹配产品`; + } else { + // 无关联产品线,显示全部 + productSel.innerHTML = ''; + paradigmProducts.forEach(p => { + const opt = document.createElement('option'); + opt.value = p.id; + opt.textContent = `${p.name} ${p.spec || ''}`; + productSel.appendChild(opt); + }); + hint.textContent = '该客户未关联产品线,显示全部产品'; + } +} + +/** 选择产品时,筛选匹配产品线的客户 */ +function onParadigmProductChange() { + const productId = document.getElementById('paradigm-product-select').value; + const customerSel = document.getElementById('paradigm-customer-select'); + const hint = document.getElementById('paradigm-match-hint'); + if (!productId) { + // 恢复全部客户 + customerSel.innerHTML = ''; + paradigmCustomers.forEach(c => { + const opt = document.createElement('option'); + opt.value = c.id; + opt.textContent = `${c.customer_name} (${c.salesperson_name}, ${c.stage})`; + customerSel.appendChild(opt); + }); + hint.textContent = ''; + return; + } + const product = paradigmProducts.find(p => String(p.id) === productId); + if (!product) return; + // 筛选产品线匹配的客户 + const matched = paradigmCustomers.filter(c => { + const cats = (c.product_categories || '').split(', ').filter(x => x); + return cats.includes(product.category); + }); + if (matched.length > 0) { + customerSel.innerHTML = ''; + matched.forEach(c => { + const opt = document.createElement('option'); + opt.value = c.id; + opt.textContent = `${c.customer_name} (${c.salesperson_name}, ${c.stage})`; + customerSel.appendChild(opt); + }); + hint.textContent = `产品线: ${product.category},已筛选 ${matched.length} 个匹配客户`; + } else { + customerSel.innerHTML = ''; + paradigmCustomers.forEach(c => { + const opt = document.createElement('option'); + opt.value = c.id; + opt.textContent = `${c.customer_name} (${c.salesperson_name}, ${c.stage})`; + customerSel.appendChild(opt); + }); + hint.textContent = `产品线: ${product.category},无匹配客户,显示全部`; } }