docs: require real facility hierarchy

This commit is contained in:
2026-07-02 19:16:58 -07:00
parent 5fe7e5a1b8
commit 1b8fae0116

View File

@@ -110,3 +110,73 @@ await database.transaction(async (transaction) => {
await transaction.update(elders).set({ status: "active" }).where(eq(elders.id, elderId));
});
```
## Scenario: Facility Room Creation Without Fabricated Hierarchy
### 1. Scope / Trigger
- Trigger: changing facility room creation APIs or any UI that creates rooms.
- Applies because `rooms.floorId` is required by schema, but the system must not create
fake campus/building/floor records to satisfy that foreign key.
### 2. Signatures
- `POST /api/facilities/rooms`
- Request: `{ name: string; code: string; floorId: string; capacity?: number }`
- Response: `ApiResult<{ room: typeof rooms.$inferSelect }>`
### 3. Contracts
- `floorId` must refer to an existing `floors.id` in the authenticated active organization.
- `capacity` is optional; omitted means schema/product default `1`.
- If `capacity` is supplied, it must be a positive integer.
- The handler may insert only the `rooms` row. It must not insert `campuses`,
`buildings`, or `floors` as placeholder hierarchy.
### 4. Validation & Error Matrix
- Missing active organization -> `400` / `请选择机构后维护房间`.
- Missing `name`, `code`, or `floorId` -> `400` / `房间名称、编号和楼层不能为空`.
- Invalid supplied `capacity` -> `400` / `房间容量需为正整数`.
- `floorId` not found in active organization -> `404` / `楼层不存在`.
- Insert returning no row -> `500` / `房间创建失败`.
### 5. Good/Base/Bad Cases
- Good: create UI first exposes real campus/building/floor selection, then submits the selected `floorId`.
- Base: if facility hierarchy management is not exposed yet, keep room creation UI hidden or disabled.
- Bad: auto-create `"默认院区"`, `"默认楼栋"`, or `"默认楼层"` inside the room API.
### 6. Tests Required
- `pnpm lint`
- `pnpm type-check`
- `pnpm build`
- Integration assertions:
- missing `floorId` returns `400`
- unknown or cross-organization `floorId` returns `404`
- valid `floorId` creates a room without inserting campus/building/floor rows
### 7. Wrong vs Correct
#### Wrong
```ts
const floor = existingFloor ?? await transaction.insert(floors).values({ name: "默认楼层" }).returning();
await transaction.insert(rooms).values({ floorId: floor.id, name, code });
```
#### Correct
```ts
const floorRows = await database
.select({ id: floors.id })
.from(floors)
.where(and(eq(floors.id, floorId), eq(floors.organizationId, organizationId)))
.limit(1);
const floor = floorRows[0];
if (!floor) {
return jsonFailure("楼层不存在", 404);
}
await database.insert(rooms).values({ organizationId, floorId: floor.id, name, code });
```