Files
alibaba/push/payload.js
T

110 lines
4.0 KiB
JavaScript
Raw Normal View History

2026-08-27 16:12:37 +08:00
// 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;
}