后台
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
import { Router } from 'express';
|
||||
import { z } from 'zod';
|
||||
import { ingestAuth } from '../middleware/ingestAuth.js';
|
||||
import { ingestLimiter } from '../middleware/rateLimit.js';
|
||||
import { ingest } from '../services/ingest.service.js';
|
||||
|
||||
const MessageSchema = z.object({
|
||||
messageId: z.string().min(1),
|
||||
sendTime: z.string().datetime(),
|
||||
sender: z.string().optional(),
|
||||
direction: z.enum(['incoming', 'outgoing', 'system']),
|
||||
content: z.string(),
|
||||
translation: z.string().optional().default(''),
|
||||
});
|
||||
|
||||
const BehaviorSchema = z
|
||||
.object({
|
||||
productViews: z.number().nullable().optional(),
|
||||
validInquiries: z.number().nullable().optional(),
|
||||
validRfq: z.number().nullable().optional(),
|
||||
loginDays: z.number().nullable().optional(),
|
||||
spamInquiries: z.number().nullable().optional(),
|
||||
blacklistCount: z.number().nullable().optional(),
|
||||
topIndustry: z.string().nullable().optional(),
|
||||
})
|
||||
.optional();
|
||||
|
||||
const PayloadSchema = z.object({
|
||||
inquiry: z.object({
|
||||
inquiryId: z.string().min(1),
|
||||
manager: z.string().optional(),
|
||||
createTime: z.string().datetime(),
|
||||
updateTime: z.string().datetime(),
|
||||
status: z.string().optional(),
|
||||
msgPreview: z.string().optional(),
|
||||
isFlagged: z.boolean().optional().default(false),
|
||||
}),
|
||||
customer: z
|
||||
.object({
|
||||
customerId: z.string().min(1),
|
||||
name: z.string().optional(),
|
||||
country: z.string().nullable().optional(),
|
||||
email: z.string().nullable().optional(),
|
||||
landline: z.string().nullable().optional(),
|
||||
businessType: z.string().nullable().optional(),
|
||||
salesPlatform: z.string().nullable().optional(),
|
||||
website: z.string().nullable().optional(),
|
||||
phone: z.string().nullable().optional(),
|
||||
company: z.string().nullable().optional(),
|
||||
registerTime: z.string().nullable().optional(), // 页面是 "2016-08-27"(日期非 ISO),放宽
|
||||
avatarUrl: z.string().optional(),
|
||||
buyerTags: z.string().optional(),
|
||||
buyerFeatures: z.string().optional(),
|
||||
recentInquiryProducts: z.array(z.string()).optional(),
|
||||
storeInquiryProduct: z.string().nullable().optional(),
|
||||
storeInquiryQty: z.string().nullable().optional(),
|
||||
storeVisitProducts: z.array(z.object({ img: z.string().optional(), url: z.string().optional(), productId: z.string().optional() })).optional(),
|
||||
behavior: BehaviorSchema,
|
||||
})
|
||||
.nullable(),
|
||||
messages: z.array(MessageSchema).min(0), // 阶段1:允许空(先走通列表+客户+行为,聊天后续补充)
|
||||
});
|
||||
|
||||
export const ingestRouter = Router();
|
||||
|
||||
ingestRouter.post('/', ingestLimiter, ingestAuth, async (req, res, next) => {
|
||||
const parsed = PayloadSchema.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
// format() 显示完整 path(如 messages.0.sendTime),便于定位 null 字段
|
||||
console.error('[ingest 400]', JSON.stringify(parsed.error.format()));
|
||||
return res
|
||||
.status(400)
|
||||
.json({ error: 'invalid payload', issues: parsed.error.flatten() });
|
||||
}
|
||||
try {
|
||||
const d = parsed.data;
|
||||
console.log(`[ingest in] inquiry=${d.inquiry?.inquiryId} customer=${d.customer?.customerId ?? '-'} messages=${d.messages?.length ?? 0}`);
|
||||
const result = await ingest(d);
|
||||
console.log(`[ingest ok] inquiry=${d.inquiry?.inquiryId} -> ${JSON.stringify(result)}`);
|
||||
res.json(result);
|
||||
} catch (e) {
|
||||
console.error('[ingest err] inquiry=', parsed.data.inquiry?.inquiryId, e);
|
||||
next(e);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user