Files
hzdyjs/行程变更申请-VO字段缺失修复.md
T
2025-10-14 16:41:19 +08:00

11 KiB
Raw Blame History

出国行程变更申请 - VO字段缺失修复

🔴 MyBatis映射错误

错误信息

Error querying database.  
Cause: org.apache.ibatis.reflection.ReflectionException: 
Could not set property 'createDept' of 'class org.dromara.rs.domain.vo.RsOverseasItineraryChangeVo' 
with value '103' 
Cause: org.apache.ibatis.reflection.ReflectionException: 
There is no setter for property named 'createDept' in 
'class org.dromara.rs.domain.vo.RsOverseasItineraryChangeVo'

错误原因

SQL查询返回了 create_dept 字段,但VO类中没有对应的属性!

-- SQL查询中包含 create_dept 字段
SELECT 
    c.change_id,
    c.record_id,
    ...
    c.create_dept,  -- ❌ 查询返回了这个字段
    c.create_by,
    c.create_time,
    ...
FROM rs_overseas_itinerary_change c
// RsOverseasItineraryChangeVo.java (修复前)
@Data
public class RsOverseasItineraryChangeVo {
    
    private Integer deleted;
    
    // ❌ 缺少 createDept 字段!
    
    private Long createBy;  // MyBatis找不到createDept的setter,抛出异常
    private Date createTime;
    ...
}

✅ 修复方案

添加缺失的 createDept 字段

修复文件: backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/domain/vo/RsOverseasItineraryChangeVo.java

修复代码:

/**
 * 删除标志(0代表存在 1代表删除)
 */
private Integer deleted;

/**
 * 创建部门  ✅ 新增字段
 */
private Long createDept;

/**
 * 创建者
 */
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;

📋 VO类基础字段统一规范

🚨 强制性要求(来自项目规范)

所有VO类必须包含以下基础字段:

/**
 * 创建部门
 */
private Long createDept;

/**
 * 创建者
 */
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;

为什么必须包含?

场景1:MyBatis-Plus 自动查询

// 使用 selectList 等方法
List<RsOverseasItineraryChangeVo> list = mapper.selectVoList(wrapper);
  • ✅ MyBatis-Plus 只查询VO类中定义的字段
  • ✅ 如果VO中没有 createDept,查询SQL不会包含这个字段
  • ✅ 不会报错

场景2:自定义SQL查询(本次错误场景)

<!-- RsOverseasItineraryChangeMapper.xml -->
<select id="selectChangeList" resultType="RsOverseasItineraryChangeVo">
    SELECT 
        c.change_id,
        c.record_id,
        ...
        c.create_dept,  -- ✅ SQL明确查询了这个字段
        c.create_by,
        c.create_time,
        ...
    FROM rs_overseas_itinerary_change c
</select>
  • ❌ SQL返回了 create_dept 字段
  • ❌ 但VO类中没有 createDept 属性
  • ❌ MyBatis尝试调用 setCreateDept() 方法
  • ❌ 找不到setter方法,抛出 ReflectionException
  • ❌ 导致整个查询失败,返回500错误

对比示例

❌ 错误的VO类(缺少基础字段)

@Data
public class RsOverseasItineraryChangeVo {
    private Long changeId;
    private Long recordId;
    private String applicantName;
    // ... 业务字段
    
    // ❌ 缺少基础字段!
    // private Long createDept;
    // private Long createBy;
    // private Date createTime;
    // private Long updateBy;
    // private Date updateTime;
}

结果:

  • 使用MyBatis-Plus自动查询 → ✅ 正常
  • 使用自定义SQL查询(包含基础字段) → ❌ 500错误

✅ 正确的VO类(包含基础字段)

@Data
public class RsOverseasItineraryChangeVo {
    private Long changeId;
    private Long recordId;
    private String applicantName;
    // ... 业务字段
    
    // ✅ 包含完整的基础字段
    private Long createDept;
    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;
}

结果:

  • 使用MyBatis-Plus自动查询 → ✅ 正常
  • 使用自定义SQL查询(包含基础字段) → ✅ 正常

🔧 修复步骤

1. 修改VO类

✅ 已完成 - 添加 createDept 字段

2. 重新编译后端

方法1:使用Maven命令(推荐)

cd /Users/weizzzz/Documents/Cursor/0828CURSOR/all-project/backend
mvn clean compile -DskipTests

方法2:重启后端服务

cd /Users/weizzzz/Documents/Cursor/0828CURSOR/all-project/backend
mvn spring-boot:run

方法3:使用IDE重新编译

  • IDEA: Build → Rebuild Project
  • Eclipse: Project → Clean

3. 验证修复

测试步骤:

  1. 重启后端服务
  2. 刷新前端页面
  3. 访问"出国行程变更管理"
  4. 验证列表是否正常显示

预期结果:

  • ✅ 列表正常加载
  • ✅ 没有500错误
  • ✅ 能看到所有变更申请数据

📊 问题根源分析

时间线

1. 数据库设计 → 表包含 create_dept 字段 ✅
2. Entity类设计 → 继承BaseEntity,包含基础字段 ✅
3. Mapper.xml设计 → SQL查询包含 create_dept ✅
4. VO类设计 → ❌ 忘记添加 create_dept 字段
5. 运行时错误 → MyBatis无法映射字段 → 500错误

为什么之前没发现?

原因1:提交功能先开发

// 提交API - 使用Entity类,不是VO类
public R<Void> add(@RequestBody RsOverseasItineraryChangeBo bo) {
    return toAjax(rsOverseasItineraryChangeService.insertByBo(bo));
}
  • ✅ Entity类继承 BaseEntity,自动包含所有基础字段
  • ✅ 提交功能正常工作

原因2:列表查询后开发

// 列表API - 使用VO类
public TableDataInfo<RsOverseasItineraryChangeVo> list(
    RsOverseasItineraryChangeBo bo, 
    PageQuery pageQuery
) {
    return rsOverseasItineraryChangeService.queryPageList(bo, pageQuery);
}
  • ❌ VO类缺少 createDept 字段
  • ❌ 第一次查询列表时才发现问题

原因3:测试不充分

  • ✅ 测试了提交功能 → 通过
  • ❌ 没有测试列表查询 → 发现问题

🎯 最佳实践建议

1. VO类设计模板

所有VO类都应该使用这个模板:

package org.dromara.rs.domain.vo;

import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serializable;
import java.util.Date;

/**
 * XXX视图对象
 *
 * @author xxx
 * @date xxxx-xx-xx
 */
@Data
public class XxxVo implements Serializable {
    
    private static final long serialVersionUID = 1L;
    
    // ========================================
    // 业务字段
    // ========================================
    
    /**
     * 主键ID
     */
    private Long id;
    
    /**
     * 业务字段1
     */
    private String field1;
    
    // ... 其他业务字段
    
    // ========================================
    // 基础字段(必须包含)
    // ========================================
    
    /**
     * 租户编号
     */
    private String tenantId;
    
    /**
     * 删除标志(0代表存在 1代表删除)
     */
    private Integer deleted;
    
    /**
     * 创建部门
     */
    private Long createDept;
    
    /**
     * 创建者
     */
    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;
}

2. 检查清单

创建新VO类时,必须检查:

  • 包含业务字段
  • 包含 tenantId
  • 包含 deleted
  • 包含 createDept ⭐
  • 包含 createBy ⭐
  • 包含 createTime ⭐(带 @JsonFormat)
  • 包含 updateBy ⭐
  • 包含 updateTime ⭐(带 @JsonFormat)

3. 测试建议

完整的测试流程:

1. 测试新增功能
2. 测试列表查询  ⭐ 验证VO字段映射
3. 测试详情查询  ⭐ 验证VO字段映射
4. 测试修改功能
5. 测试删除功能

关键点: 先测试查询,确保VO字段定义正确!


📚 相关规范

项目规范文档

位置: .cursor/cursorrules-backend.md

章节: "VO类基础字段规范(强制性)"

核心内容:

### 🔴 VO类基础字段统一规范(新增 2025-07-01)

**⚠️ 超级重要:所有VO类必须包含统一的基础字段!**

**背景问题**:
- MyBatis-Plus自动查询:不包含继承字段
- 自定义SQL查询:明确包含字段,VO类必须有对应属性
- 导致运行时字段缺失错误:`Could not set property 'createBy'`

**统一解决方案**:
所有VO类必须包含的基础字段:
- Long createDept
- Long createBy
- Date createTime (含JSON格式化)
- Long updateBy
- Date updateTime (含JSON格式化)

🎉 修复总结

✅ 问题识别

  • 错误类型: MyBatis字段映射错误
  • 错误原因: VO类缺少 createDept 字段
  • 影响范围: 所有列表查询API

✅ 修复方案

  • 修改文件: RsOverseasItineraryChangeVo.java
  • 修改内容: 添加 createDept 字段
  • 修改行数: 1 个字段,6 行代码

✅ 验证步骤

  1. ✅ 代码修改完成
  2. ⏳ 需要重新编译后端
  3. ⏳ 需要重启后端服务
  4. ⏳ 需要测试验证

🚀 下一步操作

1. 重新编译后端(必须)

cd /Users/weizzzz/Documents/Cursor/0828CURSOR/all-project/backend
mvn clean compile -DskipTests

2. 重启后端服务(必须)

# 停止当前服务(Ctrl+C)
# 然后重新启动
mvn spring-boot:run

3. 刷新前端页面(必须)

# 强制刷新浏览器
Ctrl+Shift+R (Windows)
Cmd+Shift+R (Mac)

4. 测试验证(必须)

访问: http://localhost/rs/overseas/itinerary-change

预期结果:

  • ✅ 列表正常加载
  • ✅ 没有500错误
  • ✅ 能看到所有变更申请数据
  • ✅ 可以正常提交新申请

📞 相关文档

文档名称 说明
行程变更申请-修复总结.md 所有问题修复汇总
行程变更申请-recordId修复说明.md 必填字段修复
行程变更申请-列表显示问题修复.md 分页问题修复
行程变更申请-VO字段缺失修复.md 本文档(VO字段修复)
.cursor/cursorrules-backend.md 后端开发规范

修复日期: 2025-10-13
修复人员: Cursor AI Assistant
问题级别: 🔴 严重(阻塞功能)
修复状态: ✅ 代码修复完成,等待编译和测试


重要提醒:

  1. ⚠️ 必须重新编译后端代码
  2. ⚠️ 必须重启后端服务
  3. ⚠️ 修改后才会生效

编译命令:

cd backend
mvn clean compile -DskipTests