refactor: remove unused classroom fields
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
# Classroom Field Simplification Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Remove the unused classroom `courseType` and `supervisor` fields from the admin UI, API contract, persistence model, and Excel import flow.
|
||||
|
||||
**Architecture:** Perform a clean cutover across every classroom caller. Add an idempotent bootstrap migration that drops the legacy columns on SQLite and MySQL, while leaving scheduling unchanged because schedule `subject` and class `classType` already own those concepts.
|
||||
|
||||
**Tech Stack:** React 19, Ant Design 6, NestJS 11, TypeORM 0.3, Jest, Vitest, SQLite, MySQL 8
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Do not add a replacement abstraction or compatibility alias.
|
||||
- Preserve classroom name, building, floor, capacity, room type, status, and notes behavior.
|
||||
- Do not change schedule form fields or schedule conflict behavior.
|
||||
- Use test-first changes and run focused tests before package builds.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Drop Legacy Classroom Columns
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/server/src/database/database-migrations.spec.ts`
|
||||
- Modify: `apps/server/src/database/database-migrations.service.ts:11`
|
||||
- Modify: `apps/server/src/entities/classroom.entity.ts:23`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: TypeORM `DataSource`, `QueryRunner`, and the existing bootstrap migration sequence.
|
||||
- Produces: private `removeUnusedClassroomColumns(): Promise<void>` called during application bootstrap.
|
||||
|
||||
- [ ] **Step 1: Write failing migration tests**
|
||||
|
||||
Extend `MigrationsPrivate` with `removeUnusedClassroomColumns(): Promise<void>`. Add focused tests proving that the method:
|
||||
|
||||
```typescript
|
||||
it('drops legacy classroom fields when present', async () => {
|
||||
const runner = mockRunner({
|
||||
getTable: {
|
||||
name: 'classrooms',
|
||||
columns: [{ name: 'id' }, { name: 'course_type' }, { name: 'supervisor' }],
|
||||
},
|
||||
});
|
||||
await bootstrapClassroomCleanup(runner);
|
||||
|
||||
await service.removeUnusedClassroomColumns();
|
||||
|
||||
expect(runner.query).toHaveBeenCalledWith('ALTER TABLE classrooms DROP COLUMN course_type');
|
||||
expect(runner.query).toHaveBeenCalledWith('ALTER TABLE classrooms DROP COLUMN supervisor');
|
||||
expect(runner.release).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('does nothing when classrooms or legacy fields are absent', async () => {
|
||||
const runner = mockRunner({ getTables: [], getTable: { name: 'classrooms', columns: [] } });
|
||||
await bootstrapClassroomCleanup(runner);
|
||||
|
||||
await service.removeUnusedClassroomColumns();
|
||||
|
||||
expect(runner.query).not.toHaveBeenCalled();
|
||||
expect(runner.release).toHaveBeenCalled();
|
||||
});
|
||||
```
|
||||
|
||||
Use a local `bootstrapClassroomCleanup` helper matching the existing Nest testing-module setup.
|
||||
|
||||
- [ ] **Step 2: Run tests and verify RED**
|
||||
|
||||
Run: `npm test -w apps/server -- database/database-migrations.spec.ts --runInBand`
|
||||
|
||||
Expected: FAIL because `removeUnusedClassroomColumns` does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement the idempotent migration**
|
||||
|
||||
Call `await this.removeUnusedClassroomColumns();` from `onApplicationBootstrap()`. Implement the method with one query runner: inspect `classrooms`, return when absent, and issue one `ALTER TABLE classrooms DROP COLUMN <name>` per present legacy column. Always release the runner in `finally`.
|
||||
|
||||
Remove these entity properties and decorators:
|
||||
|
||||
```typescript
|
||||
courseType: string;
|
||||
supervisor: string;
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run migration tests and verify GREEN**
|
||||
|
||||
Run: `npm test -w apps/server -- database/database-migrations.spec.ts --runInBand`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
### Task 2: Remove Fields From API and Excel Import
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/server/src/classrooms/dto/classroom.dto.ts:3`
|
||||
- Modify: `apps/server/src/classrooms/classrooms.controller.ts:48`
|
||||
- Modify: `apps/server/src/classrooms/classrooms.service.ts:126`
|
||||
- Create: `apps/server/src/classrooms/classrooms.controller.spec.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `CreateClassroomDto`, `UpdateClassroomDto`, classroom Excel template/import endpoints.
|
||||
- Produces: classroom requests and spreadsheets containing only `name`, `building`, `floor`, `roomType`, `capacity`, and optional `notes` where applicable.
|
||||
|
||||
- [ ] **Step 1: Write a failing Excel contract test**
|
||||
|
||||
Instantiate `ClassroomsController` with mocked service/log dependencies, invoke `downloadTemplate`, capture the workbook response buffer, load it with ExcelJS, and assert:
|
||||
|
||||
```typescript
|
||||
expect(worksheet.getRow(1).values).toEqual([
|
||||
undefined,
|
||||
'教室名',
|
||||
'楼栋',
|
||||
'楼层',
|
||||
'类型',
|
||||
'容量',
|
||||
]);
|
||||
```
|
||||
|
||||
Also assert the usage notes no longer mention course type or supervisor.
|
||||
|
||||
- [ ] **Step 2: Run test and verify RED**
|
||||
|
||||
Run: `npm test -w apps/server -- classrooms/classrooms.controller.spec.ts --runInBand`
|
||||
|
||||
Expected: FAIL because the generated template still includes `课程类型` and `负责人`.
|
||||
|
||||
- [ ] **Step 3: Remove fields from server contracts**
|
||||
|
||||
Delete `courseType` and `supervisor` from both DTO classes. Remove them from:
|
||||
|
||||
- template columns, example rows, and usage notes;
|
||||
- parsed import rows;
|
||||
- `batchImport` row type.
|
||||
|
||||
Keep the existing field order `name`, `building`, `floor`, `roomType`, `capacity` consistent between export and import.
|
||||
|
||||
- [ ] **Step 4: Run focused server tests**
|
||||
|
||||
Run: `npm test -w apps/server -- classrooms/classrooms.controller.spec.ts database/database-migrations.spec.ts --runInBand`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
### Task 3: Simplify Classroom Admin UI
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/admin/src/pages/Classrooms/index.tsx:143`
|
||||
- Create: `apps/admin/src/pages/Classrooms/classroom-fields.ts`
|
||||
- Create: `apps/admin/src/pages/Classrooms/classroom-fields.integration.test.ts`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: classroom records returned by `/classrooms`.
|
||||
- Produces: exported `CLASSROOM_VISIBLE_FIELDS` used to document/test the UI contract; list and form without `courseType` or `supervisor`.
|
||||
|
||||
- [ ] **Step 1: Write a failing UI contract test**
|
||||
|
||||
Create a small contract test:
|
||||
|
||||
```typescript
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { CLASSROOM_VISIBLE_FIELDS } from './classroom-fields';
|
||||
|
||||
describe('classroom visible fields', () => {
|
||||
it('excludes obsolete course and supervisor metadata', () => {
|
||||
expect(CLASSROOM_VISIBLE_FIELDS).toEqual([
|
||||
'name',
|
||||
'building',
|
||||
'floor',
|
||||
'roomType',
|
||||
'capacity',
|
||||
'status',
|
||||
'notes',
|
||||
]);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test and verify RED**
|
||||
|
||||
Run: `npm test -w apps/admin -- src/pages/Classrooms/classroom-fields.integration.test.ts`
|
||||
|
||||
Expected: FAIL because `classroom-fields.ts` does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement minimal UI cleanup**
|
||||
|
||||
Create the constant exactly as asserted. Remove the `课程类型` and `负责人` table columns and the corresponding two `Form.Item` blocks. Do not alter scheduling pages.
|
||||
|
||||
- [ ] **Step 4: Run focused admin test**
|
||||
|
||||
Run: `npm test -w apps/admin -- src/pages/Classrooms/classroom-fields.integration.test.ts`
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
### Task 4: Verify Clean Cutover
|
||||
|
||||
**Files:**
|
||||
- Verify: `apps/server/src/entities/classroom.entity.ts`
|
||||
- Verify: `apps/server/src/classrooms/`
|
||||
- Verify: `apps/admin/src/pages/Classrooms/`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: completed Tasks 1–3.
|
||||
- Produces: buildable admin/server packages with no classroom `courseType` or `supervisor` path.
|
||||
|
||||
- [ ] **Step 1: Run focused tests together**
|
||||
|
||||
Run: `npm test -w apps/server -- classrooms/classrooms.controller.spec.ts database/database-migrations.spec.ts --runInBand`
|
||||
|
||||
Run: `npm test -w apps/admin -- src/pages/Classrooms/classroom-fields.integration.test.ts`
|
||||
|
||||
Expected: all PASS.
|
||||
|
||||
- [ ] **Step 2: Run package typechecks/builds**
|
||||
|
||||
Run: `npm run typecheck -w apps/server`
|
||||
|
||||
Run: `npm run build -w apps/admin`
|
||||
|
||||
Expected: both exit successfully.
|
||||
|
||||
- [ ] **Step 3: Smoke-test observable contracts**
|
||||
|
||||
Start the existing app and verify:
|
||||
|
||||
1. Adding/editing a classroom shows no course type or supervisor field.
|
||||
2. Classroom list shows neither obsolete column.
|
||||
3. Downloaded import template has five headers: 教室名、楼栋、楼层、类型、容量.
|
||||
4. Creating a schedule still selects and saves a classroom normally.
|
||||
|
||||
Expected: all four scenarios succeed without console or API errors.
|
||||
@@ -0,0 +1,31 @@
|
||||
# 教室字段精简设计
|
||||
|
||||
## 目标
|
||||
|
||||
删除教室中没有业务联动的“课程类型”和“负责人/班主任”,避免与班级班型、真实教师关系重复维护。
|
||||
|
||||
## 范围
|
||||
|
||||
- 教室创建和编辑表单删除 `courseType`、`supervisor`。
|
||||
- 教室列表删除“课程类型”“负责人”两列。
|
||||
- 教室创建、更新 DTO 删除对应字段。
|
||||
- 教室实体删除对应列,并通过数据库迁移删除已有列。
|
||||
- Excel 导入、导入模板和示例删除对应字段。
|
||||
- 排课保持现状:`subject` 表示科目,班型来自所选班级的 `classType`,教室不重复记录班型或课程类型。
|
||||
|
||||
## 数据流
|
||||
|
||||
教室只维护名称、楼栋、楼层、容量、规格、状态和备注。创建排课时选择班级、科目、教师、教室及时间;系统继续按教室和时间检测排课及租赁冲突,不增加课程类型匹配规则。
|
||||
|
||||
## 兼容策略
|
||||
|
||||
采用干净切换:所有调用方同时迁移,不保留 DTO 字段、实体列、别名或兼容逻辑。迁移删除历史 `course_type`、`supervisor` 数据。
|
||||
|
||||
## 验证
|
||||
|
||||
- 管理端构建通过。
|
||||
- 服务端相关测试和构建通过。
|
||||
- 创建、编辑教室时不再显示或提交两个字段。
|
||||
- 教室列表不再显示两列。
|
||||
- Excel 模板及导入不再包含两个字段。
|
||||
- 创建排课仍可正常选择教室并保存。
|
||||
Reference in New Issue
Block a user