Files
hzdyjs/docs/变更管理/code-adjust-record-2025-01-21-wuzhen.md
T

10 KiB
Raw Blame History

代码调整记录 - 2025-01-21 - wuzhen

📋 变更概要

问题描述: 班级信息管理页面列表显示为空,统计信息显示为0
修复时间: 2025-01-21 15:30:00
修复类型: 🐛 Bug修复 - 功能完善
影响模块: 班级信息管理模块

🔍 问题分析

主要问题

  1. 后端统计接口缺失 - XgClassInfoController中缺少统计相关API接口
  2. 前端API路径错误 - getClassOptions等接口路径与后端不匹配
  3. 统计数据无法获取 - 前端页面统计卡片显示为0

根本原因

  • 班级信息模块开发不完整,缺少统计功能实现
  • 前后端API路径约定不一致

🛠️ 修复方案

1. 后端接口完善

A. Controller层新增统计接口

文件: backend/ruoyi-modules/ruoyi-xg-xx/src/main/java/org/dromara/xg/controller/XgClassInfoController.java 变更内容:

// 新增统计相关API接口
@GetMapping("/statistics")
public R<Map<String, Object>> getStatistics() 

@GetMapping("/statistics/college") 
public R<List<Map<String, Object>>> countByCollege()

@GetMapping("/statistics/grade")
public R<List<Map<String, Object>>> countByGrade()

@GetMapping("/statistics/major")
public R<List<Map<String, Object>>> countByMajor()

@GetMapping("/statistics/classStatus")
public R<List<Map<String, Object>>> countByClassStatus()

B. Service接口和实现

文件: backend/ruoyi-modules/ruoyi-xg-xx/src/main/java/org/dromara/xg/service/IXgClassInfoService.java 文件: backend/ruoyi-modules/ruoyi-xg-xx/src/main/java/org/dromara/xg/service/impl/XgClassInfoServiceImpl.java 变更内容: 新增5个统计方法的接口定义和具体实现

2. 前端API修正

A. API接口调用修正

文件: web/src/api/xg-xx/classInfo.ts 变更内容:

// 修正API路径
export const getClassOptions = (): AxiosPromise<R<ClassInfoVO[]>> => {
  return request({
    url: '/xg/classInfo/getClassOptions',  // 修正:从 /options 改为 /getClassOptions
    method: 'get'
  })
}

// 新增统计接口
export const getClassStatistics = (): AxiosPromise<R<Map<string, any>>> => {
  return request({
    url: '/xg/classInfo/statistics',
    method: 'get'
  })
}

B. 前端页面统计数据获取

文件: web/src/views/xg-xx/classInfo/index.vue 变更内容: 修改getOptions方法,并行获取统计数据和选项数据

📊 测试验证

验证内容

  • ✅ 统计卡片数据正常显示
  • ❌ 列表数据仍显示为空(需进一步修复)

📋 第二次修复 - 班级信息列表显示为空问题

修复时间: 2025-01-21 17:00:00
修复类型: 🐛 Bug修复 - 数据查询问题

🔍 深度问题分析

核心问题识别

通过对比学生信息管理(正常)与班级信息管理(异常),发现关键问题:

  1. 缺少自定义分页查询方法 ⚠️

    • 班级信息的XgClassInfoMapper.xml几乎为空,缺少自定义selectVoPage实现
    • 学生信息有完整的自定义分页查询方法,包含租户隔离和逻辑删除条件
  2. Mapper接口定义不完整 ⚠️

    • XgClassInfoMapper接口过于简单,缺少重写的selectVoPage方法声明
    • 缺少必要的自定义查询方法
  3. 缺少调试日志 ⚠️

    • XgClassInfoServiceImpl没有分页查询的调试日志
    • 无法跟踪查询过程和结果

根本原因

  • BaseMapperPlus默认实现限制: 默认实现缺少关键的WHERE条件(deleted = 0、tenant_id = '000000')
  • 租户隔离条件缺失: 查询未包含必要的租户过滤条件
  • 字段映射问题: 可能存在VO字段与数据库字段映射不匹配

🛠️ 第二次修复方案

1. 完善XgClassInfoMapper.xml

文件: backend/ruoyi-modules/ruoyi-xg-xx/src/main/resources/mapper/xg/XgClassInfoMapper.xml 变更内容:

<!-- 新增完整的ResultMap -->
<resultMap type="org.dromara.xg.domain.vo.XgClassInfoVo" id="XgClassInfoVoResult">
    <!-- 完整的字段映射 -->
</resultMap>

<!-- 新增自定义分页查询方法 -->
<select id="selectVoPage" resultMap="XgClassInfoVoResult">
    SELECT [完整字段列表]
    FROM xg_class_info
    WHERE deleted = 0 AND tenant_id = '000000'
    <!-- 动态条件处理 -->
</select>

<!-- 新增自定义详情查询方法 -->
<select id="selectVoById" parameterType="java.io.Serializable" resultMap="XgClassInfoVoResult">
    <!-- 完整查询实现 -->
</select>

2. 增强XgClassInfoMapper接口

文件: backend/ruoyi-modules/ruoyi-xg-xx/src/main/java/org/dromara/xg/mapper/XgClassInfoMapper.java 变更内容:

// 新增重写的分页查询方法
<P extends IPage<XgClassInfoVo>> P selectVoPage(IPage<XgClassInfo> page, @Param("ew") Wrapper<XgClassInfo> wrapper);

// 新增各种自定义查询方法
XgClassInfoVo selectByClassCode(String classCode);
List<XgClassInfoVo> selectByGrade(String grade);
// ... 其他查询方法

3. 添加Service层调试日志

文件: backend/ruoyi-modules/ruoyi-xg-xx/src/main/java/org/dromara/xg/service/impl/XgClassInfoServiceImpl.java 变更内容:

@Override
public TableDataInfo<XgClassInfoVo> queryPageList(XgClassInfoBo bo, PageQuery pageQuery) {
    log.info("=== 班级信息分页查询开始 ===");
    log.info("查询条件: {}", bo);
    log.info("分页参数: {}", pageQuery);
    
    // 详细的查询过程日志
    LambdaQueryWrapper<XgClassInfo> lqw = buildQueryWrapper(bo);
    log.info("Service构建的Lambda查询条件: {}", lqw.getTargetSql());
    
    Page<XgClassInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
    log.info("Mapper查询结果 - 总数: {}, 当前页记录数: {}", result.getTotal(), result.getRecords().size());
    
    // 结果验证和返回
}

4. 确保租户隔离和逻辑删除

所有查询方法都包含以下关键条件:

  • WHERE deleted = 0 - 逻辑删除过滤
  • AND tenant_id = '000000' - 租户隔离
  • 适当的状态过滤(class_status = '1'用于有效班级)

📊 修复预期效果

  • ✅ 列表数据正常显示,不再为空
  • ✅ 分页功能正常工作
  • ✅ 查询条件正确应用
  • ✅ 租户隔离正确生效
  • ✅ 调试日志提供完整的查询跟踪信息

🔧 技术改进

  1. 标准化查询实现: 所有自定义查询都包含必要的基础条件
  2. 完善的日志体系: 提供详细的查询过程跟踪
  3. 统一的接口设计: 与学生信息管理保持一致的实现模式
  4. 完整的字段映射: 确保VO与数据库字段的正确对应

📝 总结

本次修复通过对比分析正常工作的学生信息管理模块,识别并解决了班级信息管理的核心问题:

  1. 使用自定义分页查询方法替代默认实现
  2. 添加必要的租户隔离和逻辑删除条件
  3. 完善接口定义和字段映射
  4. 增加详细的调试日志

修复后,班级信息管理模块应该能够正常显示列表数据,与统计功能一起为用户提供完整的班级信息管理体验。


📋 第三次修复 - tenantId字段映射错误

修复时间: 2025-01-21 17:30:00
修复类型: 🐛 Bug修复 - MyBatis字段映射错误

🔍 错误信息

Could not set property 'tenantId' of 'class org.dromara.xg.domain.vo.XgClassInfoVo' with value '000000' 
Cause: There is no setter for property named 'tenantId' in 'class org.dromara.xg.domain.vo.XgClassInfoVo'

🔍 根本原因分析

主要问题

  1. Mapper.xml中查询了tenant_id字段:SQL语句包含SELECT tenant_id, ...
  2. ResultMap中映射了tenantId属性:<result property="tenantId" column="tenant_id"/>
  3. XgClassInfoVo类缺少tenantId属性:VO类中没有定义tenantId字段
  4. MyBatis无法设置值:无法将数据库值映射到不存在的属性

设计原则冲突

  • 实体类:XgClassInfo包含tenantId属性(技术字段)
  • VO类:XgClassInfoVo应该只包含业务字段,不包含技术字段
  • 映射错误:试图将技术字段映射到业务VO中

🛠️ 修复方案

采用方案2:移除tenantId相关映射

原因:

  • VO类应该只包含前端需要的业务字段
  • 租户隔离是底层技术实现,不应暴露给前端
  • 与学生信息管理的实现方式保持一致

修复内容

文件: backend/ruoyi-modules/ruoyi-xg-xx/src/main/resources/mapper/xg/XgClassInfoMapper.xml

1. 移除ResultMap中的tenantId映射

<!-- 移除这行 -->
<!-- <result property="tenantId" column="tenant_id"/> -->

2. 移除SELECT语句中的tenant_id字段

<!-- 修改前 -->
SELECT id, tenant_id, class_code, class_name, ...

<!-- 修改后 -->
SELECT id, class_code, class_name, ...

3. 保留WHERE条件中的租户过滤

<!-- 保留租户隔离条件 -->
WHERE deleted = 0 AND tenant_id = '000000'

技术细节

  • 查询字段:只查询VO类中存在的业务字段
  • 过滤条件:保留tenant_id作为WHERE条件,确保数据安全
  • 字段映射:ResultMap只映射VO类中实际存在的属性
  • 业务数据:VO类保持干净,只包含前端需要的业务字段

📊 修复预期效果

  • ✅ MyBatis字段映射错误解决
  • ✅ 班级信息列表数据正常显示
  • ✅ 租户隔离机制正常工作
  • ✅ VO类保持业务字段纯净性
  • ✅ 与学生信息管理实现方式一致

🔧 技术原则

  1. VO类设计原则:只包含前端业务需要的字段
  2. 数据安全原则:通过WHERE条件确保租户隔离
  3. 字段映射原则:ResultMap映射必须与目标类属性匹配
  4. 一致性原则:与现有成功模块保持相同的实现模式

📝 经验总结

本次错误提醒我们:

  1. 字段映射一致性:ResultMap中的属性必须在目标类中存在
  2. VO类设计规范:业务VO不应包含技术实现字段
  3. 租户隔离实现:通过SQL条件而非字段映射实现
  4. 参考成功案例:学习现有正常工作模块的实现方式

通过这次修复,建立了正确的字段映射模式,为后续类似功能开发提供了标准参考。