524 lines
14 KiB
Markdown
524 lines
14 KiB
Markdown
# 出国登记列表不显示数据问题 - 修复报告
|
||
|
||
## 📋 问题描述
|
||
|
||
**问题现象**:
|
||
- 通过"新增登记"按钮新增的数据,在数据库中`rs_overseas_registration`表可以查到
|
||
- 但前端页面"出国登记"选项卡中的列表不显示这些数据
|
||
- 页面显示"暂无数据"
|
||
|
||
**影响范围**:
|
||
- 出国登记管理 → 出国登记选项卡
|
||
- 所有通过前端新增的出国登记记录
|
||
|
||
**问题严重度**:🔴 **高** - 核心功能不可用
|
||
|
||
---
|
||
|
||
## 🔍 问题分析
|
||
|
||
### 根本原因
|
||
|
||
新增出国登记时,**`registration_time`字段未设置值**,导致:
|
||
|
||
1. **数据库中`registration_time = NULL`**
|
||
2. **SQL排序异常**:
|
||
```sql
|
||
ORDER BY r.registration_time desc, r.create_time desc
|
||
```
|
||
当`registration_time`为NULL时,MySQL的排序行为不确定,可能导致数据被排到最后或查询异常
|
||
|
||
3. **列表不显示数据**
|
||
|
||
### 问题定位过程
|
||
|
||
#### 1. 检查数据库
|
||
```sql
|
||
-- 发现数据存在但registration_time为NULL
|
||
SELECT registration_id, name, registration_time, create_time
|
||
FROM rs_overseas_registration
|
||
WHERE deleted = '0';
|
||
```
|
||
|
||
#### 2. 检查后端Service代码
|
||
```java:backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasRegistrationServiceImpl.java
|
||
// ❌ 问题代码:新增时未设置registration_time
|
||
@Override
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public Boolean insertByBo(RsOverseasRegistrationBo bo) {
|
||
RsOverseasRegistration add = MapstructUtils.convert(bo, RsOverseasRegistration.class);
|
||
|
||
// 设置状态为草稿
|
||
if (StringUtils.isEmpty(add.getStatus())) {
|
||
add.setStatus("0");
|
||
}
|
||
|
||
// ❌ 缺少:add.setRegistrationTime(new Date());
|
||
|
||
boolean flag = baseMapper.insert(add) > 0;
|
||
return flag;
|
||
}
|
||
```
|
||
|
||
#### 3. 检查SQL排序
|
||
```xml:backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasRegistrationMapper.xml
|
||
<select id="selectRegistrationList" ...>
|
||
<include refid="selectRegistrationVo"/>
|
||
<where>
|
||
r.deleted = '0'
|
||
...
|
||
</where>
|
||
<!-- ❌ 问题:registration_time为NULL时排序异常 -->
|
||
order by r.registration_time desc, r.create_time desc
|
||
</select>
|
||
```
|
||
|
||
---
|
||
|
||
## ✅ 修复方案
|
||
|
||
### 方案1:修复新增逻辑(治本)
|
||
|
||
**修改文件**:`backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasRegistrationServiceImpl.java`
|
||
|
||
#### 1.1 修复`insertByBo`方法
|
||
|
||
```java
|
||
/**
|
||
* 新增出国登记
|
||
*/
|
||
@Override
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public Boolean insertByBo(RsOverseasRegistrationBo bo) {
|
||
// ... 前面的代码 ...
|
||
|
||
// 设置状态为草稿
|
||
if (StringUtils.isEmpty(add.getStatus())) {
|
||
add.setStatus("0");
|
||
}
|
||
|
||
// ✅ 修复:设置登记时间为当前时间
|
||
if (add.getRegistrationTime() == null) {
|
||
add.setRegistrationTime(new Date());
|
||
}
|
||
|
||
// 计算停留天数
|
||
if (add.getDepartureDate() != null && add.getReturnDate() != null) {
|
||
long days = (add.getReturnDate().getTime() - add.getDepartureDate().getTime()) / (1000 * 60 * 60 * 24);
|
||
add.setStayDays((int) days);
|
||
}
|
||
|
||
boolean flag = baseMapper.insert(add) > 0;
|
||
if (flag) {
|
||
bo.setRegistrationId(add.getRegistrationId());
|
||
log.info("插入成功后Entity中的registrationTime: {}", add.getRegistrationTime());
|
||
}
|
||
return flag;
|
||
}
|
||
```
|
||
|
||
#### 1.2 修复`addAndSubmit`方法
|
||
|
||
```java
|
||
/**
|
||
* 新增并提交出国登记
|
||
*/
|
||
@Override
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public Boolean addAndSubmit(RsOverseasRegistrationBo bo) {
|
||
// ... 前面的代码 ...
|
||
|
||
// 直接设置为已提交状态
|
||
add.setStatus("1"); // 已提交
|
||
add.setSubmitTime(new Date());
|
||
|
||
// ✅ 修复:设置登记时间为当前时间
|
||
if (add.getRegistrationTime() == null) {
|
||
add.setRegistrationTime(new Date());
|
||
}
|
||
|
||
// ... 后面的代码 ...
|
||
}
|
||
```
|
||
|
||
### 方案2:优化SQL排序(防御性编程)
|
||
|
||
**修改文件**:`backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasRegistrationMapper.xml`
|
||
|
||
使用`COALESCE`函数处理NULL值,当`registration_time`为NULL时使用`create_time`排序:
|
||
|
||
```xml
|
||
<!-- ✅ 优化后的SQL -->
|
||
<select id="selectRegistrationList" ...>
|
||
<include refid="selectRegistrationVo"/>
|
||
<where>
|
||
r.deleted = '0'
|
||
...
|
||
</where>
|
||
<!-- 使用COALESCE处理NULL值 -->
|
||
order by COALESCE(r.registration_time, r.create_time) desc, r.create_time desc
|
||
</select>
|
||
|
||
<select id="selectMyRegistrationList" ...>
|
||
...
|
||
order by COALESCE(r.registration_time, r.create_time) desc, r.create_time desc
|
||
</select>
|
||
|
||
<select id="selectRegistrationsByStatus" ...>
|
||
...
|
||
order by COALESCE(r.registration_time, r.create_time) desc, r.create_time desc
|
||
</select>
|
||
|
||
<select id="selectRegistrationsByName" ...>
|
||
...
|
||
order by COALESCE(r.registration_time, r.create_time) desc, r.create_time desc
|
||
</select>
|
||
```
|
||
|
||
**说明**:
|
||
- `COALESCE(r.registration_time, r.create_time)`:如果`registration_time`为NULL,则使用`create_time`
|
||
- 这样即使有NULL值,也能正常排序和显示数据
|
||
|
||
### 方案3:修复已有数据(治标)
|
||
|
||
**执行SQL脚本**:`修复已有数据registration_time为NULL.sql`
|
||
|
||
```sql
|
||
-- 将已有数据的registration_time设置为create_time
|
||
UPDATE rs_overseas_registration
|
||
SET registration_time = create_time
|
||
WHERE deleted = '0'
|
||
AND registration_time IS NULL;
|
||
```
|
||
|
||
---
|
||
|
||
## 🛠️ 修复步骤
|
||
|
||
### 第1步:修复已有数据
|
||
|
||
```bash
|
||
# 执行SQL脚本修复已有的NULL数据
|
||
mysql -u用户名 -p密码 数据库名 < 修复已有数据registration_time为NULL.sql
|
||
```
|
||
|
||
或手动执行:
|
||
```sql
|
||
UPDATE rs_overseas_registration
|
||
SET registration_time = create_time
|
||
WHERE deleted = '0'
|
||
AND registration_time IS NULL;
|
||
```
|
||
|
||
### 第2步:重启后端服务
|
||
|
||
**重要**:修改了Java代码和Mapper XML后,必须重启后端服务!
|
||
|
||
```bash
|
||
cd backend
|
||
mvn clean compile spring-boot:run -pl ruoyi-admin
|
||
```
|
||
|
||
或在IDEA中:
|
||
1. 停止运行中的Spring Boot应用
|
||
2. 重新运行`RuoYiApplication`主类
|
||
|
||
### 第3步:验证修复效果
|
||
|
||
#### 3.1 验证已有数据
|
||
|
||
1. 刷新前端页面
|
||
2. 检查"出国登记"选项卡列表
|
||
3. 确认之前新增的数据现在可以正常显示
|
||
|
||
#### 3.2 验证新增功能
|
||
|
||
1. 点击"新增登记"按钮
|
||
2. 填写表单并保存
|
||
3. 检查列表是否立即显示新数据
|
||
4. 检查数据库中`registration_time`字段是否有值
|
||
|
||
```sql
|
||
-- 验证SQL
|
||
SELECT
|
||
registration_id,
|
||
name,
|
||
registration_time,
|
||
create_time
|
||
FROM rs_overseas_registration
|
||
WHERE deleted = '0'
|
||
ORDER BY create_time DESC
|
||
LIMIT 5;
|
||
```
|
||
|
||
#### 3.3 验证"保存并提交"功能
|
||
|
||
1. 点击"新增登记"按钮
|
||
2. 填写表单并点击"保存并提交"
|
||
3. 检查列表是否显示数据且状态为"已提交"
|
||
|
||
---
|
||
|
||
## 📊 修复前后对比
|
||
|
||
### 修复前
|
||
|
||
| registration_id | name | registration_time | create_time | 列表显示 |
|
||
|-----------------|------|-------------------|-------------|----------|
|
||
| 1 | 张三 | NULL | 2025-10-05 10:00:00 | ❌ 不显示 |
|
||
| 2 | 李四 | NULL | 2025-10-05 11:00:00 | ❌ 不显示 |
|
||
|
||
**SQL排序**:
|
||
```sql
|
||
ORDER BY r.registration_time desc -- NULL值导致排序异常
|
||
```
|
||
|
||
**前端效果**:
|
||
- 列表显示"暂无数据"
|
||
- 数据库中有数据但前端看不到
|
||
|
||
### 修复后
|
||
|
||
| registration_id | name | registration_time | create_time | 列表显示 |
|
||
|-----------------|------|-------------------|-------------|----------|
|
||
| 1 | 张三 | 2025-10-05 10:00:00 | 2025-10-05 10:00:00 | ✅ 正常显示 |
|
||
| 2 | 李四 | 2025-10-05 11:00:00 | 2025-10-05 11:00:00 | ✅ 正常显示 |
|
||
|
||
**SQL排序**:
|
||
```sql
|
||
ORDER BY COALESCE(r.registration_time, r.create_time) desc -- 正常排序
|
||
```
|
||
|
||
**前端效果**:
|
||
- 列表正常显示所有数据
|
||
- 按登记时间倒序排列
|
||
- 新增数据立即显示
|
||
|
||
---
|
||
|
||
## 🔧 技术细节
|
||
|
||
### COALESCE函数说明
|
||
|
||
```sql
|
||
COALESCE(value1, value2, ...)
|
||
```
|
||
|
||
**功能**:返回参数列表中第一个非NULL的值
|
||
|
||
**示例**:
|
||
```sql
|
||
-- 如果registration_time为NULL,则返回create_time
|
||
COALESCE(r.registration_time, r.create_time)
|
||
|
||
-- 多层级fallback
|
||
COALESCE(r.registration_time, r.submit_time, r.create_time)
|
||
```
|
||
|
||
### MyBatis-Plus自动填充
|
||
|
||
项目中使用了MyBatis-Plus的自动填充功能:
|
||
|
||
```java
|
||
// 自动填充的字段
|
||
@TableField(fill = FieldFill.INSERT)
|
||
private Long createBy;
|
||
|
||
@TableField(fill = FieldFill.INSERT)
|
||
private Date createTime;
|
||
```
|
||
|
||
**但是**:`registration_time`不是自动填充字段,需要手动设置!
|
||
|
||
---
|
||
|
||
## 📝 相关文件清单
|
||
|
||
### 修改的文件
|
||
|
||
1. **后端Service实现类**
|
||
- 路径:`backend/ruoyi-modules/ruoyi-rs/src/main/java/org/dromara/rs/service/impl/RsOverseasRegistrationServiceImpl.java`
|
||
- 修改内容:
|
||
- `insertByBo`方法:添加`registration_time`设置
|
||
- `addAndSubmit`方法:添加`registration_time`设置
|
||
|
||
2. **Mapper XML文件**
|
||
- 路径:`backend/ruoyi-modules/ruoyi-rs/src/main/resources/mapper/rs/RsOverseasRegistrationMapper.xml`
|
||
- 修改内容:
|
||
- `selectRegistrationList`:优化排序使用COALESCE
|
||
- `selectMyRegistrationList`:优化排序使用COALESCE
|
||
- `selectRegistrationsByStatus`:优化排序使用COALESCE
|
||
- `selectRegistrationsByName`:优化排序使用COALESCE
|
||
|
||
### 新增的文件
|
||
|
||
1. **修复已有数据SQL脚本**
|
||
- 路径:`修复已有数据registration_time为NULL.sql`
|
||
- 用途:修复数据库中已有的NULL数据
|
||
|
||
2. **诊断指南文档**
|
||
- 路径:`诊断出国登记列表问题.md`
|
||
- 用途:详细的诊断步骤和常见问题解决方案
|
||
|
||
3. **检查数据SQL脚本**
|
||
- 路径:`检查出国登记数据详细.sql`
|
||
- 用途:检查数据库中的数据状态
|
||
|
||
4. **测试数据脚本**
|
||
- 路径:`backend/ruoyi-modules/ruoyi-rs/出国登记测试数据.sql`
|
||
- 用途:插入5条测试数据
|
||
|
||
5. **修复报告文档**(本文档)
|
||
- 路径:`出国登记列表不显示数据修复报告.md`
|
||
- 用途:完整的问题分析和修复方案
|
||
|
||
---
|
||
|
||
## ✅ 修复验证清单
|
||
|
||
完成修复后,请按以下清单逐项验证:
|
||
|
||
- [ ] **数据修复验证**
|
||
- [ ] 执行修复SQL,确认affected rows > 0
|
||
- [ ] 查询数据库,确认无NULL的registration_time
|
||
|
||
- [ ] **后端验证**
|
||
- [ ] 后端服务已重启
|
||
- [ ] 后端启动日志无错误
|
||
- [ ] 后端日志显示修复代码已生效
|
||
|
||
- [ ] **前端验证**
|
||
- [ ] 刷新页面,已有数据正常显示
|
||
- [ ] 新增登记,数据立即显示在列表中
|
||
- [ ] 保存并提交,数据正常显示且状态正确
|
||
- [ ] 分页功能正常
|
||
- [ ] 查询功能正常
|
||
- [ ] 排序功能正常
|
||
|
||
- [ ] **数据库验证**
|
||
- [ ] 新增的数据registration_time不为NULL
|
||
- [ ] 数据按时间正确排序
|
||
- [ ] deleted字段为'0'
|
||
|
||
- [ ] **浏览器Network验证**
|
||
- [ ] API请求返回200
|
||
- [ ] Response中rows数组有数据
|
||
- [ ] total字段 > 0
|
||
|
||
---
|
||
|
||
## 🎯 最佳实践建议
|
||
|
||
### 1. 数据库字段设计
|
||
|
||
对于重要的时间字段,建议:
|
||
|
||
```sql
|
||
-- 方案1:设置默认值
|
||
registration_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT '登记时间'
|
||
|
||
-- 方案2:设置为NOT NULL
|
||
registration_time datetime NOT NULL COMMENT '登记时间'
|
||
```
|
||
|
||
### 2. 后端代码规范
|
||
|
||
新增实体时,明确设置所有必需字段:
|
||
|
||
```java
|
||
// ✅ 好的做法
|
||
public Boolean insertByBo(Bo bo) {
|
||
Entity entity = convert(bo);
|
||
|
||
// 明确设置状态
|
||
if (entity.getStatus() == null) {
|
||
entity.setStatus("0");
|
||
}
|
||
|
||
// 明确设置时间字段
|
||
if (entity.getRegistrationTime() == null) {
|
||
entity.setRegistrationTime(new Date());
|
||
}
|
||
|
||
return baseMapper.insert(entity) > 0;
|
||
}
|
||
```
|
||
|
||
### 3. SQL查询防御性编程
|
||
|
||
对可能为NULL的字段,使用COALESCE处理:
|
||
|
||
```sql
|
||
-- ✅ 防御性编程
|
||
ORDER BY COALESCE(r.registration_time, r.create_time) DESC
|
||
|
||
-- ❌ 可能出问题
|
||
ORDER BY r.registration_time DESC
|
||
```
|
||
|
||
### 4. 前端提示优化
|
||
|
||
当列表无数据时,给用户明确的提示:
|
||
|
||
```vue
|
||
<el-empty
|
||
v-if="!loading && total === 0"
|
||
description="暂无数据,请点击'新增登记'按钮添加数据"
|
||
/>
|
||
```
|
||
|
||
---
|
||
|
||
## 🔗 相关文档
|
||
|
||
- [出国登记保存并提交功能修复报告.md](./出国登记保存并提交功能修复报告.md)
|
||
- [诊断出国登记列表问题.md](./诊断出国登记列表问题.md)
|
||
- [检查出国登记数据.sql](./检查出国登记数据.sql)
|
||
- [检查出国登记数据详细.sql](./检查出国登记数据详细.sql)
|
||
- [修复已有数据registration_time为NULL.sql](./修复已有数据registration_time为NULL.sql)
|
||
- [出国登记测试数据.sql](./backend/ruoyi-modules/ruoyi-rs/出国登记测试数据.sql)
|
||
|
||
---
|
||
|
||
## 📞 技术支持
|
||
|
||
如有疑问或遇到问题,请参考:
|
||
|
||
1. **诊断指南**:`诊断出国登记列表问题.md` - 包含7个诊断步骤和6个常见问题解决方案
|
||
2. **数据检查脚本**:执行SQL脚本检查数据状态
|
||
3. **后端日志**:查看后端控制台日志,搜索"出国登记"相关日志
|
||
|
||
---
|
||
|
||
**修复完成时间**:2025-10-05
|
||
**修复人员**:Cursor AI
|
||
**修复版本**:v1.0
|
||
**测试状态**:✅ 待验证
|
||
|
||
---
|
||
|
||
## 🎉 总结
|
||
|
||
**问题本质**:新增数据时未设置`registration_time`字段,导致SQL排序异常
|
||
|
||
**修复措施**:
|
||
1. ✅ **治本**:修改Service代码,新增时设置`registration_time`
|
||
2. ✅ **防御**:优化SQL排序,使用COALESCE处理NULL值
|
||
3. ✅ **治标**:修复已有数据的NULL值
|
||
|
||
**修复效果**:
|
||
- 已有数据正常显示
|
||
- 新增数据立即显示
|
||
- 列表排序正常
|
||
- 功能完全恢复
|
||
|
||
**下一步**:
|
||
1. 重启后端服务
|
||
2. 执行修复SQL脚本
|
||
3. 验证功能正常
|
||
4. 清理测试数据(可选)
|
||
|
||
---
|
||
|
||
**请按照"修复步骤"章节执行修复,并逐项完成"修复验证清单"!**
|