feat: build collaboration workspaces

This commit is contained in:
2026-07-03 03:02:36 -07:00
parent bf3dd256ab
commit 4ba2a11b88
53 changed files with 9557 additions and 29 deletions

View File

@@ -251,6 +251,73 @@ return jsonSuccess("健康数据已加载", data);
#### Correct
## Scenario: Collaboration Module Data Management
### 1. Scope / Trigger
- Trigger: changing 设备运维, 公告通知, 规则预警, or 家属服务 pages, APIs, seed data, or schema.
- Applies because these modules are real Drizzle/PostgreSQL-backed collaboration workspaces, not reserved/static module pages.
### 2. Signatures
- `GET /api/devices/assets`, `POST /api/devices/assets`, `PATCH|DELETE /api/devices/assets/[id]`
- `POST /api/devices/tickets`, `PATCH|DELETE /api/devices/tickets/[id]`
- `GET|POST /api/notices`, `POST|PATCH|DELETE /api/notices/[id]`
- `GET|POST /api/alerts/rules`, `PATCH|DELETE /api/alerts/rules/[id]`
- `POST /api/alerts/triggers`, `PATCH|DELETE /api/alerts/triggers/[id]`
- `GET|POST /api/family/contacts`, `PATCH|DELETE /api/family/contacts/[id]`
- `POST /api/family/visits`, `PATCH|DELETE /api/family/visits/[id]`
- `POST /api/family/feedback`, `PATCH|DELETE /api/family/feedback/[id]`
### 3. Contracts
- Reads require module read permission: `device:read`, `notice:read`, `alert:read`, or `family:read`.
- Mutations require module manage permission: `device:manage`, `notice:manage`, `alert:manage`, or `family:manage`.
- All records must be scoped by active `organizationId`; updates/deletes must filter by both `id` and `organizationId`.
- Read APIs return `{ success: true, reason, data }` where `data` is the module workspace DTO.
- Mutation APIs return the changed resource under a resource-specific key and write an audit log after successful persistence.
- Seed demo rows only inside `seedDefaultWorkspaceData(organizationId)` and connect them to real seeded elders/devices/rules where applicable.
### 4. Validation & Error Matrix
- Missing session or permission -> response from `requirePermission`; domain helper must not be called.
- Missing active organization -> `400` with a Chinese reason asking the user to select an organization.
- Invalid enum, empty required title/name/content, invalid date -> `400`.
- Cross-organization or missing referenced record -> `404` with entity-specific reason.
- Insert/update returning no row -> structured mutation failure with status `500` or `404`.
### 5. Good/Base/Bad Cases
- Good: Server page checks read permission, loads persisted workspace data, and passes serializable DTOs to a Client Component.
- Good: Client refreshes by calling the module read API after mutations.
- Good: notice read receipts use `POST /api/notices/[id]` with read permission; create/update/delete use manage permission.
- Base: alert rules and triggers are manually managed in v1; no background rule engine is implied.
- Bad: rendering fake collaboration counters or rows on reserved pages after these modules have real tables.
- Bad: updating a record by `id` alone without the active organization filter.
### 6. Tests Required
- `pnpm db:generate`, then review SQL for additive collaboration enums/tables/indexes/FKs.
- `pnpm lint`, `pnpm type-check`, `pnpm test`, and `pnpm build`.
- API route tests should cover permission denial, missing active organization, successful create/update audit, and missing/cross-organization mutation failures.
### 7. Wrong vs Correct
#### Wrong
```ts
await database.update(alertTriggers).set({ status }).where(eq(alertTriggers.id, id));
```
#### Correct
```ts
await database
.update(alertTriggers)
.set({ status, updatedAt: new Date() })
.where(and(eq(alertTriggers.id, id), eq(alertTriggers.organizationId, organizationId)));
```
```ts
const data = await listHealthAdminData(organizationId);
return jsonSuccess("健康数据已加载", { data });