Files
ai-learning-platform/backend/src/modules/admin/ai-assistant.service.ts
T

571 lines
34 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Injectable } from '@nestjs/common';
import * as bcrypt from 'bcryptjs';
import { PrismaService } from '../../prisma/prisma.service';
export interface ToolDefinition {
name: string;
description: string;
parameters: Record<string, any>;
}
export interface ToolCall {
tool: string;
params: Record<string, any>;
description?: string;
}
export interface ToolResult {
success: boolean;
data: any;
summary: string;
}
@Injectable()
export class AdminAiAssistantService {
constructor(
private prisma: PrismaService,
) {}
private crudTools(): ToolDefinition[] {
return [
{
name: 'navigate',
description: '跳转到管理后台的某个页面',
parameters: { path: { type: 'string', description: '页面路径,如 /admin/users、/admin/orders、/admin/analytics、/admin/enterprise、/admin/operations/banners、/admin/operations/notifications、/admin/settings/roles、/admin/settings/config、/admin/comments、/admin/courses、/admin/prompts、/admin/contents、/admin/tools' } },
},
{
name: 'get-dashboard',
description: '获取仪表盘概览数据(用户数、课程数、内容数、提示词数、订单数)',
parameters: {},
},
// ---- 用户管理 ----
{
name: 'list-users',
description: '列出用户,可按关键词搜索',
parameters: { search: { type: 'string', description: '搜索关键词(可选)' }, page: { type: 'number', description: '页码(可选)' }, pageSize: { type: 'number', description: '每页数量(可选)' } },
},
{
name: 'get-user',
description: '查看用户详情(包含最近订单)',
parameters: { id: { type: 'number', description: '用户ID' } },
},
{
name: 'update-user-status',
description: '修改用户状态',
parameters: { id: { type: 'number', description: '用户ID' }, status: { type: 'string', enum: ['ACTIVE', 'INACTIVE', 'BANNED'], description: '新状态' } },
},
{
name: 'update-user',
description: '修改用户信息(昵称、手机号、邮箱、状态等)',
parameters: { id: { type: 'number', description: '用户ID' }, nickname: { type: 'string', description: '新昵称(可选)' }, phone: { type: 'string', description: '新手机号(可选)' }, email: { type: 'string', description: '新邮箱(可选)' }, status: { type: 'string', enum: ['ACTIVE', 'INACTIVE', 'BANNED'], description: '新状态(可选)' }, memberPlan: { type: 'string', enum: ['FREE', 'MONTHLY', 'YEARLY'], description: '会员计划(可选)' }, sandboxDaily: { type: 'number', description: '每日沙盒次数(可选)' } },
},
{
name: 'delete-user',
description: '软删除一个用户',
parameters: { id: { type: 'number', description: '用户ID' } },
},
// ---- 订单 ----
{
name: 'list-orders',
description: '查看最近订单列表',
parameters: {},
},
{
name: 'get-analytics-overview',
description: '获取数据分析概览(用户增长、收入、趋势)',
parameters: {},
},
// ---- 评论审核 ----
{
name: 'list-comments',
description: '查看评论列表,可按状态筛选',
parameters: { status: { type: 'string', enum: ['PENDING_REVIEW', 'PUBLISHED', 'REJECTED'], description: '评论状态(可选)' } },
},
{
name: 'approve-comment', description: '通过一条待审核评论', parameters: { id: { type: 'number', description: '评论ID' } },
},
{
name: 'reject-comment', description: '拒绝一条评论', parameters: { id: { type: 'number', description: '评论ID' }, reason: { type: 'string', description: '拒绝原因(可选)' } },
},
// ---- 课程管理 ----
{
name: 'list-courses', description: '查看所有课程', parameters: {},
},
{
name: 'get-course', description: '查看课程详情', parameters: { id: { type: 'number', description: '课程ID' } },
},
{
name: 'create-course', description: '创建新课程(title 必填,其他可选)',
parameters: { title: { type: 'string', description: '课程标题(必填)' }, description: { type: 'string', description: '课程描述' }, price: { type: 'number', description: '价格(默认0' }, isFree: { type: 'boolean', description: '是否免费(默认true' }, status: { type: 'string', enum: ['DRAFT', 'PUBLISHED', 'ARCHIVED'], description: '状态(可选)' } },
},
{
name: 'update-course', description: '修改课程信息',
parameters: { id: { type: 'number', description: '课程ID' }, title: { type: 'string', description: '新标题' }, description: { type: 'string', description: '新描述' }, price: { type: 'number', description: '新价格' }, isFree: { type: 'boolean', description: '是否免费' }, status: { type: 'string', enum: ['DRAFT', 'PUBLISHED', 'ARCHIVED'], description: '新状态' } },
},
{
name: 'delete-course', description: '软删除一个课程', parameters: { id: { type: 'number', description: '课程ID' } },
},
{
name: 'toggle-course-status', description: '上架/下架一个课程(自动切换)', parameters: { id: { type: 'number', description: '课程ID' } },
},
// ---- 内容管理 ----
{
name: 'list-contents', description: '查看所有内容', parameters: {},
},
{
name: 'create-content', description: '创建内容',
parameters: { title: { type: 'string', description: '标题(必填)' }, summary: { type: 'string', description: '摘要' }, content: { type: 'string', description: '正文' }, tags: { type: 'string', description: '标签(逗号分隔)' }, status: { type: 'string', enum: ['DRAFT', 'PUBLISHED', 'ARCHIVED'], description: '状态' } },
},
{
name: 'update-content', description: '修改内容',
parameters: { id: { type: 'number', description: '内容ID' }, title: { type: 'string', description: '新标题' }, summary: { type: 'string', description: '新摘要' }, content: { type: 'string', description: '新正文' }, tags: { type: 'string', description: '新标签' }, status: { type: 'string', enum: ['DRAFT', 'PUBLISHED', 'ARCHIVED'], description: '新状态' } },
},
{
name: 'delete-content', description: '软删除一个内容', parameters: { id: { type: 'number', description: '内容ID' } },
},
{
name: 'toggle-content-status', description: '上架/下架一个内容', parameters: { id: { type: 'number', description: '内容ID' } },
},
// ---- 提示词 ----
{
name: 'list-prompts', description: '查看所有提示词', parameters: {},
},
{
name: 'create-prompt', description: '创建提示词',
parameters: { title: { type: 'string', description: '标题(必填)' }, content: { type: 'string', description: '提示词内容(必填)' }, description: { type: 'string', description: '描述' }, tags: { type: 'string', description: '标签' }, status: { type: 'string', enum: ['DRAFT', 'PUBLISHED', 'ARCHIVED'], description: '状态' } },
},
{
name: 'update-prompt', description: '修改提示词',
parameters: { id: { type: 'number', description: '提示词ID' }, title: { type: 'string', description: '新标题' }, content: { type: 'string', description: '新内容' }, description: { type: 'string', description: '新描述' }, tags: { type: 'string', description: '新标签' }, status: { type: 'string', enum: ['DRAFT', 'PUBLISHED', 'ARCHIVED'], description: '新状态' } },
},
{
name: 'delete-prompt', description: '软删除一个提示词', parameters: { id: { type: 'number', description: '提示词ID' } },
},
{
name: 'toggle-prompt-status', description: '上架/下架一个提示词', parameters: { id: { type: 'number', description: '提示词ID' } },
},
// ---- Banner ----
{
name: 'list-banners', description: '查看所有Banner', parameters: {},
},
{
name: 'create-banner', description: '创建Banner',
parameters: { title: { type: 'string', description: '标题(必填)' }, image: { type: 'string', description: '图片URL(必填)' }, link: { type: 'string', description: '跳转链接' }, position: { type: 'string', description: '位置(默认home' }, sortOrder: { type: 'number', description: '排序(可选)' } },
},
{
name: 'update-banner', description: '修改Banner',
parameters: { id: { type: 'number', description: 'BannerID' }, title: { type: 'string', description: '新标题' }, image: { type: 'string', description: '新图片URL' }, link: { type: 'string', description: '新链接' }, sortOrder: { type: 'number', description: '新排序' } },
},
{
name: 'delete-banner', description: '删除一个Banner', parameters: { id: { type: 'number', description: 'BannerID' } },
},
// ---- 通知 ----
{
name: 'list-notifications', description: '查看系统通知', parameters: {},
},
{
name: 'create-notification', description: '创建系统通知(发送给所有用户或指定用户)',
parameters: { title: { type: 'string', description: '通知标题(必填)' }, content: { type: 'string', description: '通知内容(可选)' }, link: { type: 'string', description: '跳转链接(可选)' }, userId: { type: 'number', description: '指定用户ID,不填则发全体通知(可选)' } },
},
{
name: 'delete-notification', description: '删除一条通知', parameters: { id: { type: 'number', description: '通知ID' } },
},
// ---- 配置 ----
{
name: 'list-config', description: '查看系统配置', parameters: {},
},
{
name: 'update-config', description: '修改系统配置值',
parameters: { key: { type: 'string', description: '配置键(必填)' }, value: { type: 'string', description: '新值(必填)' }, category: { type: 'string', description: '分类(可选)' }, description: { type: 'string', description: '描述(可选)' } },
},
// ---- 角色权限 ----
{
name: 'list-roles', description: '查看所有管理角色', parameters: {},
},
{
name: 'create-role', description: '创建管理角色',
parameters: { name: { type: 'string', description: '角色名称(必填)' }, description: { type: 'string', description: '角色描述' }, permissions: { type: 'array', description: '权限列表,如["dashboard","users","courses"](可选)' } },
},
{
name: 'update-role', description: '修改角色',
parameters: { id: { type: 'string', description: '角色ID' }, name: { type: 'string', description: '新名称' }, description: { type: 'string', description: '新描述' }, permissions: { type: 'array', description: '新权限列表' } },
},
{
name: 'delete-role', description: '禁用一个角色', parameters: { id: { type: 'string', description: '角色ID' } },
},
// ---- 管理员 ----
{
name: 'list-admins', description: '查看所有管理员', parameters: {},
},
{
name: 'create-admin', description: '创建管理员账号',
parameters: { username: { type: 'string', description: '用户名(必填)' }, password: { type: 'string', description: '密码(必填)' }, nickname: { type: 'string', description: '昵称(可选)' }, roleId: { type: 'string', description: '角色ID(可选)' } },
},
{
name: 'update-admin', description: '修改管理员信息',
parameters: { id: { type: 'number', description: '管理员ID' }, nickname: { type: 'string', description: '新昵称' }, roleId: { type: 'string', description: '新角色ID' }, password: { type: 'string', description: '新密码' } },
},
{
name: 'delete-admin', description: '禁用一个管理员账号', parameters: { id: { type: 'number', description: '管理员ID' } },
},
// ---- 企业版 ----
{
name: 'get-enterprise-orgs', description: '查看所有企业组织', parameters: {},
},
{
name: 'create-organization', description: '创建企业组织',
parameters: { name: { type: 'string', description: '组织名称(必填)' }, description: { type: 'string', description: '描述' }, contactName: { type: 'string', description: '联系人' }, contactPhone: { type: 'string', description: '联系电话' } },
},
{
name: 'update-organization', description: '修改企业组织信息',
parameters: { id: { type: 'number', description: '组织ID' }, name: { type: 'string', description: '新名称' }, description: { type: 'string', description: '新描述' }, contactName: { type: 'string', description: '新联系人' }, contactPhone: { type: 'string', description: '新电话' } },
},
{
name: 'delete-organization', description: '删除一个企业组织', parameters: { id: { type: 'number', description: '组织ID' } },
},
{
name: 'add-org-member', description: '向企业组织添加成员',
parameters: { organizationId: { type: 'number', description: '组织ID' }, userId: { type: 'number', description: '用户ID' }, role: { type: 'string', enum: ['ADMIN', 'MEMBER'], description: '角色(默认MEMBER' } },
},
{
name: 'remove-org-member', description: '从企业组织移除成员',
parameters: { organizationId: { type: 'number', description: '组织ID' }, userId: { type: 'number', description: '用户ID' } },
},
];
}
getAvailableTools(): ToolDefinition[] {
return this.crudTools();
}
getToolsDescription(): string {
return this.getAvailableTools().map(t => {
const params = Object.entries(t.parameters)
.map(([k, v]: any) => ` - ${k}: ${v.type}${v.description ? ' (' + v.description + ')' : ''}${v.enum ? ' [' + v.enum.join('|') + ']' : ''}`)
.join('\n');
return `- **${t.name}**: ${t.description}\n${params}`;
}).join('\n\n');
}
async execute(call: ToolCall): Promise<ToolResult> {
try {
switch (call.tool) {
case 'navigate':
return { success: true, data: { path: call.params.path }, summary: `跳转到 ${call.params.path}` };
case 'get-dashboard': {
const data = await this.prisma.user.count({ where: { deletedAt: null } });
const courses = await this.prisma.course.count({ where: { deletedAt: null } });
const contents = await this.prisma.content.count({ where: { deletedAt: null, status: 'PUBLISHED' } });
const prompts = await this.prisma.prompt.count({ where: { deletedAt: null, status: 'PUBLISHED' } });
const orders = await this.prisma.order.count();
return { success: true, data: { userCount: data, courseCount: courses, contentCount: contents, promptCount: prompts, orderCount: orders }, summary: `总用户${data},课程${courses},内容${contents},提示词${prompts},订单${orders}` };
}
case 'list-users': {
const { search, page = 1, pageSize = 20 } = call.params;
const where: any = { deletedAt: null };
if (search) where.OR = [
{ nickname: { contains: search } },
{ phone: { contains: search } },
{ email: { contains: search } },
];
const [items, total] = await Promise.all([
this.prisma.user.findMany({ where, skip: (page - 1) * pageSize, take: pageSize, orderBy: { createdAt: 'desc' }, select: { id: true, nickname: true, phone: true, email: true, status: true, memberPlan: true, createdAt: true } }),
this.prisma.user.count({ where }),
]);
return { success: true, data: { items, total, page, pageSize }, summary: `找到 ${total} 个用户` };
}
case 'get-user': {
const user = await this.prisma.user.findUnique({ where: { id: call.params.id }, include: { orders: { take: 5, orderBy: { createdAt: 'desc' } } } });
if (!user) throw new Error('用户不存在');
return { success: true, data: user, summary: `用户 ${user.nickname || user.phone || '未知'} (ID:${user.id})` };
}
case 'update-user-status': {
const { id, status } = call.params;
await this.prisma.user.update({ where: { id }, data: { status } });
return { success: true, data: { id, status }, summary: `用户 ${id} 状态已改为 ${status}` };
}
case 'list-orders': {
const items = await this.prisma.order.findMany({
take: 50, orderBy: { createdAt: 'desc' },
include: { user: { select: { id: true, nickname: true } } },
});
const total = items.length;
const revenue = items.filter(o => o.status === 'PAID').reduce((s, o) => s + o.amount, 0);
return { success: true, data: { items, total, revenue }, summary: `最近 ${total} 笔订单,已收金额 ¥${revenue}` };
}
case 'get-analytics-overview': {
const now = new Date();
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
const [userTotal, ordersTotal, todayOrders, todayUsers, revenue] = await Promise.all([
this.prisma.user.count({ where: { deletedAt: null } }),
this.prisma.order.count(),
this.prisma.order.count({ where: { createdAt: { gte: today } } }),
this.prisma.user.count({ where: { deletedAt: null, createdAt: { gte: today } } }),
this.prisma.order.aggregate({ _sum: { amount: true }, where: { status: 'PAID' } }),
]);
return {
success: true,
data: { users: userTotal, orders: ordersTotal, todayOrders, todayUsers, revenue: revenue._sum.amount || 0 },
summary: `总用户 ${userTotal},今日新增 ${todayUsers};总订单 ${ordersTotal},今日 ${todayOrders};总收入 ¥${revenue._sum.amount || 0}`,
};
}
case 'list-comments': {
const where: any = {};
if (call.params.status) where.status = call.params.status;
const items = await this.prisma.comment.findMany({
where, take: 50, orderBy: { createdAt: 'desc' },
include: { user: { select: { id: true, nickname: true } } },
});
return { success: true, data: { items, total: items.length }, summary: `${items.length} 条评论` };
}
case 'approve-comment': {
await this.prisma.comment.update({ where: { id: call.params.id }, data: { status: 'PUBLISHED', reviewedAt: new Date() } });
return { success: true, data: { id: call.params.id }, summary: `评论 ${call.params.id} 已通过` };
}
case 'reject-comment': {
await this.prisma.comment.update({ where: { id: call.params.id }, data: { status: 'REJECTED', reviewNote: call.params.reason || '违规内容', reviewedAt: new Date() } });
return { success: true, data: { id: call.params.id }, summary: `评论 ${call.params.id} 已拒绝` };
}
case 'list-banners': {
const items = await this.prisma.banner.findMany({ orderBy: { createdAt: 'desc' }, take: 50 });
return { success: true, data: { items }, summary: `${items.length} 个Banner` };
}
case 'list-notifications': {
const items = await this.prisma.notification.findMany({ orderBy: { createdAt: 'desc' }, take: 50 });
return { success: true, data: { items, total: items.length }, summary: `${items.length} 条通知` };
}
case 'list-config': {
const items = await this.prisma.systemConfig.findMany();
return { success: true, data: { items }, summary: `${items.length} 个配置项` };
}
case 'list-roles': {
const items = await this.prisma.adminRole.findMany();
return { success: true, data: { items }, summary: `${items.length} 个角色` };
}
case 'list-admins': {
const items = await this.prisma.adminUser.findMany({ include: { role: { select: { name: true } } } });
return { success: true, data: { items }, summary: `${items.length} 个管理员` };
}
case 'toggle-course-status': {
const course = await this.prisma.course.findUnique({ where: { id: call.params.id } });
if (!course) throw new Error('课程不存在');
const newStatus = course.status === 'PUBLISHED' ? 'INACTIVE' : 'PUBLISHED';
await this.prisma.course.update({ where: { id: call.params.id }, data: { status: newStatus as any } });
return { success: true, data: { id: call.params.id, status: newStatus }, summary: `课程 ${call.params.id} 状态已切换为 ${newStatus}` };
}
case 'toggle-content-status': {
const content = await this.prisma.content.findUnique({ where: { id: call.params.id } });
if (!content) throw new Error('内容不存在');
const newContentStatus = content.status === 'PUBLISHED' ? 'INACTIVE' : 'PUBLISHED';
await this.prisma.content.update({ where: { id: call.params.id }, data: { status: newContentStatus as any } });
return { success: true, data: { id: call.params.id, status: newContentStatus }, summary: `内容 ${call.params.id} 状态已切换为 ${newContentStatus}` };
}
case 'toggle-prompt-status': {
const prompt = await this.prisma.prompt.findUnique({ where: { id: call.params.id } });
if (!prompt) throw new Error('提示词不存在');
const newPromptStatus = prompt.status === 'PUBLISHED' ? 'INACTIVE' : 'PUBLISHED';
await this.prisma.prompt.update({ where: { id: call.params.id }, data: { status: newPromptStatus as any } });
return { success: true, data: { id: call.params.id, status: newPromptStatus }, summary: `提示词 ${call.params.id} 状态已切换为 ${newPromptStatus}` };
}
case 'get-enterprise-orgs': {
const items = await this.prisma.organization.findMany({ take: 50, orderBy: { createdAt: 'desc' } });
return { success: true, data: { items, total: items.length }, summary: `${items.length} 个企业组织` };
}
// ---- 用户 CRUD ----
case 'update-user': {
const { id, ...data } = call.params;
await this.prisma.user.update({ where: { id }, data });
return { success: true, data: { id }, summary: `用户 ${id} 已更新` };
}
case 'delete-user': {
await this.prisma.user.update({ where: { id: call.params.id }, data: { deletedAt: new Date() } });
return { success: true, data: { id: call.params.id }, summary: `用户 ${call.params.id} 已删除` };
}
// ---- 课程 CRUD ----
case 'list-courses': {
const items = await this.prisma.course.findMany({ where: { deletedAt: null }, take: 50, orderBy: { createdAt: 'desc' }, select: { id: true, title: true, price: true, isFree: true, status: true, createdAt: true } });
return { success: true, data: { items, total: items.length }, summary: `${items.length} 个课程` };
}
case 'get-course': {
const item = await this.prisma.course.findUnique({ where: { id: call.params.id } });
if (!item) throw new Error('课程不存在');
return { success: true, data: item, summary: `课程: ${item.title}` };
}
case 'create-course': {
const created = await this.prisma.course.create({ data: { title: call.params.title, description: call.params.description || '', price: call.params.price ?? 0, isFree: call.params.isFree ?? true, status: call.params.status || 'DRAFT' } });
return { success: true, data: created, summary: `课程「${created.title}」已创建 (ID:${created.id})` };
}
case 'update-course': {
const { id: courseId, ...courseData } = call.params;
await this.prisma.course.update({ where: { id: courseId }, data: courseData });
return { success: true, data: { id: courseId }, summary: `课程 ${courseId} 已更新` };
}
case 'delete-course': {
await this.prisma.course.update({ where: { id: call.params.id }, data: { deletedAt: new Date() } });
return { success: true, data: { id: call.params.id }, summary: `课程 ${call.params.id} 已删除` };
}
// ---- 内容 CRUD ----
case 'list-contents': {
const items = await this.prisma.content.findMany({ where: { deletedAt: null }, take: 50, orderBy: { createdAt: 'desc' }, select: { id: true, title: true, status: true, contentType: true, createdAt: true } });
return { success: true, data: { items, total: items.length }, summary: `${items.length} 个内容` };
}
case 'create-content': {
const created = await this.prisma.content.create({ data: { title: call.params.title, summary: call.params.summary || '', content: call.params.content || '', tags: call.params.tags || '', status: call.params.status || 'DRAFT' } });
return { success: true, data: created, summary: `内容「${created.title}」已创建 (ID:${created.id})` };
}
case 'update-content': {
const { id: contentId, ...contentData } = call.params;
await this.prisma.content.update({ where: { id: contentId }, data: contentData });
return { success: true, data: { id: contentId }, summary: `内容 ${contentId} 已更新` };
}
case 'delete-content': {
await this.prisma.content.update({ where: { id: call.params.id }, data: { deletedAt: new Date() } });
return { success: true, data: { id: call.params.id }, summary: `内容 ${call.params.id} 已删除` };
}
// ---- 提示词 CRUD ----
case 'list-prompts': {
const items = await this.prisma.prompt.findMany({ where: { deletedAt: null }, take: 50, orderBy: { createdAt: 'desc' }, select: { id: true, title: true, status: true, isPublic: true, createdAt: true } });
return { success: true, data: { items, total: items.length }, summary: `${items.length} 个提示词` };
}
case 'create-prompt': {
const created = await this.prisma.prompt.create({ data: { title: call.params.title, content: call.params.content, description: call.params.description || '', tags: call.params.tags || '', status: call.params.status || 'PUBLISHED' } });
return { success: true, data: created, summary: `提示词「${created.title}」已创建 (ID:${created.id})` };
}
case 'update-prompt': {
const { id: promptId, ...promptData } = call.params;
await this.prisma.prompt.update({ where: { id: promptId }, data: promptData });
return { success: true, data: { id: promptId }, summary: `提示词 ${promptId} 已更新` };
}
case 'delete-prompt': {
await this.prisma.prompt.update({ where: { id: call.params.id }, data: { deletedAt: new Date() } });
return { success: true, data: { id: call.params.id }, summary: `提示词 ${call.params.id} 已删除` };
}
// ---- Banner CRUD ----
case 'create-banner': {
const created = await this.prisma.banner.create({ data: { title: call.params.title, image: call.params.image, link: call.params.link || '', position: call.params.position || 'home', sortOrder: call.params.sortOrder ?? 0 } });
return { success: true, data: created, summary: `Banner「${created.title}」已创建` };
}
case 'update-banner': {
const { id: bannerId, ...bannerData } = call.params;
await this.prisma.banner.update({ where: { id: bannerId }, data: bannerData });
return { success: true, data: { id: bannerId }, summary: `Banner ${bannerId} 已更新` };
}
case 'delete-banner': {
await this.prisma.banner.delete({ where: { id: call.params.id } });
return { success: true, data: { id: call.params.id }, summary: `Banner ${call.params.id} 已删除` };
}
// ---- 通知 ----
case 'create-notification': {
const { title: notifTitle, content: notifContent, link: notifLink, userId: notifUserId } = call.params;
if (notifUserId) {
await this.prisma.notification.create({ data: { userId: notifUserId, type: 'system', title: notifTitle, content: notifContent || '', link: notifLink || '' } });
return { success: true, data: {}, summary: `通知已发送给用户 ${notifUserId}` };
}
const users = await this.prisma.user.findMany({ where: { deletedAt: null }, select: { id: true } });
await this.prisma.notification.createMany({ data: users.map(u => ({ userId: u.id, type: 'system', title: notifTitle, content: notifContent || '', link: notifLink || '' })) });
return { success: true, data: { userCount: users.length }, summary: `通知已发送给 ${users.length} 个用户` };
}
case 'delete-notification': {
await this.prisma.notification.delete({ where: { id: call.params.id } });
return { success: true, data: { id: call.params.id }, summary: `通知 ${call.params.id} 已删除` };
}
// ---- 配置 ----
case 'update-config': {
const { key: cfgKey, value: cfgValue, category: cfgCategory, description: cfgDesc } = call.params;
const existing = await this.prisma.systemConfig.findUnique({ where: { key: cfgKey } });
if (existing) {
await this.prisma.systemConfig.update({ where: { key: cfgKey }, data: { value: cfgValue } });
return { success: true, data: { key: cfgKey }, summary: `配置 ${cfgKey} 已更新为 ${cfgValue}` };
}
await this.prisma.systemConfig.create({ data: { key: cfgKey, value: cfgValue, category: cfgCategory || 'general', description: cfgDesc || '' } });
return { success: true, data: { key: cfgKey }, summary: `配置 ${cfgKey} 已创建` };
}
// ---- 角色 CRUD ----
case 'create-role': {
const created = await this.prisma.adminRole.create({ data: { name: call.params.name, description: call.params.description || '', permissions: call.params.permissions || [] } });
return { success: true, data: created, summary: `角色「${created.name}」已创建 (ID:${created.id})` };
}
case 'update-role': {
const { id: roleId, ...roleData } = call.params;
if (roleData.permissions) roleData.permissions = JSON.stringify(roleData.permissions);
await this.prisma.adminRole.update({ where: { id: roleId }, data: roleData });
return { success: true, data: { id: roleId }, summary: `角色 ${roleId} 已更新` };
}
case 'delete-role': {
await this.prisma.adminRole.update({ where: { id: call.params.id }, data: { status: 'INACTIVE' } });
return { success: true, data: { id: call.params.id }, summary: `角色 ${call.params.id} 已禁用` };
}
// ---- 管理员 CRUD ----
case 'create-admin': {
const hash = await bcrypt.hash(call.params.password, 10);
const created = await this.prisma.adminUser.create({ data: { username: call.params.username, passwordHash: hash, nickname: call.params.nickname || call.params.username, roleId: call.params.roleId || null } });
return { success: true, data: { id: created.id }, summary: `管理员「${created.username}」已创建` };
}
case 'update-admin': {
const { id: adminId, password, ...adminData } = call.params;
if (password) adminData['passwordHash'] = await bcrypt.hash(password, 10);
await this.prisma.adminUser.update({ where: { id: adminId }, data: adminData as any });
return { success: true, data: { id: adminId }, summary: `管理员 ${adminId} 已更新` };
}
case 'delete-admin': {
await this.prisma.adminUser.update({ where: { id: call.params.id }, data: { status: 'INACTIVE' } });
return { success: true, data: { id: call.params.id }, summary: `管理员 ${call.params.id} 已禁用` };
}
// ---- 企业版 ----
case 'create-organization': {
const created = await this.prisma.organization.create({ data: { name: call.params.name, description: call.params.description || '', contactName: call.params.contactName || '', contactPhone: call.params.contactPhone || '' } });
return { success: true, data: created, summary: `组织「${created.name}」已创建` };
}
case 'update-organization': {
const { id: orgId, ...orgData } = call.params;
await this.prisma.organization.update({ where: { id: orgId }, data: orgData });
return { success: true, data: { id: orgId }, summary: `组织 ${orgId} 已更新` };
}
case 'delete-organization': {
await this.prisma.organization.update({ where: { id: call.params.id }, data: { status: 'INACTIVE' } });
return { success: true, data: { id: call.params.id }, summary: `组织 ${call.params.id} 已删除` };
}
case 'add-org-member': {
await this.prisma.organizationMember.create({ data: { organizationId: call.params.organizationId, userId: call.params.userId, role: call.params.role || 'MEMBER' } });
return { success: true, data: {}, summary: `用户 ${call.params.userId} 已加入组织 ${call.params.organizationId}` };
}
case 'remove-org-member': {
await this.prisma.organizationMember.delete({ where: { organizationId_userId: { organizationId: call.params.organizationId, userId: call.params.userId } } });
return { success: true, data: {}, summary: `用户 ${call.params.userId} 已从组织 ${call.params.organizationId} 移除` };
}
default:
throw new Error(`未知工具: ${call.tool}`);
}
} catch (err: any) {
return { success: false, data: null, summary: `执行失败: ${err.message}` };
}
}
}