import { Injectable } from '@nestjs/common'; import { ClassroomRentalsService } from '../../classroom-rentals/classroom-rentals.service'; import type { AgentToolContext, ToolDef, ToolInputResult } from '../agent-tool.types'; import { optionalPositiveInt, rejectUnknownKeys } from './tool-input'; interface Input { classroomId?: number; month?: string; includeEnded?: boolean; limit?: number } function optionalMonth(value: unknown): ToolInputResult { if (value === undefined) return { ok: true, value: undefined }; if (typeof value !== 'string' || !/^\d{4}-\d{2}$/.test(value)) { return { ok: false, error: 'month 必须是 YYYY-MM 格式' }; } const [year, month] = value.split('-').map(Number); if (month < 1 || month > 12 || year < 2000 || year > 2100) { return { ok: false, error: 'month 不是有效月份' }; } return { ok: true, value }; } @Injectable() export class SearchClassroomRentalsTool implements ToolDef { readonly name = 'search_classroom_rentals'; readonly skillKey = 'classroom'; readonly description = '查询教室租赁订单(教室、承租方机构、起止日期、租金、状态)。'; readonly requiredPermission = 'rental:view'; readonly inputSchema = { type: 'object', properties: { classroomId: { type: 'integer', minimum: 1 }, month: { type: 'string', description: 'YYYY-MM' }, includeEnded: { type: 'boolean', description: '是否包含已结束订单' }, limit: { type: 'integer', minimum: 1, maximum: 50 }, }, additionalProperties: false, }; constructor(private readonly service: ClassroomRentalsService) {} validate(raw: Record): ToolInputResult { const invalid = rejectUnknownKeys(raw, ['classroomId', 'month', 'includeEnded', 'limit']); if (invalid) return invalid; const classroomId = optionalPositiveInt(raw.classroomId, 'classroomId'); if (!classroomId.ok) return classroomId; const month = optionalMonth(raw.month); if (!month.ok) return month; if (raw.includeEnded !== undefined && typeof raw.includeEnded !== 'boolean') { return { ok: false, error: 'includeEnded 必须是布尔值' }; } const limit = optionalPositiveInt(raw.limit, 'limit', 50); if (!limit.ok) return limit; return { ok: true, value: { classroomId: classroomId.value, month: month.value, includeEnded: raw.includeEnded === undefined ? undefined : Boolean(raw.includeEnded), limit: limit.value, }, }; } execute(input: Input, _context: AgentToolContext) { return this.service.agentSearchRentals(input); } }