feat: 访问埋点统计——VisitLog模型+/api GET埋点中间件(小程序/H5/直接访问来源识别)+访问统计接口+看板PV/UV/来源分布卡+日志页访问埋点Tab
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
const express = require('express')
|
||||
const router = express.Router()
|
||||
const mongoose = require('mongoose')
|
||||
const { User, Order, Knowledge, ShopItem, AdminLog, AIChatQuota, Feedback, LoginLog, Trend, KnowledgeLit, CoinLedger } = require('../models')
|
||||
const { User, Order, Knowledge, ShopItem, AdminLog, AIChatQuota, Feedback, LoginLog, Trend, KnowledgeLit, CoinLedger, VisitLog } = require('../models')
|
||||
const { authMiddleware, requirePermission } = require('../middleware/auth')
|
||||
const ApiResponse = require('../utils/response')
|
||||
const logger = require('../utils/logger')
|
||||
@@ -33,6 +33,54 @@ const logAction = async (req, action, resource, resourceId = null, details = {})
|
||||
* 获取仪表盘数据
|
||||
* GET /api/admin/dashboard
|
||||
*/
|
||||
// 访问统计(含 H5 / 小程序 / 直接访问)
|
||||
router.get('/visit-stats', requirePermission('dashboard:read'), async (req, res) => {
|
||||
try {
|
||||
const startOfDay = new Date(new Date().toLocaleDateString('sv-SE') + 'T00:00:00+08:00')
|
||||
const sevenAgo = new Date(Date.now() - 6 * 86400000)
|
||||
const startOf7 = new Date(new Date(sevenAgo).toLocaleDateString('sv-SE') + 'T00:00:00+08:00')
|
||||
const [pvToday, uvAgg, loginToday, h5Today, miniToday] = await Promise.all([
|
||||
VisitLog.countDocuments({ createdAt: { $gte: startOfDay } }),
|
||||
VisitLog.distinct('ip', { createdAt: { $gte: startOfDay } }),
|
||||
LoginLog.countDocuments({ createdAt: { $gte: startOfDay } }),
|
||||
VisitLog.countDocuments({ createdAt: { $gte: startOfDay }, source: 'H5' }),
|
||||
VisitLog.countDocuments({ createdAt: { $gte: startOfDay }, source: '小程序' }),
|
||||
])
|
||||
const sources = await VisitLog.aggregate([
|
||||
{ $match: { createdAt: { $gte: startOf7 } } },
|
||||
{ $group: { _id: '$source', c: { $sum: 1 } } },
|
||||
{ $sort: { c: -1 } },
|
||||
])
|
||||
return ApiResponse.success(res, {
|
||||
pvToday,
|
||||
uvToday: uvAgg.length,
|
||||
loginToday,
|
||||
h5Today,
|
||||
miniToday,
|
||||
sources7d: sources.map(s => ({ source: s._id || '直接访问', count: s.c })),
|
||||
})
|
||||
} catch (error) {
|
||||
logger.error('访问统计失败:', error)
|
||||
return ApiResponse.serverError(res, error.message)
|
||||
}
|
||||
})
|
||||
|
||||
// 访问日志列表
|
||||
router.get('/visits', requirePermission('dashboard:read'), async (req, res) => {
|
||||
try {
|
||||
const page = Math.max(1, parseInt(req.query.page) || 1)
|
||||
const limit = Math.min(50, parseInt(req.query.limit) || 20)
|
||||
const [total, items] = await Promise.all([
|
||||
VisitLog.countDocuments({}),
|
||||
VisitLog.find({}).sort({ createdAt: -1 }).skip((page - 1) * limit).limit(limit).lean(),
|
||||
])
|
||||
return ApiResponse.success(res, { items, total, page, limit })
|
||||
} catch (error) {
|
||||
logger.error('访问日志查询失败:', error)
|
||||
return ApiResponse.serverError(res, error.message)
|
||||
}
|
||||
})
|
||||
|
||||
router.get('/dashboard', requirePermission('dashboard:read'), async (req, res) => {
|
||||
try {
|
||||
const today = new Date()
|
||||
|
||||
Reference in New Issue
Block a user