2026-02-03 15:31:56 +08:00
|
|
|
|
# 教师多班级功能技术方案
|
|
|
|
|
|
|
2026-02-03 20:50:11 +08:00
|
|
|
|
> 作者:pangu
|
2026-02-03 15:31:56 +08:00
|
|
|
|
> 创建时间:2026-02-03
|
|
|
|
|
|
> 状态:待审核
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 一、需求描述
|
|
|
|
|
|
|
|
|
|
|
|
### 1.1 业务场景
|
|
|
|
|
|
|
|
|
|
|
|
- 张老师是武汉二中高二年级的数学老师
|
|
|
|
|
|
- 他同时教 1班、2班、3班
|
|
|
|
|
|
- 系统应支持在**一个教育身份**中选择**多个班级**
|
|
|
|
|
|
|
|
|
|
|
|
### 1.2 预期效果
|
|
|
|
|
|
|
|
|
|
|
|
**添加/编辑教育身份时**:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
所在地区: 湖北省 / 武汉市 / 硚口区
|
|
|
|
|
|
学校: 武汉市第二中学
|
|
|
|
|
|
年级: 高二
|
|
|
|
|
|
班级: [1班] [2班] [3班] ← 多选
|
|
|
|
|
|
学科: 数学
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**个人中心展示**:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
┌────────────────────────────────┐
|
|
|
|
|
|
│ 📘 数学 当前使用 │
|
|
|
|
|
|
│ 武汉市第二中学 │
|
|
|
|
|
|
│ 班级: 高二1班、高二2班、高二3班 │
|
|
|
|
|
|
│ 学科: 数学 │
|
|
|
|
|
|
│ 编辑 解除 切换 │
|
|
|
|
|
|
└────────────────────────────────┘
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 二、当前架构分析
|
|
|
|
|
|
|
|
|
|
|
|
### 2.1 现有数据结构
|
|
|
|
|
|
|
|
|
|
|
|
| 表名 | 字段 | 类型 | 说明 |
|
|
|
|
|
|
|------|------|------|------|
|
|
|
|
|
|
| `pg_member` | `school_class_id` | `bigint` | 单值,只能存一个班级 |
|
|
|
|
|
|
|
|
|
|
|
|
### 2.2 现有接口
|
|
|
|
|
|
|
|
|
|
|
|
| 接口 | 方法 | 说明 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| `/h5/member/education` | GET | 返回单个教育身份 |
|
|
|
|
|
|
| `/h5/member/education` | POST | 保存单个教育身份(覆盖) |
|
|
|
|
|
|
| `/h5/member/education` | DELETE | 删除教育身份 |
|
|
|
|
|
|
|
|
|
|
|
|
### 2.3 问题
|
|
|
|
|
|
|
|
|
|
|
|
- 班级字段是单值,不支持多班级
|
|
|
|
|
|
- 接口设计为覆盖式更新
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 三、技术方案
|
|
|
|
|
|
|
|
|
|
|
|
### 3.1 数据库改造
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.1.1 新建关联表
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 会员-班级关联表(多对多)
|
|
|
|
|
|
CREATE TABLE pg_member_class (
|
|
|
|
|
|
id BIGINT PRIMARY KEY AUTO_INCREMENT,
|
|
|
|
|
|
member_id BIGINT NOT NULL COMMENT '会员ID',
|
|
|
|
|
|
school_class_id BIGINT NOT NULL COMMENT '学校班级关联ID',
|
|
|
|
|
|
create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
|
UNIQUE KEY uk_member_class (member_id, school_class_id),
|
|
|
|
|
|
KEY idx_member_id (member_id)
|
|
|
|
|
|
) COMMENT '会员班级关联表';
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.1.2 数据迁移脚本
|
|
|
|
|
|
|
|
|
|
|
|
```sql
|
|
|
|
|
|
-- 将现有数据迁移到关联表
|
|
|
|
|
|
INSERT INTO pg_member_class (member_id, school_class_id)
|
|
|
|
|
|
SELECT member_id, school_class_id
|
|
|
|
|
|
FROM pg_member
|
|
|
|
|
|
WHERE school_class_id IS NOT NULL AND identity_type = '2';
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.1.3 pg_member 表调整
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 处理方式 |
|
|
|
|
|
|
|------|----------|
|
|
|
|
|
|
| `school_class_id` | 保留(向后兼容)或后续版本删除 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 3.2 后端改造
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.2.1 新建实体类
|
|
|
|
|
|
|
2026-02-03 20:47:45 +08:00
|
|
|
|
**文件**:`pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/domain/PgMemberClass.java`
|
2026-02-03 15:31:56 +08:00
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package org.dromara.pangu.member.domain;
|
|
|
|
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.annotation.IdType;
|
|
|
|
|
|
import com.baomidou.mybatisplus.annotation.TableId;
|
|
|
|
|
|
import com.baomidou.mybatisplus.annotation.TableName;
|
|
|
|
|
|
import lombok.Data;
|
|
|
|
|
|
import java.util.Date;
|
|
|
|
|
|
|
|
|
|
|
|
@Data
|
|
|
|
|
|
@TableName("pg_member_class")
|
|
|
|
|
|
public class PgMemberClass {
|
|
|
|
|
|
@TableId(type = IdType.AUTO)
|
|
|
|
|
|
private Long id;
|
|
|
|
|
|
private Long memberId;
|
|
|
|
|
|
private Long schoolClassId;
|
|
|
|
|
|
private Date createTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.2.2 新建 Mapper
|
|
|
|
|
|
|
2026-02-03 20:47:45 +08:00
|
|
|
|
**文件**:`pangu-modules/pangu-business/src/main/java/org/dromara/pangu/member/mapper/PgMemberClassMapper.java`
|
2026-02-03 15:31:56 +08:00
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
package org.dromara.pangu.member.mapper;
|
|
|
|
|
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
|
|
|
|
|
import org.apache.ibatis.annotations.Mapper;
|
|
|
|
|
|
import org.dromara.pangu.member.domain.PgMemberClass;
|
|
|
|
|
|
|
|
|
|
|
|
@Mapper
|
|
|
|
|
|
public interface PgMemberClassMapper extends BaseMapper<PgMemberClass> {
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.2.3 修改 DTO
|
|
|
|
|
|
|
|
|
|
|
|
**文件**:`H5EducationDto.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
// 修改前
|
|
|
|
|
|
@Schema(description = "学校班级关联ID")
|
|
|
|
|
|
@NotNull(message = "请选择班级")
|
|
|
|
|
|
private Long schoolClassId;
|
|
|
|
|
|
|
|
|
|
|
|
// 修改后
|
|
|
|
|
|
@Schema(description = "学校班级关联ID列表")
|
|
|
|
|
|
@NotEmpty(message = "请选择班级")
|
|
|
|
|
|
private List<Long> schoolClassIds;
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.2.4 修改 VO
|
|
|
|
|
|
|
|
|
|
|
|
**文件**:`H5EducationVo.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
// 新增字段
|
|
|
|
|
|
private List<Long> schoolClassIds; // 班级ID列表
|
|
|
|
|
|
private List<String> classNames; // 班级名称列表(用于前端展示)
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.2.5 修改 Service
|
|
|
|
|
|
|
|
|
|
|
|
**文件**:`H5MemberServiceImpl.java`
|
|
|
|
|
|
|
|
|
|
|
|
```java
|
|
|
|
|
|
@Autowired
|
|
|
|
|
|
private PgMemberClassMapper memberClassMapper;
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
|
public void saveEducation(H5EducationDto dto) {
|
|
|
|
|
|
Long memberId = getCurrentMemberId();
|
|
|
|
|
|
PgMember member = memberMapper.selectById(memberId);
|
|
|
|
|
|
if (member == null) {
|
|
|
|
|
|
throw new ServiceException("会员不存在");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 校验学校、年级存在
|
|
|
|
|
|
// ... 原有校验逻辑 ...
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 校验所有班级是否存在且属于该年级
|
|
|
|
|
|
for (Long classId : dto.getSchoolClassIds()) {
|
|
|
|
|
|
PgSchoolClass schoolClass = schoolClassMapper.selectById(classId);
|
|
|
|
|
|
if (schoolClass == null || !schoolClass.getSchoolGradeId().equals(dto.getSchoolGradeId())) {
|
|
|
|
|
|
throw new ServiceException("班级不存在或不属于该年级");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 更新会员基本信息
|
|
|
|
|
|
member.setSchoolId(dto.getSchoolId());
|
|
|
|
|
|
member.setSchoolGradeId(dto.getSchoolGradeId());
|
|
|
|
|
|
member.setSubjectId(dto.getSubjectId());
|
|
|
|
|
|
member.setIdentityType("2");
|
|
|
|
|
|
member.setRegionId(school.getRegionId());
|
|
|
|
|
|
memberMapper.updateById(member);
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 删除旧的班级关联
|
|
|
|
|
|
memberClassMapper.delete(
|
|
|
|
|
|
new LambdaQueryWrapper<PgMemberClass>()
|
|
|
|
|
|
.eq(PgMemberClass::getMemberId, memberId)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 插入新的班级关联
|
|
|
|
|
|
for (Long classId : dto.getSchoolClassIds()) {
|
|
|
|
|
|
PgMemberClass mc = new PgMemberClass();
|
|
|
|
|
|
mc.setMemberId(memberId);
|
|
|
|
|
|
mc.setSchoolClassId(classId);
|
|
|
|
|
|
memberClassMapper.insert(mc);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
public H5EducationVo getEducation() {
|
|
|
|
|
|
Long memberId = getCurrentMemberId();
|
|
|
|
|
|
PgMember member = memberMapper.selectById(memberId);
|
|
|
|
|
|
if (member == null) {
|
|
|
|
|
|
throw new ServiceException("会员不存在");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!"2".equals(member.getIdentityType()) || member.getSchoolId() == null) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
H5EducationVo vo = buildEducationVo(member);
|
|
|
|
|
|
|
|
|
|
|
|
// 查询关联的班级列表
|
|
|
|
|
|
List<PgMemberClass> memberClasses = memberClassMapper.selectList(
|
|
|
|
|
|
new LambdaQueryWrapper<PgMemberClass>()
|
|
|
|
|
|
.eq(PgMemberClass::getMemberId, memberId)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
List<Long> classIds = memberClasses.stream()
|
|
|
|
|
|
.map(PgMemberClass::getSchoolClassId)
|
|
|
|
|
|
.collect(Collectors.toList());
|
|
|
|
|
|
vo.setSchoolClassIds(classIds);
|
|
|
|
|
|
|
|
|
|
|
|
// 填充班级名称
|
|
|
|
|
|
if (!classIds.isEmpty()) {
|
|
|
|
|
|
List<String> classNames = new ArrayList<>();
|
|
|
|
|
|
for (Long classId : classIds) {
|
|
|
|
|
|
PgSchoolClass schoolClass = schoolClassMapper.selectById(classId);
|
|
|
|
|
|
if (schoolClass != null) {
|
|
|
|
|
|
PgClass pgClass = classMapper.selectById(schoolClass.getClassId());
|
|
|
|
|
|
if (pgClass != null) {
|
|
|
|
|
|
classNames.add(pgClass.getClassName());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
vo.setClassNames(classNames);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return vo;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
|
|
|
public void deleteEducation() {
|
|
|
|
|
|
Long memberId = getCurrentMemberId();
|
|
|
|
|
|
|
|
|
|
|
|
// 删除班级关联
|
|
|
|
|
|
memberClassMapper.delete(
|
|
|
|
|
|
new LambdaQueryWrapper<PgMemberClass>()
|
|
|
|
|
|
.eq(PgMemberClass::getMemberId, memberId)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 清空会员教育信息
|
|
|
|
|
|
// ... 原有逻辑 ...
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
### 3.3 H5 前端改造
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.3.1 修改表单组件
|
|
|
|
|
|
|
|
|
|
|
|
**文件**:`user_authentication_center_front/user-front/src/components/TeacherIdentityForm.vue`
|
|
|
|
|
|
|
|
|
|
|
|
**表单数据修改**:
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// 修改前
|
|
|
|
|
|
const teacherForm = reactive({
|
|
|
|
|
|
classId: '',
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 修改后
|
|
|
|
|
|
const teacherForm = reactive({
|
|
|
|
|
|
classIds: [],
|
|
|
|
|
|
})
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**模板修改**:
|
|
|
|
|
|
|
|
|
|
|
|
```vue
|
|
|
|
|
|
<!-- 修改前 -->
|
|
|
|
|
|
<el-form-item label="班级" prop="classId">
|
|
|
|
|
|
<el-select v-model="teacherForm.classId">
|
|
|
|
|
|
<!-- ... -->
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 修改后 -->
|
|
|
|
|
|
<el-form-item label="班级" prop="classIds">
|
|
|
|
|
|
<el-select
|
|
|
|
|
|
v-model="teacherForm.classIds"
|
|
|
|
|
|
multiple
|
|
|
|
|
|
collapse-tags
|
|
|
|
|
|
collapse-tags-tooltip
|
|
|
|
|
|
:max-collapse-tags="3"
|
|
|
|
|
|
placeholder="请选择班级(可多选)"
|
|
|
|
|
|
size="large"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-option
|
|
|
|
|
|
v-for="classItem in classOptions"
|
|
|
|
|
|
:key="classItem.schoolClassId"
|
|
|
|
|
|
:label="classItem.className"
|
|
|
|
|
|
:value="classItem.schoolClassId"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**验证规则修改**:
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// 修改前
|
|
|
|
|
|
classId: [{ required: true, message: '请选择班级', trigger: 'change' }],
|
|
|
|
|
|
|
|
|
|
|
|
// 修改后
|
|
|
|
|
|
classIds: [{
|
|
|
|
|
|
required: true,
|
|
|
|
|
|
type: 'array',
|
|
|
|
|
|
min: 1,
|
|
|
|
|
|
message: '请至少选择一个班级',
|
|
|
|
|
|
trigger: 'change'
|
|
|
|
|
|
}],
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**提交逻辑修改**:
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
const requestData = {
|
|
|
|
|
|
schoolId: teacherForm.schoolId,
|
|
|
|
|
|
schoolGradeId: teacherForm.gradeId,
|
|
|
|
|
|
schoolClassIds: teacherForm.classIds, // 数组
|
|
|
|
|
|
subjectId: teacherForm.subjectId,
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
**回显逻辑修改**:
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// setFormData 中
|
|
|
|
|
|
teacherForm.classIds = formData.schoolClassIds || []
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 3.3.2 修改列表展示
|
|
|
|
|
|
|
|
|
|
|
|
**文件**:`user_authentication_center_front/user-front/src/views/userCenter/index.vue`
|
|
|
|
|
|
|
|
|
|
|
|
```javascript
|
|
|
|
|
|
// getEducationIdentities 方法中
|
|
|
|
|
|
educationIdentities.value = [{
|
|
|
|
|
|
id: 1,
|
|
|
|
|
|
role: `${item.subjectName || '教师'}`,
|
|
|
|
|
|
school: item.schoolName || '',
|
|
|
|
|
|
icon: 'mdi:account-tie',
|
|
|
|
|
|
active: true,
|
|
|
|
|
|
details: [
|
|
|
|
|
|
{ label: '班级', value: item.classNames?.join('、') || '' }, // 多班级用顿号连接
|
|
|
|
|
|
{ label: '学科', value: item.subjectName || '' },
|
|
|
|
|
|
],
|
|
|
|
|
|
}]
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 四、文件改动清单
|
|
|
|
|
|
|
|
|
|
|
|
| 层级 | 文件路径 | 改动类型 |
|
|
|
|
|
|
|------|----------|----------|
|
|
|
|
|
|
| **数据库** | DDL 脚本 | 新建 |
|
|
|
|
|
|
| **后端** | `pangu-business/.../member/domain/PgMemberClass.java` | 新建 |
|
|
|
|
|
|
| **后端** | `pangu-business/.../member/mapper/PgMemberClassMapper.java` | 新建 |
|
|
|
|
|
|
| **后端** | `pangu-business/.../h5/domain/dto/H5EducationDto.java` | 修改 |
|
|
|
|
|
|
| **后端** | `pangu-business/.../h5/domain/vo/H5EducationVo.java` | 修改 |
|
|
|
|
|
|
| **后端** | `pangu-business/.../h5/service/impl/H5MemberServiceImpl.java` | 修改 |
|
|
|
|
|
|
| **H5前端** | `user-front/src/components/TeacherIdentityForm.vue` | 修改 |
|
|
|
|
|
|
| **H5前端** | `user-front/src/views/userCenter/index.vue` | 修改 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 五、接口变更
|
|
|
|
|
|
|
|
|
|
|
|
### 5.1 POST /h5/member/education
|
|
|
|
|
|
|
|
|
|
|
|
**请求参数变更**:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
// 修改前
|
|
|
|
|
|
{
|
|
|
|
|
|
"schoolId": 1,
|
|
|
|
|
|
"schoolGradeId": 5,
|
|
|
|
|
|
"schoolClassId": 104,
|
|
|
|
|
|
"subjectId": 1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 修改后
|
|
|
|
|
|
{
|
|
|
|
|
|
"schoolId": 1,
|
|
|
|
|
|
"schoolGradeId": 5,
|
|
|
|
|
|
"schoolClassIds": [104, 105, 106],
|
|
|
|
|
|
"subjectId": 1
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
### 5.2 GET /h5/member/education
|
|
|
|
|
|
|
|
|
|
|
|
**响应参数变更**:
|
|
|
|
|
|
|
|
|
|
|
|
```json
|
|
|
|
|
|
// 修改前
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": 200,
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"schoolId": 1,
|
|
|
|
|
|
"schoolName": "武汉市第二中学",
|
|
|
|
|
|
"schoolGradeId": 5,
|
|
|
|
|
|
"gradeName": "高二",
|
|
|
|
|
|
"schoolClassId": 104,
|
|
|
|
|
|
"className": "1班",
|
|
|
|
|
|
"subjectId": 1,
|
|
|
|
|
|
"subjectName": "数学"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 修改后
|
|
|
|
|
|
{
|
|
|
|
|
|
"code": 200,
|
|
|
|
|
|
"data": {
|
|
|
|
|
|
"schoolId": 1,
|
|
|
|
|
|
"schoolName": "武汉市第二中学",
|
|
|
|
|
|
"schoolGradeId": 5,
|
|
|
|
|
|
"gradeName": "高二",
|
|
|
|
|
|
"schoolClassIds": [104, 105, 106],
|
|
|
|
|
|
"classNames": ["1班", "2班", "3班"],
|
|
|
|
|
|
"subjectId": 1,
|
|
|
|
|
|
"subjectName": "数学"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 六、测试要点
|
|
|
|
|
|
|
|
|
|
|
|
| 场景 | 测试内容 | 预期结果 |
|
|
|
|
|
|
|------|----------|----------|
|
|
|
|
|
|
| 新增 | 选择多个班级保存 | 保存成功,关联表有多条记录 |
|
|
|
|
|
|
| 编辑 | 回显多个班级 | 多选框显示已选班级 |
|
|
|
|
|
|
| 编辑 | 增加/减少班级 | 关联表正确更新 |
|
|
|
|
|
|
| 删除 | 删除教育身份 | 关联表数据同步删除 |
|
|
|
|
|
|
| 展示 | 列表显示多班级 | 显示"1班、2班、3班" |
|
|
|
|
|
|
| 边界 | 不选班级提交 | 校验失败,提示选择班级 |
|
|
|
|
|
|
| 边界 | 选择不同年级的班级 | 校验失败,提示班级不属于该年级 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 七、上线计划
|
|
|
|
|
|
|
|
|
|
|
|
| 步骤 | 内容 | 负责人 | 备注 |
|
|
|
|
|
|
|------|------|--------|------|
|
|
|
|
|
|
| 1 | 执行数据库 DDL | DBA | 建表 |
|
|
|
|
|
|
| 2 | 执行数据迁移脚本 | DBA | 迁移现有数据 |
|
|
|
|
|
|
| 3 | 部署后端服务 | 运维 | - |
|
|
|
|
|
|
| 4 | 部署 H5 前端 | 运维 | 需同步上线 |
|
|
|
|
|
|
| 5 | 验证测试 | 测试 | 回归测试 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 八、回滚方案
|
|
|
|
|
|
|
|
|
|
|
|
1. 后端代码回滚到上一版本
|
|
|
|
|
|
2. 前端代码回滚到上一版本
|
|
|
|
|
|
3. 数据库:
|
|
|
|
|
|
- 从 `pg_member_class` 恢复 `pg_member.school_class_id`(取第一条)
|
|
|
|
|
|
- 删除 `pg_member_class` 表
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 九、风险评估
|
|
|
|
|
|
|
|
|
|
|
|
| 风险 | 等级 | 应对措施 |
|
|
|
|
|
|
|------|------|----------|
|
|
|
|
|
|
| 数据迁移失败 | 中 | 先在测试环境验证,生产环境备份 |
|
|
|
|
|
|
| 前后端不同步 | 高 | 同步上线,灰度发布 |
|
|
|
|
|
|
| 性能问题 | 低 | 关联表已加索引 |
|
|
|
|
|
|
| 兼容性问题 | 中 | 管理后台如有相关页面需同步修改 |
|
|
|
|
|
|
|
|
|
|
|
|
---
|
|
|
|
|
|
|
|
|
|
|
|
## 附录:相关表结构
|
|
|
|
|
|
|
|
|
|
|
|
### pg_member(会员表,现有)
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 类型 | 说明 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| member_id | bigint | 主键 |
|
|
|
|
|
|
| consumer_id | bigint | 用户ID |
|
|
|
|
|
|
| school_id | bigint | 学校ID |
|
|
|
|
|
|
| school_grade_id | bigint | 年级ID |
|
|
|
|
|
|
| school_class_id | bigint | 班级ID(单值,改造后可废弃) |
|
|
|
|
|
|
| subject_id | bigint | 学科ID |
|
|
|
|
|
|
| identity_type | varchar | 身份类型:1-家长,2-教师 |
|
|
|
|
|
|
|
|
|
|
|
|
### pg_member_class(会员班级关联表,新建)
|
|
|
|
|
|
|
|
|
|
|
|
| 字段 | 类型 | 说明 |
|
|
|
|
|
|
|------|------|------|
|
|
|
|
|
|
| id | bigint | 主键 |
|
|
|
|
|
|
| member_id | bigint | 会员ID |
|
|
|
|
|
|
| school_class_id | bigint | 学校班级关联ID |
|
|
|
|
|
|
| create_time | datetime | 创建时间 |
|