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

531 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 出国行程变更申请 - 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
-- 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
```
```java
// 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`
**修复代码**:
```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类必须包含以下基础字段**:
```java
/**
* 创建部门
*/
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 自动查询
```java
// 使用 selectList 等方法
List<RsOverseasItineraryChangeVo> list = mapper.selectVoList(wrapper);
```
- ✅ MyBatis-Plus 只查询VO类中定义的字段
- ✅ 如果VO中没有 `createDept`,查询SQL不会包含这个字段
- ✅ 不会报错
#### 场景2:自定义SQL查询(本次错误场景)
```xml
<!-- 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类(缺少基础字段)
```java
@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类(包含基础字段)
```java
@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命令(推荐)**
```bash
cd /Users/weizzzz/Documents/Cursor/0828CURSOR/all-project/backend
mvn clean compile -DskipTests
```
**方法2:重启后端服务**
```bash
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:提交功能先开发
```java
// 提交API - 使用Entity类,不是VO类
public R<Void> add(@RequestBody RsOverseasItineraryChangeBo bo) {
return toAjax(rsOverseasItineraryChangeService.insertByBo(bo));
}
```
- ✅ Entity类继承 `BaseEntity`,自动包含所有基础字段
- ✅ 提交功能正常工作
#### 原因2:列表查询后开发
```java
// 列表API - 使用VO类
public TableDataInfo<RsOverseasItineraryChangeVo> list(
RsOverseasItineraryChangeBo bo,
PageQuery pageQuery
) {
return rsOverseasItineraryChangeService.queryPageList(bo, pageQuery);
}
```
- ❌ VO类缺少 `createDept` 字段
- ❌ 第一次查询列表时才发现问题
#### 原因3:测试不充分
- ✅ 测试了提交功能 → 通过
- ❌ 没有测试列表查询 → 发现问题
---
## 🎯 最佳实践建议
### 1. VO类设计模板
**所有VO类都应该使用这个模板**:
```java
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类基础字段规范(强制性)"
**核心内容**:
```markdown
### 🔴 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. 重新编译后端(必须)
```bash
cd /Users/weizzzz/Documents/Cursor/0828CURSOR/all-project/backend
mvn clean compile -DskipTests
```
### 2. 重启后端服务(必须)
```bash
# 停止当前服务(Ctrl+C)
# 然后重新启动
mvn spring-boot:run
```
### 3. 刷新前端页面(必须)
```bash
# 强制刷新浏览器
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. ⚠️ 修改后才会生效
**编译命令**:
```bash
cd backend
mvn clean compile -DskipTests
```