10 KiB
代码调整记录 - 2025-01-21 - wuzhen
📋 变更概要
问题描述: 班级信息管理页面列表显示为空,统计信息显示为0
修复时间: 2025-01-21 15:30:00
修复类型: 🐛 Bug修复 - 功能完善
影响模块: 班级信息管理模块
🔍 问题分析
主要问题
- 后端统计接口缺失 -
XgClassInfoController中缺少统计相关API接口 - 前端API路径错误 -
getClassOptions等接口路径与后端不匹配 - 统计数据无法获取 - 前端页面统计卡片显示为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修复 - 数据查询问题
🔍 深度问题分析
核心问题识别
通过对比学生信息管理(正常)与班级信息管理(异常),发现关键问题:
-
缺少自定义分页查询方法 ⚠️
- 班级信息的
XgClassInfoMapper.xml几乎为空,缺少自定义selectVoPage实现 - 学生信息有完整的自定义分页查询方法,包含租户隔离和逻辑删除条件
- 班级信息的
-
Mapper接口定义不完整 ⚠️
XgClassInfoMapper接口过于简单,缺少重写的selectVoPage方法声明- 缺少必要的自定义查询方法
-
缺少调试日志 ⚠️
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'用于有效班级)
📊 修复预期效果
- ✅ 列表数据正常显示,不再为空
- ✅ 分页功能正常工作
- ✅ 查询条件正确应用
- ✅ 租户隔离正确生效
- ✅ 调试日志提供完整的查询跟踪信息
🔧 技术改进
- 标准化查询实现: 所有自定义查询都包含必要的基础条件
- 完善的日志体系: 提供详细的查询过程跟踪
- 统一的接口设计: 与学生信息管理保持一致的实现模式
- 完整的字段映射: 确保VO与数据库字段的正确对应
📝 总结
本次修复通过对比分析正常工作的学生信息管理模块,识别并解决了班级信息管理的核心问题:
- 使用自定义分页查询方法替代默认实现
- 添加必要的租户隔离和逻辑删除条件
- 完善接口定义和字段映射
- 增加详细的调试日志
修复后,班级信息管理模块应该能够正常显示列表数据,与统计功能一起为用户提供完整的班级信息管理体验。
📋 第三次修复 - 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'
🔍 根本原因分析
主要问题
- Mapper.xml中查询了tenant_id字段:SQL语句包含
SELECT tenant_id, ... - ResultMap中映射了tenantId属性:
<result property="tenantId" column="tenant_id"/> - XgClassInfoVo类缺少tenantId属性:VO类中没有定义tenantId字段
- 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类保持业务字段纯净性
- ✅ 与学生信息管理实现方式一致
🔧 技术原则
- VO类设计原则:只包含前端业务需要的字段
- 数据安全原则:通过WHERE条件确保租户隔离
- 字段映射原则:ResultMap映射必须与目标类属性匹配
- 一致性原则:与现有成功模块保持相同的实现模式
📝 经验总结
本次错误提醒我们:
- 字段映射一致性:ResultMap中的属性必须在目标类中存在
- VO类设计规范:业务VO不应包含技术实现字段
- 租户隔离实现:通过SQL条件而非字段映射实现
- 参考成功案例:学习现有正常工作模块的实现方式
通过这次修复,建立了正确的字段映射模式,为后续类似功能开发提供了标准参考。