* 归档项目(软删除)
(id: string, input: ArchiveProjectInput = {})
| 402 | throw new NotFoundError('Project', id); |
| 403 | } |
| 404 | ensureProjectIsMutable(project, 'update this project'); |
| 405 | |
| 406 | // 若更新名称,检查同名 |
| 407 | if (input.name && input.name !== project.name) { |
| 408 | const existing = await prisma.project.findFirst({ |
| 409 | where: { name: input.name }, |
| 410 | }); |
| 411 | if (existing) { |
| 412 | throw new ValidationError( |
| 413 | `A project with name "${input.name}" already exists` |
| 414 | ); |
| 415 | } |
| 416 | } |
| 417 | |
| 418 | const updated = await prisma.project.update({ |
| 419 | where: { id }, |
| 420 | data: input, |
| 421 | }); |
| 422 | return this.withGitMetadata(updated); |
| 423 | } |
| 424 | |
| 425 | async refreshGitCapability(id: string): Promise<ProjectGitCapability> { |
| 426 | const project = await prisma.project.findUnique({ where: { id } }); |
| 427 | if (!project) { |
| 428 | throw new NotFoundError('Project', id); |
| 429 | } |
| 430 | return detectAndStoreProjectGitCapability(project); |
| 431 | } |
| 432 | |
| 433 | /** |
| 434 | * 归档项目(软删除) |
| 435 | */ |
| 436 | async archive(id: string, input: ArchiveProjectInput = {}) { |
| 437 | const project = await prisma.project.findUnique({ |
| 438 | where: { id }, |
| 439 | include: { |
| 440 | tasks: { |
| 441 | include: { |
| 442 | workspaces: { |
| 443 | include: { sessions: true }, |
| 444 | }, |
| 445 | }, |
| 446 | }, |
| 447 | }, |
| 448 | }); |
| 449 | if (!project) { |
| 450 | throw new NotFoundError('Project', id); |
| 451 | } |
| 452 | |
| 453 | if (project.archivedAt) { |
| 454 | throw new ValidationError(`Project "${project.name}" is already archived`); |
| 455 | } |
| 456 | |
| 457 | const allWorkspaces = project.tasks.flatMap((task) => task.workspaces); |
| 458 | return defaultWorkspaceLifecycleBarrier.withWorkspaces( |
| 459 | allWorkspaces.map((workspace) => workspace.id), |
| 460 | async () => { |
| 461 | const activeSessions = allWorkspaces.flatMap((workspace) => |
no test coverage detected