feat: 知识管理加更新时间列;趋势管理待补筛选+统计+AI批量补齐人话解读接口

This commit is contained in:
Hermes Agent
2026-09-19 18:51:44 +08:00
parent 7cd01d8104
commit 61069cd095
3 changed files with 74 additions and 4 deletions
+52 -2
View File
@@ -1157,18 +1157,68 @@ router.get('/login-logs', requirePermission('user:read'), async (req, res) => {
*/
router.get('/trends', requirePermission('trend:read'), async (req, res) => {
try {
const { page = 1, limit = 20, keyword = '' } = req.query
const { page = 1, limit = 20, keyword = '', missing } = req.query
const query = keyword ? { title: { $regex: escapeRegExp(keyword), $options: 'i' } } : {}
if (missing === '1') query.plainExplain = { $in: ['', null, undefined] }
const total = await Trend.countDocuments(query)
const missingCount = await Trend.countDocuments({ plainExplain: { $in: ['', null, undefined] } })
const items = await Trend.find(query).sort({ newsDate: -1, createdAt: -1 })
.skip((parseInt(page) - 1) * parseInt(limit)).limit(parseInt(limit)).lean()
return ApiResponse.paginated(res, items, { page: parseInt(page), limit: parseInt(limit), total })
return res.status(200).json({
success: true,
message: '查询成功',
data: { items, missingCount, pagination: { page: parseInt(page), limit: parseInt(limit), total, totalPages: Math.ceil(total / parseInt(limit)) } },
})
} catch (error) {
logger.error('获取趋势列表失败:', error)
return ApiResponse.serverError(res, '获取趋势列表失败')
}
})
// AI 批量补齐人话解读(复用 aiChat 的模型配置:数据库活跃模型或环境变量)
router.post('/trends/fill-plain', requirePermission('trend:write'), async (req, res) => {
try {
const limit = Math.min(30, parseInt(req.body?.limit) || 20)
const missing = await Trend.find({ plainExplain: { $in: ['', null, undefined] } })
.sort({ newsDate: -1 }).limit(limit).lean()
if (!missing.length) return ApiResponse.success(res, { filled: 0, total: 0 }, '没有待补条目')
// 取模型配置
const active = await AIModel.getActiveModels().catch(() => [])
const model = (active && active[0]) || {
apiUrl: process.env.OPENAI_API_URL || 'https://api.openai.com/v1/chat/completions',
apiKey: process.env.OPENAI_API_KEY,
modelId: process.env.OPENAI_MODEL || 'gpt-3.5-turbo',
}
if (!model.apiKey) return ApiResponse.error(res, '未配置 AI 模型(系统设置→AI模型)', 400)
let filled = 0
for (const t of missing) {
try {
const prompt = `给下面这条AI行业新闻写两句中文解读。要求:1) 第一句"人话":用不超过40字的大白话说清这件事;2) 第二句"对你":说明对普通用户/从业者意味着什么,不超过40字。只输出两行,格式:人话内容\n对你内容\n\n新闻标题:${t.title}\n摘要:${(t.summary || '').slice(0, 200)}`
const r = await fetch(model.apiUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${model.apiKey}` },
body: JSON.stringify({ model: model.modelId, messages: [{ role: 'user', content: prompt }], max_tokens: 300, temperature: 0.5 }),
signal: AbortSignal.timeout(60_000),
})
const j = await r.json()
const text = String(j?.choices?.[0]?.message?.content || '').trim()
const lines = text.split('\n').map(x => x.replace(/^(人话|对你)[::]?\s*/, '').trim()).filter(Boolean)
if (lines.length >= 2) {
await Trend.updateOne({ _id: t._id }, { plainExplain: lines[0].slice(0, 60), takeAway: lines[1].slice(0, 60) })
filled++
}
} catch (e) { logger.warn('补齐失败:', t.title, e.message) }
}
const remain = await Trend.countDocuments({ plainExplain: { $in: ['', null, undefined] } })
return ApiResponse.success(res, { filled, remain }, `已补齐 ${filled} 条,剩余 ${remain} 条待补`)
} catch (error) {
logger.error('批量补齐失败:', error)
return ApiResponse.serverError(res, '批量补齐失败')
}
})
router.post('/trends', requirePermission('trend:write'), async (req, res) => {
try {
const { title, summary, source, sourceUrl, category, tags, hot, picked, newsDate, plainExplain, takeAway } = req.body