144 lines
4.9 KiB
Markdown
144 lines
4.9 KiB
Markdown
# 学生考核申请更新主键冲突修复报告
|
||
|
||
## 问题描述
|
||
|
||
### 现象
|
||
在考核申请表单中,对已有数据进行修改后点击保存按钮,调用PUT接口更新数据时出现主键冲突错误:
|
||
|
||
```
|
||
Duplicate entry '1958434137320771585' for key 'PRIMARY'
|
||
```
|
||
|
||
### 错误详情
|
||
- **接口**: `PUT /assessment/application`
|
||
- **错误类型**: `java.sql.SQLIntegrityConstraintViolationException`
|
||
- **错误位置**: `XskhAssessmentSelfScoreMapper.insert`
|
||
- **根本原因**: 更新操作误用插入逻辑
|
||
|
||
## 问题分析
|
||
|
||
### 根本原因
|
||
在 `XskhAssessmentApplicationApprovalServiceImpl.updateSelfScores()` 和 `updateBonusApprovals()` 方法中,更新逻辑存在严重缺陷:
|
||
|
||
**原有错误逻辑**:
|
||
1. 删除所有关联的子表记录
|
||
2. 重新插入新的记录
|
||
|
||
**问题所在**:
|
||
- 前端传递的数据包含已存在记录的ID
|
||
- 后端删除后重新插入时,由于ID已存在导致主键冲突
|
||
- 这种"删除重建"的方式会丢失原有的创建时间、创建人等审计信息
|
||
|
||
### 影响范围
|
||
- 学生考核自评得分表 (`xskh_assessment_self_score`)
|
||
- 附加分申请表 (`xskh_assessment_bonus_approval`)
|
||
|
||
## 解决方案
|
||
|
||
### 修复策略
|
||
采用"智能更新"策略,根据记录ID是否存在决定执行更新或插入操作:
|
||
|
||
```java
|
||
// 如果ID存在,则更新;否则新增
|
||
if (selfScore.getId() != null) {
|
||
// 更新现有记录
|
||
selfScoreMapper.updateById(selfScore);
|
||
} else {
|
||
// 新增记录
|
||
selfScoreMapper.insert(selfScore);
|
||
}
|
||
```
|
||
|
||
### 具体修改
|
||
|
||
#### 1. 修复自评得分更新逻辑
|
||
**文件**: `XskhAssessmentApplicationApprovalServiceImpl.java`
|
||
**方法**: `updateSelfScores()`
|
||
|
||
```java
|
||
private void updateSelfScores(Long applicationId, List<XskhAssessmentSelfScoreBo> selfScoreList) {
|
||
if (selfScoreList != null && !selfScoreList.isEmpty()) {
|
||
for (XskhAssessmentSelfScoreBo selfScoreBo : selfScoreList) {
|
||
XskhAssessmentSelfScore selfScore = MapstructUtils.convert(selfScoreBo, XskhAssessmentSelfScore.class);
|
||
selfScore.setApplicationId(applicationId);
|
||
|
||
// 如果ID存在,则更新;否则新增
|
||
if (selfScore.getId() != null) {
|
||
// 更新现有记录
|
||
selfScoreMapper.updateById(selfScore);
|
||
} else {
|
||
// 新增记录
|
||
selfScoreMapper.insert(selfScore);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 2. 修复附加分申请更新逻辑
|
||
**文件**: `XskhAssessmentApplicationApprovalServiceImpl.java`
|
||
**方法**: `updateBonusApprovals()`
|
||
|
||
```java
|
||
private void updateBonusApprovals(Long applicationId, List<XskhAssessmentBonusApprovalBo> bonusApprovalList) {
|
||
if (bonusApprovalList != null && !bonusApprovalList.isEmpty()) {
|
||
for (XskhAssessmentBonusApprovalBo bonusApprovalBo : bonusApprovalList) {
|
||
XskhAssessmentBonusApproval bonusApproval = MapstructUtils.convert(bonusApprovalBo,
|
||
XskhAssessmentBonusApproval.class);
|
||
bonusApproval.setApplicationId(applicationId);
|
||
|
||
// 如果ID存在,则更新;否则新增
|
||
if (bonusApproval.getId() != null) {
|
||
// 更新现有记录
|
||
bonusApprovalMapper.updateById(bonusApproval);
|
||
} else {
|
||
// 新增记录,设置默认状态
|
||
bonusApproval.setStatus("1"); // 默认待审核状态
|
||
bonusApprovalMapper.insert(bonusApproval);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 修复效果
|
||
|
||
### 优势
|
||
1. **保留审计信息**: 不再删除重建,保留原有的创建时间、创建人等字段
|
||
2. **避免主键冲突**: 根据ID存在性智能选择更新或插入操作
|
||
3. **数据完整性**: 确保数据的连续性和完整性
|
||
4. **性能优化**: 避免不必要的删除和插入操作
|
||
|
||
### 测试验证
|
||
- ✅ 新增考核申请:正常插入新记录
|
||
- ✅ 修改考核申请:正常更新现有记录
|
||
- ✅ 混合操作:同时包含新增和修改的记录
|
||
- ✅ 数据完整性:审计字段正确保留
|
||
|
||
## 经验总结
|
||
|
||
### 开发规范
|
||
1. **更新操作原则**: 避免"删除重建"模式,优先使用"智能更新"
|
||
2. **ID处理策略**: 根据ID存在性决定操作类型
|
||
3. **审计字段保护**: 更新时注意保护创建时间、创建人等审计信息
|
||
4. **事务完整性**: 确保关联数据的更新在同一个事务中
|
||
|
||
### 代码审查要点
|
||
- 检查更新逻辑是否采用"删除重建"模式
|
||
- 验证是否正确处理已存在记录的ID
|
||
- 确认审计字段的保留策略
|
||
- 测试混合操作场景(新增+修改)
|
||
|
||
## 相关文件
|
||
|
||
- **修复文件**: `backend/ruoyi-modules/ruoyi-xskh/src/main/java/org/dromara/xskh/service/impl/XskhAssessmentApplicationApprovalServiceImpl.java`
|
||
- **涉及表**:
|
||
- `xskh_assessment_self_score` (学生考核自评得分表)
|
||
- `xskh_assessment_bonus_approval` (附加分申请表)
|
||
- **相关接口**: `PUT /assessment/application`
|
||
|
||
---
|
||
|
||
**修复时间**: 2025-08-21
|
||
**修复人员**: AI Assistant
|
||
**状态**: 已完成 |