插件
This commit is contained in:
+109
@@ -0,0 +1,109 @@
|
||||
// Adapter: maps the page-side "record + messages" shape scraped from
|
||||
// Alibaba's CRM pages to the backend `/api/ingest` zod PayloadSchema.
|
||||
|
||||
// Trim undefined/empty/null to null for optional string fields.
|
||||
const s = v => (v == null || v === '') ? null : v;
|
||||
|
||||
// Coerce numeric strings (e.g. "39") to number; otherwise null.
|
||||
const n = v => {
|
||||
if (v == null || v === '') return null;
|
||||
const x = Number(v);
|
||||
return Number.isFinite(x) ? x : null;
|
||||
};
|
||||
|
||||
// 只接受标准 ISO(YYYY-MM-DDTHH:...);非 ISO(列表页 "11:58"/"2026-7-8")跳过 → 兜底当前时间
|
||||
// 注:取消聊天时无 gmtCreate,列表时间为非 ISO,这里保证 createTime/updateTime 始终合法 ISO。
|
||||
// 准确时间待抓聊天时由 gmtCreate 通过 upsert 覆盖。
|
||||
const isIso = v => typeof v === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(v);
|
||||
// 解析 alibaba 列表的非 ISO 时间:
|
||||
// "2026-7-1"(YYYY-M-D,创建时间)→ 当天 00:00(本地)
|
||||
// "13:58"(HH:mm,今天的更新时间)→ 今天该时分(本地)
|
||||
const parseListTime = v => {
|
||||
if (v == null || v === '') return null;
|
||||
const s = String(v).trim();
|
||||
const d = s.match(/^(\d{4})-(\d{1,2})-(\d{1,2})$/);
|
||||
if (d) return new Date(Number(d[1]), Number(d[2]) - 1, Number(d[3])).toISOString();
|
||||
const hm = s.match(/^(\d{1,2}):(\d{2})$/);
|
||||
if (hm) {
|
||||
const n = new Date();
|
||||
return new Date(n.getFullYear(), n.getMonth(), n.getDate(), Number(hm[1]), Number(hm[2])).toISOString();
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const isoFallback = (...vals) => {
|
||||
for (const v of vals) {
|
||||
if (isIso(v)) return v;
|
||||
const p = parseListTime(v);
|
||||
if (p) return p;
|
||||
}
|
||||
return new Date(Date.now()).toISOString();
|
||||
};
|
||||
|
||||
export function buildIngestPayload(record, messages) {
|
||||
// 阶段1:允许 messages 为空(先走通列表+客户+行为,聊天后续补充)
|
||||
const msgs = messages || [];
|
||||
const iq = record || {};
|
||||
|
||||
const inquiry = {
|
||||
inquiryId: String(iq.inquiryId),
|
||||
manager: s(iq.manager) || undefined,
|
||||
createTime: isoFallback(iq.createTimeFromApi, iq.createTime),
|
||||
updateTime: isoFallback(
|
||||
iq.updateTime,
|
||||
iq.updateTimeFromApi,
|
||||
iq.createTimeFromApi,
|
||||
),
|
||||
status: s(iq.status) || undefined,
|
||||
msgPreview: s(iq.msgPreview) || undefined,
|
||||
isFlagged: !!iq.isFlagged,
|
||||
};
|
||||
|
||||
const customer = iq.customerId
|
||||
? {
|
||||
customerId: String(iq.customerId),
|
||||
name: s(iq.detailBuyerName) || s(iq.buyerName),
|
||||
country: s(iq.detailCountry) || s(iq.countryCode),
|
||||
email: s(iq.email),
|
||||
phone: s(iq.phone),
|
||||
landline: s(iq.landline),
|
||||
businessType: s(iq.businessType),
|
||||
salesPlatform: s(iq.salesPlatform),
|
||||
website: s(iq.website),
|
||||
company: s(iq.company),
|
||||
registerTime: s(iq.registerTime),
|
||||
avatarUrl: s(iq.detailAvatarUrl) || s(iq.avatarUrl) || undefined,
|
||||
buyerTags: s(iq.buyerTags) || undefined,
|
||||
buyerFeatures: s(iq.buyerFeatures) || undefined,
|
||||
recentInquiryProducts: Array.isArray(iq.recentInquiryProducts) ? iq.recentInquiryProducts : undefined,
|
||||
storeInquiryProduct: s(iq.storeInquiryProduct) || undefined,
|
||||
storeInquiryQty: s(iq.storeInquiryQty) || undefined,
|
||||
storeVisitProducts: Array.isArray(iq.storeVisitProducts) ? iq.storeVisitProducts : undefined,
|
||||
behavior: iq.behavior
|
||||
? {
|
||||
productViews: n(iq.behavior.productViews),
|
||||
validInquiries: n(iq.behavior.validInquiries),
|
||||
validRfq: n(iq.behavior.validRFQ), // detail 用 validRFQ(大写)
|
||||
loginDays: n(iq.behavior.loginDays),
|
||||
spamInquiries: n(iq.behavior.spamInquiries),
|
||||
blacklistCount: n(iq.behavior.blacklistCount),
|
||||
topIndustry: s(iq.behavior.topIndustry),
|
||||
}
|
||||
: undefined,
|
||||
}
|
||||
: null;
|
||||
|
||||
const out = {
|
||||
inquiry,
|
||||
customer,
|
||||
messages: msgs.map(m => ({
|
||||
messageId: String(m.messageId),
|
||||
sendTime: m.sendTime,
|
||||
sender: s(m.sender) || undefined,
|
||||
direction: m.direction,
|
||||
content: m.original ?? m.content ?? '',
|
||||
translation: s(m.translation) ?? '',
|
||||
})),
|
||||
};
|
||||
|
||||
return out;
|
||||
}
|
||||
Reference in New Issue
Block a user