docs: plan teacher student roster scope

main
chenyuan 4 weeks ago
parent 36a616cccc
commit 1ec067d77e

@ -0,0 +1,138 @@
# Teacher Student Roster Scope 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:** Make student management list and authorize students strictly by teaching-class ownership for ordinary teachers and by school scope for administrator teachers.
**Architecture:** `UserController` will derive the permitted teaching-class set from the authenticated operator record and use it for listing and every roster mutation. The Vue page will show only teaching classes, use each returned row's `teachingClassId` for unfiltered actions, and render the four actions in a single horizontal line.
**Tech Stack:** Spring Boot, MyBatis, JUnit 5 + Mockito, Vue 3 Composition API, Element Plus, Node static assertions.
## Global Constraints
- Only `TEACHING` classes are eligible for student management.
- Ordinary teachers can access only teaching classes whose `created_by` equals their user ID.
- Administrator teachers are users with role `3` and `teacherAdmin=true`; they can access every `TEACHING` class in their own school.
- Backend authorization must use the server-resolved operator record; client-supplied class and operator values cannot widen access.
- The operation column must render 编辑、删除、密码初始化、数据初始化 in one non-wrapping horizontal row.
---
### Task 1: Define and test backend teaching-class scope
**Files:**
- Modify: `link_commerce/src/test/java/com/sztzjy/linkCommerce/controller/stu/UserControllerTeacherAdminTest.java`
- Modify: `link_commerce/src/main/java/com/sztzjy/linkCommerce/controller/stu/UserController.java`
**Interfaces:**
- Consumes: `Userinfo.teacherAdmin`, `SchoolClass.classType`, `SchoolClass.createdBy`, `TeachingClassStudentMapper`.
- Produces: `resolveManagedRosterStudentIds(Userinfo operator, String teachingClassId)` and `canManageTeachingClass(Userinfo operator, SchoolClass teachingClass)` for list and mutation authorization.
- [ ] **Step 1: Write failing controller tests for the manager scope**
Add tests that configure one owned teaching class and one other teacher's teaching class in the same school. Assert that an administrator teacher's unfiltered `selectStudent` query includes the member IDs from both classes, while an ordinary teacher's unfiltered query includes only the owned member ID. Add a test that an administrator teacher can update a student member of the other class and that an ordinary teacher receives `BAD_REQUEST` for that same class.
```java
assertTrue(hasUserCondition(capturedExample, "user_id in", Arrays.asList("owned-student", "other-student")));
assertEquals(HttpStatus.OK, managerResult.getStatusCode());
assertEquals(HttpStatus.BAD_REQUEST, ordinaryResult.getStatusCode());
```
- [ ] **Step 2: Run the targeted test and verify RED**
Run: `mvn -Dtest=UserControllerTeacherAdminTest test`
Expected: the manager-scope assertions fail because the current implementation resolves only `created_by = operatorId` and `isOwnedTeachingClass` rejects another teacher's class.
- [ ] **Step 3: Implement one shared teaching-class scope helper**
In `UserController`, add `canManageTeachingClass(Userinfo operator, SchoolClass teachingClass)`. It must return true only for a `TEACHING` class in the operator's school when the operator is an administrator teacher or the class creator. Add `resolveManagedRosterStudentIds` that resolves the selected class when present, or all same-school teaching classes otherwise, then unions active membership student IDs.
Replace `resolveOwnedRosterStudentIds` use in `selectStudent`; replace direct `isOwnedTeachingClass` checks in teacher-roster add/update and single-student data initialization with the shared helper. Update `canOperateClass` so an administrator teacher may operate same-school `TEACHING` classes in roster mode. Keep cross-school and ordinary-teacher foreign-class requests rejected.
- [ ] **Step 4: Run targeted tests and verify GREEN**
Run: `mvn -Dtest=UserControllerTeacherAdminTest test`
Expected: all tests pass, including new ordinary/administrator scope assertions.
- [ ] **Step 5: Commit the backend scope change**
```bash
git -C link_commerce add src/main/java/com/sztzjy/linkCommerce/controller/stu/UserController.java src/test/java/com/sztzjy/linkCommerce/controller/stu/UserControllerTeacherAdminTest.java
git -C link_commerce commit -m "feat: scope student roster by teacher authority"
```
### Task 2: Render scoped teaching classes and usable row actions
**Files:**
- Modify: `e-commerce-internet/src/views/teacherEnd/student/index.vue`
- Modify: `e-commerce-internet/tests/teacher-owned-student-management.static.test.cjs`
- Modify: `e-commerce-internet/tests/teacher-student-roster-mode.static.test.cjs`
**Interfaces:**
- Consumes: `userStore.userInfo.teacherAdmin`, teaching-class list from `/api/user/seleteSchoolClassListBySchoolId`, and row field `teachingClassId`.
- Produces: `teachingClassOptions`, `resolveRowTeachingClassId(row)`, and a non-wrapping action group used by all four student actions.
- [ ] **Step 1: Write failing static tests for the page contract**
Extend the Node static tests to assert that the page filters every dropdown option to `classType === "TEACHING"`, branches on `userStore.userInfo.teacherAdmin`, and passes `resolveRowTeachingClassId(row)` to delete, password initialization, and data initialization. Assert the operation template always contains all four buttons and CSS has `white-space: nowrap` for its action group.
```js
assert(page.includes('const isManagerTeacher = computed(() => userStore.userInfo.teacherAdmin === true)'), "管理员教师身份必须显式参与学生范围判断");
assert(page.includes('resolveRowTeachingClassId(row)'), "未选择班级时,行操作必须使用行所属教学班");
assert(page.includes('white-space: nowrap'), "学生操作按钮不得换行");
```
- [ ] **Step 2: Run the static tests and verify RED**
Run: `node tests/teacher-owned-student-management.static.test.cjs; node tests/teacher-student-roster-mode.static.test.cjs`
Expected: the new assertions fail because the page currently mixes administrative classes, hides operations when no class is selected, and sends an empty teaching class for unfiltered row actions.
- [ ] **Step 3: Implement the page scope and action layout**
Add `isManagerTeacher`. Build `teachingClassOptions` solely from same-school `TEACHING` classes: for a manager teacher use all of them; otherwise retain only `createdBy === currentUserId`. Use it for the dropdown in all modes. Keep the default `schoolClassId` empty so the backend returns the applicable aggregate list.
Replace row-level view-only rendering with an action group containing the four buttons. Add `resolveRowTeachingClassId(row)` that uses the selected class ID first and otherwise `row.teachingClassId`; use it for edit, delete, password initialization, and data initialization. When a row has no teaching class ID, show an error and do not call the API. Add scoped CSS for the action group with flex layout, `flex-wrap: nowrap`, and `white-space: nowrap`.
- [ ] **Step 4: Run static tests and production build**
Run: `node tests/teacher-owned-student-management.static.test.cjs; node tests/teacher-student-roster-mode.static.test.cjs; npm run build:prod`
Expected: both static checks pass and Vite emits `dist` successfully.
- [ ] **Step 5: Commit the frontend page change**
```bash
git -C e-commerce-internet add src/views/teacherEnd/student/index.vue tests/teacher-owned-student-management.static.test.cjs tests/teacher-student-roster-mode.static.test.cjs
git -C e-commerce-internet commit -m "feat: scope student management by teacher authority"
```
### Task 3: Full regression verification
**Files:**
- Verify: `link_commerce/src/test/java/**`
- Verify: `e-commerce-internet/tests/*.cjs`
**Interfaces:**
- Consumes: completed backend scope and frontend page changes.
- Produces: verified local behavior ready for merge and release.
- [ ] **Step 1: Run all backend tests**
Run: `mvn test`
Expected: exit code 0 with all JUnit tests passing.
- [ ] **Step 2: Run relevant frontend regression checks**
Run: `node tests/teacher-owned-student-management.static.test.cjs; node tests/teacher-student-roster-mode.static.test.cjs; node tests/school-product-config.static.test.cjs`
Expected: each command prints its passing confirmation and exits 0.
- [ ] **Step 3: Inspect the final diffs**
Run: `git -C link_commerce diff --check HEAD~1..HEAD; git -C e-commerce-internet diff --check HEAD~1..HEAD`
Expected: no whitespace errors and no unrelated tracked files.
Loading…
Cancel
Save