fix(agents): resolve NestJS route collision for evaluation-rules endpoints
AdminConversationController's GET /:id was intercepting requests to AdminEvaluationRuleController (matching "evaluation-rules" as an id param). Similarly, DELETE /:id was matching "cache" as an id. Changes: - conversation.module.ts: Register AdminMcpController and AdminEvaluationRuleController before AdminConversationController (more specific prefixes must come first in NestJS) - admin-evaluation-rule.controller.ts: Move static routes (POST /test, DELETE /cache) before dynamic routes (GET/:id, DELETE/:id) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
51a7589fbf
commit
b75d607e2b
|
|
@ -7,6 +7,9 @@
|
||||||
* 2. 开关规则
|
* 2. 开关规则
|
||||||
* 3. 测试规则(dry-run)
|
* 3. 测试规则(dry-run)
|
||||||
* 4. 清除规则缓存
|
* 4. 清除规则缓存
|
||||||
|
*
|
||||||
|
* ⚠️ 路由顺序重要:静态路由(test, cache)必须在动态路由(:id)之前声明,
|
||||||
|
* 否则 NestJS 会把 "test"/"cache" 当作 :id 参数匹配。
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
|
@ -71,6 +74,45 @@ export class AdminEvaluationRuleController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── 静态路由(必须在 :id 之前) ─────────────────────────
|
||||||
|
|
||||||
|
// POST /conversations/admin/evaluation-rules/test
|
||||||
|
@Post('test')
|
||||||
|
async testEvaluation(
|
||||||
|
@Headers('authorization') auth: string,
|
||||||
|
@Body() dto: TestEvaluationDto,
|
||||||
|
) {
|
||||||
|
const admin = this.verifyAdmin(auth);
|
||||||
|
const tenantId = admin.tenantId || null;
|
||||||
|
|
||||||
|
if (!dto.stage || !dto.responseText) {
|
||||||
|
throw new BadRequestException('stage and responseText are required');
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await this.evaluationGate.evaluate(tenantId, {
|
||||||
|
stage: dto.stage,
|
||||||
|
collectedInfo: dto.collectedInfo || null,
|
||||||
|
assessmentResult: dto.assessmentResult || null,
|
||||||
|
responseText: dto.responseText,
|
||||||
|
turnCount: dto.turnCount || 1,
|
||||||
|
messageCount: dto.messageCount || 1,
|
||||||
|
hasConverted: false,
|
||||||
|
agentsUsed: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
return { success: true, data: result };
|
||||||
|
}
|
||||||
|
|
||||||
|
// DELETE /conversations/admin/evaluation-rules/cache
|
||||||
|
@Delete('cache')
|
||||||
|
async clearCache(@Headers('authorization') auth: string) {
|
||||||
|
this.verifyAdmin(auth);
|
||||||
|
this.evaluationGate.clearCache();
|
||||||
|
return { success: true, message: 'Cache cleared' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── 集合路由 ──────────────────────────────────────────
|
||||||
|
|
||||||
// GET /conversations/admin/evaluation-rules
|
// GET /conversations/admin/evaluation-rules
|
||||||
@Get()
|
@Get()
|
||||||
async listRules(
|
async listRules(
|
||||||
|
|
@ -97,22 +139,6 @@ export class AdminEvaluationRuleController {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// GET /conversations/admin/evaluation-rules/:id
|
|
||||||
@Get(':id')
|
|
||||||
async getRule(
|
|
||||||
@Headers('authorization') auth: string,
|
|
||||||
@Param('id') id: string,
|
|
||||||
) {
|
|
||||||
this.verifyAdmin(auth);
|
|
||||||
|
|
||||||
const rule = await this.repo.findById(id);
|
|
||||||
if (!rule) {
|
|
||||||
return { success: false, error: 'Rule not found' };
|
|
||||||
}
|
|
||||||
|
|
||||||
return { success: true, data: this.toResponse(rule) };
|
|
||||||
}
|
|
||||||
|
|
||||||
// POST /conversations/admin/evaluation-rules
|
// POST /conversations/admin/evaluation-rules
|
||||||
@Post()
|
@Post()
|
||||||
@HttpCode(HttpStatus.CREATED)
|
@HttpCode(HttpStatus.CREATED)
|
||||||
|
|
@ -156,6 +182,24 @@ export class AdminEvaluationRuleController {
|
||||||
return { success: true, data: this.toResponse(saved) };
|
return { success: true, data: this.toResponse(saved) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── 动态路由(:id 在静态路由之后) ───────────────────────
|
||||||
|
|
||||||
|
// GET /conversations/admin/evaluation-rules/:id
|
||||||
|
@Get(':id')
|
||||||
|
async getRule(
|
||||||
|
@Headers('authorization') auth: string,
|
||||||
|
@Param('id') id: string,
|
||||||
|
) {
|
||||||
|
this.verifyAdmin(auth);
|
||||||
|
|
||||||
|
const rule = await this.repo.findById(id);
|
||||||
|
if (!rule) {
|
||||||
|
return { success: false, error: 'Rule not found' };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { success: true, data: this.toResponse(rule) };
|
||||||
|
}
|
||||||
|
|
||||||
// PUT /conversations/admin/evaluation-rules/:id
|
// PUT /conversations/admin/evaluation-rules/:id
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
async updateRule(
|
async updateRule(
|
||||||
|
|
@ -233,40 +277,7 @@ export class AdminEvaluationRuleController {
|
||||||
return { success: true, data: this.toResponse(updated) };
|
return { success: true, data: this.toResponse(updated) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST /conversations/admin/evaluation-rules/test
|
// ─── 私有方法 ─────────────────────────────────────────
|
||||||
@Post('test')
|
|
||||||
async testEvaluation(
|
|
||||||
@Headers('authorization') auth: string,
|
|
||||||
@Body() dto: TestEvaluationDto,
|
|
||||||
) {
|
|
||||||
const admin = this.verifyAdmin(auth);
|
|
||||||
const tenantId = admin.tenantId || null;
|
|
||||||
|
|
||||||
if (!dto.stage || !dto.responseText) {
|
|
||||||
throw new BadRequestException('stage and responseText are required');
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = await this.evaluationGate.evaluate(tenantId, {
|
|
||||||
stage: dto.stage,
|
|
||||||
collectedInfo: dto.collectedInfo || null,
|
|
||||||
assessmentResult: dto.assessmentResult || null,
|
|
||||||
responseText: dto.responseText,
|
|
||||||
turnCount: dto.turnCount || 1,
|
|
||||||
messageCount: dto.messageCount || 1,
|
|
||||||
hasConverted: false,
|
|
||||||
agentsUsed: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
return { success: true, data: result };
|
|
||||||
}
|
|
||||||
|
|
||||||
// DELETE /conversations/admin/evaluation-rules/cache
|
|
||||||
@Delete('cache')
|
|
||||||
async clearCache(@Headers('authorization') auth: string) {
|
|
||||||
this.verifyAdmin(auth);
|
|
||||||
this.evaluationGate.clearCache();
|
|
||||||
return { success: true, message: 'Cache cleared' };
|
|
||||||
}
|
|
||||||
|
|
||||||
private toResponse(rule: EvaluationRuleEntity) {
|
private toResponse(rule: EvaluationRuleEntity) {
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ import { ConversationGateway } from '../adapters/inbound/conversation.gateway';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [TypeOrmModule.forFeature([ConversationORM, MessageORM, TokenUsageORM, AgentExecutionORM])],
|
imports: [TypeOrmModule.forFeature([ConversationORM, MessageORM, TokenUsageORM, AgentExecutionORM])],
|
||||||
controllers: [ConversationController, InternalConversationController, AdminConversationController, AdminMcpController, AdminEvaluationRuleController],
|
controllers: [ConversationController, InternalConversationController, AdminMcpController, AdminEvaluationRuleController, AdminConversationController],
|
||||||
providers: [
|
providers: [
|
||||||
ConversationService,
|
ConversationService,
|
||||||
ConversationGateway,
|
ConversationGateway,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue