pangu-user-platform/docs/05-模块技术方案/学校管理/学校管理模块_开发任务清单.md

743 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 学校管理模块 - 开发任务清单
---
| 文档信息 | 内容 |
|---------|------|
| **文档版本** | V1.0 |
| **模块名称** | 学校管理模块 |
| **编写团队** | pangu |
| **创建日期** | 2026-01-31 |
---
## 任务概览
| 类别 | 任务数 | 预估工时 | 状态 |
|-----|:------:|:------:|:----:|
| 后端开发 | 14 | 25h | 待开始 |
| 前端开发 | 10 | 23h | 待开始 |
| 测试 | 3 | 8h | 待开始 |
| **合计** | **27** | **56h** | - |
---
## 一、后端开发任务
### 1.1 实体层开发
| 任务编号 | 任务名称 | 描述 | 优先级 | 工时 | 状态 |
|:------:|---------|------|:------:|:----:|:----:|
| BE-SCH-01 | 创建实体类 | 创建 School、SchoolGrade、SchoolClass 实体类 | P0 | 1h | ☐ |
**BE-SCH-01 详细说明**
创建以下文件:
```
domain/entity/
├── School.java # 学校实体
├── SchoolGrade.java # 学校年级关联实体
└── SchoolClass.java # 学校班级关联实体
domain/dto/
├── SchoolQueryDTO.java # 学校查询DTO
├── SchoolCreateDTO.java # 学校新增DTO
├── BindGradesDTO.java # 年级挂载DTO
└── BindClassesDTO.java # 班级挂载DTO
domain/vo/
├── SchoolVO.java # 学校VO
└── SchoolTreeVO.java # 学校树VO
```
**验收标准**
- [ ] 所有实体类包含表中所有字段
- [ ] DTO包含必要的验证注解@NotNull, @NotBlank等
- [ ] VO符合接口响应规范
---
### 1.2 Mapper层开发
| 任务编号 | 任务名称 | 描述 | 优先级 | 工时 | 状态 |
|:------:|---------|------|:------:|:----:|:----:|
| BE-SCH-02 | SchoolMapper开发 | 创建学校Mapper接口及XML | P0 | 2h | ☐ |
| BE-SCH-03 | SchoolGradeMapper开发 | 创建学校年级Mapper接口及XML | P0 | 1h | ☐ |
| BE-SCH-04 | SchoolClassMapper开发 | 创建学校班级Mapper接口及XML | P0 | 1h | ☐ |
**BE-SCH-02 详细说明 - SchoolMapper**
```java
public interface SchoolMapper extends BaseMapper<School> {
/**
* 按区域查询学校列表
*/
List<School> selectSchoolsByRegionId(@Param("regionId") Long regionId);
/**
* 查询学校列表(带数据权限)
*/
List<SchoolVO> selectSchoolList(SchoolQueryDTO query);
/**
* 查询当前年份最大编码
*/
String selectMaxCode(@Param("prefix") String prefix);
/**
* 统计学校数量(按区域)
*/
int countByRegionId(@Param("regionId") Long regionId);
}
```
**BE-SCH-03 详细说明 - SchoolGradeMapper**
```java
public interface SchoolGradeMapper extends BaseMapper<SchoolGrade> {
/**
* 按学校ID批量查询年级
*/
List<SchoolGrade> selectBySchoolIds(@Param("schoolIds") List<Long> schoolIds);
/**
* 检查是否已存在
*/
boolean exists(@Param("schoolId") Long schoolId, @Param("gradeId") Long gradeId);
/**
* 统计学校下的年级数量
*/
int countBySchoolId(@Param("schoolId") Long schoolId);
}
```
**BE-SCH-04 详细说明 - SchoolClassMapper**
```java
public interface SchoolClassMapper extends BaseMapper<SchoolClass> {
/**
* 按学校年级ID批量查询班级
*/
List<SchoolClass> selectBySchoolGradeIds(@Param("schoolGradeIds") List<Long> schoolGradeIds);
/**
* 检查是否已存在
*/
boolean exists(@Param("schoolGradeId") Long schoolGradeId, @Param("classId") Long classId);
/**
* 统计年级下的班级数量
*/
int countBySchoolGradeId(@Param("schoolGradeId") Long schoolGradeId);
}
```
**验收标准**
- [ ] 所有SQL语句已测试通过
- [ ] 软删除条件已正确添加del_flag = '0'
- [ ] 数据权限注解已添加
---
### 1.3 Service层开发
| 任务编号 | 任务名称 | 描述 | 优先级 | 工时 | 状态 |
|:------:|---------|------|:------:|:----:|:----:|
| BE-SCH-05 | 创建Service接口 | 创建 ISchoolService 接口 | P0 | 1h | ☐ |
| BE-SCH-06 | 实现学校CRUD | 实现学校增删改查方法 | P0 | 3h | ☐ |
| BE-SCH-07 | 实现年级/班级挂载 | 实现 bindGrades、bindClasses 方法 | P0 | 2h | ☐ |
| BE-SCH-08 | 实现学校树查询 | 实现 selectSchoolTree 方法 | P0 | 2h | ☐ |
| BE-SCH-09 | 实现删除校验 | 实现删除前关联检查逻辑 | P0 | 2h | ☐ |
| BE-SCH-10 | 实现编码生成 | 实现学校编码自动生成逻辑 | P0 | 1h | ☐ |
**BE-SCH-06 详细说明 - 学校CRUD**
```java
// 查询方法
List<SchoolVO> selectSchoolList(SchoolQueryDTO query);
SchoolVO selectSchoolById(Long schoolId);
// 新增方法
int insertSchool(SchoolCreateDTO dto);
// 要点:
// 1. 调用 generateSchoolCode() 生成编码
// 2. 查询并设置 regionPath
// 3. 设置 createBy、createTime
// 修改方法
int updateSchool(School school);
// 要点:
// 1. 如果修改了regionId需要更新regionPath
// 2. 设置 updateBy、updateTime
// 删除方法
int deleteSchool(Long schoolId);
// 要点:
// 1. 调用 deleteSchool 校验逻辑
// 2. 执行软删除(设置 del_flag = '1'
```
**BE-SCH-08 详细说明 - 学校树查询**
```java
/**
* 查询学校树形结构
*
* 实现步骤:
* 1. 根据 regionId 查询学校列表
* 2. 提取所有 schoolId批量查询学校年级关联
* 3. 提取所有 schoolGradeId批量查询学校班级关联
* 4. 内存中组装三级树形结构
*
* 性能优化:
* - 使用批量查询减少数据库交互
* - 使用 Map 进行分组,避免循环嵌套查询
*/
List<SchoolTreeVO> selectSchoolTree(Long regionId);
```
**BE-SCH-09 详细说明 - 删除校验**
```java
/**
* 删除学校前校验
*
* 校验规则:
* 1. 检查是否有年级/班级子节点
* - 调用 schoolGradeMapper.countBySchoolId(schoolId)
* - 如果 > 0抛出异常"该学校下存在年级数据,请先删除年级"
*
* 2. 检查是否被学生信息引用
* - 调用 studentMapper.countBySchoolId(schoolId)
* - 如果 > 0抛出异常"该学校已被学生信息引用,无法删除"
*/
/**
* 删除年级前校验
*
* 校验规则:
* 1. 检查是否有班级子节点
* 2. 检查是否被学生信息引用school_grade_id
*/
/**
* 删除班级前校验
*
* 校验规则:
* 1. 检查是否被学生信息引用school_class_id
*/
```
**BE-SCH-10 详细说明 - 编码生成**
```java
/**
* 生成学校编码
*
* 格式SCH + 年份(4位) + 序号(4位)
* 示例SCH20260001, SCH20260002
*
* 实现步骤:
* 1. 获取当前年份
* 2. 拼接前缀 "SCH" + year
* 3. 查询数据库中该前缀的最大编码
* 4. 提取序号部分 +1
* 5. 格式化为4位数字不足补0
*
* 并发处理:
* - 使用数据库唯一索引保证唯一性
* - 冲突时重试生成
*/
private String generateSchoolCode() {
String year = String.valueOf(LocalDate.now().getYear());
String prefix = "SCH" + year;
String maxCode = schoolMapper.selectMaxCode(prefix);
int seq = 1;
if (maxCode != null) {
seq = Integer.parseInt(maxCode.substring(7)) + 1;
}
return prefix + String.format("%04d", seq);
}
```
**验收标准**
- [ ] 所有Service方法已实现并通过单元测试
- [ ] 事务注解已正确添加
- [ ] 异常信息清晰易懂
---
### 1.4 Controller层开发
| 任务编号 | 任务名称 | 描述 | 优先级 | 工时 | 状态 |
|:------:|---------|------|:------:|:----:|:----:|
| BE-SCH-11 | 创建Controller | 创建 SchoolController 控制器 | P0 | 2h | ☐ |
**BE-SCH-11 详细说明**
```java
@RestController
@RequestMapping("/api/school")
public class SchoolController {
// 接口列表
GET /tree # 获取学校树
GET /list # 获取学校列表分页
GET /{schoolId} # 获取学校详情
POST / # 新增学校
PUT / # 修改学校
DELETE /{schoolId} # 删除学校
POST /bindGrades # 挂载年级
POST /bindClasses # 挂载班级
DELETE /grade/{schoolGradeId} # 删除学校年级
DELETE /class/{schoolClassId} # 删除学校班级
}
```
**验收标准**
- [ ] 所有接口遵循RESTful规范
- [ ] 参数校验注解已添加
- [ ] 操作日志注解已添加(@Log
- [ ] 权限注解已添加(@PreAuthorize
---
### 1.5 权限与测试
| 任务编号 | 任务名称 | 描述 | 优先级 | 工时 | 状态 |
|:------:|---------|------|:------:|:----:|:----:|
| BE-SCH-12 | 数据权限控制 | 实现分公司用户数据隔离 | P1 | 2h | ☐ |
| BE-SCH-13 | 单元测试 | 编写Service层单元测试 | P1 | 3h | ☐ |
| BE-SCH-14 | 接口联调 | 与前端联调并修复Bug | P1 | 2h | ☐ |
**BE-SCH-12 详细说明 - 数据权限**
```java
/**
* 数据权限实现
*
* 超级管理员:查看全部学校
* 分公司用户:只能查看所属区域的学校
*
* 实现方式:
* 1. 在 selectSchoolList 方法上添加 @DataScope 注解
* 2. SQL中关联区域表和部门表
* 3. MyBatis拦截器自动拼接权限条件
*/
// Mapper.xml 示例
<select id="selectSchoolList" resultType="SchoolVO">
SELECT s.*, r.region_name
FROM pg_school s
LEFT JOIN pg_region r ON s.region_id = r.region_id
LEFT JOIN sys_dept d ON s.region_id = d.dept_id
WHERE s.del_flag = '0'
<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>
<!-- 数据权限会自动拼接在这里 -->
${params.dataScope}
</select>
```
**BE-SCH-13 详细说明 - 单元测试**
```java
// 测试用例清单
@Test void testInsertSchool() // 新增学校
@Test void testUpdateSchool() // 修改学校
@Test void testDeleteSchool_Success() // 删除学校成功
@Test void testDeleteSchool_HasGrades() // 删除学校失败-有年级
@Test void testDeleteSchool_HasStudents() // 删除学校失败-有学生
@Test void testBindGrades() // 挂载年级
@Test void testBindGrades_Duplicate() // 重复挂载年级
@Test void testBindClasses() // 挂载班级
@Test void testDeleteSchoolGrade_Success() // 删除年级成功
@Test void testDeleteSchoolGrade_HasClasses() // 删除年级失败-有班级
@Test void testDeleteSchoolClass_Success() // 删除班级成功
@Test void testDeleteSchoolClass_HasStudents() // 删除班级失败-有学生
@Test void testSelectSchoolTree() // 查询学校树
@Test void testGenerateSchoolCode() // 生成学校编码
```
---
## 二、前端开发任务
### 2.1 页面框架开发
| 任务编号 | 任务名称 | 描述 | 优先级 | 工时 | 状态 |
|:------:|---------|------|:------:|:----:|:----:|
| FE-SCH-01 | 创建主页面框架 | 创建 school/index.vue 主页面 | P0 | 2h | ☐ |
**FE-SCH-01 详细说明**
```vue
<!-- 页面结构 -->
<div class="school-container">
<!-- 左侧区域树宽度250px固定 -->
<div class="region-tree-wrapper">
<RegionTree />
</div>
<!-- 右侧内容区 -->
<div class="main-content">
<!-- 搜索栏 -->
<el-form />
<!-- 工具栏 -->
<div class="toolbar">
<el-button>新增学校</el-button>
</div>
<!-- 学校树表格 -->
<SchoolTree />
</div>
</div>
<!-- 样式 -->
.school-container {
display: flex;
height: calc(100vh - 84px);
}
.region-tree-wrapper {
width: 250px;
border-right: 1px solid #e6e6e6;
padding: 20px;
overflow-y: auto;
}
.main-content {
flex: 1;
padding: 20px;
overflow-y: auto;
}
```
**验收标准**
- [ ] 左右分栏布局正确
- [ ] 响应式滚动正常
- [ ] 加载状态显示
---
### 2.2 组件开发
| 任务编号 | 任务名称 | 描述 | 优先级 | 工时 | 状态 |
|:------:|---------|------|:------:|:----:|:----:|
| FE-SCH-02 | RegionTree 组件 | 开发左侧区域树组件 | P0 | 2h | ☐ |
| FE-SCH-03 | SchoolTree 组件 | 开发右侧学校树表格组件 | P0 | 4h | ☐ |
| FE-SCH-04 | SchoolDialog 组件 | 开发新增/编辑学校弹窗 | P0 | 3h | ☐ |
| FE-SCH-05 | GradeSelectDialog 组件 | 开发年级选择弹窗 | P0 | 2h | ☐ |
| FE-SCH-06 | ClassSelectDialog 组件 | 开发班级选择弹窗 | P0 | 2h | ☐ |
**FE-SCH-02 详细说明 - RegionTree**
```vue
<!-- 组件功能 -->
1. 加载并展示区域树--
2. 默认展开第一级
3. 点击节点触发 node-click 事件
4. 高亮当前选中节点
<!-- 核心代码 -->
<el-tree
:data="regionTree"
:props="{ label: 'regionName', children: 'children' }"
:default-expand-all="false"
:expand-on-click-node="false"
highlight-current
node-key="regionId"
@node-click="handleNodeClick"
/>
```
**FE-SCH-03 详细说明 - SchoolTree**
```vue
<!-- 组件功能 -->
1. 使用 el-table row-key tree-props 实现树形展示
2. 根据节点类型school/grade/class显示不同操作按钮
3. 支持展开/收起
4. 加载状态显示
<!-- 核心代码 -->
<el-table
:data="schoolTreeData"
row-key="id"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
default-expand-all
>
<el-table-column prop="name" label="名称" />
<el-table-column prop="code" label="编码" width="150">
<template #default="{ row }">
{{ row.type === 'school' ? row.code : '-' }}
</template>
</el-table-column>
<!-- ... -->
<el-table-column label="操作" width="250">
<template #default="{ row }">
<!-- 学校操作 -->
<template v-if="row.type === 'school'">
<el-button link @click="handleEdit(row)">编辑</el-button>
<el-button link @click="handleAddGrade(row)">新增年级</el-button>
<el-popconfirm @confirm="handleDelete(row)">
<template #reference>
<el-button link type="danger">删除</el-button>
</template>
</el-popconfirm>
</template>
<!-- 年级操作 -->
<template v-else-if="row.type === 'grade'">
<el-button link @click="handleAddClass(row)">新增班级</el-button>
<el-popconfirm @confirm="handleDeleteGrade(row)">
<template #reference>
<el-button link type="danger">删除</el-button>
</template>
</el-popconfirm>
</template>
<!-- 班级操作 -->
<template v-else>
<el-popconfirm @confirm="handleDeleteClass(row)">
<template #reference>
<el-button link type="danger">删除</el-button>
</template>
</el-popconfirm>
</template>
</template>
</el-table-column>
</el-table>
```
**FE-SCH-04 详细说明 - SchoolDialog**
```vue
<!-- 组件功能 -->
1. 支持新增和编辑两种模式
2. 新增时学校编码不显示后端自动生成
3. 所属区域使用级联选择器
4. 表单验证
<!-- 表单字段 -->
| 字段 | 组件 | 验证规则 |
|-----|------|---------|
| 学校名称 | el-input | 必填最大100字符 |
| 学校编码 | el-input | 编辑时只读显示 |
| 学校类型 | el-select | 必填 |
| 所属区域 | el-cascader | 必填 |
| 详细地址 | el-input | 选填 |
| 联系人 | el-input | 选填 |
| 联系电话 | el-input | 选填手机号格式 |
| 状态 | el-switch | 默认启用 |
<!-- 核心方法 -->
// 暴露给父组件的方法
defineExpose({
open, // 打开新增弹窗
openEdit // 打开编辑弹窗
})
```
**FE-SCH-05 详细说明 - GradeSelectDialog**
```vue
<!-- 组件功能 -->
1. 加载年级字典列表
2. 已挂载的年级禁用disabled
3. 支持多选el-checkbox-group
4. 确认后调用 bindGrades 接口
<!-- 核心代码 -->
<el-checkbox-group v-model="selectedGrades">
<el-checkbox
v-for="item in gradeList"
:key="item.gradeId"
:label="item.gradeId"
:disabled="item.disabled"
>
{{ item.gradeName }}
<el-tag v-if="item.disabled" size="small" type="info">已挂载</el-tag>
</el-checkbox>
</el-checkbox-group>
```
**FE-SCH-06 详细说明 - ClassSelectDialog**
与 GradeSelectDialog 类似,调用 bindClasses 接口。
**验收标准**
- [ ] 组件功能完整
- [ ] Props/Events 定义清晰
- [ ] 加载状态处理
- [ ] 错误提示友好
---
### 2.3 接口与联调
| 任务编号 | 任务名称 | 描述 | 优先级 | 工时 | 状态 |
|:------:|---------|------|:------:|:----:|:----:|
| FE-SCH-07 | 封装API接口 | 创建 api/school.js | P0 | 1h | ☐ |
| FE-SCH-08 | 主页面逻辑实现 | 实现主页面交互逻辑并联调 | P0 | 3h | ☐ |
**FE-SCH-07 详细说明 - API封装**
```javascript
// api/school.js
// 获取学校树
export function getSchoolTree(regionId)
// 获取学校列表
export function getSchoolList(query)
// 获取学校详情
export function getSchool(schoolId)
// 新增学校
export function addSchool(data)
// 修改学校
export function updateSchool(data)
// 删除学校
export function deleteSchool(schoolId)
// 挂载年级
export function bindGrades(data)
// 挂载班级
export function bindClasses(data)
// 删除学校年级
export function deleteSchoolGrade(schoolGradeId)
// 删除学校班级
export function deleteSchoolClass(schoolClassId)
```
**FE-SCH-08 详细说明 - 主页面逻辑**
```javascript
// 主要逻辑点
1. 初始化加载区域树和学校树
2. 区域树节点点击 -> 刷新学校树
3. 搜索栏查询 -> 刷新学校树
4. 新增/编辑学校 -> 打开弹窗 -> 提交 -> 刷新列表
5. 删除学校 -> 确认 -> 调用接口 -> 刷新列表
6. 新增年级 -> 打开选择弹窗 -> 确认 -> 刷新列表
7. 新增班级 -> 打开选择弹窗 -> 确认 -> 刷新列表
```
---
### 2.4 优化与测试
| 任务编号 | 任务名称 | 描述 | 优先级 | 工时 | 状态 |
|:------:|---------|------|:------:|:----:|:----:|
| FE-SCH-09 | 样式优化 | 优化页面样式和交互体验 | P1 | 2h | ☐ |
| FE-SCH-10 | Bug修复 | 修复测试发现的问题 | P1 | 2h | ☐ |
**FE-SCH-09 详细说明 - 样式优化**
```
优化项:
1. 区域树节点悬停效果
2. 表格行悬停高亮
3. 操作按钮间距调整
4. 弹窗宽度适配
5. 加载骨架屏
6. 空状态提示
```
---
## 三、测试任务
| 任务编号 | 任务名称 | 描述 | 优先级 | 工时 | 状态 |
|:------:|---------|------|:------:|:----:|:----:|
| TEST-01 | 接口测试 | 使用Postman测试所有接口 | P0 | 3h | ☐ |
| TEST-02 | 功能测试 | UI功能测试 | P0 | 3h | ☐ |
| TEST-03 | 集成测试 | 前后端联调测试 | P1 | 2h | ☐ |
**TEST-01 详细说明 - 接口测试用例**
| 用例编号 | 测试场景 | 预期结果 |
|:------:|---------|---------|
| API-01 | 获取学校树有区域ID| 返回该区域下学校树 |
| API-02 | 获取学校树无区域ID| 返回全部学校树 |
| API-03 | 新增学校(正常)| 成功,学校编码自动生成 |
| API-04 | 新增学校(名称为空)| 失败,返回验证错误 |
| API-05 | 编辑学校 | 成功 |
| API-06 | 删除学校(无子级)| 成功 |
| API-07 | 删除学校(有年级)| 失败,提示有年级 |
| API-08 | 删除学校(有学生)| 失败,提示被引用 |
| API-09 | 挂载年级 | 成功 |
| API-10 | 重复挂载年级 | 成功(忽略重复)|
| API-11 | 挂载班级 | 成功 |
| API-12 | 删除年级(有班级)| 失败,提示有班级 |
| API-13 | 删除班级(有学生)| 失败,提示被引用 |
**TEST-02 详细说明 - 功能测试用例**
| 用例编号 | 测试场景 | 测试步骤 | 预期结果 |
|:------:|---------|---------|---------|
| UI-01 | 区域树展示 | 进入页面 | 左侧显示区域树 |
| UI-02 | 区域筛选 | 点击区域节点 | 右侧学校列表刷新 |
| UI-03 | 学校树展开 | 点击展开图标 | 显示年级和班级 |
| UI-04 | 新增学校 | 点击新增,填写表单,提交 | 成功,列表刷新 |
| UI-05 | 编辑学校 | 点击编辑,修改,提交 | 成功,数据更新 |
| UI-06 | 删除学校 | 点击删除,确认 | 成功/失败提示 |
| UI-07 | 新增年级 | 点击新增年级,选择,确认 | 成功,树刷新 |
| UI-08 | 新增班级 | 点击新增班级,选择,确认 | 成功,树刷新 |
| UI-09 | 表单验证 | 必填项留空提交 | 显示验证错误 |
| UI-10 | 搜索功能 | 输入关键词,点击搜索 | 列表过滤 |
---
## 四、开发进度跟踪
### 4.1 每日站会记录
| 日期 | 昨日完成 | 今日计划 | 阻塞问题 |
|-----|---------|---------|---------|
| | | | |
| | | | |
| | | | |
### 4.2 里程碑
| 里程碑 | 目标日期 | 完成日期 | 状态 |
|-------|---------|---------|:----:|
| 后端实体层完成 | | | ☐ |
| 后端Service层完成 | | | ☐ |
| 后端Controller完成 | | | ☐ |
| 前端页面框架完成 | | | ☐ |
| 前端组件开发完成 | | | ☐ |
| 前后端联调完成 | | | ☐ |
| 测试验收完成 | | | ☐ |
---
## 五、问题跟踪
| 问题编号 | 问题描述 | 发现日期 | 负责人 | 状态 | 解决方案 |
|:------:|---------|---------|-------|:----:|---------|
| | | | | | |
---
*文档结束*