# 学校管理模块 - 完整开发计划 (Day 3-7)

---

## 📋 项目概览

**当前进度：** 70% (后端95%完成)  
**剩余工作：** 前端开发、数据权限、接口联调、测试验收  
**预计完成时间：** Day 7 (2026-02-06)  
**开发团队 | pangu

---

## 🎯 整体目标

完成学校管理模块的所有剩余开发任务，包括：
1. ✅ 后端开发 (已完成95%)
2. ⏳ 前端开发 (0% → 100%)
3. ⏳ 数据权限 (0% → 100%)
4. ⏳ 接口联调 (0% → 100%)
5. ⏳ 测试验收 (20% → 100%)

---

## 📅 Day 3: 前端基础框架 (8小时)

### 任务清单

#### FE-SCH-01: 创建主页面框架 (2h)

**目标文件：**
- `pangu-ui/src/views/school/index.vue`

**实现内容：**
```vue
<template>
  <div class="school-management">
    <!-- 左侧：区域树 -->
    <div class="left-panel">
      <RegionTree @node-click="handleRegionClick" />
    </div>
    
    <!-- 右侧：学校树和操作 -->
    <div class="right-panel">
      <!-- 搜索栏 -->
      <div class="search-bar">
        <el-input placeholder="搜索学校" />
        <el-button type="primary">新增学校</el-button>
      </div>
      
      <!-- 学校树 -->
      <SchoolTree :region-id="selectedRegionId" />
    </div>
  </div>
</template>
```

**验收标准：**
- ✅ 左右分栏布局完成
- ✅ 响应式设计
- ✅ 基础样式美观

---

#### FE-SCH-02: RegionTree组件 (2h)

**目标文件：**
- `pangu-ui/src/components/school/RegionTree.vue`

**实现内容：**
```vue
<template>
  <div class="region-tree">
    <el-tree
      :data="regionData"
      :props="treeProps"
      node-key="regionId"
      highlight-current
      @node-click="handleNodeClick"
    />
  </div>
</template>

<script>
export default {
  data() {
    return {
      regionData: [],
      treeProps: {
        label: 'regionName',
        children: 'children'
      }
    }
  },
  mounted() {
    this.loadRegionTree()
  },
  methods: {
    async loadRegionTree() {
      // 调用区域树接口
      const res = await this.$api.region.getTree()
      this.regionData = res.data
    },
    handleNodeClick(node) {
      this.$emit('node-click', node.regionId)
    }
  }
}
</script>
```

**验收标准：**
- ✅ 区域树正确展示
- ✅ 节点点击事件触发
- ✅ 高亮选中状态

---

#### FE-SCH-03: SchoolTree组件 (4h)

**目标文件：**
- `pangu-ui/src/components/school/SchoolTree.vue`

**实现内容：**
```vue
<template>
  <div class="school-tree">
    <el-table
      :data="treeData"
      row-key="id"
      :tree-props="{children: 'children'}"
      border
    >
      <el-table-column prop="name" label="名称" width="300" />
      <el-table-column prop="code" label="编码" width="150" />
      <el-table-column prop="type" label="类型" width="100">
        <template #default="scope">
          <el-tag v-if="scope.row.type === 'school'" type="primary">学校</el-tag>
          <el-tag v-else-if="scope.row.type === 'grade'" type="success">年级</el-tag>
          <el-tag v-else type="info">班级</el-tag>
        </template>
      </el-table-column>
      <el-table-column prop="status" label="状态" width="80">
        <template #default="scope">
          <el-tag v-if="scope.row.status === '0'" type="success">正常</el-tag>
          <el-tag v-else type="danger">停用</el-tag>
        </template>
      </el-table-column>
      <el-table-column label="操作" width="300" fixed="right">
        <template #default="scope">
          <!-- 学校操作 -->
          <template v-if="scope.row.type === 'school'">
            <el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
            <el-button size="small" @click="handleBindGrade(scope.row)">挂载年级</el-button>
            <el-button size="small" type="danger" @click="handleDelete(scope.row)">删除</el-button>
          </template>
          
          <!-- 年级操作 -->
          <template v-else-if="scope.row.type === 'grade'">
            <el-button size="small" @click="handleBindClass(scope.row)">挂载班级</el-button>
            <el-button size="small" type="danger" @click="handleDeleteGrade(scope.row)">删除</el-button>
          </template>
          
          <!-- 班级操作 -->
          <template v-else>
            <el-button size="small" type="danger" @click="handleDeleteClass(scope.row)">删除</el-button>
          </template>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  props: {
    regionId: {
      type: Number,
      default: null
    }
  },
  data() {
    return {
      treeData: []
    }
  },
  watch: {
    regionId: {
      handler(val) {
        if (val) {
          this.loadSchoolTree()
        }
      },
      immediate: true
    }
  },
  methods: {
    async loadSchoolTree() {
      const res = await this.$api.school.getTree(this.regionId)
      this.treeData = res.data
    },
    handleEdit(row) {
      this.$emit('edit', row)
    },
    handleBindGrade(row) {
      this.$emit('bind-grade', row)
    },
    handleBindClass(row) {
      this.$emit('bind-class', row)
    },
    async handleDelete(row) {
      await this.$confirm('确认删除该学校吗？')
      await this.$api.school.delete(row.schoolId)
      this.$message.success('删除成功')
      this.loadSchoolTree()
    },
    async handleDeleteGrade(row) {
      await this.$confirm('确认删除该年级吗？')
      await this.$api.school.deleteGrade(row.schoolGradeId)
      this.$message.success('删除成功')
      this.loadSchoolTree()
    },
    async handleDeleteClass(row) {
      await this.$confirm('确认删除该班级吗？')
      await this.$api.school.deleteClass(row.schoolClassId)
      this.$message.success('删除成功')
      this.loadSchoolTree()
    }
  }
}
</script>
```

**验收标准：**
- ✅ 树形表格正确展示
- ✅ 三级树结构完整
- ✅ 操作按钮功能正常
- ✅ 删除操作有确认提示

---

## 📅 Day 4: 前端弹窗与API (8小时)

### 任务清单

#### FE-SCH-04: 学校编辑弹窗 (3h)

**目标文件：**
- `pangu-ui/src/components/school/SchoolDialog.vue`

**实现内容：**
```vue
<template>
  <el-dialog
    :title="isEdit ? '编辑学校' : '新增学校'"
    v-model="visible"
    width="600px"
  >
    <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
      <el-form-item label="学校名称" prop="schoolName">
        <el-input v-model="form.schoolName" placeholder="请输入学校名称" />
      </el-form-item>
      
      <el-form-item label="学校类型" prop="schoolType">
        <el-select v-model="form.schoolType" placeholder="请选择学校类型">
          <el-option label="小学" value="01" />
          <el-option label="初中" value="02" />
          <el-option label="高中" value="03" />
          <el-option label="完全中学" value="04" />
          <el-option label="其他" value="99" />
        </el-select>
      </el-form-item>
      
      <el-form-item label="所属区域" prop="regionId">
        <el-cascader
          v-model="form.regionId"
          :options="regionOptions"
          :props="cascaderProps"
          placeholder="请选择所属区域"
        />
      </el-form-item>
      
      <el-form-item label="详细地址" prop="address">
        <el-input v-model="form.address" type="textarea" />
      </el-form-item>
      
      <el-form-item label="联系人" prop="contactPerson">
        <el-input v-model="form.contactPerson" />
      </el-form-item>
      
      <el-form-item label="联系电话" prop="contactPhone">
        <el-input v-model="form.contactPhone" />
      </el-form-item>
      
      <el-form-item label="状态" prop="status">
        <el-radio-group v-model="form.status">
          <el-radio label="0">正常</el-radio>
          <el-radio label="1">停用</el-radio>
        </el-radio-group>
      </el-form-item>
    </el-form>
    
    <template #footer>
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="handleSubmit">确定</el-button>
    </template>
  </el-dialog>
</template>

<script>
export default {
  data() {
    return {
      visible: false,
      isEdit: false,
      form: {
        schoolName: '',
        schoolType: '',
        regionId: null,
        address: '',
        contactPerson: '',
        contactPhone: '',
        status: '0'
      },
      rules: {
        schoolName: [
          { required: true, message: '请输入学校名称', trigger: 'blur' }
        ],
        schoolType: [
          { required: true, message: '请选择学校类型', trigger: 'change' }
        ],
        regionId: [
          { required: true, message: '请选择所属区域', trigger: 'change' }
        ],
        contactPhone: [
          { pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
        ]
      },
      regionOptions: [],
      cascaderProps: {
        label: 'regionName',
        value: 'regionId',
        children: 'children'
      }
    }
  },
  methods: {
    open(row) {
      this.visible = true
      this.isEdit = !!row
      if (row) {
        this.form = { ...row }
      } else {
        this.resetForm()
      }
      this.loadRegionOptions()
    },
    async loadRegionOptions() {
      const res = await this.$api.region.getTree()
      this.regionOptions = res.data
    },
    resetForm() {
      this.form = {
        schoolName: '',
        schoolType: '',
        regionId: null,
        address: '',
        contactPerson: '',
        contactPhone: '',
        status: '0'
      }
    },
    async handleSubmit() {
      await this.$refs.formRef.validate()
      
      if (this.isEdit) {
        await this.$api.school.update(this.form)
        this.$message.success('修改成功')
      } else {
        await this.$api.school.create(this.form)
        this.$message.success('新增成功')
      }
      
      this.visible = false
      this.$emit('success')
    }
  }
}
</script>
```

**验收标准：**
- ✅ 表单字段完整
- ✅ 表单验证正确
- ✅ 新增/编辑功能正常
- ✅ 区域级联选择器正常

---

#### FE-SCH-05: 年级挂载弹窗 (2h)

**目标文件：**
- `pangu-ui/src/components/school/BindGradeDialog.vue`

**实现内容：**
```vue
<template>
  <el-dialog title="挂载年级" v-model="visible" width="500px">
    <el-checkbox-group v-model="selectedGrades">
      <el-checkbox
        v-for="grade in gradeList"
        :key="grade.gradeId"
        :label="grade.gradeId"
      >
        {{ grade.gradeName }}
      </el-checkbox>
    </el-checkbox-group>
    
    <template #footer>
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="handleSubmit">确定</el-button>
    </template>
  </el-dialog>
</template>

<script>
export default {
  data() {
    return {
      visible: false,
      schoolId: null,
      gradeList: [],
      selectedGrades: []
    }
  },
  methods: {
    async open(schoolId) {
      this.visible = true
      this.schoolId = schoolId
      await this.loadGradeList()
    },
    async loadGradeList() {
      const res = await this.$api.grade.getList()
      this.gradeList = res.data
    },
    async handleSubmit() {
      if (this.selectedGrades.length === 0) {
        this.$message.warning('请至少选择一个年级')
        return
      }
      
      await this.$api.school.bindGrades({
        schoolId: this.schoolId,
        gradeIds: this.selectedGrades
      })
      
      this.$message.success('挂载成功')
      this.visible = false
      this.$emit('success')
    }
  }
}
</script>
```

**验收标准：**
- ✅ 年级列表正确展示
- ✅ 多选功能正常
- ✅ 挂载接口调用成功

---

#### FE-SCH-06: 班级挂载弹窗 (2h)

**目标文件：**
- `pangu-ui/src/components/school/BindClassDialog.vue`

**实现内容：**
```vue
<template>
  <el-dialog title="挂载班级" v-model="visible" width="500px">
    <el-checkbox-group v-model="selectedClasses">
      <el-checkbox
        v-for="cls in classList"
        :key="cls.classId"
        :label="cls.classId"
      >
        {{ cls.className }}
      </el-checkbox>
    </el-checkbox-group>
    
    <template #footer>
      <el-button @click="visible = false">取消</el-button>
      <el-button type="primary" @click="handleSubmit">确定</el-button>
    </template>
  </el-dialog>
</template>

<script>
export default {
  data() {
    return {
      visible: false,
      schoolGradeId: null,
      classList: [],
      selectedClasses: []
    }
  },
  methods: {
    async open(schoolGradeId) {
      this.visible = true
      this.schoolGradeId = schoolGradeId
      await this.loadClassList()
    },
    async loadClassList() {
      const res = await this.$api.class.getList()
      this.classList = res.data
    },
    async handleSubmit() {
      if (this.selectedClasses.length === 0) {
        this.$message.warning('请至少选择一个班级')
        return
      }
      
      await this.$api.school.bindClasses({
        schoolGradeId: this.schoolGradeId,
        classIds: this.selectedClasses
      })
      
      this.$message.success('挂载成功')
      this.visible = false
      this.$emit('success')
    }
  }
}
</script>
```

**验收标准：**
- ✅ 班级列表正确展示
- ✅ 多选功能正常
- ✅ 挂载接口调用成功

---

#### FE-SCH-07: API封装 (1h)

**目标文件：**
- `pangu-ui/src/api/school.js`

**实现内容：**
```javascript
import request from '@/utils/request'

export default {
  // 获取学校树
  getTree(regionId) {
    return request({
      url: '/api/school/tree',
      method: 'get',
      params: { regionId }
    })
  },
  
  // 获取学校列表（分页）
  getList(query) {
    return request({
      url: '/api/school/list',
      method: 'get',
      params: query
    })
  },
  
  // 获取学校详情
  getDetail(schoolId) {
    return request({
      url: `/api/school/${schoolId}`,
      method: 'get'
    })
  },
  
  // 新增学校
  create(data) {
    return request({
      url: '/api/school',
      method: 'post',
      data
    })
  },
  
  // 修改学校
  update(data) {
    return request({
      url: '/api/school',
      method: 'put',
      data
    })
  },
  
  // 删除学校
  delete(schoolId) {
    return request({
      url: `/api/school/${schoolId}`,
      method: 'delete'
    })
  },
  
  // 挂载年级
  bindGrades(data) {
    return request({
      url: '/api/school/bindGrades',
      method: 'post',
      data
    })
  },
  
  // 挂载班级
  bindClasses(data) {
    return request({
      url: '/api/school/bindClasses',
      method: 'post',
      data
    })
  },
  
  // 删除学校年级
  deleteGrade(schoolGradeId) {
    return request({
      url: `/api/school/grade/${schoolGradeId}`,
      method: 'delete'
    })
  },
  
  // 删除学校班级
  deleteClass(schoolClassId) {
    return request({
      url: `/api/school/class/${schoolClassId}`,
      method: 'delete'
    })
  }
}
```

**验收标准：**
- ✅ 所有接口封装完成
- ✅ 请求参数正确
- ✅ 响应处理正确

---

## 📅 Day 5: 数据权限与联调 (8小时)

### 任务清单

#### BE-SCH-12: 数据权限控制 (3h)

**目标：** 实现分公司用户只能看到自己区域的学校数据

**修改文件：**
- `SchoolServiceImpl.java`
- `SchoolMapper.xml`

**实现步骤：**

1. 在Service方法上添加@DataScope注解
```java
@Override
@DataScope(deptAlias = "s", userAlias = "s")
public List<SchoolVO> selectSchoolList(SchoolQueryDTO query) {
    return schoolMapper.selectSchoolList(query);
}
```

2. 在Mapper XML中添加数据权限SQL片段
```xml
<sql id="dataScopeFilter">
    <!-- 数据权限过滤 -->
    ${params.dataScope}
</sql>

<select id="selectSchoolList" resultMap="SchoolVOResult">
    <include refid="selectSchoolVo"/>
    <where>
        <if test="regionId != null">
            AND s.region_id = #{regionId}
        </if>
        <if test="schoolName != null and schoolName != ''">
            AND s.school_name LIKE CONCAT('%', #{schoolName}, '%')
        </if>
        <if test="status != null and status != ''">
            AND s.status = #{status}
        </if>
        <!-- 数据权限 -->
        <include refid="dataScopeFilter"/>
    </where>
    ORDER BY s.create_time DESC
</select>
```

3. 配置用户-区域关联关系
```sql
-- 在用户表中关联区域ID
-- 或通过角色-区域关联表实现
```

**验收标准：**
- ✅ 管理员可以看到所有学校
- ✅ 分公司用户只能看到自己区域的学校
- ✅ 数据权限不影响性能

---

#### BE-SCH-14: 接口联调与Bug修复 (3h)

**测试清单：**

1. **学校CRUD测试**
   - ✅ 新增学校
   - ✅ 修改学校
   - ✅ 删除学校
   - ✅ 查询学校列表
   - ✅ 查询学校详情
   - ✅ 查询学校树

2. **年级挂载测试**
   - ✅ 挂载年级
   - ✅ 重复挂载（去重）
   - ✅ 删除年级
   - ✅ 删除有班级的年级（应失败）

3. **班级挂载测试**
   - ✅ 挂载班级
   - ✅ 重复挂载（去重）
   - ✅ 删除班级

4. **数据权限测试**
   - ✅ 管理员权限
   - ✅ 分公司用户权限

**Bug修复流程：**
1. 记录Bug现象
2. 定位Bug原因
3. 修复Bug
4. 回归测试
5. 更新文档

**验收标准：**
- ✅ 所有接口测试通过
- ✅ 发现的Bug全部修复
- ✅ 无遗留问题

---

#### FE-SCH-08: 主页面逻辑整合 (2h)

**目标文件：**
- `pangu-ui/src/views/school/index.vue`

**实现内容：**
```vue
<template>
  <div class="school-management">
    <div class="left-panel">
      <RegionTree @node-click="handleRegionClick" />
    </div>
    
    <div class="right-panel">
      <div class="search-bar">
        <el-input
          v-model="searchKeyword"
          placeholder="搜索学校"
          @keyup.enter="handleSearch"
        />
        <el-button type="primary" @click="handleAdd">新增学校</el-button>
      </div>
      
      <SchoolTree
        ref="schoolTree"
        :region-id="selectedRegionId"
        @edit="handleEdit"
        @bind-grade="handleBindGrade"
        @bind-class="handleBindClass"
      />
    </div>
    
    <!-- 弹窗 -->
    <SchoolDialog ref="schoolDialog" @success="refreshTree" />
    <BindGradeDialog ref="bindGradeDialog" @success="refreshTree" />
    <BindClassDialog ref="bindClassDialog" @success="refreshTree" />
  </div>
</template>

<script>
import RegionTree from '@/components/school/RegionTree.vue'
import SchoolTree from '@/components/school/SchoolTree.vue'
import SchoolDialog from '@/components/school/SchoolDialog.vue'
import BindGradeDialog from '@/components/school/BindGradeDialog.vue'
import BindClassDialog from '@/components/school/BindClassDialog.vue'

export default {
  components: {
    RegionTree,
    SchoolTree,
    SchoolDialog,
    BindGradeDialog,
    BindClassDialog
  },
  data() {
    return {
      selectedRegionId: null,
      searchKeyword: ''
    }
  },
  methods: {
    handleRegionClick(regionId) {
      this.selectedRegionId = regionId
    },
    handleAdd() {
      this.$refs.schoolDialog.open()
    },
    handleEdit(row) {
      this.$refs.schoolDialog.open(row)
    },
    handleBindGrade(row) {
      this.$refs.bindGradeDialog.open(row.schoolId)
    },
    handleBindClass(row) {
      this.$refs.bindClassDialog.open(row.schoolGradeId)
    },
    handleSearch() {
      this.$refs.schoolTree.search(this.searchKeyword)
    },
    refreshTree() {
      this.$refs.schoolTree.loadSchoolTree()
    }
  }
}
</script>

<style scoped>
.school-management {
  display: flex;
  height: calc(100vh - 120px);
}

.left-panel {
  width: 300px;
  border-right: 1px solid #dcdfe6;
  padding: 20px;
  overflow-y: auto;
}

.right-panel {
  flex: 1;
  padding: 20px;
  display: flex;
  flex-direction: column;
}

.search-bar {
  display: flex;
  gap: 10px;
  margin-bottom: 20px;
}

.search-bar .el-input {
  flex: 1;
}
</style>
```

**验收标准：**
- ✅ 所有组件正确集成
- ✅ 事件传递正常
- ✅ 页面交互流畅
- ✅ 样式美观

---

## 📅 Day 6: 样式优化与测试 (8小时)

### 任务清单

#### FE-SCH-09: 样式优化 (3h)

**优化内容：**

1. **响应式布局**
   - 适配不同屏幕尺寸
   - 移动端友好

2. **交互优化**
   - 加载状态提示
   - 操作反馈动画
   - 错误提示优化

3. **视觉优化**
   - 统一配色方案
   - 图标美化
   - 间距调整

**验收标准：**
- ✅ 页面美观大方
- ✅ 交互流畅自然
- ✅ 响应式效果良好

---

#### TEST-01: 接口测试 (2h)

**测试工具：** Postman

**测试用例：**

| 接口 | 方法 | 测试场景 | 预期结果 |
|-----|------|---------|---------|
| /api/school/tree | GET | 查询学校树 | 返回树形结构 |
| /api/school/list | GET | 查询学校列表 | 返回分页数据 |
| /api/school/{id} | GET | 查询学校详情 | 返回学校信息 |
| /api/school | POST | 新增学校 | 返回成功 |
| /api/school | PUT | 修改学校 | 返回成功 |
| /api/school/{id} | DELETE | 删除学校 | 返回成功 |
| /api/school/bindGrades | POST | 挂载年级 | 返回成功 |
| /api/school/bindClasses | POST | 挂载班级 | 返回成功 |
| /api/school/grade/{id} | DELETE | 删除年级 | 返回成功 |
| /api/school/class/{id} | DELETE | 删除班级 | 返回成功 |

**验收标准：**
- ✅ 所有接口测试通过
- ✅ 响应时间 < 500ms
- ✅ 错误处理正确

---

#### TEST-02: 功能测试 (2h)

**测试场景：**

1. **学校管理流程**
   - ✅ 新增学校 → 查看 → 编辑 → 删除

2. **年级管理流程**
   - ✅ 挂载年级 → 查看 → 删除

3. **班级管理流程**
   - ✅ 挂载班级 → 查看 → 删除

4. **数据权限验证**
   - ✅ 管理员登录 → 查看所有学校
   - ✅ 分公司用户登录 → 只看到自己区域

5. **异常场景测试**
   - ✅ 删除有子级的学校 → 提示错误
   - ✅ 重复挂载年级 → 自动去重
   - ✅ 必填项为空 → 提示错误

**验收标准：**
- ✅ 所有功能正常
- ✅ 异常处理正确
- ✅ 用户体验良好

---

#### TEST-03: 集成测试 (1h)

**测试内容：**

1. **前后端集成**
   - ✅ 接口调用正常
   - ✅ 数据展示正确
   - ✅ 操作反馈及时

2. **数据一致性**
   - ✅ 新增后立即可见
   - ✅ 修改后立即更新
   - ✅ 删除后立即消失

3. **性能测试**
   - ✅ 页面加载时间 < 2s
   - ✅ 树形数据渲染流畅
   - ✅ 批量操作不卡顿

**验收标准：**
- ✅ 集成测试全部通过
- ✅ 性能指标达标
- ✅ 无遗留问题

---

## 📅 Day 7: 文档完善与最终验收 (4小时)

### 任务清单

#### DOC-01: API文档生成 (1h)

**工具：** Swagger

**内容：**
- ✅ 接口列表
- ✅ 请求参数说明
- ✅ 响应格式说明
- ✅ 错误码说明

---

#### DOC-02: 用户手册 (1h)

**目标文件：**
- `docs/05-模块技术方案/学校管理/用户手册.md`

**内容：**
1. 功能介绍
2. 操作指南
3. 常见问题
4. 注意事项

---

#### DOC-03: 开发总结报告 (1h)

**目标文件：**
- `docs/05-模块技术方案/学校管理/最终开发总结报告.md`

**内容：**
1. 项目概述
2. 完成情况统计
3. 技术亮点总结
4. 遇到的问题与解决方案
5. 经验教训
6. 后续优化建议

---

#### FINAL: 最终验收准备 (1h)

**准备内容：**

1. **代码检查**
   - ✅ 代码编译通过
   - ✅ 无Lint错误
   - ✅ 注释完整

2. **功能演示**
   - ✅ 准备演示数据
   - ✅ 准备演示流程
   - ✅ 准备演示环境

3. **文档整理**
   - ✅ 所有文档齐全
   - ✅ 文档格式统一
   - ✅ 文档内容准确

4. **交付清单**
   - ✅ 源代码
   - ✅ 数据库脚本
   - ✅ 技术文档
   - ✅ 用户手册
   - ✅ 测试报告

---

## 📊 工时统计

| 阶段 | 任务数 | 计划工时 | 预计完成日期 |
|-----|:-----:|:-------:|:----------:|
| Day 3 | 3 | 8h | 2026-02-01 |
| Day 4 | 4 | 8h | 2026-02-02 |
| Day 5 | 3 | 8h | 2026-02-03 |
| Day 6 | 3 | 8h | 2026-02-04 |
| Day 7 | 4 | 4h | 2026-02-05 |
| **总计** | **17** | **36h** | - |

**累计工时（含Day 1-2）：** 16.4h + 36h = 52.4h  
**原计划工时：** 53.5h  
**预计提前：** 1.1h ✨

---

## 🎯 验收标准

### 功能验收

- ✅ 所有功能点实现完整
- ✅ 业务流程正确
- ✅ 数据权限生效
- ✅ 异常处理完善

### 性能验收

- ✅ 页面加载时间 < 2s
- ✅ 接口响应时间 < 500ms
- ✅ 树形数据渲染流畅
- ✅ 批量操作不卡顿

### 质量验收

- ✅ 代码编译通过
- ✅ 单元测试通过
- ✅ 集成测试通过
- ✅ 代码规范符合标准
- ✅ 注释完整清晰

### 文档验收

- ✅ 技术方案文档完整
- ✅ API文档齐全
- ✅ 用户手册清晰
- ✅ 测试报告详细

---

## 🚀 执行策略

### 开发顺序

```
Day 3: 前端框架 → 基础组件
Day 4: 弹窗组件 → API封装
Day 5: 数据权限 → 接口联调
Day 6: 样式优化 → 功能测试
Day 7: 文档完善 → 最终验收
```

### 质量保证

1. **每日编译检查** - 确保代码无错误
2. **每日功能测试** - 确保新功能正常
3. **每日代码审查** - 确保代码质量
4. **每日进度更新** - 确保进度可控

### 风险控制

| 风险 | 应对措施 |
|-----|---------|
| 前端组件开发延期 | 简化UI，先实现核心功能 |
| 数据权限实现困难 | 参考现有模块，复用框架 |
| 接口联调发现Bug | 预留缓冲时间，及时修复 |
| 性能不达标 | 优化SQL，增加缓存 |

---

## 📞 沟通机制

### 进度汇报

- **每日下班前** - 更新开发进度文档
- **遇到阻塞** - 立即反馈
- **完成里程碑** - 提交阶段性成果

### 问题升级

- **技术问题** - 先自行研究，30分钟未解决则求助
- **需求问题** - 立即与产品确认
- **环境问题** - 联系运维解决

---

## ✅ 最终交付物清单

### 代码

- ✅ 后端代码（Entity、Mapper、Service、Controller）
- ✅ 前端代码（页面、组件、API）
- ✅ 单元测试代码

### 数据库

- ✅ 建表SQL脚本
- ✅ 初始化数据SQL
- ✅ 索引优化SQL

### 文档

- ✅ 技术方案文档
- ✅ API接口文档
- ✅ 用户操作手册
- ✅ 开发总结报告
- ✅ 测试报告

### 其他

- ✅ 开发进度记录
- ✅ Bug修复记录
- ✅ 代码审查记录

---

## 🎊 预期成果

完成后，学校管理模块将具备：

1. ✅ **完整的CRUD功能** - 学校、年级、班级的增删改查
2. ✅ **灵活的树形展示** - 三级树形结构，清晰直观
3. ✅ **严格的数据权限** - 分公司用户只能看自己的数据
4. ✅ **完善的数据校验** - 多层校验，保证数据完整性
5. ✅ **优秀的用户体验** - 界面美观，交互流畅
6. ✅ **高质量的代码** - 规范、清晰、易维护
7. ✅ **完整的测试覆盖** - 单元测试、集成测试全覆盖
8. ✅ **齐全的文档资料** - 技术文档、用户手册一应俱全

---

**🎯 目标：7天内完成学校管理模块的完整开发，交付高质量的产品！**

---

*计划制定时间：2026-01-31 22:00*  
*计划执行时间：2026-02-01 至 2026-02-05*  
*制定人：pangu*
