You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
302 lines
12 KiB
Markdown
302 lines
12 KiB
Markdown
|
2 months ago
|
# Class Management V1 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:** Implement the first working slice of the merged class-management requirements: administrative/teaching class types, school-wide class-name uniqueness, teaching-class membership, dependency-safe deletes, and teaching-class data initialization entry points.
|
||
|
|
|
||
|
|
**Architecture:** Keep the existing `UserController` endpoints compatible while adding small focused model objects and mapper methods. `school_class` remains the class master table and gains type/source fields; a new `teaching_class_student` table records many-to-many student membership/history for teaching classes. Frontend class management remains under the teacher end but is split into administrative and teaching tabs.
|
||
|
|
|
||
|
|
**Tech Stack:** Spring Boot, MyBatis XML mappers, JUnit 5 + Mockito, Vue 3, Element Plus, Vite.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## File Structure
|
||
|
|
|
||
|
|
- Backend SQL:
|
||
|
|
- Create `docs/sql/2026-06-23-class-management-v1.sql` with new columns and new mapping tables.
|
||
|
|
- Backend model:
|
||
|
|
- Modify `src/main/java/com/sztzjy/linkCommerce/entity/SchoolClass.java` for `classType`, `dataSource`, and `sourceClassIds`.
|
||
|
|
- Modify `src/main/java/com/sztzjy/linkCommerce/entity/SchoolClassExample.java` for class-type criteria.
|
||
|
|
- Create `src/main/java/com/sztzjy/linkCommerce/entity/TeachingClassStudent.java`.
|
||
|
|
- Create `src/main/java/com/sztzjy/linkCommerce/entity/ExternalResourceMapping.java`.
|
||
|
|
- Backend mapper:
|
||
|
|
- Modify `src/main/java/com/sztzjy/linkCommerce/mapper/SchoolClassMapper.java` and `src/main/resources/mappers/SchoolClassMapper.xml`.
|
||
|
|
- Create `src/main/java/com/sztzjy/linkCommerce/mapper/TeachingClassStudentMapper.java`.
|
||
|
|
- Create `src/main/resources/mappers/TeachingClassStudentMapper.xml`.
|
||
|
|
- Create `src/main/java/com/sztzjy/linkCommerce/mapper/ExternalResourceMappingMapper.java`.
|
||
|
|
- Create `src/main/resources/mappers/ExternalResourceMappingMapper.xml`.
|
||
|
|
- Backend service/controller:
|
||
|
|
- Modify `src/main/java/com/sztzjy/linkCommerce/controller/stu/UserController.java` for type-aware create/update/delete/list and initialization endpoints.
|
||
|
|
- Reuse existing `/api/cptf/restartTrainingByAll` for current class initialization, and add user/controller endpoints that explicitly check teaching-class ownership before calling it in frontend.
|
||
|
|
- Backend tests:
|
||
|
|
- Modify `src/test/java/com/sztzjy/linkCommerce/controller/stu/UserControllerTeacherAdminTest.java`.
|
||
|
|
- Create `src/test/java/com/sztzjy/linkCommerce/entity/TeachingClassStudentModelTest.java`.
|
||
|
|
- Frontend API:
|
||
|
|
- Modify `E:/workspace/dianshang/e-commerce-internet/src/api/teacher.js`.
|
||
|
|
- Frontend views:
|
||
|
|
- Modify `E:/workspace/dianshang/e-commerce-internet/src/views/teacherEnd/class/index.vue`.
|
||
|
|
- Modify `E:/workspace/dianshang/e-commerce-internet/src/views/teacherEnd/student/index.vue`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Task 1: Backend Model And SQL
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Create: `docs/sql/2026-06-23-class-management-v1.sql`
|
||
|
|
- Modify: `src/main/java/com/sztzjy/linkCommerce/entity/SchoolClass.java`
|
||
|
|
- Modify: `src/main/resources/mappers/SchoolClassMapper.xml`
|
||
|
|
- Modify: `src/main/java/com/sztzjy/linkCommerce/entity/SchoolClassExample.java`
|
||
|
|
- Create: `src/main/java/com/sztzjy/linkCommerce/entity/TeachingClassStudent.java`
|
||
|
|
- Create: `src/main/java/com/sztzjy/linkCommerce/mapper/TeachingClassStudentMapper.java`
|
||
|
|
- Create: `src/main/resources/mappers/TeachingClassStudentMapper.xml`
|
||
|
|
- Create: `src/main/java/com/sztzjy/linkCommerce/entity/ExternalResourceMapping.java`
|
||
|
|
- Create: `src/main/java/com/sztzjy/linkCommerce/mapper/ExternalResourceMappingMapper.java`
|
||
|
|
- Create: `src/main/resources/mappers/ExternalResourceMappingMapper.xml`
|
||
|
|
- Test: `src/test/java/com/sztzjy/linkCommerce/entity/TeachingClassStudentModelTest.java`
|
||
|
|
|
||
|
|
- [ ] **Step 1: Write failing model tests**
|
||
|
|
|
||
|
|
Add tests asserting `SchoolClass` can store `ADMIN`/`TEACHING` class types and `TeachingClassStudent` stores member status.
|
||
|
|
|
||
|
|
Run: `mvn -Dtest=TeachingClassStudentModelTest test`
|
||
|
|
|
||
|
|
Expected before implementation: compilation fails because `TeachingClassStudent` does not exist and `SchoolClass#getClassType` does not exist.
|
||
|
|
|
||
|
|
- [ ] **Step 2: Implement model fields and mapper XML**
|
||
|
|
|
||
|
|
Add:
|
||
|
|
|
||
|
|
```java
|
||
|
|
private String classType;
|
||
|
|
private String dataSource;
|
||
|
|
private String sourceClassIds;
|
||
|
|
```
|
||
|
|
|
||
|
|
to `SchoolClass`; map columns `class_type`, `data_source`, and `source_class_ids`.
|
||
|
|
|
||
|
|
Create `TeachingClassStudent` with fields:
|
||
|
|
|
||
|
|
```java
|
||
|
|
private String id;
|
||
|
|
private String teachingClassId;
|
||
|
|
private String studentUserId;
|
||
|
|
private String adminClassId;
|
||
|
|
private String status;
|
||
|
|
private String joinReason;
|
||
|
|
private Date joinTime;
|
||
|
|
private Date exitTime;
|
||
|
|
private Date createTime;
|
||
|
|
```
|
||
|
|
|
||
|
|
Create mapper methods:
|
||
|
|
|
||
|
|
```java
|
||
|
|
long countByTeachingClassId(String teachingClassId);
|
||
|
|
long countByStudentUserIdAndTeachingClassId(@Param("studentUserId") String studentUserId, @Param("teachingClassId") String teachingClassId);
|
||
|
|
int insertSelective(TeachingClassStudent record);
|
||
|
|
int deleteByTeachingClassId(String teachingClassId);
|
||
|
|
```
|
||
|
|
|
||
|
|
Create `ExternalResourceMapping` and mapper as the reserved foundation for Zhiyun duplicate external account mapping.
|
||
|
|
|
||
|
|
- [ ] **Step 3: Run model tests**
|
||
|
|
|
||
|
|
Run: `mvn -Dtest=TeachingClassStudentModelTest test`
|
||
|
|
|
||
|
|
Expected after implementation: tests pass.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Task 2: Backend Class Rules
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `src/main/java/com/sztzjy/linkCommerce/controller/stu/UserController.java`
|
||
|
|
- Modify: `src/test/java/com/sztzjy/linkCommerce/controller/stu/UserControllerTeacherAdminTest.java`
|
||
|
|
|
||
|
|
- [ ] **Step 1: Write failing controller tests**
|
||
|
|
|
||
|
|
Add tests for:
|
||
|
|
|
||
|
|
```java
|
||
|
|
addSchoolClassRejectsDuplicateNameInSameSchool()
|
||
|
|
addSchoolClassDefaultsToTeachingClassWhenTeacherCreatesIt()
|
||
|
|
deleteFacultyRejectsWhenMajorExists()
|
||
|
|
deleteMajorRejectsWhenClassExists()
|
||
|
|
deleteClassRejectsWhenStudentsExist()
|
||
|
|
```
|
||
|
|
|
||
|
|
Run: `mvn -Dtest=UserControllerTeacherAdminTest test`
|
||
|
|
|
||
|
|
Expected before implementation: duplicate-by-school and dependency-delete tests fail.
|
||
|
|
|
||
|
|
- [ ] **Step 2: Implement class uniqueness and class type**
|
||
|
|
|
||
|
|
Change `addSchoolClass` to accept optional `classType`; default to `TEACHING` when a teacher creates the class and to `ADMIN` when omitted by platform/admin contexts. Check duplicates by `school_id + class_name`, not by `school_major_id + class_name`.
|
||
|
|
|
||
|
|
Change `updateSchoolClass` to reject updates that would duplicate `school_id + class_name` on a different `school_class_id`.
|
||
|
|
|
||
|
|
- [ ] **Step 3: Implement dependency delete checks**
|
||
|
|
|
||
|
|
Before deleting:
|
||
|
|
|
||
|
|
```java
|
||
|
|
deleteSchoolFaculty -> reject if SchoolMajor exists under faculty
|
||
|
|
deleteSchoolMajor -> reject if SchoolClass exists under major
|
||
|
|
deleteSchoolClass -> reject if Userinfo students exist in admin class or TeachingClassStudent members exist in teaching class
|
||
|
|
```
|
||
|
|
|
||
|
|
Expected error messages can be simple Chinese user-facing strings such as `院系下存在专业,不能删除`.
|
||
|
|
|
||
|
|
- [ ] **Step 4: Run controller tests**
|
||
|
|
|
||
|
|
Run: `mvn -Dtest=UserControllerTeacherAdminTest test`
|
||
|
|
|
||
|
|
Expected after implementation: tests pass.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Task 3: Backend Teaching-Class Membership And Initialization
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `src/main/java/com/sztzjy/linkCommerce/controller/stu/UserController.java`
|
||
|
|
- Modify: `src/test/java/com/sztzjy/linkCommerce/controller/stu/UserControllerTeacherAdminTest.java`
|
||
|
|
|
||
|
|
- [ ] **Step 1: Write failing tests**
|
||
|
|
|
||
|
|
Add tests for:
|
||
|
|
|
||
|
|
```java
|
||
|
|
addStudentToTeachingClassCreatesMembershipInsteadOfChangingAdminClass()
|
||
|
|
teacherCannotInitializeTeachingClassCreatedByAnotherTeacher()
|
||
|
|
teacherCanInitializeSingleStudentOnlyInOwnedTeachingClass()
|
||
|
|
```
|
||
|
|
|
||
|
|
Run: `mvn -Dtest=UserControllerTeacherAdminTest test`
|
||
|
|
|
||
|
|
Expected before implementation: tests fail because membership mapper/initialization endpoints are missing.
|
||
|
|
|
||
|
|
- [ ] **Step 2: Add membership creation**
|
||
|
|
|
||
|
|
When adding a student to a teaching class, preserve the student `schoolClassId` as the administrative class and insert a `TeachingClassStudent` row linking `student_user_id` to `teaching_class_id`.
|
||
|
|
|
||
|
|
For compatibility, keep existing student account creation working for administrative classes.
|
||
|
|
|
||
|
|
- [ ] **Step 3: Add initialization endpoints**
|
||
|
|
|
||
|
|
Add:
|
||
|
|
|
||
|
|
```java
|
||
|
|
@PostMapping("/initializeTeachingClassTrainingData")
|
||
|
|
ResultEntity initializeTeachingClassTrainingData(@RequestParam String teachingClassId, @RequestParam String operatorId)
|
||
|
|
|
||
|
|
@PostMapping("/initializeTeachingClassStudentTrainingData")
|
||
|
|
ResultEntity initializeTeachingClassStudentTrainingData(@RequestParam String teachingClassId, @RequestParam String studentUserId, @RequestParam String operatorId)
|
||
|
|
```
|
||
|
|
|
||
|
|
Both endpoints must check that the class is `TEACHING` and owned by `operatorId`. The first initializes all teaching-class members; the second initializes one member. In V1, return success after validation and keep the actual existing training cleanup call delegated to current module endpoints until training-data table ownership is mapped in a follow-up.
|
||
|
|
|
||
|
|
- [ ] **Step 4: Run controller tests**
|
||
|
|
|
||
|
|
Run: `mvn -Dtest=UserControllerTeacherAdminTest test`
|
||
|
|
|
||
|
|
Expected after implementation: tests pass.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Task 4: Frontend Class Tabs And Init Buttons
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `E:/workspace/dianshang/e-commerce-internet/src/api/teacher.js`
|
||
|
|
- Modify: `E:/workspace/dianshang/e-commerce-internet/src/views/teacherEnd/class/index.vue`
|
||
|
|
|
||
|
|
- [ ] **Step 1: Update API helpers**
|
||
|
|
|
||
|
|
Add:
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
export function initializeTeachingClassTrainingData(params) {
|
||
|
|
return request({ url: "/api/user/initializeTeachingClassTrainingData", method: "POST", params });
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
Update class list requests to pass `classType`.
|
||
|
|
|
||
|
|
- [ ] **Step 2: Split UI into tabs**
|
||
|
|
|
||
|
|
Use `el-tabs` with `ADMIN` and `TEACHING`. Administrative tab hides data initialization. Teaching tab shows edit/delete/member/data-init actions according to ownership.
|
||
|
|
|
||
|
|
- [ ] **Step 3: Verify build**
|
||
|
|
|
||
|
|
Run: `npm run build:prod`
|
||
|
|
|
||
|
|
Expected: build exits 0.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Task 5: Frontend Student Teaching-Class Actions
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- Modify: `E:/workspace/dianshang/e-commerce-internet/src/api/teacher.js`
|
||
|
|
- Modify: `E:/workspace/dianshang/e-commerce-internet/src/views/teacherEnd/student/index.vue`
|
||
|
|
|
||
|
|
- [ ] **Step 1: Update API helper**
|
||
|
|
|
||
|
|
Add:
|
||
|
|
|
||
|
|
```javascript
|
||
|
|
export function initializeTeachingClassStudentTrainingData(params) {
|
||
|
|
return request({ url: "/api/user/initializeTeachingClassStudentTrainingData", method: "POST", params });
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
- [ ] **Step 2: Scope student initialization to teaching class**
|
||
|
|
|
||
|
|
Require a teaching class selection before single-student data initialization. Send `teachingClassId`, `studentUserId`, and `operatorId`.
|
||
|
|
|
||
|
|
- [ ] **Step 3: Verify build**
|
||
|
|
|
||
|
|
Run: `npm run build:prod`
|
||
|
|
|
||
|
|
Expected: build exits 0.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
### Task 6: Full Verification And Restart
|
||
|
|
|
||
|
|
**Files:**
|
||
|
|
- No new source files unless verification reveals failures.
|
||
|
|
|
||
|
|
- [ ] **Step 1: Run backend tests**
|
||
|
|
|
||
|
|
Run: `mvn test`
|
||
|
|
|
||
|
|
Expected: all tests pass.
|
||
|
|
|
||
|
|
- [ ] **Step 2: Run frontend build**
|
||
|
|
|
||
|
|
Run: `npm run build:prod`
|
||
|
|
|
||
|
|
Expected: build exits 0.
|
||
|
|
|
||
|
|
- [ ] **Step 3: Restart services**
|
||
|
|
|
||
|
|
Stop listeners on ports `7548` and `147`, then start backend and frontend dev server:
|
||
|
|
|
||
|
|
```powershell
|
||
|
|
cd E:\workspace\dianshang\link_commerce
|
||
|
|
mvn spring-boot:run -Dspring-boot.run.profiles=dev
|
||
|
|
|
||
|
|
cd E:\workspace\dianshang\e-commerce-internet
|
||
|
|
npm run dev -- --host 0.0.0.0 --port 147
|
||
|
|
```
|
||
|
|
|
||
|
|
Expected: backend listens on `7548`, frontend listens on `147`.
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Plan Self-Review
|
||
|
|
|
||
|
|
- Spec coverage in V1: class tabs, class type, school-wide class-name uniqueness, delete dependency checks, teaching-class membership foundation, student one-admin-class/many-teaching-class model, initialization entry points, and Zhiyun external mapping foundation.
|
||
|
|
- Deferred by design: full historical migration of existing student/class data, actual deletion of every training module table during initialization, complete Zhiyun pull/push synchronization policy, and score-center query rewrites across every report. These require a separate implementation plan after V1 lands because they touch many scoring/training modules.
|
||
|
|
- No placeholder tasks remain in this plan; every task has concrete files, commands, and expected results.
|