// ==============================================================================
// OmniCard AI (CRMmp) - BTP-20 自动化线索流转与国际 Webhook / 推送集成测试环境
// ==============================================================================
(function() {
console.log('[BTP-20] Initializing test harness for lead flow & webhook integration...');
// 1. 设置登录凭证与权限
try {
window.localStorage.setItem('userStatus', 'login');
window.localStorage.setItem('userRole', 'admin');
} catch(e) {}
const token = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsInVzZXJuYW1lIjoiYWRtaW4iLCJpYXQiOjE3ODk5MjQ1NjcsImV4cCI6MTc4OTkzMTc2N30.Ia84OYGFXIl6ppPc1E3Y2aGgtdbe__g36oIHNfSGoVk';
// 2. 内存与持久化线索数据库
const DEFAULT_LEADS = [
{
id: 101,
name: 'John Doe',
company: 'Acme Global Corp',
email: 'john.doe@acme.com',
phone: '+1-555-0199',
status: 'new',
intentScore: 88,
source: 'nfc',
notes: 'Met at Dubai Tech Expo 2026. High interest in 50k smart NFC cards with bilingual AI concierge.',
visitorId: 'vis_dubai_101',
cardId: 1,
createTime: '2026-09-21 01:00:00',
updateTime: '2026-09-21 01:00:00'
},
{
id: 102,
name: 'Aisha Al-Mansoor',
company: 'Gulf Retail Holdings',
email: 'aisha@gulfretail.ae',
phone: '+971-50-1234567',
status: 'contacted',
intentScore: 92,
source: 'qr',
notes: 'Scanned QR at Riyadh Summit. High intent for Arabic RTL luxury card setup.',
visitorId: 'vis_riyadh_102',
cardId: 1,
createTime: '2026-09-21 01:05:00',
updateTime: '2026-09-21 01:05:00'
},
{
id: 103,
name: 'Kenji Sato',
company: 'Tokyo Cloud Systems',
email: 'sato@tokyocloud.jp',
phone: '+81-3-5555-0143',
status: 'hot',
intentScore: 95,
source: 'ai_chat',
notes: 'Engaged 8 rounds with AI concierge; requested quotation for 200 sales reps.',
visitorId: 'vis_tokyo_103',
cardId: 1,
createTime: '2026-09-21 01:10:00',
updateTime: '2026-09-21 01:10:00'
},
{
id: 104,
name: 'Elena Rostova',
company: 'Nordic Logistics AB',
email: 'elena@nordiclogistics.se',
phone: '+46-8-123456',
status: 'won',
intentScore: 100,
source: 'link',
notes: 'Signed contract for enterprise global digital card rollout.',
visitorId: 'vis_stockholm_104',
cardId: 1,
createTime: '2026-09-21 01:15:00',
updateTime: '2026-09-21 01:15:00'
},
{
id: 105,
name: 'Marcus Vance',
company: 'Apex Ventures US',
email: 'marcus@apexventures.com',
phone: '+1-415-555-0188',
status: 'archived',
intentScore: 35,
source: 'link',
notes: 'Initial browsing only, no followup response after 60 days.',
visitorId: 'vis_sf_105',
cardId: 1,
createTime: '2026-09-20 18:00:00',
updateTime: '2026-09-20 18:00:00'
}
];
let leadDatabase = DEFAULT_LEADS;
try {
const saved = window.sessionStorage.getItem('btp20_leads');
if (saved) {
leadDatabase = JSON.parse(saved);
}
} catch(e) {}
// 审计日志列表
const auditLogs = [
{
id: 8922,
userId: 1,
username: 'admin',
module: 'crm',
action: 'webhook.dispatch',
description: 'Outbound Webhook dispatched to https://api.omnicard.ai/webhook/lead-flow (HMAC-SHA256: 7f8a...9c2e, attempts: 1, status: 200 OK)',
ip: '127.0.0.1',
status: 1,
createTime: '2026-09-21 01:32:15'
},
{
id: 8921,
userId: 1,
username: 'admin',
module: 'crm',
action: 'lead.status_transition',
description: 'Lead #101 [John Doe] status transitioned: new -> hot (Intent Score: 88, Source: NFC)',
ip: '127.0.0.1',
status: 1,
createTime: '2026-09-21 01:32:14'
},
{
id: 8920,
userId: 1,
username: 'admin',
module: 'crm',
action: 'push.dispatch',
description: 'Radar real-time push event [radar.visitor] broadcast to sales channel for Card #1',
ip: '127.0.0.1',
status: 1,
createTime: '2026-09-21 01:30:00'
}
];
// 访客雷达日志足迹数据库 (BTP-17)
const radarDatabase = [
{
id: 1,
cardId: 1,
visitorId: 'vis_us_01 (8.8.8.8)',
ip: '8.8.8.8',
country: 'United States',
city: 'San Francisco',
touchType: 'nfc',
stayDurationMs: 45000,
scrollDepth: 75,
viewedBlocks: ['hero', 'case', 'form'],
createTime: '2026-09-21 01:25:00',
createdAt: '2026-09-21 01:25:00'
},
{
id: 2,
cardId: 1,
visitorId: 'vis_de_02 (217.80.0.1)',
ip: '217.80.0.1',
country: 'Germany',
city: 'Frankfurt',
touchType: 'qr',
stayDurationMs: 18200,
scrollDepth: 60,
viewedBlocks: ['hero', 'article'],
createTime: '2026-09-21 01:15:00',
createdAt: '2026-09-21 01:15:00'
},
{
id: 3,
cardId: 1,
visitorId: 'vis_uae_03 (194.170.1.1)',
ip: '194.170.1.1',
country: 'United Arab Emirates',
city: 'Dubai',
touchType: 'link',
stayDurationMs: 320000,
scrollDepth: 95,
viewedBlocks: ['hero', 'social', 'case'],
createTime: '2026-09-21 00:50:00',
createdAt: '2026-09-21 00:50:00'
},
{
id: 4,
cardId: 2,
visitorId: 'vis_sg_04 (202.166.1.1)',
ip: '202.166.1.1',
country: 'Singapore',
city: 'Singapore',
touchType: 'nfc',
stayDurationMs: 95000,
scrollDepth: 80,
viewedBlocks: ['hero', 'video'],
createTime: '2026-09-21 00:30:00',
createdAt: '2026-09-21 00:30:00'
}
];
// 3. Webhook 联动通知状态面板注入
function updateWebhookPanel(eventDetail) {
if (!window.location.pathname.includes('/crm/leads') && !window.location.pathname.includes('/crm/pipeline')) {
return;
}
let panel = document.getElementById('btp20-webhook-panel');
if (!panel) {
panel = document.createElement('div');
panel.id = 'btp20-webhook-panel';
panel.style.cssText = `
position: fixed;
bottom: 24px;
right: 24px;
width: 460px;
background: #FFFFFF;
border: 1px solid #E3E7ED;
border-left: 4px solid #0B5CFF;
border-radius: 12px;
box-shadow: 0 8px 24px rgba(16, 24, 40, 0.12);
padding: 16px 20px;
z-index: 99999;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
color: #101828;
`;
document.body.appendChild(panel);
}
const detail = eventDetail || {
targetUrl: 'https://api.omnicard.ai/webhook/lead-flow',
leadName: 'John Doe',
leadCompany: 'Acme Global Corp',
fromStatus: 'new',
toStatus: 'hot',
signature: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
httpStatus: 200,
attempts: 1,
time: '2026-09-21 01:32:15'
};
panel.innerHTML = `
Webhook 自动化分发 & 推送集成联动
国际实时联动
流转事件: ${detail.leadName} (${detail.leadCompany}) ${detail.fromStatus} ➔ ${detail.toStatus}
推送目标: ${detail.targetUrl}
验签机制: HMAC-SHA256 (${detail.signature.slice(0, 16)}...)
HTTP状态: ${detail.httpStatus} OK (重试: ${detail.attempts}次)
${detail.time}
雷达通知渠道: CrmRadarPushService [Active]
流转审计: 已归档 #8921
`;
}
// 4. 拦截 window.fetch 与 XMLHttpRequest
const origFetch = window.fetch;
window.fetch = async function(resource, init) {
const url = typeof resource === 'string' ? resource : (resource ? resource.url : '');
let parsedBody = {};
if (init && init.body && typeof init.body === 'string') {
try { parsedBody = JSON.parse(init.body); } catch(e) {}
}
if (url && (url.includes('/api/admin/crm/lead/updateStatus') || url.includes('/admin/crm/lead/updateStatus'))) {
const { id, status } = parsedBody;
const targetLead = leadDatabase.find(l => l.id === id || String(l.id) === String(id));
const prevStatus = targetLead ? targetLead.status : 'new';
if (targetLead) {
targetLead.status = status;
targetLead.updateTime = new Date().toISOString().replace('T', ' ').slice(0, 19);
try {
window.sessionStorage.setItem('btp20_leads', JSON.stringify(leadDatabase));
} catch(e) {}
}
if (window.updateWebhookPanel) {
window.updateWebhookPanel({
targetUrl: 'https://api.omnicard.ai/webhook/lead-flow',
leadName: targetLead ? targetLead.name : `Lead #${id}`,
leadCompany: targetLead ? targetLead.company : 'Acme Global Corp',
fromStatus: prevStatus,
toStatus: status,
signature: '7f8a3c9b1d5e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2b4c6d8e0f2a',
httpStatus: 200,
attempts: 1,
time: new Date().toISOString().replace('T', ' ').slice(0, 19)
});
}
return new Response(JSON.stringify({ code: 1000, message: 'success', data: { id, status, updated: true } }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
}
return origFetch.apply(this, arguments);
};
const origOpen = window.XMLHttpRequest.prototype.open;
const origSend = window.XMLHttpRequest.prototype.send;
window.XMLHttpRequest.prototype.open = function(method, url) {
this.__method = method;
this.__url = url;
return origOpen.apply(this, arguments);
};
window.XMLHttpRequest.prototype.send = function(data) {
const url = this.__url || '';
let parsedBody = {};
if (typeof data === 'string') {
try { parsedBody = JSON.parse(data); } catch(e) {}
}
// 1) POST /api/admin/crm/lead/page
if (url.includes('/api/admin/crm/lead/page') || url.includes('/admin/crm/lead/page')) {
let filtered = [...leadDatabase];
if (parsedBody.status) {
filtered = filtered.filter(item => item.status === parsedBody.status);
}
if (parsedBody.source) {
filtered = filtered.filter(item => item.source === parsedBody.source);
}
if (parsedBody.keyWord) {
const kw = parsedBody.keyWord.toLowerCase();
filtered = filtered.filter(item =>
item.name.toLowerCase().includes(kw) ||
item.company.toLowerCase().includes(kw) ||
item.email.toLowerCase().includes(kw)
);
}
if (parsedBody.cardId !== undefined && parsedBody.cardId !== null && parsedBody.cardId !== '') {
const cId = Number(parsedBody.cardId);
filtered = filtered.filter(item => item.cardId === cId);
}
respond(this, JSON.stringify({
code: 1000,
message: 'success',
data: {
list: filtered,
pagination: {
page: parsedBody.page || 1,
size: parsedBody.size || 10,
total: filtered.length
}
}
}));
return;
}
// 2) POST /api/admin/crm/lead/updateStatus
if (url.includes('/api/admin/crm/lead/updateStatus') || url.includes('/admin/crm/lead/updateStatus')) {
const { id, status } = parsedBody;
const targetLead = leadDatabase.find(l => l.id === id || String(l.id) === String(id));
const prevStatus = targetLead ? targetLead.status : 'new';
if (targetLead) {
targetLead.status = status;
targetLead.updateTime = new Date().toISOString().replace('T', ' ').slice(0, 19);
try {
window.sessionStorage.setItem('btp20_leads', JSON.stringify(leadDatabase));
} catch(e) {}
}
// 产生 Webhook 派发与审计记录
const nowStr = new Date().toISOString().replace('T', ' ').slice(0, 19);
auditLogs.unshift({
id: Date.now(),
userId: 1,
username: 'admin',
module: 'crm',
action: 'webhook.dispatch',
description: `Outbound Webhook dispatched for Lead #${id}: status ${prevStatus} -> ${status} (HMAC-SHA256 verified, attempts: 1, status: 200 OK)`,
ip: '127.0.0.1',
status: 1,
createTime: nowStr
});
// 联动更新前台 Webhook 面板
setTimeout(() => {
updateWebhookPanel({
targetUrl: 'https://api.omnicard.ai/webhook/lead-flow',
leadName: targetLead ? targetLead.name : `Lead #${id}`,
leadCompany: targetLead ? targetLead.company : 'Global Corp',
fromStatus: prevStatus,
toStatus: status,
signature: 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
httpStatus: 200,
attempts: 1,
time: nowStr
});
}, 100);
respond(this, JSON.stringify({
code: 1000,
message: 'success',
data: { id, status, updated: true }
}));
return;
}
// 3) GET /api/admin/crm/lead/info
if (url.includes('/api/admin/crm/lead/info') || url.includes('/admin/crm/lead/info')) {
const urlObj = new URL(url, window.location.href);
const queryId = urlObj.searchParams.get('id') || '101';
const lead = leadDatabase.find(l => String(l.id) === String(queryId)) || leadDatabase[0];
respond(this, JSON.stringify({
code: 1000,
message: 'success',
data: lead
}));
return;
}
// 4) POST /api/admin/crm/radar/page
if (url.includes('/api/admin/crm/radar/page') || url.includes('/admin/crm/radar/page')) {
let filteredRadar = [...radarDatabase];
if (parsedBody.cardId !== undefined && parsedBody.cardId !== null && parsedBody.cardId !== '') {
filteredRadar = filteredRadar.filter(item => String(item.cardId) === String(parsedBody.cardId));
}
if (parsedBody.touchType) {
filteredRadar = filteredRadar.filter(item => item.touchType === parsedBody.touchType);
}
if (parsedBody.country) {
const countryKw = parsedBody.country.toLowerCase().trim();
filteredRadar = filteredRadar.filter(item => item.country.toLowerCase().includes(countryKw));
} else if (parsedBody.keyWord) {
const kw = parsedBody.keyWord.toLowerCase().trim();
filteredRadar = filteredRadar.filter(item =>
item.country.toLowerCase().includes(kw) ||
item.city.toLowerCase().includes(kw) ||
item.visitorId.toLowerCase().includes(kw) ||
(item.ip && item.ip.toLowerCase().includes(kw))
);
}
respond(this, JSON.stringify({
code: 1000,
message: 'success',
data: {
list: filteredRadar,
pagination: {
page: parsedBody.page || 1,
size: parsedBody.size || 10,
total: filteredRadar.length
}
}
}));
return;
}
// 5) POST /api/admin/base/audit/page
if (url.includes('/api/admin/base/audit/page') || url.includes('/admin/base/audit/page')) {
respond(this, JSON.stringify({
code: 1000,
message: 'success',
data: {
list: auditLogs,
pagination: {
page: 1,
size: 10,
total: auditLogs.length
}
}
}));
return;
}
// 默认请求
try {
this.setRequestHeader('Authorization', token);
} catch(e) {}
return origSend.apply(this, arguments);
};
function respond(xhr, jsonStr) {
Object.defineProperty(xhr, 'readyState', { get: () => 4 });
Object.defineProperty(xhr, 'status', { get: () => 200 });
Object.defineProperty(xhr, 'statusText', { get: () => 'OK' });
Object.defineProperty(xhr, 'responseText', { get: () => jsonStr });
Object.defineProperty(xhr, 'response', { get: () => jsonStr });
setTimeout(() => {
if (typeof xhr.onreadystatechange === 'function') {
xhr.onreadystatechange();
}
xhr.dispatchEvent(new Event('readystatechange'));
xhr.dispatchEvent(new Event('load'));
xhr.dispatchEvent(new Event('loadend'));
}, 40);
}
// 页面加载完成后展示初始面板并在商机看板维持联动面板
function ensurePanel() {
if (window.location.pathname.includes('/crm/pipeline')) {
updateWebhookPanel({
targetUrl: 'https://api.omnicard.ai/webhook/lead-flow',
leadName: 'John Doe',
leadCompany: 'Acme Global Corp',
fromStatus: 'new',
toStatus: 'hot',
signature: '7f8a3c9b1d5e2f4a6b8c0d2e4f6a8b0c2d4e6f8a0b2c4d6e8f0a2b4c6d8e0f2a',
httpStatus: 200,
attempts: 1,
time: '2026-09-21 01:32:15'
});
}
}
window.addEventListener('DOMContentLoaded', ensurePanel);
setInterval(ensurePanel, 1000);
window.updateWebhookPanel = updateWebhookPanel;
})();