# 开发日志 2026-03-23

## 班级名称唯一性校验

### 问题描述

当前系统中，同一个租户下可以创建多个相同名称的班级，导致数据混乱和管理困难。

### 需求

在新增和修改班级时，需要校验班级名称在同一租户下的唯一性。

### 实现方案

#### 1. 修改文件

| 文件 | 修改内容 |
|------|----------|
| `reading-platform-java/src/main/java/com/reading/platform/service/impl/ClassServiceImpl.java` | 在 `createClass` 和 `updateClass` 方法中添加唯一性校验 |

#### 2. 代码修改

**创建班级时校验**（`createClass` 方法）：

```java
// 检查班级名称是否唯一（MyBatis-Plus 会自动排除 deleted=1 的记录）
LambdaQueryWrapper<Clazz> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(Clazz::getTenantId, tenantId)
       .eq(Clazz::getName, request.getName());
Long count = clazzMapper.selectCount(wrapper);
if (count > 0) {
    throw new BusinessException(ErrorCode.INVALID_PARAM, "该班级名称已存在");
}
```

**更新班级时校验**（`updateClass` 方法）：

```java
if (StringUtils.hasText(request.getName())) {
    // 检查班级名称是否唯一（排除当前班级和已删除）
    LambdaQueryWrapper<Clazz> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(Clazz::getTenantId, clazz.getTenantId())
           .eq(Clazz::getName, request.getName())
           .ne(Clazz::getId, id);  // 排除当前班级
    Long count = clazzMapper.selectCount(wrapper);
    if (count > 0) {
        throw new BusinessException(ErrorCode.INVALID_PARAM, "该班级名称已存在");
    }
    clazz.setName(request.getName());
}
```

### 技术要点

1. **逻辑删除处理**: `Clazz` 实体继承 `BaseEntity`，包含 `deleted` 字段，MyBatis-Plus 的 `@TableLogic` 注解会在查询时自动添加 `deleted = 0` 条件
2. **唯一性校验范围**: 仅检查同一租户下的未删除记录
3. **错误提示**: 统一返回 "该班级名称已存在"
4. **错误码**: 使用 `ErrorCode.INVALID_PARAMETER`（参数校验错误，错误码 2004）

### 测试场景

- [x] 创建班级 "小一班" → 成功
- [x] 再次创建班级 "小一班"（同租户） → 应抛出异常 "该班级名称已存在"
- [x] 删除 "小一班" 后，再次创建 "小一班" → 成功（因为原记录已删除）
- [x] 更新班级，将名称改为已存在的名称 → 应抛出异常
- [x] 更新班级，名称不变 → 成功
- [x] 后端编译通过

### 文件变更列表

| 文件 | 变更说明 |
|------|---------|
| `reading-platform-java/src/main/java/com/reading/platform/service/impl/ClassServiceImpl.java` | 在 `createClass` 和 `updateClass` 方法中添加唯一性校验逻辑 |

---

**今日完成**: 班级名称唯一性校验、教师手机号唯一性校验

---

## 教师手机号唯一性校验

### 问题描述

在学校端教师管理中，新增和编辑教师时未对手机号唯一性进行校验，导致同一租户下不同教师可能使用相同的手机号。

### 需求

- **新增教师**：检查手机号在当前租户下是否已存在
- **编辑教师**：检查手机号是否与当前租户下其他教师重复（排除自己）

### 实现方案

#### 修改文件

| 文件 | 修改内容 |
|------|----------|
| `TeacherServiceImpl.java` | 添加 `checkPhoneUnique` 方法，在 `createTeacher` 和 `updateTeacher` 中调用 |

#### 代码修改

**新增手机号校验方法**：

```java
/**
 * 检查手机号唯一性
 */
private void checkPhoneUnique(Long tenantId, String phone, Long excludeTeacherId) {
    if (!StringUtils.hasText(phone)) {
        return; // 手机号为空时不校验（由 @NotBlank 校验）
    }

    LambdaQueryWrapper<Teacher> wrapper = new LambdaQueryWrapper<Teacher>()
            .eq(Teacher::getTenantId, tenantId)
            .eq(Teacher::getPhone, phone);

    // 编辑时排除当前教师
    if (excludeTeacherId != null) {
        wrapper.ne(Teacher::getId, excludeTeacherId);
    }

    Teacher existing = teacherMapper.selectOne(wrapper);
    if (existing != null) {
        log.warn("手机号已存在，tenantId: {}, phone: {}", tenantId, phone);
        throw new BusinessException(ErrorCode.DATA_ALREADY_EXISTS, "手机号已存在");
    }
}
```

**createTeacher 方法**：在插入前调用校验

```java
// 检查手机号是否已存在
checkPhoneUnique(tenantId, request.getPhone(), null);
```

**updateTeacher 方法**：在更新前调用校验（排除当前教师）

```java
// 检查手机号是否已存在（排除当前教师）
checkPhoneUnique(teacher.getTenantId(), request.getPhone(), teacher.getId());
```

### 技术要点

1. **租户隔离**：校验时带上 `tenantId` 条件，确保不同租户之间数据隔离
2. **编辑排除逻辑**：使用 `ne` 条件排除当前教师 ID，避免自己修改自己时报错
3. **空值处理**：手机号为空时不校验（由 Controller 层的 `@NotBlank` 注解校验）
4. **错误码**：使用 `ErrorCode.DATA_ALREADY_EXISTS`（数据已存在）

### 测试场景

- [x] 创建教师使用手机号 13800138000 → 成功
- [x] 再次创建教师使用相同手机号 13800138000（同租户） → 应失败，提示"手机号已存在"
- [x] 创建教师使用不同手机号 13800138001 → 成功
- [x] 编辑教师 A，不修改手机号 → 成功
- [x] 编辑教师 A，修改为教师 B 的手机号 → 应失败
- [x] 编辑教师 A，修改为未使用的手机号 → 成功
- [x] 租户 1 下已有手机号 13800138000，租户 2 下可以使用相同手机号 → 成功（租户隔离）
- [x] 后端编译通过

### 文件变更列表

| 文件 | 变更说明 |
|------|---------|
| `reading-platform-java/src/main/java/com/reading/platform/service/impl/TeacherServiceImpl.java` | 添加 `checkPhoneUnique` 方法，在 `createTeacher` 和 `updateTeacher` 中调用 |功能
