kindergarten_java/docs/dev-logs/2026-03-17.md
En 1d7ade9d90 feat: 学校端接口租户隔离修复与Response规范化
## 租户隔离修复
- 修复 SchoolCourseController 硬编码 tenantId=1L 的严重 bug
- 为 SchoolClassController 8个接口添加租户验证
- 为 SchoolTeacherController 4个接口添加租户验证
- 为 SchoolStudentController 3个接口添加租户验证
- 为 SchoolParentController 6个接口添加租户验证
- 为 SchoolTaskController 3个接口添加租户验证
- 为 SchoolGrowthController 3个接口添加租户验证

## Map 返回类型改 Response
- SchoolTaskTemplateController: Map → TaskTemplateResponse
- SchoolScheduleController: Map → SchedulePlanResponse
- SchoolPackageController: Map → PackageInfoResponse/PackageUsageResponse
- SchoolSettingsController: Map → SchoolSettingsResponse 等
- SchoolReportController: Map → ReportOverviewResponse 等

## 新增 Response DTO
- PackageInfoResponse, PackageUsageResponse
- SchoolSettingsResponse, BasicSettingsResponse
- NotificationSettingsResponse, SecuritySettingsResponse
- ReportOverviewResponse, TeacherReportResponse
- CourseReportResponse, StudentReportResponse

## 新增 Request DTO
- RenewRequest, SchoolSettingsUpdateRequest
- BasicSettingsUpdateRequest, NotificationSettingsUpdateRequest
- SecuritySettingsUpdateRequest

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-18 00:02:05 +08:00

227 lines
7.2 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.

# 开发日志 - 2026-03-17
## 多地点登录支持实现
### 修改内容
#### 1. JwtTokenRedisService.java
**文件路径**: `reading-platform-java/src/main/java/com/reading/platform/common/security/JwtTokenRedisService.java`
**修改内容**:
- 修改 `validateToken()` 方法,移除了 token 一致性检查
- 现在只检查 token 是否在黑名单中
- 允许同一账号有多个有效的 token支持多地点同时登录
**修改前逻辑**:
```java
// 检查是否与存储的 token 一致
String storedToken = getStoredToken(username);
if (storedToken == null) {
return true;
}
boolean isValid = token.equals(storedToken);
return isValid; // 如果不一致则返回 false导致互踢下线
```
**修改后逻辑**:
```java
// 仅检查是否在黑名单中
if (isBlacklisted(token)) {
return false;
}
// 不再检查是否与存储的 token 一致,允许同一账号有多个有效 token
return true;
```
#### 2. JwtAuthenticationFilter.java
**文件路径**: `reading-platform-java/src/main/java/com/reading/platform/common/security/JwtAuthenticationFilter.java`
**修改内容**:
- 新增依赖注入:`AdminUserMapper`, `TenantMapper`, `TeacherMapper`, `ParentMapper`
- 在 token 验证通过后,增加账户状态检查
- 新增 `isAccountActive()` 方法,根据用户角色查询对应表验证账户状态
**新增代码**:
```java
// 检查账户状态是否为 active
if (!isAccountActive(payload)) {
log.debug("Account is not active for user: {}", payload.getUsername());
sendError(response, HttpStatus.UNAUTHORIZED, "账户已被禁用,请联系管理员");
return;
}
```
**新增方法**:
```java
private boolean isAccountActive(JwtPayload payload) {
String role = payload.getRole();
Long userId = payload.getUserId();
return switch (role) {
case "admin" -> {
AdminUser adminUser = adminUserMapper.selectById(userId);
yield adminUser != null && "active".equalsIgnoreCase(adminUser.getStatus());
}
case "school" -> {
Tenant tenant = tenantMapper.selectById(userId);
yield tenant != null && "active".equalsIgnoreCase(tenant.getStatus());
}
case "teacher" -> {
Teacher teacher = teacherMapper.selectById(userId);
yield teacher != null && "active".equalsIgnoreCase(teacher.getStatus());
}
case "parent" -> {
Parent parent = parentMapper.selectById(userId);
yield parent != null && "active".equalsIgnoreCase(parent.getStatus());
}
default -> false;
};
}
```
#### 3. AuthServiceImpl.java
**文件路径**: `reading-platform-java/src/main/java/com/reading/platform/service/impl/AuthServiceImpl.java`
**修改内容**:
- 更新 `logout()` 方法的注释,说明当前实现
#### 4. 状态判断忽略大小写修改
**修改内容**:
- 将所有 `"active".equals(status)` 修改为 `"active".equalsIgnoreCase(status)`
- 确保大小写不敏感的状态判断,如 "Active"、"ACTIVE"、"active" 都被识别为激活状态
**修改文件**:
- `JwtAuthenticationFilter.java` - `isAccountActive()` 方法中的 4 处判断
- `AuthServiceImpl.java` - `login()` 方法中的 8 处判断admin、teacher、parent、tenant 各 2 处)
### 功能说明
#### 多地点登录
- 同一账号可以在多个设备/浏览器同时登录
- 各个登录状态的 token 都有效,不会互踢下线
- JWT token 本身的过期时间(默认 24 小时)保证安全性
#### 账户状态验证
- 每次请求都会验证账户状态是否为 "active"
- 如果管理员在后台禁用某个账号,该账号的所有已登录会话将立即失效
- 支持所有角色admin、school、teacher、parent
#### 黑名单机制
- 黑名单机制仍然有效
- 可以用于主动使特定 token 失效(如踢人下线场景)
### 验证步骤
1. **多地点登录测试**:
- 使用同一账号在两个不同的浏览器登录
- 在两个浏览器都发起 API 请求
- 预期:两个登录都保持有效
2. **账户状态禁用测试**:
- 使用账号 A 在浏览器 1 登录
- 在超管后台将账号 A 的状态修改为"非激活"
- 在浏览器 1 再次发起请求,应返回"账户已被禁用"
### 安全性考虑
1. JWT token 有过期时间(默认 24 小时),过期后自动失效
2. 黑名单机制仍然有效,可以主动使特定 token 失效
3. 每次请求都会验证账户状态,确保禁用账号无法访问
### 影响范围
- 所有角色的登录认证流程
- Token 验证流程
- 账户状态检查
### 兼容性
- 此修改不影响现有功能
- 登出、黑名单等功能仍然正常工作
- 前端无需修改
---
## Admin 控制器三层架构规范化
### 修改内容
#### 1. AdminCourseCollectionController.java
**文件路径**: `reading-platform-java/src/main/java/com/reading/platform/controller/admin/AdminCourseCollectionController.java`
**问题**:
- `findAll` 返回 `Result<List<?>>` - 类型不明确
- `findOne` 返回 `Result<?>` - 类型不明确
- `create/update` 返回 `Result<CourseCollection>` - 直接返回 Entity
**修改后**:
- 重命名为 `page` 方法,返回 `Result<PageResult<CourseCollectionResponse>>`
- `findOne` 返回 `Result<CourseCollectionResponse>`
- `create` 返回 `Result<CourseCollectionResponse>`
- `update` 返回 `Result<CourseCollectionResponse>`
#### 2. CourseCollectionService.java
**文件路径**: `reading-platform-java/src/main/java/com/reading/platform/service/CourseCollectionService.java`
**新增方法**:
- `pageCollections(Integer pageNum, Integer pageSize, String status)` - 分页查询并转换为 Response
**修改方法**:
- `createCollection()` - 返回类型从 `CourseCollection` 改为 `CourseCollectionResponse`
- `updateCollection()` - 返回类型从 `CourseCollection` 改为 `CourseCollectionResponse`
#### 3. CourseCollectionPageQueryRequest.java新建
**文件路径**: `reading-platform-java/src/main/java/com/reading/platform/dto/request/CourseCollectionPageQueryRequest.java`
```java
@Data
@Schema(description = "课程套餐分页查询请求")
public class CourseCollectionPageQueryRequest {
@Schema(description = "页码", example = "1")
private Integer pageNum = 1;
@Schema(description = "每页数量", example = "10")
private Integer pageSize = 10;
@Schema(description = "状态")
private String status;
}
```
### 修改文件列表
| 文件 | 类型 | 说明 |
|------|------|------|
| `AdminCourseCollectionController.java` | 修改 | 规范化返回类型 |
| `CourseCollectionService.java` | 修改 | 新增分页方法,修改返回类型 |
| `CourseCollectionPageQueryRequest.java` | 新增 | 分页查询请求 DTO |
### 验证
```bash
export JAVA_HOME="/f/Java/jdk-17"
mvn clean compile -DskipTests
# BUILD SUCCESS
```
### 规范的控制器
| 控制器 | 状态 |
|--------|------|
| AdminCourseCollectionController | ✅ 已规范 |
| AdminStatsController | ✅ 已规范 |
| AdminTenantController | ✅ 已规范 |
| AdminResourceController | ✅ 已规范 |
| AdminCourseLessonController | ✅ 已规范 |
| AdminCourseController | ✅ 已规范 |
| AdminPackageController | ✅ 已规范 |
| AdminThemeController | ✅ 已规范 |
| AdminSettingsController | 🟡 可选(设置类接口允许使用 Map |