Files
hzdyjs/docs/技术问题解决记录/勤工俭学申请详情页面问题修复.md
T
2025-08-12 16:52:10 +08:00

6.2 KiB
Raw Blame History

勤工俭学申请详情页面问题修复记录

问题描述

  • 时间: 2025-01-03
  • 现象: 点击勤工俭学申请列表页面的"查看详情"按钮,页面弹出错误信息"申请信息不存在或已被删除,请刷新页面重试",并且详情页面的所有字段都不显示任何值。

问题分析

1. 前端调用链

// 列表页面调用详情
function handleDetail(row) {
  proxy.$refs['applicationDetailRef']?.handleDetail(row.id)
}

// 详情组件中的处理
function handleDetail(id) {
  getWorkStudyApplication(id).then(response => {
    // 数据处理...
  })
}

2. 后端API调用链

// Controller
@GetMapping("/{id}")
public R<JcbtWorkStudyApplicationVo> getInfo(@PathVariable Long id) {
    return R.ok(workStudyApplicationService.queryById(id));
}

// Service
public JcbtWorkStudyApplicationVo queryById(Long id) {
    JcbtWorkStudyApplicationVo applicationVo = baseMapper.selectVoById(id);
    return applicationVo; // 返回 null
}

3. 根本原因

经过调试发现:

  • baseMapper.selectById(id) 能够成功查询到Entity对象
  • baseMapper.selectVoById(id) 返回null

问题出现在 BaseMapperPlus 的 selectVoById 实现中:

// BaseMapperPlus.java 第144-150行
default <C> C selectVoById(Serializable id, Class<C> voClass) {
    T obj = this.selectById(id);  // Entity查询成功
    if (ObjectUtil.isNull(obj)) {
        return null;
    }
    return MapstructUtils.convert(obj, voClass);  // MapStruct转换失败
}

核心问题: MapstructUtils.convert() 转换从 Entity 到 VO 时失败,导致返回 null。

解决方案

方案1: 添加自定义selectVoById方法 ✅ 已实施

在 JcbtWorkStudyApplicationMapper.xml 中添加自定义的 selectVoById 方法,直接使用SQL查询和ResultMap映射,避免MapStruct转换:

<!-- 根据ID查询VO对象 - 自定义实现 -->
<select id="selectVoById" parameterType="java.io.Serializable" resultMap="JcbtWorkStudyApplicationResult">
    <include refid="selectJcbtWorkStudyApplicationVo"/>
    WHERE id = #{id}
    AND deleted = 0
</select>

方案2: 完善VO类基础字段 ✅ 已验证

确保VO类包含所有必要的基础字段,避免字段缺失导致的映射问题:

// JcbtWorkStudyApplicationVo.java 必须包含的基础字段
private Long createBy;      // 创建者
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;    // 创建时间
private Long updateBy;      // 更新者  
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;    // 更新时间

技术细节

BaseMapperPlus工作原理

  1. 继承关系: JcbtWorkStudyApplicationMapper extends BaseMapperPlus<Entity, VO>
  2. 默认selectVoById: 先查Entity,再用MapStruct转换为VO
  3. 自定义selectVoById: 直接SQL查询映射为VO,绕过MapStruct转换

MapStruct转换失败的可能原因

  1. 字段映射不匹配: Entity字段与VO字段名称或类型不对应
  2. 注解配置问题: @AutoMapper(target = JcbtWorkStudyApplication.class) 配置有误
  3. 编译时生成问题: MapStruct生成的转换器代码有问题

ResultMap映射

<resultMap type="org.dromara.jcbt.domain.vo.JcbtWorkStudyApplicationVo" id="JcbtWorkStudyApplicationResult">
    <result property="id" column="id"/>
    <result property="studentName" column="student_name"/>
    <result property="positionName" column="position_name"/>
    <!-- ... 其他字段映射 ... -->
    <result property="createBy" column="create_by"/>
    <result property="createTime" column="create_time"/>
    <result property="updateBy" column="update_by"/>
    <result property="updateTime" column="update_time"/>
</resultMap>

修复文件清单

1. 修改的文件

backend/ruoyi-modules/ruoyi-jcbt/src/main/resources/mapper/jcbt/JcbtWorkStudyApplicationMapper.xml

2. 添加的内容

  • 自定义 selectVoById 方法
  • 使用现有的 ResultMap 和 SQL 模板
  • 添加逻辑删除和条件查询

3. 调试日志增强

在 JcbtWorkStudyApplicationServiceImpl.java 中添加了详细的调试日志:

@Override
public JcbtWorkStudyApplicationVo queryById(Long id) {
    log.info("查询勤工俭学申请信息,ID: {}", id);
    
    JcbtWorkStudyApplicationVo applicationVo = baseMapper.selectVoById(id);
    log.info("查询结果: {}", applicationVo != null ? "找到记录" : "未找到记录");
    
    if (applicationVo == null) {
        // 调试:尝试直接查询Entity
        JcbtWorkStudyApplication entity = baseMapper.selectById(id);
        log.info("直接查询Entity结果: {}", entity != null ? "找到记录" : "未找到记录");
        return null;
    }
    
    // ... 其他逻辑 ...
}

验证方法

1. 后端API测试

# 使用Python脚本测试
python3 test_api.py

# 或者使用curl测试
curl -X GET "http://localhost:8080/jcbt/workStudyApplication/1" \
     -H "Authorization: Bearer {token}" \
     -H "Content-Type: application/json"

2. 前端功能测试

  1. 打开勤工俭学申请列表页面
  2. 点击任意记录的"查看详情"按钮
  3. 验证详情弹窗正常显示所有字段信息

3. 日志验证

查看后端日志中的调试信息:

tail -f backend/logs/admin.log | grep "查询勤工俭学申请信息"

预防措施

1. VO类规范

  • 所有VO类必须包含统一的基础字段(createBy, createTime, updateBy, updateTime)
  • 使用@JsonFormat注解格式化时间字段
  • 确保@AutoMapper注解正确配置

2. Mapper接口规范

  • 对于复杂的查询逻辑,优先使用自定义SQL而非依赖MapStruct转换
  • 在Mapper.xml中提供完整的ResultMap映射
  • 添加适当的WHERE条件(如逻辑删除、租户隔离)

3. 测试规范

  • 新增API接口必须编写对应的测试脚本
  • 包含正常情况和异常情况的测试用例
  • 验证前后端集成的完整流程

相关链接

  • RuoYi-Cloud文档: BaseMapperPlus使用说明
  • MapStruct官方文档: https://mapstruct.org/
  • 项目规范: .cursor/cursorrules-backend.md → "VO类基础字段规范"

修复人员: AI Assistant
修复时间: 2025-01-03
修复状态: ✅ 已完成,待验证