# 套餐数据显示问题修复测试 ## 问题描述 数据库中有很多套餐和课程包数据,但超管端、学校端页面上没有显示数据。接口有返回数据,但前端无法正确解析显示。 ## 问题原因 1. **字段格式不匹配**:后端 `gradeLevels` 存储的是 JSON 字符串或逗号分隔字符串,前端期望数组格式 2. **数据结构不匹配**:学校端期望的数据结构与后端返回不一致 3. **缺少字段**:前端需要 `courses` 字段(包含的课程列表),但后端未返回 ## 修复方案 ### 后端修改 #### 1. CoursePackageResponse.java 修改字段类型和添加新字段: ```java // gradeLevels: String → String[] @Schema(description = "年级水平(数组)") private String[] gradeLevels; // 新增字段 @Schema(description = "包含的课程") private List 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 packageCourses = packageCourseMapper.selectList( new LambdaQueryWrapper() .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> findTenantPackages() { Long tenantId = SecurityUtils.getCurrentTenantId(); return Result.success(packageService.findTenantPackages(tenantId)); } ``` ### 前端修改 #### 1. PackageView.vue 修改数据访问路径: ```vue
{{ item.package.name }}
{{ item.package.courseCount }} 个
{{ item.name }}
{{ item.courseCount }} 个 ``` #### 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 " ``` **返回结果**: ```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 " ``` **返回结果**: ```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