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

7.2 KiB
Raw Blame History

套餐数据显示问题修复测试

问题描述

数据库中有很多套餐和课程包数据,但超管端、学校端页面上没有显示数据。接口有返回数据,但前端无法正确解析显示。

问题原因

  1. 字段格式不匹配:后端 gradeLevels 存储的是 JSON 字符串或逗号分隔字符串,前端期望数组格式
  2. 数据结构不匹配:学校端期望的数据结构与后端返回不一致
  3. 缺少字段:前端需要 courses 字段(包含的课程列表),但后端未返回

修复方案

后端修改

1. CoursePackageResponse.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 填充逻辑:

// 解析 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 类型转换方法:

@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

修改返回类型:

// 导入
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

修改数据访问路径:

<!-- 修改前 -->
<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 接口定义:

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;
  }>;
}

测试验证

超管端套餐列表接口测试

curl -X GET "http://localhost:8080/api/v1/admin/packages?page=1&pageSize=10" \
  -H "Authorization: Bearer <admin_token>"

返回结果

{
  "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}
        ]
      }
    ]
  }
}

学校端套餐列表接口测试

curl -X GET "http://localhost:8080/api/v1/school/packages" \
  -H "Authorization: Bearer <school_token>"

返回结果

{
  "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 迁移脚本添加的测试数据:

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. 学校端套餐接口返回包含 startDateendDate 字段
  4. 前端正确解析并显示数据

修复后,超管端和学校端的套餐页面都能正确显示数据。

测试日期

2026-03-14