feat: 新增项目发现任务系统并修复代码审查发现的高优先级问题

新增功能:
- 新增 ProjectDiscoveryTask 数据模型,支持异步项目探索任务管理
- 新增 /api/discovery/tasks CRUD API,支持任务创建、查询和状态更新
- 新增 /api/discovery/tasks/:id/complete API,完成探索并自动创建项目
- 新增 discovery-service 服务层,封装多级去重和标签处理逻辑
- 新增 discover-projects 命令,支持批量探索项目
- 新增 PROJECT_CONTENT_STANDARD.md 内容标准模板

代码质量修复:
- 修复状态转换验证未使用问题,防止非法状态转换
- 修复 explorationData 类型安全问题,使用 z.record(z.unknown()) 替代 z.any()
- 添加事务保护到 complete API,确保项目创建和任务状态更新的原子性

代码重构:
- 将 webhook/projects 的去重逻辑迁移到 discovery-service
- 统一探索任务相关的验证模式到 validations.ts

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-01-17 15:44:33 +08:00
co-authored by Claude
parent 2025db4a47
commit e3e72006b0
11 changed files with 942 additions and 601 deletions
+31
View File
@@ -64,6 +64,33 @@ export const WebhookPayloadSchema = WebhookAuthSchema.extend({
projects: z.array(ProjectInputSchema).min(1).max(100)
})
// ================================
// Discovery Task Schemas
// ================================
export const TaskStatusEnum = z.enum(['PENDING', 'IN_PROGRESS', 'COMPLETED', 'FAILED'])
export const CreateDiscoveryTaskSchema = WebhookAuthSchema.extend({
tasks: z.array(z.object({
sourceUrl: z.string().url().max(2000),
sourceType: z.string().max(50).default('manual'),
})).min(1).max(50)
})
export const UpdateDiscoveryTaskSchema = z.object({
apiKey: z.string().min(32),
status: TaskStatusEnum,
explorationData: z.record(z.unknown()).optional(),
explorationSummary: z.string().max(1000).optional(),
errorMessage: z.string().max(2000).optional(),
})
export const GetDiscoveryTasksQuerySchema = z.object({
status: TaskStatusEnum.optional(),
limit: z.coerce.number().int().positive().max(100).default(10),
offset: z.coerce.number().int().nonnegative().default(0),
})
// ================================
// Query Schemas
// ================================
@@ -85,3 +112,7 @@ export type Tag = z.infer<typeof TagSchema>
export type ProjectInput = z.infer<typeof ProjectInputSchema>
export type WebhookPayload = z.infer<typeof WebhookPayloadSchema>
export type ProjectQuery = z.infer<typeof ProjectQuerySchema>
export type TaskStatus = z.infer<typeof TaskStatusEnum>
export type CreateDiscoveryTask = z.infer<typeof CreateDiscoveryTaskSchema>
export type UpdateDiscoveryTask = z.infer<typeof UpdateDiscoveryTaskSchema>
export type GetDiscoveryTasksQuery = z.infer<typeof GetDiscoveryTasksQuerySchema>