# 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.