analysis.js 4.16 KB
/**
 * analysis.js — AI 深度分析 REST 路由
 *
 * GET  /api/analysis/communities         → detectCommunities()
 * GET  /api/analysis/followups?days=30   → getFollowUpCandidates()
 * POST /api/analysis/activate            body: { personId } → activate + return summary
 */

const express = require('express');
const router = express.Router();
const { getGraphStore } = require('../db/GraphStore');

// ── GET /api/analysis/communities ───────────────────────────────────
router.get('/communities', (req, res) => {
    try {
        const maxIter = parseInt(req.query.maxIter, 10) || 10;
        const g = getGraphStore();
        const result = g.detectCommunities(maxIter);
        res.json({ success: true, ...result });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

// ── GET /api/analysis/followups ──────────────────────────────────────
router.get('/followups', (req, res) => {
    try {
        const days  = parseInt(req.query.days, 10)  || 30;
        const limit = parseInt(req.query.limit, 10) || 20;
        const g = getGraphStore();
        const candidates = g.getFollowUpCandidates(days, limit);
        res.json({ success: true, total: candidates.length, count: candidates.length, candidates });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

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

        const g = getGraphStore();

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

        // 聚合分析
        const { outgoing, incoming } = g.getEdges(person.id);
        const allEdges = [...outgoing, ...incoming];

        let interactionCount = 0;
        const relatedOrgs = new Set();
        const recentEvents = [];
        const now = Date.now();
        let latestEventDate = null;

        for (const edge of allEdges) {
            const neighborId = edge.from_id === person.id ? edge.to_id : edge.from_id;
            const neighbor = g.getNode(neighborId);
            if (!neighbor) continue;

            if (neighbor._type === 'Event') {
                interactionCount++;
                const dateStr = neighbor.date || neighbor.createdAt || null;
                if (dateStr) {
                    recentEvents.push({ title: neighbor.title || neighbor.summary, date: dateStr });
                    if (!latestEventDate || dateStr > latestEventDate) {
                        latestEventDate = dateStr;
                    }
                }
            } else if (neighbor._type === 'Organization') {
                relatedOrgs.add(neighbor.name || neighborId);
            }
        }

        recentEvents.sort((a, b) => (b.date > a.date ? 1 : -1));
        const daysSinceLast = latestEventDate
            ? Math.floor((now - new Date(latestEventDate).getTime()) / (1000 * 60 * 60 * 24))
            : null;

        // 更新状态
        g.setAnalysisStatus(person.id, 'complete');

        res.json({
            success: true,
            personId: person.id,
            personName: person.name,
            analysisStatus: 'complete',
            summary: {
                interactionCount,
                relatedOrganizations: [...relatedOrgs],
                latestInteraction: latestEventDate,
                daysSinceLastInteraction: daysSinceLast,
                recentEvents: recentEvents.slice(0, 5),
            },
        });
    } catch (err) {
        res.status(500).json({ error: err.message });
    }
});

module.exports = router;