autosnippet
Version:
Extract code patterns into a knowledge base for AI coding assistants
335 lines (334 loc) • 11.5 kB
JavaScript
/**
* Panorama API 路由
*
* 端点:
* GET /api/v1/panorama — 项目全景概览
* GET /api/v1/panorama/health — 全景健康度
* GET /api/v1/panorama/gaps — 知识空白区
* GET /api/v1/panorama/module/:name — 单模块详情
*/
import express from 'express';
import { getServiceContainer } from '../../injection/ServiceContainer.js';
const router = express.Router();
/**
* GET /api/v1/panorama
* 返回项目全景概览(层级、模块、覆盖率)
*/
router.get('/', async (req, res) => {
try {
const container = getServiceContainer();
const panoramaService = container.get('panoramaService');
if (!panoramaService) {
res.status(503).json({
success: false,
error: { code: 'SERVICE_UNAVAILABLE', message: 'PanoramaService not available' },
});
return;
}
if (req.query.refresh === 'true' && typeof panoramaService.invalidate === 'function') {
panoramaService.invalidate();
}
if (typeof panoramaService.ensureData === 'function') {
await panoramaService.ensureData();
}
const overview = await panoramaService.getOverview();
res.json({ success: true, data: overview });
}
catch (err) {
res.status(500).json({
success: false,
error: { code: 'PANORAMA_ERROR', message: err.message },
});
}
});
/**
* GET /api/v1/panorama/health
* 返回全景健康度评分
*/
router.get('/health', async (req, res) => {
try {
const container = getServiceContainer();
const panoramaService = container.get('panoramaService');
if (!panoramaService) {
res.status(503).json({
success: false,
error: { code: 'SERVICE_UNAVAILABLE', message: 'PanoramaService not available' },
});
return;
}
if (req.query.refresh === 'true' && typeof panoramaService.invalidate === 'function') {
panoramaService.invalidate();
}
if (typeof panoramaService.ensureData === 'function') {
await panoramaService.ensureData();
}
const health = await panoramaService.getHealth();
res.json({ success: true, data: health });
}
catch (err) {
res.status(500).json({
success: false,
error: { code: 'PANORAMA_ERROR', message: err.message },
});
}
});
/**
* GET /api/v1/panorama/gaps
* 返回知识空白区列表
*/
router.get('/gaps', async (req, res) => {
try {
const container = getServiceContainer();
const panoramaService = container.get('panoramaService');
if (!panoramaService) {
res.status(503).json({
success: false,
error: { code: 'SERVICE_UNAVAILABLE', message: 'PanoramaService not available' },
});
return;
}
if (req.query.refresh === 'true' && typeof panoramaService.invalidate === 'function') {
panoramaService.invalidate();
}
if (typeof panoramaService.ensureData === 'function') {
await panoramaService.ensureData();
}
const gaps = await panoramaService.getGaps();
res.json({ success: true, data: gaps });
}
catch (err) {
res.status(500).json({
success: false,
error: { code: 'PANORAMA_ERROR', message: err.message },
});
}
});
/**
* GET /api/v1/panorama/coverage
* 返回各模块知识覆盖率热力图数据
*/
router.get('/coverage', async (req, res) => {
try {
const container = getServiceContainer();
const panoramaService = container.get('panoramaService');
if (!panoramaService) {
res.status(503).json({
success: false,
error: { code: 'SERVICE_UNAVAILABLE', message: 'PanoramaService not available' },
});
return;
}
if (req.query.refresh === 'true' && typeof panoramaService.invalidate === 'function') {
panoramaService.invalidate();
}
if (typeof panoramaService.ensureData === 'function') {
await panoramaService.ensureData();
}
const overview = await panoramaService.getOverview();
const gaps = (await panoramaService.getGaps?.()) ?? [];
// 构建模块级覆盖率数据:从 overview.layers 中提取每个模块的文件数和 recipe 数
const modules = [];
for (const layer of overview.layers || []) {
const layerModules = layer.modules || [];
for (const mod of layerModules) {
const fileCount = mod.fileCount || 0;
const recipeCount = mod.recipeCount || 0;
const coverage = fileCount > 0 ? Math.round((recipeCount / fileCount) * 100) : 0;
modules.push({
name: mod.name || 'unknown',
layer: layer.name,
fileCount,
recipeCount,
coverage: Math.min(coverage, 100),
});
}
}
// 按覆盖率升序(低覆盖在前,方便高亮)
modules.sort((a, b) => a.coverage - b.coverage);
// 空白区按维度聚合
const gapsByDimension = {};
for (const gap of gaps) {
const dim = gap.dimensionName || 'unknown';
gapsByDimension[dim] = (gapsByDimension[dim] || 0) + 1;
}
res.json({
success: true,
data: {
modules,
gapsByDimension,
overallCoverage: overview.overallCoverage ?? 0,
totalFiles: overview.totalFiles ?? 0,
totalRecipes: overview.totalRecipes ?? 0,
},
});
}
catch (err) {
res.status(500).json({
success: false,
error: { code: 'PANORAMA_ERROR', message: err.message },
});
}
});
/**
* GET /api/v1/panorama/module/:name
* 返回单模块详情
*/
router.get('/module/:name', async (req, res) => {
try {
const container = getServiceContainer();
const panoramaService = container.get('panoramaService');
if (!panoramaService) {
res.status(503).json({
success: false,
error: { code: 'SERVICE_UNAVAILABLE', message: 'PanoramaService not available' },
});
return;
}
if (typeof panoramaService.ensureData === 'function') {
await panoramaService.ensureData();
}
const detail = await panoramaService.getModule(req.params.name);
if (!detail) {
res.status(404).json({
success: false,
error: { code: 'MODULE_NOT_FOUND', message: `Module "${req.params.name}" not found` },
});
return;
}
res.json({ success: true, data: detail });
}
catch (err) {
res.status(500).json({
success: false,
error: { code: 'PANORAMA_ERROR', message: err.message },
});
}
});
/* ═══ 治理 (Governance) ═══════════════════════════════════════ */
/**
* POST /api/v1/panorama/governance/cycle
* 执行完整治理周期(矛盾检测 + 冗余分析 + 衰退扫描)
*/
router.post('/governance/cycle', async (_req, res) => {
try {
const container = getServiceContainer();
const metabolism = container.get('knowledgeMetabolism');
if (!metabolism) {
res.status(503).json({
success: false,
error: { code: 'SERVICE_UNAVAILABLE', message: 'KnowledgeMetabolism not available' },
});
return;
}
const report = await metabolism.runFullCycle();
res.json({ success: true, data: report });
}
catch (err) {
res.status(500).json({
success: false,
error: { code: 'GOVERNANCE_ERROR', message: err.message },
});
}
});
/**
* GET /api/v1/panorama/governance/decay
* 获取衰退评估报告
*/
router.get('/governance/decay', async (_req, res) => {
try {
const container = getServiceContainer();
const decayDetector = container.get('decayDetector');
if (!decayDetector) {
res.status(503).json({
success: false,
error: { code: 'SERVICE_UNAVAILABLE', message: 'DecayDetector not available' },
});
return;
}
const results = await decayDetector.scanAll();
res.json({ success: true, data: { results } });
}
catch (err) {
res.status(500).json({
success: false,
error: { code: 'GOVERNANCE_ERROR', message: err.message },
});
}
});
/**
* POST /api/v1/panorama/governance/staging-check
* 检查 staging 条目并自动发布到期的
*/
router.post('/governance/staging-check', async (_req, res) => {
try {
const container = getServiceContainer();
const stagingManager = container.get('stagingManager');
if (!stagingManager) {
res.status(503).json({
success: false,
error: { code: 'SERVICE_UNAVAILABLE', message: 'StagingManager not available' },
});
return;
}
const checkResult = await stagingManager.checkAndPromote();
const currentStaging = await stagingManager.listStaging();
res.json({ success: true, data: { checkResult, currentStaging } });
}
catch (err) {
res.status(500).json({
success: false,
error: { code: 'GOVERNANCE_ERROR', message: err.message },
});
}
});
/**
* GET /api/v1/panorama/governance/staging
* 获取当前 staging 列表(只读)
*/
router.get('/governance/staging', async (_req, res) => {
try {
const container = getServiceContainer();
const stagingManager = container.get('stagingManager');
if (!stagingManager) {
res.status(503).json({
success: false,
error: { code: 'SERVICE_UNAVAILABLE', message: 'StagingManager not available' },
});
return;
}
const entries = await stagingManager.listStaging();
res.json({ success: true, data: { entries } });
}
catch (err) {
res.status(500).json({
success: false,
error: { code: 'GOVERNANCE_ERROR', message: err.message },
});
}
});
/**
* GET /api/v1/panorama/governance/enhancements
* 获取增强建议
*/
router.get('/governance/enhancements', async (_req, res) => {
try {
const container = getServiceContainer();
const suggester = container.get('enhancementSuggester');
if (!suggester) {
res.status(503).json({
success: false,
error: { code: 'SERVICE_UNAVAILABLE', message: 'EnhancementSuggester not available' },
});
return;
}
const suggestions = await suggester.analyzeAll();
res.json({ success: true, data: { suggestions } });
}
catch (err) {
res.status(500).json({
success: false,
error: { code: 'GOVERNANCE_ERROR', message: err.message },
});
}
});
export default router;