Files
ai-dimension/backend/wdkj-server/src/routes/aiModel.js
T
Yuzhiran Dev 24cd9cef53 🎉 feat: initialize 宇之然AI维度 project
- uni-app Vue3 frontend with dark glassmorphism theme
- 5 AI dimensions: Origin / Development / Current / Learning / Trend
- AI Chat system prompt updated from geometry to AI
- 23 AI knowledge articles initialized in DB
- Trend API + cron job for daily news
- Product pricing (Pro ¥19.9, VIP ¥39.9)
- Pinia stores + API utilities
- AGENTS.md documentation
2026-07-10 13:26:04 +08:00

352 lines
9.4 KiB
JavaScript
Executable File

const express = require('express')
const router = express.Router()
const { AIModel } = require('../models')
const { authMiddleware, requirePermission } = require('../middleware/auth')
const ApiResponse = require('../utils/response')
const logger = require('../utils/logger')
// 所有路由都需要认证
router.use(authMiddleware)
/**
* 获取当前使用的模型(公开接口,用于AI问答)
* GET /api/ai-model/current
*/
router.get('/current', async (req, res) => {
try {
const model = await AIModel.getCurrentModel()
if (!model) {
// 如果没有配置模型,返回环境变量中的默认配置
return ApiResponse.success(res, {
name: '默认模型',
modelId: process.env.OPENAI_MODEL || 'gpt-3.5-turbo',
apiUrl: process.env.OPENAI_API_URL || 'https://api.openai.com/v1/chat/completions',
apiKey: process.env.OPENAI_API_KEY || '',
config: {
temperature: 0.7,
maxTokens: 800,
topP: 1
}
})
}
// 返回模型信息(不返回API Key)
return ApiResponse.success(res, {
name: model.name,
modelId: model.modelId,
config: model.config
})
} catch (error) {
logger.error('获取当前模型失败:', error)
return ApiResponse.serverError(res, error.message)
}
})
/**
* 获取模型列表(管理后台)
* GET /api/ai-model/list
*/
router.get('/list', requirePermission('dashboard:read'), async (req, res) => {
try {
const { page = 1, pageSize = 10, keyword = '' } = req.query
const query = keyword
? { $or: [
{ name: { $regex: keyword, $options: 'i' } },
{ modelId: { $regex: keyword, $options: 'i' } }
]}
: {}
const [list, total] = await Promise.all([
AIModel.find(query)
.sort({ isDefault: -1, priority: 1, createdAt: -1 })
.skip((page - 1) * pageSize)
.limit(parseInt(pageSize)),
AIModel.countDocuments(query)
])
return ApiResponse.success(res, {
list,
total,
page: parseInt(page),
pageSize: parseInt(pageSize)
})
} catch (error) {
logger.error('获取模型列表失败:', error)
return ApiResponse.serverError(res, error.message)
}
})
/**
* 获取所有启用的模型(下拉选择用)
* GET /api/ai-model/active
*/
router.get('/active', requirePermission('dashboard:read'), async (req, res) => {
try {
const models = await AIModel.getActiveModels()
return ApiResponse.success(res, {
list: models.map(m => ({
_id: m._id,
name: m.name,
modelId: m.modelId,
isDefault: m.isDefault
}))
})
} catch (error) {
logger.error('获取启用模型失败:', error)
return ApiResponse.serverError(res, error.message)
}
})
/**
* 获取模型详情
* GET /api/ai-model/:id
*/
router.get('/:id', requirePermission('dashboard:read'), async (req, res) => {
try {
const { id } = req.params
const model = await AIModel.findById(id)
if (!model) {
return ApiResponse.notFound(res, '模型不存在')
}
return ApiResponse.success(res, { model })
} catch (error) {
logger.error('获取模型详情失败:', error)
return ApiResponse.serverError(res, error.message)
}
})
/**
* 创建模型
* POST /api/ai-model
*/
router.post('/', requirePermission('settings:write'), async (req, res) => {
try {
const { name, modelId, apiUrl, apiKey, description, config, priority } = req.body
// 验证必填字段
if (!name || !modelId || !apiUrl || !apiKey) {
return ApiResponse.error(res, '请填写所有必填字段', 400)
}
// 检查modelId是否已存在
const existingModel = await AIModel.findOne({ modelId })
if (existingModel) {
return ApiResponse.error(res, '该模型ID已存在', 400)
}
const model = new AIModel({
name,
modelId,
apiUrl,
apiKey,
description,
config,
priority
})
await model.save()
logger.info('创建AI模型:', { name, modelId, admin: req.admin.username })
return ApiResponse.success(res, {
message: '创建成功',
model: {
_id: model._id,
name: model.name,
modelId: model.modelId,
isDefault: model.isDefault,
isActive: model.isActive
}
})
} catch (error) {
logger.error('创建模型失败:', error)
return ApiResponse.serverError(res, error.message)
}
})
/**
* 更新模型
* PUT /api/ai-model/:id
*/
router.put('/:id', requirePermission('settings:write'), async (req, res) => {
try {
const { id } = req.params
const { name, modelId, apiUrl, apiKey, description, isActive, isDefault, config, priority } = req.body
const model = await AIModel.findById(id)
if (!model) {
return ApiResponse.notFound(res, '模型不存在')
}
// 如果修改了modelId,检查是否与其他模型冲突
if (modelId && modelId !== model.modelId) {
const existingModel = await AIModel.findOne({ modelId, _id: { $ne: id } })
if (existingModel) {
return ApiResponse.error(res, '该模型ID已存在', 400)
}
}
// 更新字段
if (name) model.name = name
if (modelId) model.modelId = modelId
if (apiUrl) model.apiUrl = apiUrl
if (apiKey) model.apiKey = apiKey
if (description !== undefined) model.description = description
if (isActive !== undefined) model.isActive = isActive
if (isDefault !== undefined) model.isDefault = isDefault
if (config) model.config = { ...model.config, ...config }
if (priority !== undefined) model.priority = priority
await model.save()
logger.info('更新AI模型:', { id, name: model.name, admin: req.admin.username })
return ApiResponse.success(res, {
message: '更新成功',
model: {
_id: model._id,
name: model.name,
modelId: model.modelId,
isDefault: model.isDefault,
isActive: model.isActive
}
})
} catch (error) {
logger.error('更新模型失败:', error)
return ApiResponse.serverError(res, error.message)
}
})
/**
* 删除模型
* DELETE /api/ai-model/:id
*/
router.delete('/:id', requirePermission('settings:write'), async (req, res) => {
try {
const { id } = req.params
const model = await AIModel.findById(id)
if (!model) {
return ApiResponse.notFound(res, '模型不存在')
}
// 检查是否是唯一的默认模型
if (model.isDefault) {
const defaultCount = await AIModel.countDocuments({ isDefault: true })
if (defaultCount <= 1) {
return ApiResponse.error(res, '不能删除唯一的默认模型,请先设置其他默认模型', 400)
}
}
await AIModel.findByIdAndDelete(id)
logger.info('删除AI模型:', { id, name: model.name, admin: req.admin.username })
return ApiResponse.success(res, { message: '删除成功' })
} catch (error) {
logger.error('删除模型失败:', error)
return ApiResponse.serverError(res, error.message)
}
})
/**
* 设置默认模型
* POST /api/ai-model/:id/set-default
*/
router.post('/:id/set-default', requirePermission('settings:write'), async (req, res) => {
try {
const { id } = req.params
const model = await AIModel.findById(id)
if (!model) {
return ApiResponse.notFound(res, '模型不存在')
}
if (!model.isActive) {
return ApiResponse.error(res, '不能将禁用模型设为默认', 400)
}
model.isDefault = true
await model.save()
logger.info('设置默认AI模型:', { id, name: model.name, admin: req.admin.username })
return ApiResponse.success(res, {
message: '设置默认模型成功',
model: {
_id: model._id,
name: model.name,
modelId: model.modelId,
isDefault: true
}
})
} catch (error) {
logger.error('设置默认模型失败:', error)
return ApiResponse.serverError(res, error.message)
}
})
/**
* 初始化默认模型(从环境变量)
* POST /api/ai-model/init-default
*/
router.post('/init-default', requirePermission('settings:write'), async (req, res) => {
try {
// 检查是否已有模型
const existingCount = await AIModel.countDocuments()
if (existingCount > 0) {
return ApiResponse.error(res, '已有模型配置,如需重新初始化请先删除现有模型', 400)
}
// 从环境变量创建默认模型
const defaultModel = new AIModel({
name: '默认模型',
modelId: process.env.OPENAI_MODEL || 'gpt-3.5-turbo',
apiUrl: process.env.OPENAI_API_URL || 'https://api.openai.com/v1/chat/completions',
apiKey: process.env.OPENAI_API_KEY || '',
description: '从环境变量初始化的默认模型',
isDefault: true,
isActive: true,
priority: 0
})
await defaultModel.save()
logger.info('初始化默认AI模型:', { admin: req.admin.username })
return ApiResponse.success(res, {
message: '初始化默认模型成功',
model: {
_id: defaultModel._id,
name: defaultModel.name,
modelId: defaultModel.modelId,
isDefault: true
}
})
} catch (error) {
logger.error('初始化默认模型失败:', error)
return ApiResponse.serverError(res, error.message)
}
})
module.exports = router