571 lines
34 KiB
TypeScript
571 lines
34 KiB
TypeScript
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}` };
|
||
}
|
||
}
|
||
}
|