11 KiB
【问题修复】出国申请OSS ID字段缺失问题
🔴 问题现象
用户反馈:
测试点击查看详情按钮弹窗界面中的出国境登记表盖章文件下载按钮提示"文件OSS ID不存在,无法下载"
具体表现:
- 前端提示:
文件OSS ID不存在,无法下载 - 控制台日志显示:
stampedFileOssId: undefined或null - 党籍保留附件也无法显示(相同原因)
🔍 问题根本原因
问题定位
虽然:
- ✅ 后端VO类已补充
stampedFileOssId和partyRetentionFileOssId字段 - ✅ 前端TypeScript接口已补充相应字段
- ✅ 下载功能已改进(使用request工具)
但是: ❌ Mapper XML查询SQL中缺少这两个字段!
技术分析
文件:backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasApplicationMapper.xml
问题代码(第91-93行):
<sql id="selectOverseasApplicationVo">
select oa.application_id, oa.serial_number, ...,
oa.stamped_registration_file_path, oa.stamped_registration_file_name,
oa.stamped_file_upload_time, oa.stamped_file_upload_status,
<!-- ❌ 缺少:oa.stamped_file_oss_id -->
<!-- ❌ 缺少:oa.party_retention_file_path -->
<!-- ❌ 缺少:oa.party_retention_file_name -->
<!-- ❌ 缺少:oa.party_retention_upload_time -->
<!-- ❌ 缺少:oa.party_retention_upload_status -->
<!-- ❌ 缺少:oa.party_retention_file_oss_id -->
oa.tenant_id, oa.create_by, oa.create_time, ...
影响:
- SQL查询未包含
stamped_file_oss_id和party_retention_file_oss_id字段 - 导致查询结果中这两个字段始终为
null - 前端收到的API响应中OSS ID为空
- 下载功能无法工作(缺少OSS ID)
- 党籍保留附件无法显示(缺少完整字段)
关键点:
MyBatis的
AutoMapper只映射SELECT查询中明确指定的字段。
即使VO类中声明了字段,如果SQL语句没有查询该字段,结果中也不会包含!
✅ 修复方案
修复内容
文件:RsOverseasApplicationMapper.xml
修改位置:第91-95行
修改前:
oa.emergency_contact, oa.signature,
oa.stamped_registration_file_path, oa.stamped_registration_file_name,
oa.stamped_file_upload_time, oa.stamped_file_upload_status,
oa.tenant_id, oa.create_by, oa.create_time, oa.update_by, oa.update_time, oa.deleted,
修改后:
oa.emergency_contact, oa.signature,
oa.stamped_registration_file_path, oa.stamped_registration_file_name,
oa.stamped_file_upload_time, oa.stamped_file_upload_status, oa.stamped_file_oss_id,
oa.party_retention_file_path, oa.party_retention_file_name,
oa.party_retention_upload_time, oa.party_retention_upload_status, oa.party_retention_file_oss_id,
oa.tenant_id, oa.create_by, oa.create_time, oa.update_by, oa.update_time, oa.deleted,
补充字段:
oa.stamped_file_oss_id- 盖章文件OSS IDoa.party_retention_file_path- 党籍保留附件文件路径oa.party_retention_file_name- 党籍保留附件文件名oa.party_retention_upload_time- 党籍保留附件上传时间oa.party_retention_upload_status- 党籍保留附件上传状态oa.party_retention_file_oss_id- 党籍保留附件OSS ID
📊 修复状态
| 项目 | 状态 | 说明 |
|---|---|---|
| 后端VO补充字段 | ✅ 已完成 | 补充OSS ID等字段(第335-356行) |
| 前端TS接口补充 | ✅ 已完成 | 补充OSS ID等字段 |
| Mapper XML修复 | ✅ 已完成 | 补充SQL查询字段(关键修复) |
| 后端编译 | ✅ 成功 | BUILD SUCCESS(22.813s) |
| 前端下载功能 | ✅ 已完成 | 使用request工具 |
| 调试日志 | ✅ 已完成 | 输出字段详细信息 |
| 重启后端服务 | ⏳ 待您执行 | 应用Mapper XML修改 |
| 浏览器刷新 | ⏳ 待您执行 | 硬刷新清除缓存 |
| 功能测试 | ⏳ 待您验证 | 按操作指南测试 |
🔧 完整修复链路
修复1:后端VO字段补充 ✅
文件:RsOverseasApplicationVo.java
/**
* 盖章文件OSS ID(用于安全下载)
*/
private Long stampedFileOssId;
/**
* 党籍保留附件文件路径
*/
private String partyRetentionFilePath;
/**
* 党籍保留附件文件名
*/
private String partyRetentionFileName;
/**
* 党籍保留附件上传时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date partyRetentionUploadTime;
/**
* 党籍保留附件上传状态:0-未上传,1-已上传
*/
private String partyRetentionUploadStatus;
/**
* 党籍保留附件OSS ID(用于安全下载)
*/
private Long partyRetentionFileOssId;
修复2:Mapper XML查询字段 ✅ (本次关键修复)
文件:RsOverseasApplicationMapper.xml
补充SQL查询字段:
oa.stamped_file_oss_id,
oa.party_retention_file_path, oa.party_retention_file_name,
oa.party_retention_upload_time, oa.party_retention_upload_status, oa.party_retention_file_oss_id,
修复3:前端TypeScript接口 ✅
文件:web/src/api/rs/overseas.ts
export interface OverseasApplicationVO {
// ... 其他字段 ...
stampedFileOssId?: number;
partyRetentionFilePath?: string;
partyRetentionFileName?: string;
partyRetentionUploadTime?: string;
partyRetentionUploadStatus?: string;
partyRetentionFileOssId?: number;
}
修复4:前端下载功能 ✅
文件:web/src/views/rs/overseas/index.vue
使用request工具替代window.open:
const handleDownloadStampedFile = async (data: OverseasApplicationVO) => {
if (!data.stampedFileOssId) {
ElMessage.warning('文件OSS ID不存在,无法下载');
return;
}
const response = await request({
url: `/resource/oss/download/${data.stampedFileOssId}`,
method: 'get',
responseType: 'blob'
});
const blob = response instanceof Blob ? response : new Blob([response.data]);
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.download = data.stampedRegistrationFileName || '文件.pdf';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
ElMessage.success('文件下载成功');
};
🚀 立即执行的操作
Step 1: 重启后端服务 ⏳
IDEA重启:
- 停止当前运行的
RuoYi-Admin服务 - 重新点击运行按钮
或命令行重启:
# 查找进程
ps aux | grep ruoyi-admin
# 停止进程(替换<PID>为实际进程ID)
kill -9 <PID>
# 重新启动
cd backend/ruoyi-admin
mvn spring-boot:run
Step 2: 刷新浏览器 ⏳
硬刷新(清除缓存):
- Windows:
Ctrl + Shift + R - Mac:
Cmd + Shift + R
Step 3: 测试功能 ⏳
1️⃣ 打开开发者工具
按 F12 → 点击 Console 标签
2️⃣ 查看详情并检查日志
- 在出国申请列表中找到有附件的申请
- 点击"查看详情"按钮
- 检查控制台日志:
期望日志:
📄 [查看详情] 开始查看申请详情,申请ID: 1234567890
✅ [查看详情] 申请详情获取成功: {...}
🔍 [党籍附件] 检查党籍保留附件字段:
- partyRetentionUploadStatus: "1" ← ✅ 应该有值
- partyRetentionFileName: "党籍保留材料.pdf" ← ✅ 应该有值
- partyRetentionFileOssId: 987654321 ← ✅ 关键:应该有OSS ID!
- partyRetentionUploadTime: "2025-10-11 11:47:05"
🔍 [盖章文件] 检查盖章文件字段:
- stampedFileUploadStatus: "1"
- stampedRegistrationFileName: "出国登记表-盖章.pdf"
- stampedFileOssId: 123456789 ← ✅ 关键:应该有OSS ID!
- stampedFileUploadTime: "2025-10-11 11:47:03"
关键检查点:
- ✅
stampedFileOssId应该有数字值(不是undefined或null) - ✅
partyRetentionFileOssId应该有数字值(如果是党员且已上传)
3️⃣ 测试下载功能
-
下载盖章文件:
- 点击"出国境登记表盖章文件"的"下载"按钮
- ✅ 应该成功下载,不再提示"文件OSS ID不存在"
-
下载党籍保留附件(如果有):
- 点击"党籍保留附件"的"下载"按钮
- ✅ 应该成功下载
🎯 期望测试结果
控制台日志
- ✅
stampedFileOssId有数字值(如:1933084077469929474) - ✅
partyRetentionFileOssId有数字值(如果是党员且已上传) - ❌ 不应该是
undefined或null
详情弹窗
- ✅ 显示出国境登记表盖章文件(绿色背景)
- ✅ 显示党籍保留附件(红色边框,如果是党员且已上传)
- ✅ 两个附件都可以点击下载按钮
下载功能
- ✅ 点击下载按钮后文件开始下载
- ✅ 不再提示"文件OSS ID不存在"
- ✅ 下载的文件可以正常打开
📝 技术要点总结
1. MyBatis字段映射机制
VO类声明字段 ≠ 自动查询字段
正确流程:
- VO类中声明字段(Java属性)
- Mapper XML中SELECT查询明确包含该字段
- MyBatis自动映射到VO对象
错误理解:
- ❌ 以为VO类声明了字段,MyBatis会自动查询
- ❌ 以为AutoMapper会自动处理所有字段
2. 排查思路
当发现字段值为 null 时:
- 检查VO类 → 是否有该字段声明?
- 检查Mapper XML → SQL查询是否包含该字段? ← 本次问题所在
- 检查数据库 → 表中是否有该字段且有数据?
- 检查前端 → TypeScript接口是否有该字段?
3. 完整修复检查清单
- 后端VO类:字段声明 ✅
- Mapper XML:SQL查询包含字段 ✅ (本次修复)
- 数据库表:字段存在且有数据
- 前端接口:TypeScript接口声明 ✅
- 前端逻辑:正确使用字段 ✅
- 后端编译:编译成功 ✅
- 后端重启:应用修改 ⏳
- 浏览器刷新:清除缓存 ⏳
- 功能测试:验证效果 ⏳
💡 经验教训
问题关键
即使后端VO类和前端接口都正确声明了字段,如果Mapper XML的SQL查询中没有包含该字段,查询结果中该字段仍然为null!
类似问题排查
如果遇到类似的"字段值为null"问题:
- 不要只检查VO类 - 可能是Mapper XML缺少字段查询
- 不要只检查数据库 - 可能是SQL语句没写字段
- 检查完整链路 - VO类 → Mapper XML → 数据库 → 前端
最佳实践
-
新增VO字段时:
- ✅ 同步更新Mapper XML的SELECT语句
- ✅ 添加字段到SQL查询中
- ✅ 重新编译和重启
-
测试时:
- ✅ 使用浏览器控制台检查API响应
- ✅ 验证字段值是否真实存在
- ✅ 不要只依赖UI显示判断
-
开发规范:
- 📋 新增字段时创建完整的检查清单
- 📋 包括VO、Mapper、数据库、前端等所有环节
- 📋 确保每个环节都正确配置
📞 后续支持
如果测试时仍有问题,请提供:
- 控制台日志截图(F12 → Console标签)
- Network标签截图(F12 → Network标签,查看API响应)
- 具体错误信息
修复完成时间:2025-10-11
修复内容:Mapper XML补充OSS ID字段查询
修复文件:RsOverseasApplicationMapper.xml
关键改进:完善SQL查询字段列表