You need to sign in before continuing.
import.js 4.83 KB
/**
 * import.js — 多源数据导入 REST 路由
 *
 * POST /api/import/linkedin   body: { filePath }
 * POST /api/import/x          body: { filePath, personNameOrId }
 * POST /api/import/meeting    body: { title?, date?, summary, rawText?, participants? }
 */

const express = require('express');
const router = express.Router();

const { LinkedInReader } = require('../services/linkedin/linkedin-reader');
const { XReader }        = require('../services/x/x-reader');
const { MeetingImporter } = require('../services/meeting/meeting-importer');
const { getGraphStore }  = require('../db/GraphStore');

// ── POST /api/import/linkedin ────────────────────────────────────────
router.post('/linkedin', (req, res) => {
    try {
        const { filePath } = req.body;
        if (!filePath) {
            return res.status(400).json({ error: 'filePath 为必填字段' });
        }

        const reader = new LinkedInReader(filePath);
        const contacts = reader.parse();
        const g = getGraphStore();

        let created = 0;
        let updated = 0;

        for (const c of contacts) {
            const fullName = [c.firstName, c.lastName].filter(Boolean).join(' ');
            if (!fullName) continue;

            const existing = g.findByName(fullName, 'Person');
            const id = existing[0]?.id;

            g.createNode('Person', {
                ...(id ? { id } : {}),
                name: fullName,
                email: c.email || undefined,
                company: c.company || undefined,
                role: c.position || undefined,
                linkedin: c.connectedOn ? `connected:${c.connectedOn}` : undefined,
                source: 'linkedin',
            });

            if (id) updated++; else created++;
        }

        res.json({
            success: true,
            total: contacts.length,
            created,
            updated,
            message: `LinkedIn 导入完成:新建 ${created} 人,更新 ${updated} 人`,
        });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

// ── POST /api/import/x ───────────────────────────────────────────────
router.post('/x', (req, res) => {
    try {
        const { filePath, personNameOrId } = req.body;
        if (!filePath || !personNameOrId) {
            return res.status(400).json({ error: 'filePath 和 personNameOrId 为必填字段' });
        }

        const g = getGraphStore();

        // 解析联系人
        let person = g.getNode(personNameOrId);
        if (!person) {
            const results = g.findByName(personNameOrId, 'Person');
            person = results[0] || null;
        }
        if (!person) {
            return res.status(404).json({ error: `未找到联系人:${personNameOrId}` });
        }

        const reader = new XReader(filePath);
        const tweets = reader.parseTweets();
        let imported = 0;

        for (const tw of tweets) {
            const eventNode = g.createNode('Event', {
                type: 'post',
                summary: tw.text.slice(0, 140),
                rawText: tw.text,
                date: tw.createdAt
                    ? new Date(tw.createdAt).toISOString().slice(0, 10)
                    : undefined,
                tweetId: tw.id,
                hashtags: tw.hashtags,
                source: 'x_twitter',
            });

            g.createEdge(person.id, eventNode.id, 'ASSOCIATED');
            imported++;
        }

        res.json({
            success: true,
            imported,
            personId: person.id,
            personName: person.name,
            message: `X 动态导入完成:共导入 ${imported} 条推文`,
        });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

// ── POST /api/import/meeting ─────────────────────────────────────────
router.post('/meeting', (req, res) => {
    try {
        const { title, date, summary, rawText, participants } = req.body;
        if (!summary) {
            return res.status(400).json({ error: 'summary 为必填字段' });
        }

        const importer = new MeetingImporter();
        const result = importer.importMeetingNotes({ title, date, summary, rawText, participants });

        res.json({
            success: true,
            eventId: result.eventId,
            linkedParticipants: result.linkedParticipants,
            skippedParticipants: result.skippedParticipants,
            message: `会议纪要已导入,关联 ${result.linkedParticipants.length} 位参与者`,
        });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

module.exports = router;