import { Injectable, NotFoundException } from '@nestjs/common'; import { StudentsService } from '../../students/students.service'; import { StudentAccessScopeFactory } from '../../students/student-access-scope.factory'; import type { AgentToolContext, ToolDef, ToolInputResult } from '../agent-tool.types'; interface GetStudentBasicInput { studentId: number; } /** Forbidden input keys — if the model sends these, validation fails. */ const FORBIDDEN_INPUT_KEYS = new Set([ 'userId', 'isSuperAdmin', 'permissions', 'roles', 'ability', 'user', 'password', 'token', ]); @Injectable() export class GetStudentBasicTool implements ToolDef { readonly inputSchema = { type: 'object', properties: { studentId: { type: 'integer', description: '学生ID', minimum: 1, }, }, required: ['studentId'], additionalProperties: false, }; readonly name = 'get_student_basic'; readonly description = '获取单个学生基本信息。仅返回基础公开字段。'; readonly requiredPermission = 'student:view'; constructor( private readonly studentsService: StudentsService, private readonly scopeFactory: StudentAccessScopeFactory, ) {} validate(input: Record): ToolInputResult { for (const key of Object.keys(input)) { if (FORBIDDEN_INPUT_KEYS.has(key)) { return { ok: false, error: `不允许的输入字段: ${key}` }; } } if (input.studentId === undefined) { return { ok: false, error: '缺少必填字段: studentId' }; } const studentId = Number(input.studentId); if (!Number.isInteger(studentId) || studentId <= 0) { return { ok: false, error: 'studentId 必须是正整数' }; } // Reject unexpected keys const allowedKeys = new Set(['studentId']); for (const key of Object.keys(input)) { if (!allowedKeys.has(key)) { return { ok: false, error: `不允许的输入字段: ${key}` }; } } return { ok: true, value: { studentId } }; } async execute(input: GetStudentBasicInput, context: AgentToolContext): Promise { const scope = this.scopeFactory.buildScope(context); const result = await this.studentsService.agentGetStudentBasic(scope, input.studentId); if (result === null) { throw new NotFoundException('记录不存在或无权访问'); } return result; } }