feat: add care execution workspace
This commit is contained in:
@@ -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)));
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user