kindergarten_java/docs/test-logs/admin/2026-03-14-package-test.md
En 938503f2b8 chore: 更新启动脚本使用 Java (Spring Boot) 后端
- 修改后端目录从 reading-platform-backend 改为 reading-platform-java
- 修改后端端口从 3000 改为 8080
- 修改启动命令从 npm run start:dev 改为 mvn spring-boot:run
- 添加 JAVA_HOME 自动检测和设置(默认使用 /f/Java/jdk-17)
- 修改日志文件从 reading-platform-backend.log 改为 reading-platform-java.log
- 修改健康检查接口为 /actuator/health
- 增加启动等待超时时间到 60 秒(Java 启动较慢)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-16 10:35:30 +08:00

273 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.

# 套餐数据显示问题修复测试
## 问题描述
数据库中有很多套餐和课程包数据,但超管端、学校端页面上没有显示数据。接口有返回数据,但前端无法正确解析显示。
## 问题原因
1. **字段格式不匹配**:后端 `gradeLevels` 存储的是 JSON 字符串或逗号分隔字符串,前端期望数组格式
2. **数据结构不匹配**:学校端期望的数据结构与后端返回不一致
3. **缺少字段**:前端需要 `courses` 字段(包含的课程列表),但后端未返回
## 修复方案
### 后端修改
#### 1. CoursePackageResponse.java
修改字段类型和添加新字段:
```java
// gradeLevels: String → String[]
@Schema(description = "年级水平(数组)")
private String[] gradeLevels;
// 新增字段
@Schema(description = "包含的课程")
private List<CoursePackageCourseItem> courses;
@Schema(description = "开始日期(租户套餐)")
private LocalDate startDate;
@Schema(description = "结束日期(租户套餐)")
private LocalDate endDate;
```
#### 2. CoursePackageService.java
添加 `gradeLevels` 转换逻辑和 `courses` 填充逻辑:
```java
// 解析 gradeLevels数据库中存储的是 JSON 数组字符串
String[] gradeLevelsArray = null;
if (pkg.getGradeLevels() != null && !pkg.getGradeLevels().isEmpty()) {
try {
// 尝试解析 JSON 数组格式:["小班","中班"]
gradeLevelsArray = JSON.parseArray(pkg.getGradeLevels(), String.class).toArray(new String[0]);
} catch (Exception e) {
// 如果不是 JSON 格式,尝试按逗号分隔处理
gradeLevelsArray = pkg.getGradeLevels().split(",");
}
}
// 查询套餐包含的课程
List<CoursePackageCourse> packageCourses = packageCourseMapper.selectList(
new LambdaQueryWrapper<CoursePackageCourse>()
.eq(CoursePackageCourse::getPackageId, pkg.getId())
.orderByAsc(CoursePackageCourse::getSortOrder)
);
```
#### 3. CoursePackageMapper.java
添加 MapStruct 类型转换方法:
```java
@Named("stringToArray")
default String[] stringToArray(String value) {
if (value == null || value.isEmpty()) {
return new String[0];
}
return value.split(",");
}
@Named("arrayToString")
default String arrayToString(String[] value) {
if (value == null || value.length == 0) {
return null;
}
return String.join(",", value);
}
```
#### 4. SchoolPackageController.java
修改返回类型:
```java
// 导入
import com.reading.platform.dto.response.CoursePackageResponse;
// 修改返回类型
public Result<List<CoursePackageResponse>> findTenantPackages() {
Long tenantId = SecurityUtils.getCurrentTenantId();
return Result.success(packageService.findTenantPackages(tenantId));
}
```
### 前端修改
#### 1. PackageView.vue
修改数据访问路径:
```vue
<!-- 修改前 -->
<div class="package-name">{{ item.package.name }}</div>
<span class="info-value">{{ item.package.courseCount }} </span>
<!-- 修改后 -->
<div class="package-name">{{ item.name }}</div>
<span class="info-value">{{ item.courseCount }} </span>
```
#### 2. school.ts
更新 `CoursePackage` 接口定义:
```typescript
export interface CoursePackage {
id: number;
name: string;
description?: string;
price: number;
discountPrice?: number;
discountType?: string;
gradeLevels: string[];
status: string;
courseCount: number;
tenantCount: number;
createdAt: string;
startDate?: string;
endDate?: string;
courses?: Array<{
id: number;
name: string;
gradeLevel: string;
sortOrder: number;
}>;
}
```
## 测试验证
### 超管端套餐列表接口测试
```bash
curl -X GET "http://localhost:8080/api/v1/admin/packages?page=1&pageSize=10" \
-H "Authorization: Bearer <admin_token>"
```
**返回结果**
```json
{
"code": 200,
"data": {
"list": [
{
"id": 3,
"name": "幼儿园阅读启蒙套餐",
"gradeLevels": ["小班", "中班"],
"courseCount": 5,
"tenantCount": 1,
"courses": [
{"id": 6, "name": "小猪佩奇绘本阅读", "gradeLevel": "小班", "sortOrder": 1},
{"id": 7, "name": "彩虹色的花", "gradeLevel": "小班", "sortOrder": 2},
{"id": 8, "name": "牙齿大街的新鲜事", "gradeLevel": "中班", "sortOrder": 3},
{"id": 9, "name": "猜猜我有多爱你", "gradeLevel": "小班", "sortOrder": 4},
{"id": 10, "name": "逃家小兔", "gradeLevel": "小班", "sortOrder": 5}
]
}
]
}
}
```
### 学校端套餐列表接口测试
```bash
curl -X GET "http://localhost:8080/api/v1/school/packages" \
-H "Authorization: Bearer <school_token>"
```
**返回结果**
```json
{
"code": 200,
"data": [
{
"id": 3,
"name": "幼儿园阅读启蒙套餐",
"gradeLevels": ["小班", "中班"],
"courseCount": 5,
"startDate": "2026-01-01",
"endDate": "2026-12-31",
"courses": [
{"id": 6, "name": "小猪佩奇绘本阅读", "gradeLevel": "小班", "sortOrder": 1},
{"id": 7, "name": "彩虹色的花", "gradeLevel": "小班", "sortOrder": 2},
...
]
},
{
"id": 4,
"name": "亲子共读成长套餐",
"gradeLevels": ["小班", "中班", "大班"],
"courseCount": 5,
"startDate": "2026-02-01",
"endDate": "2027-01-31",
"courses": [...]
}
]
}
```
## 测试结果
| 接口 | 测试项 | 结果 |
|------|--------|------|
| 超管端套餐列表 | 返回数据格式 | ✅ 通过 |
| 超管端套餐列表 | gradeLevels 数组格式 | ✅ 通过 |
| 超管端套餐列表 | courses 字段填充 | ✅ 通过 |
| 超管端套餐列表 | tenantCount 统计 | ✅ 通过 |
| 学校端套餐列表 | 返回数据格式 | ✅ 通过 |
| 学校端套餐列表 | startDate/endDate 字段 | ✅ 通过 |
| 学校端套餐列表 | gradeLevels 数组格式 | ✅ 通过 |
| 学校端套餐列表 | courses 字段填充 | ✅ 通过 |
## 修改的文件
### 后端文件
| 文件 | 修改内容 |
|------|----------|
| `CoursePackageResponse.java` | gradeLevels 改为 String[],添加 courses、startDate、endDate 字段 |
| `CoursePackageService.java` | 添加 gradeLevels 转换和 courses 填充逻辑 |
| `CoursePackageMapper.java` | 添加 MapStruct 类型转换方法 |
| `SchoolPackageController.java` | 修改返回类型为 CoursePackageResponse |
| `V8__add_tenant_package_test_data.sql` | 添加租户套餐测试数据 |
### 前端文件
| 文件 | 修改内容 |
|------|----------|
| `PackageView.vue` | 修改数据访问路径 |
| `school.ts` | 更新 CoursePackage 接口定义 |
## 测试数据
V8 迁移脚本添加的测试数据:
```sql
INSERT INTO tenant_package (id, tenant_id, package_id, start_date, end_date, price_paid, status) VALUES
(1, 1, 3, '2026-01-01', '2026-12-31', 7999, 'ACTIVE'),
(2, 1, 4, '2026-02-01', '2027-01-31', 15999, 'ACTIVE');
```
## 总结
本次修复解决了前后端数据结构不匹配的问题:
1. 后端正确返回 `gradeLevels` 数组格式
2. 后端补充了前端需要的 `courses` 字段
3. 学校端套餐接口返回包含 `startDate``endDate` 字段
4. 前端正确解析并显示数据
修复后,超管端和学校端的套餐页面都能正确显示数据。
## 测试日期
2026-03-14