refactor: 学校端课程响应重构
- 将 toSchoolCourseResponse 转换逻辑从 Controller 移到 Response 类 - 使用静态方法引用简化 Controller 代码 - 删除已弃用的文档文件
This commit is contained in:
parent
c3b1056c29
commit
a054c410c2
@ -1,35 +0,0 @@
|
|||||||
# 提示词记录
|
|
||||||
|
|
||||||
## 2026-03-15
|
|
||||||
|
|
||||||
| 时间 | 提示词 | 备注 |
|
|
||||||
|------|--------|------|
|
|
||||||
| 11:31 | 全面测试,有头模式,超管端的所有页面的新增,修改,查看 | 创建了 27 个 E2E 测试用例,覆盖超管端所有功能模块,通过率 100% |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 详细信息
|
|
||||||
|
|
||||||
### 测试任务执行
|
|
||||||
|
|
||||||
**用户请求**: "全面测试,有头模式,超管端的所有页面的新增,修改,查看"
|
|
||||||
|
|
||||||
**执行内容**:
|
|
||||||
1. 创建 comprehensive 测试文件 `admin-comprehensive.spec.ts`
|
|
||||||
2. 修复测试中的问题:
|
|
||||||
- 登录流程超时
|
|
||||||
- 表格选择器严格模式冲突
|
|
||||||
- 公告管理页面未实现 (404)
|
|
||||||
3. 运行有头模式测试
|
|
||||||
4. 创建测试报告文档
|
|
||||||
|
|
||||||
**测试结果**: 27 个测试全部通过 ✅
|
|
||||||
|
|
||||||
**修改的文件**:
|
|
||||||
- `tests/e2e/admin/helpers.ts` - 修复登录函数和表格等待函数
|
|
||||||
- `tests/e2e/admin/admin-comprehensive.spec.ts` - 新建测试文件
|
|
||||||
- `docs/test-logs/admin/2026-03-15-comprehensive-test.md` - 测试报告
|
|
||||||
- `docs/dev-logs/2026-03-15.md` - 更新开发日志
|
|
||||||
- `docs/CHANGELOG.md` - 更新变更日志
|
|
||||||
|
|
||||||
---
|
|
||||||
@ -53,7 +53,7 @@ public class SchoolCourseController {
|
|||||||
tenantId, pageNum, pageSize, keyword, grade, domain, lessonType, null);
|
tenantId, pageNum, pageSize, keyword, grade, domain, lessonType, null);
|
||||||
|
|
||||||
List<SchoolCourseResponse> list = page.getRecords().stream()
|
List<SchoolCourseResponse> list = page.getRecords().stream()
|
||||||
.map(pkg -> toSchoolCourseResponse(pkg))
|
.map(SchoolCourseResponse::toSchoolCourseResponse)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
// 填充 lessonTags
|
// 填充 lessonTags
|
||||||
@ -76,85 +76,8 @@ public class SchoolCourseController {
|
|||||||
log.info("获取课程详情,id={}", id);
|
log.info("获取课程详情,id={}", id);
|
||||||
Long tenantId = SecurityUtils.getCurrentTenantId();
|
Long tenantId = SecurityUtils.getCurrentTenantId();
|
||||||
CoursePackage course = courseService.getCourseByIdWithTenantCheck(id, tenantId);
|
CoursePackage course = courseService.getCourseByIdWithTenantCheck(id, tenantId);
|
||||||
return Result.success(toSchoolCourseResponse(course));
|
return Result.success(SchoolCourseResponse.toSchoolCourseResponse(course));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 转换为学校端课程响应(gradeTags/domainTags 规范为 String[])
|
|
||||||
*/
|
|
||||||
private SchoolCourseResponse toSchoolCourseResponse(CoursePackage pkg) {
|
|
||||||
return SchoolCourseResponse.builder()
|
|
||||||
.id(pkg.getId())
|
|
||||||
.tenantId(pkg.getTenantId())
|
|
||||||
.name(pkg.getName())
|
|
||||||
.code(pkg.getCode())
|
|
||||||
.description(pkg.getDescription())
|
|
||||||
.pictureBookName(pkg.getPictureBookName())
|
|
||||||
.coverImagePath(pkg.getCoverImagePath())
|
|
||||||
.coverUrl(pkg.getCoverUrl())
|
|
||||||
.gradeTags(parseJsonArray(pkg.getGradeTags()))
|
|
||||||
.domainTags(parseJsonArray(pkg.getDomainTags()))
|
|
||||||
.duration(pkg.getDurationMinutes())
|
|
||||||
.usageCount(pkg.getUsageCount())
|
|
||||||
.teacherCount(pkg.getTeacherCount())
|
|
||||||
.avgRating(pkg.getAvgRating())
|
|
||||||
.status(pkg.getStatus())
|
|
||||||
.createdAt(pkg.getCreatedAt())
|
|
||||||
.updatedAt(pkg.getUpdatedAt())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析 JSON 数组为 String[],兼容多种格式:
|
|
||||||
* - 标准 JSON: ["小班","中班"]
|
|
||||||
* - 逗号分隔: 小班,中班
|
|
||||||
* - 错误格式(split 导致): ["[\"小班\"", " \"中班\""] -> 提取有效值
|
|
||||||
*/
|
|
||||||
private String[] parseJsonArray(String json) {
|
|
||||||
if (!StringUtils.hasText(json)) {
|
|
||||||
return new String[0];
|
|
||||||
}
|
|
||||||
String s = json.trim();
|
|
||||||
try {
|
|
||||||
if (s.startsWith("[")) {
|
|
||||||
var list = JSON.parseArray(s, String.class);
|
|
||||||
if (list != null && !list.isEmpty()) {
|
|
||||||
// 检查是否为被错误 split 的格式,如 ["[\"小班\"", " \"中班\""]
|
|
||||||
String first = list.get(0);
|
|
||||||
if (first != null && first.startsWith("[\"") && !first.contains(",")) {
|
|
||||||
return list.stream()
|
|
||||||
.map(String::trim)
|
|
||||||
.map(this::extractQuotedValue)
|
|
||||||
.filter(v -> v != null && !v.isEmpty())
|
|
||||||
.toArray(String[]::new);
|
|
||||||
}
|
|
||||||
return list.stream()
|
|
||||||
.map(v -> v != null ? v.trim() : "")
|
|
||||||
.filter(v -> !v.isEmpty())
|
|
||||||
.toArray(String[]::new);
|
|
||||||
}
|
|
||||||
return new String[0];
|
|
||||||
}
|
|
||||||
return java.util.Arrays.stream(s.split(","))
|
|
||||||
.map(String::trim)
|
|
||||||
.filter(v -> !v.isEmpty())
|
|
||||||
.toArray(String[]::new);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warn("解析 JSON 数组失败: {}", json, e);
|
|
||||||
return new String[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String extractQuotedValue(String s) {
|
|
||||||
if (s == null) return null;
|
|
||||||
s = s.trim();
|
|
||||||
int start = s.indexOf('"');
|
|
||||||
if (start >= 0) {
|
|
||||||
int end = s.indexOf('"', start + 1);
|
|
||||||
if (end > start) {
|
|
||||||
return s.substring(start + 1, end).trim();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return s.replaceAll("^\\[\"|\"\\]?$", "").trim();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
package com.reading.platform.dto.response;
|
package com.reading.platform.dto.response;
|
||||||
|
|
||||||
|
import com.alibaba.fastjson2.JSON;
|
||||||
|
import com.reading.platform.entity.CoursePackage;
|
||||||
import io.swagger.v3.oas.annotations.media.Schema;
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
@ -69,4 +72,82 @@ public class SchoolCourseResponse {
|
|||||||
|
|
||||||
@Schema(description = "课程环节标签(列表展示用,仅 name 和 lessonType)")
|
@Schema(description = "课程环节标签(列表展示用,仅 name 和 lessonType)")
|
||||||
private List<LessonTagResponse> lessonTags;
|
private List<LessonTagResponse> lessonTags;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为学校端课程响应(gradeTags/domainTags 规范为 String[])
|
||||||
|
*/
|
||||||
|
public static SchoolCourseResponse toSchoolCourseResponse(CoursePackage pkg) {
|
||||||
|
return SchoolCourseResponse.builder()
|
||||||
|
.id(pkg.getId())
|
||||||
|
.tenantId(pkg.getTenantId())
|
||||||
|
.name(pkg.getName())
|
||||||
|
.code(pkg.getCode())
|
||||||
|
.description(pkg.getDescription())
|
||||||
|
.pictureBookName(pkg.getPictureBookName())
|
||||||
|
.coverImagePath(pkg.getCoverImagePath())
|
||||||
|
.coverUrl(pkg.getCoverUrl())
|
||||||
|
.gradeTags(parseJsonArray(pkg.getGradeTags()))
|
||||||
|
.domainTags(parseJsonArray(pkg.getDomainTags()))
|
||||||
|
.duration(pkg.getDurationMinutes())
|
||||||
|
.usageCount(pkg.getUsageCount())
|
||||||
|
.teacherCount(pkg.getTeacherCount())
|
||||||
|
.avgRating(pkg.getAvgRating())
|
||||||
|
.status(pkg.getStatus())
|
||||||
|
.createdAt(pkg.getCreatedAt())
|
||||||
|
.updatedAt(pkg.getUpdatedAt())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 JSON 数组为 String[],兼容多种格式:
|
||||||
|
* - 标准 JSON: ["小班","中班"]
|
||||||
|
* - 逗号分隔: 小班,中班
|
||||||
|
* - 错误格式(split 导致): ["[\"小班\"", " \"中班\""] -> 提取有效值
|
||||||
|
*/
|
||||||
|
private static String[] parseJsonArray(String json) {
|
||||||
|
if (!StringUtils.hasText(json)) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
String s = json.trim();
|
||||||
|
try {
|
||||||
|
if (s.startsWith("[")) {
|
||||||
|
var list = JSON.parseArray(s, String.class);
|
||||||
|
if (list != null && !list.isEmpty()) {
|
||||||
|
// 检查是否为被错误 split 的格式,如 ["[\"小班\"", " \"中班\""]
|
||||||
|
String first = list.get(0);
|
||||||
|
if (first != null && first.startsWith("[\"") && !first.contains(",")) {
|
||||||
|
return list.stream()
|
||||||
|
.map(String::trim)
|
||||||
|
.map(SchoolCourseResponse::extractQuotedValue)
|
||||||
|
.filter(v -> v != null && !v.isEmpty())
|
||||||
|
.toArray(String[]::new);
|
||||||
|
}
|
||||||
|
return list.stream()
|
||||||
|
.map(v -> v != null ? v.trim() : "")
|
||||||
|
.filter(v -> !v.isEmpty())
|
||||||
|
.toArray(String[]::new);
|
||||||
|
}
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
return java.util.Arrays.stream(s.split(","))
|
||||||
|
.map(String::trim)
|
||||||
|
.filter(v -> !v.isEmpty())
|
||||||
|
.toArray(String[]::new);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new String[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String extractQuotedValue(String s) {
|
||||||
|
if (s == null) return null;
|
||||||
|
s = s.trim();
|
||||||
|
int start = s.indexOf('"');
|
||||||
|
if (start >= 0) {
|
||||||
|
int end = s.indexOf('"', start + 1);
|
||||||
|
if (end > start) {
|
||||||
|
return s.substring(start + 1, end).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s.replaceAll("^\\[\"|\"\\]?$", "").trim();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user