feat(admin): Implement operational monitoring MVP and login optimization
Summary: - Add SimpleLog table for activity tracking (admin_schema) - Implement ActivityService with fire-and-forget pattern - Add stats API endpoints (overview/live-feed/user-overview/cleanup) - Complete activity logging for 7 modules (SYSTEM/AIA/PKB/ASL/DC/RVW/IIT) - Update Admin Dashboard with DAU/DAT metrics and live feed - Fix user module permission display logic - Fix login redirect to /ai-qa instead of homepage - Replace top navigation LOGO with brand image - Fix PKB workspace layout CSS conflict (rename to .pa-chat-container) New files: - backend/src/common/services/activity.service.ts - backend/src/modules/admin/controllers/statsController.ts - backend/src/modules/admin/routes/statsRoutes.ts - frontend-v2/src/modules/admin/api/statsApi.ts - docs/03-.../04-operational-monitoring-mvp-plan.md - docs/03-.../04-operational-monitoring-mvp-implementation.md Tested: All features verified locally
This commit is contained in:
126
backend/src/modules/admin/controllers/statsController.ts
Normal file
126
backend/src/modules/admin/controllers/statsController.ts
Normal file
@@ -0,0 +1,126 @@
|
||||
/**
|
||||
* Stats Controller - 运营统计控制器
|
||||
*
|
||||
* 提供运营看板数据接口
|
||||
*
|
||||
* @version 1.0.0
|
||||
* @date 2026-01-25
|
||||
*/
|
||||
|
||||
import type { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { activityService } from '../../../common/services/activity.service.js';
|
||||
import { logger } from '../../../common/logging/index.js';
|
||||
|
||||
/**
|
||||
* 获取今日大盘数据
|
||||
* GET /api/admin/stats/overview
|
||||
*/
|
||||
export async function getOverview(
|
||||
request: FastifyRequest,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const data = await activityService.getTodayOverview();
|
||||
return reply.send({
|
||||
success: true,
|
||||
data,
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[StatsController] 获取大盘数据失败', { error: error.message });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '获取大盘数据失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实时流水账
|
||||
* GET /api/admin/stats/live-feed?limit=100
|
||||
*/
|
||||
export async function getLiveFeed(
|
||||
request: FastifyRequest<{ Querystring: { limit?: string } }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const limit = Math.min(Number(request.query.limit) || 100, 500); // 最大500条
|
||||
const data = await activityService.getLiveFeed(limit);
|
||||
return reply.send({
|
||||
success: true,
|
||||
data,
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[StatsController] 获取流水账失败', { error: error.message });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '获取流水账失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户360画像
|
||||
* GET /api/admin/users/:id/overview
|
||||
*/
|
||||
export async function getUserOverview(
|
||||
request: FastifyRequest<{ Params: { id: string } }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const { id } = request.params;
|
||||
|
||||
if (!id) {
|
||||
return reply.status(400).send({
|
||||
success: false,
|
||||
message: '用户ID不能为空',
|
||||
});
|
||||
}
|
||||
|
||||
const data = await activityService.getUserOverview(id);
|
||||
|
||||
if (!data.profile) {
|
||||
return reply.status(404).send({
|
||||
success: false,
|
||||
message: '用户不存在',
|
||||
});
|
||||
}
|
||||
|
||||
return reply.send({
|
||||
success: true,
|
||||
data,
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[StatsController] 获取用户画像失败', { error: error.message });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '获取用户画像失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理过期日志(管理接口)
|
||||
* POST /api/admin/stats/cleanup
|
||||
*/
|
||||
export async function cleanupLogs(
|
||||
request: FastifyRequest,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
try {
|
||||
const deletedCount = await activityService.cleanupOldLogs();
|
||||
return reply.send({
|
||||
success: true,
|
||||
data: {
|
||||
deletedCount,
|
||||
message: `已清理 ${deletedCount} 条过期日志`,
|
||||
},
|
||||
});
|
||||
} catch (error: any) {
|
||||
logger.error('[StatsController] 清理日志失败', { error: error.message });
|
||||
return reply.status(500).send({
|
||||
success: false,
|
||||
message: error.message || '清理日志失败',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user