Update server.js via upload script - 2026-08-13 13:53:19

This commit is contained in:
mike 2026-08-13 13:53:35 +08:00
parent 9a66d7c333
commit f3c8aecd25
1 changed files with 0 additions and 66 deletions

View File

@ -107,29 +107,6 @@ function saveAiConfig(apiKey) {
fs.writeFileSync(AI_CONFIG_FILE, JSON.stringify({ apiKeyEnc: encrypt(apiKey) }, null, 2), 'utf8'); fs.writeFileSync(AI_CONFIG_FILE, JSON.stringify({ apiKeyEnc: encrypt(apiKey) }, null, 2), 'utf8');
} }
const REGION_LABELS = {
africa: 'Africa', middle_east: 'Middle East', australia: 'Australia/Oceania',
europe: 'Europe', sea: 'Southeast Asia', other: 'Other',
};
function buildCustomerContext(c) {
const lines = [];
lines.push(`Company: ${c.company || '(unknown / not on file)'}`);
if (c.contact) lines.push(`Contact person: ${c.contact}`);
const place = [c.country, REGION_LABELS[c.region]].filter(Boolean).join(', ');
if (place) lines.push(`Location: ${place}`);
if (c.productLines && c.productLines.length) lines.push(`Product interest: ${c.productLines.join(', ')}`);
if (c.intendedModel || c.intendedQty) {
lines.push(`Specific model interest: ${c.intendedModel || '(model unspecified)'}${c.intendedQty ? `, quantity: ${c.intendedQty}` : ''}`);
}
if (c.lastContact) lines.push(`Last contact date: ${c.lastContact}`);
if (c.notes) lines.push(`Background research on this customer: ${c.notes}`);
if (c.tags) lines.push(`Additional remarks: ${c.tags}`);
if (c.isVip) lines.push('This is a VIP / priority customer — extra attentive tone.');
if (c.hasOrdered) lines.push('This customer has already placed an order before — this follow-up is with an existing buyer, not a cold lead.');
return lines.join('\n');
}
async function callDeepSeek(apiKey, messages, opts) { async function callDeepSeek(apiKey, messages, opts) {
const { maxTokens = 600, temperature = 0.7, model = 'deepseek-v4-flash' } = opts || {}; const { maxTokens = 600, temperature = 0.7, model = 'deepseek-v4-flash' } = opts || {};
const resp = await fetch(`${DEEPSEEK_API_BASE}/chat/completions`, { const resp = await fetch(`${DEEPSEEK_API_BASE}/chat/completions`, {
@ -384,49 +361,6 @@ const aiLimiter = rateLimit({
message: { error: 'too many AI requests, please slow down' }, message: { error: 'too many AI requests, please slow down' },
}); });
app.post('/api/ai/draft', requireAuth, aiLimiter, async (req, res) => {
const body = req.body || {};
const customerId = body.customerId;
const channel = body.channel;
if (!customerId || !['email', 'whatsapp'].includes(channel)) {
return res.status(400).json({ error: 'invalid request' });
}
const cfg = loadAiConfig();
if (!cfg.apiKey) return res.status(400).json({ error: 'DeepSeek API key not configured' });
const data = readData();
let customers = [];
try { customers = JSON.parse(data['crm:customers'] || '[]'); } catch (e) {}
const customer = customers.find((c) => c.id === customerId);
if (!customer) return res.status(404).json({ error: 'customer not found' });
const context = buildCustomerContext(customer);
const systemPrompt = 'You are a sales assistant helping a B2B export salesperson at a Chinese power-equipment company (UPS systems, rectifiers, solar inverters, battery storage) write natural, professional follow-up outreach to overseas customers. Always write in English. Never sound like a generic template, reference specifics from the customer info given. Output only the requested content, with no meta-commentary, explanations, or markdown formatting.';
const userPrompt = channel === 'email'
? `Customer info:\n${context}\n\nWrite a short, warm but professional follow-up email to this customer. Respond in EXACTLY this format with no extra text:\nSUBJECT: <subject line>\nBODY:\n<email body, 3-5 short paragraphs max, sign off with "[Your name]">`
: `Customer info:\n${context}\n\nWrite a short, casual WhatsApp message to check in with this customer (2-4 sentences, friendly tone, no formal greeting like "Dear", a light emoji is fine if it feels natural). Respond with ONLY the message text, nothing else.`;
try {
const content = await callDeepSeek(cfg.apiKey, [
{ role: 'system', content: systemPrompt },
{ role: 'user', content: userPrompt },
]);
if (channel === 'email') {
const subjectMatch = content.match(/SUBJECT:\s*(.+)/i);
const bodyMatch = content.match(/BODY:\s*([\s\S]*)/i);
res.json({
subject: subjectMatch ? subjectMatch[1].trim() : 'Following up',
body: bodyMatch ? bodyMatch[1].trim() : content.trim(),
});
} else {
res.json({ body: content.trim() });
}
} catch (e) {
res.status(502).json({ error: e.message });
}
});
/* --------------------------- ai catalog import --------------------------- */ /* --------------------------- ai catalog import --------------------------- */
// 上传一份/多份PDF或图片形式的产品目录可能包含多个型号做OCR文字识别后交给 // 上传一份/多份PDF或图片形式的产品目录可能包含多个型号做OCR文字识别后交给
// DeepSeek解析成结构化的产品字段数组前端拿到后先给用户一个预览/确认页面, // DeepSeek解析成结构化的产品字段数组前端拿到后先给用户一个预览/确认页面,