feat: add care execution workspace

This commit is contained in:
2026-07-03 00:35:04 -07:00
parent df7c2efcc5
commit b6b15acc69
20 changed files with 4270 additions and 18 deletions

View File

@@ -270,3 +270,70 @@ await database
.set({ status, updatedAt: new Date() })
.where(and(eq(healthAnomalyReviews.id, id), eq(healthAnomalyReviews.organizationId, organizationId)));
```
## Scenario: Care Execution Workspace
### 1. Scope / Trigger
- Trigger: adding or changing persisted daily care task execution behavior.
- Applies when replacing reserved operational module placeholders with real care task data. Care records must be Drizzle-backed and organization-scoped.
### 2. Signatures
- `GET /api/care/tasks`
- `PATCH /api/care/tasks/[id]`
- `listCareExecutionData(organizationId: string): Promise<CareExecutionData>`
- `updateCareTaskStatus(input): Promise<CareTask | { success: false; reason: string; status: number }>`
- Drizzle table: `care_tasks`
### 3. Contracts
- `/app/care` and `/app/{organizationSlug}/care` render the real care workspace once `care_tasks` exists.
- Reads require `care:read`; mutations require `care:manage`.
- `GET /api/care/tasks` returns `{ success: true; reason: string; data: CareExecutionData }`; client refresh code must read `result.data`.
- `PATCH /api/care/tasks/[id]` request: `{ status: "pending" | "in_progress" | "completed" | "cancelled"; executionNotes?: string }`.
- Completing a task sets `completedAt` and `completedByAccountId`; moving a task away from `completed` clears completion metadata.
- Mutations update by both `id` and active `organizationId`, then write an audit log after success.
### 4. Validation & Error Matrix
- Missing active organization on list -> `400` / `请选择机构后查看护理任务`.
- Missing active organization on mutation -> `400` / `请选择机构后处理护理任务`.
- Invalid status -> `400` / `护理任务状态无效`.
- Missing or cross-organization task ID -> `404` / `护理任务不存在`.
- Missing permission -> `403` from `requirePermission`; route must not call domain helpers.
- Update returning no row -> structured mutation failure.
### 5. Good/Base/Bad Cases
- Good: keep care task examples in default workspace seed tied to real seeded elders.
- Good: use one additive `care_tasks` table for MVP execution records instead of building a full recurring care-plan engine.
- Good: preserve `/app/health` separation; care execution is operational and not the health admin settings page.
- Base: `elderId` can be nullable for future public-area checks, but seeded MVP examples should use real elders.
- Bad: render fake care metrics in `ModulePage` after the module becomes real.
- Bad: update task status by ID alone without `organizationId`.
- Bad: only disable buttons in the UI while leaving Route Handlers on broad elder permissions.
### 6. Tests Required
- `pnpm test` with API assertions for list and status update routes.
- Route tests must cover happy path, permission denial, missing organization, invalid status, and missing/cross-organization task IDs.
- `pnpm db:generate` after schema changes and review generated SQL for only care enum/table/index/FK changes.
- `pnpm lint`, `pnpm type-check`, and `pnpm build`.
### 7. Wrong vs Correct
#### Wrong
```ts
await database.update(careTasks).set({ status }).where(eq(careTasks.id, id));
```
#### Correct
```ts
await database
.update(careTasks)
.set({ status, updatedAt: new Date() })
.where(and(eq(careTasks.id, id), eq(careTasks.organizationId, organizationId)));
```