Files
VX5_KRO_GearPump/doc/scene模块健壮性更新.md
2026-05-23 14:16:57 +08:00

311 lines
11 KiB
Markdown
Raw Permalink 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.
# Scene 模块健壮性更新说明
## 概述
本文档针对 `scene/` 目录下所有类进行健壮性审查,识别出以下几类典型问题:
1. **外部参数输入未校验** — 公共方法接收指针、索引、容器等参数时缺少有效性检查
2. **空指针解引用风险** — 存储的原始指针在使用前未做 null 检查
3. **数组/容器越界访问** — 使用索引访问 QVector/QMap 时未验证边界
4. **缺少错误处理** — 文件操作、类型转换等缺少失败路径处理
5. **类型安全问题** — QVariant 类型擦除后未做类型校验
---
## 各类问题详细清单
### 1. BarcodeEditor
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| BarcodeEditor.h/cpp | `bindSysPara(SysPara*)` 未校验 nullptr | 高 | 入口处添加 `if (!sysPara) return;` 并记录日志 |
| BarcodeEditor.cpp | `applyCounterConfigToSysPara()` 直接解引用 `m_sysPara` 无 null 检查 | 高 | 使用前添加 `Q_ASSERT(m_sysPara); if (!m_sysPara) return;` |
| BarcodeEditor.cpp | `bindViewModel()` 中 `format` 字段通过 `QVariant::value<T>()` 取值,无类型校验 | 中 | 使用 `canConvert<T>()` 先行判断 |
| BarcodeEditor.cpp | 计数器相关逻辑中 `counterValues` 索引访问无边界检查 | 高 | 访问前检查 `counterValues.size() >= 2` |
---
### 2. CounterEditor
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| CounterEditor.cpp | `bindSysPara(SysPara*)` 未校验 nullptr | 高 | 入口处添加空指针检查 |
| CounterEditor.cpp | 计数器参数(步长、初始值、最大值)未做范围校验 | 中 | 添加数值范围合法性检查(如步长不为0,最大值>最小值) |
| CounterEditor.cpp | UI 控件取值后直接使用,未校验转换是否成功 | 低 | `toInt()` 等调用使用带 `ok` 参数的重载 |
---
### 3. DateFormatEditor
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| DateFormatEditor.cpp | `setDateModel(const DateItemModel&)` 未校验 model 内部容器是否为空 | 中 | 检查关键 map 的 size 是否满足预期 |
| DateFormatEditor.cpp | 日期偏移量输入未做范围限制 | 低 | 限制偏移量在合理范围内(如 ±9999) |
---
### 4. DateReplaceEditor
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| DateReplaceEditor.cpp | `onLineEditReplaceChanged(int i, QString txt)` 中 `mapIdx` 可能为负值 | 中 | 添加 `if (mapIdx < 0) return;` |
| DateReplaceEditor.cpp | `on_cb_replaceType_currentIndexChanged(int index)` 未校验 index 范围 | 低 | 添加 `if (index < 0 || index > 5) return;` |
| DateReplaceEditor.cpp | 分页逻辑中 `currPage` 无上下界保护 | 中 | 翻页时 clamp 到 `[0, maxPage]` |
---
### 5. DlgBarcodeFormat
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| DlgBarcodeFormat.cpp | 条码类型索引直接映射到枚举,无范围校验 | 中 | 添加枚举范围检查 |
| DlgBarcodeFormat.cpp | 条码内容长度未做上限校验 | 中 | 根据条码类型限制最大长度 |
---
### 6. DlgQrcodeFormat
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| DlgQrcodeFormat.cpp | 二维码内容长度未做上限校验 | 中 | 限制输入长度(QR Code 最大约 4296 字符) |
| DlgQrcodeFormat.cpp | 纠错等级索引未校验 | 低 | 确保 index 在 0-3 范围内 |
---
### 7. ItemCommon
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| ItemCommon.cpp | `generateTxtFromDateItemModel` 中 `monthsMap[month-1]`、`daysMap[day-1]`、`hoursMap[hour]`、`weeksMap[dayOfWeek-1]` 均无边界检查 | **高** | 访问前检查 map/list 的 size 是否足够 |
| ItemCommon.cpp | `generateTxtFromBarcodeViewModel` 中 `counterValues[0]`/`[1]` 无 size 检查 | **高** | 添加 `if (counterValues.size() < 2) return QString();` |
| ItemCommon.cpp | 日期格式解析中未处理无效 datetime 的情况 | 中 | 检查 `QDateTime::isValid()` |
---
### 8. ItemGeneralSettings
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| ItemGeneralSettings.cpp | `bindItem(QGraphicsItem*)` 未校验 nullptr | 高 | 入口处添加空指针检查 |
| ItemGeneralSettings.cpp | 坐标/尺寸输入未做负值或零值校验 | 中 | 宽高不允许为0或负数 |
| ItemGeneralSettings.cpp | `dynamic_cast` 结果未检查 | 中 | 转换后检查是否为 nullptr |
---
### 9. picscene (PicScene)
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| picscene.cpp | 多处 `dynamic_cast` 后直接使用结果,未检查 nullptr | **高** | 每次 `dynamic_cast` 后添加 null 检查 |
| picscene.cpp | `addItem` 系列方法中传入的参数(如文本内容、位置坐标)未校验 | 中 | 校验坐标在场景范围内,文本非空 |
| picscene.cpp | 场景 item 列表遍历中假设 item 类型,未做防御性检查 | 中 | 遍历时使用 `qgraphicsitem_cast` 并检查结果 |
| picscene.cpp | 文件读写操作缺少错误处理(如文件打开失败) | 中 | 检查 `QFile::open()` 返回值 |
---
### 10. piccodeitem (PicCodeItem)
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| piccodeitem.cpp | `updateVariants(QVector<int> counters)` 未校验 `counters.size() >= 2` | **高** | 添加 `if (counters.size() < 2) return defaultImage;` |
| piccodeitem.cpp | `setBarcodeViewModel(const BarcodeViewModel& vm)` 未校验 vm 字段有效性 | 中 | 检查 `vm.type` 和 `vm.contentPattern` 非空 |
| piccodeitem.cpp | `setSource(QVariant var)` 参数被完全忽略 | 低 | 添加注释说明或移除无用参数 |
---
### 11. PicCounterItem
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| PicCounterItem.cpp | `updateVariants(QVector<int> counters)` 未校验容器大小 | 高 | 同 piccodeitem |
| PicCounterItem.cpp | 计数器溢出未处理(超过最大值后的行为) | 中 | 添加溢出保护逻辑 |
---
### 12. PicDateItem
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| PicDateItem.cpp | `setDateModel(const DateItemModel&)` 未校验 model 内部 map 完整性 | 高 | 检查各 map 的 size 满足最低要求 |
| PicDateItem.cpp | 日期替换表访问无边界保护 | 高 | 同 ItemCommon 中的修复 |
---
### 13. PicImageItem
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| PicImageItem.cpp | `setSource(QVariant var)` 未校验 QVariant 类型 | 低 | 使用 `var.canConvert<QString>()` 判断 |
| PicImageItem.cpp | `setCustomAttrs` 未检查 "source" 属性是否存在 | 低 | 添加 `attrs.contains("source")` 检查 |
| PicImageItem.cpp | 图片加载失败无日志输出 | 低 | 添加 `qWarning()` 日志 |
---
### 14. PicScheduleShiftItem
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| PicScheduleShiftItem.cpp | `findScheduleName()` 存在不可达代码 | 低 | 清理逻辑 |
| PicScheduleShiftItem.cpp | `itemImage()` 中存在未赋值的 QImage 返回路径 | 中 | 确保所有分支都有有效返回 |
---
### 15. pictextitem (PicTextItem)
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| pictextitem.cpp | 文本内容设置未做长度上限校验 | 低 | 根据打印能力限制最大字符数 |
| pictextitem.cpp | 字体大小设置未做合理范围校验 | 低 | 限制在合理范围(如 1-999) |
---
### 16. ViewModels
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| ViewModels.cpp | `makeCounterVM(SysPara*, ...)` 接收指针但部分路径未检查 null | 中 | 统一在入口处检查 |
| ViewModels.cpp | ViewModel 构造时未校验字段合法性 | 低 | 添加工厂方法中的断言 |
---
### 17. ScheduleShiftsEditor
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| ScheduleShiftsEditor.cpp | 班次时间输入未校验逻辑合理性(如结束时间>开始时间) | 中 | 添加时间区间合法性校验 |
| ScheduleShiftsEditor.cpp | 班次数量未做上限限制 | 低 | 限制最大班次数 |
---
### 18. myview / sceneviewwidget
| 文件 | 问题 | 严重程度 | 建议修复 |
|------|------|----------|----------|
| myview.cpp | 缩放因子未做范围限制 | 低 | 限制在合理范围(如 0.1 ~ 10.0) |
| sceneviewwidget.cpp | `setScene(PicScene*)` 未校验 nullptr | 中 | 添加空指针检查 |
---
## 修复优先级建议
### P0 — 立即修复(可能导致崩溃)
1. **所有 `bindSysPara(SysPara*)` 调用处** — 添加 nullptr 检查
2. **`ItemCommon::generateTxtFromDateItemModel`** — 所有 map 索引访问添加边界检查
3. **`ItemCommon::generateTxtFromBarcodeViewModel`** — counterValues size 检查
4. **`PicCodeItem::updateVariants`** — counters 容器大小校验
5. **`picscene.cpp` 中所有 `dynamic_cast`** — 添加 null 检查
### P1 — 尽快修复(可能导致数据错误)
1. DateReplaceEditor 分页索引保护
2. 条码/二维码内容长度限制
3. ItemGeneralSettings 中 `dynamic_cast` 结果检查
4. PicDateItem 日期替换表边界保护
5. ScheduleShiftsEditor 时间区间合法性校验
### P2 — 后续优化(改善代码质量)
1. QVariant 类型安全检查
2. 日志输出完善
3. 无用参数清理
4. 数值输入范围限制
---
## 修复模式参考
### 模式 A:指针参数校验
```cpp
// 修复前
void BarcodeEditor::bindSysPara(SysPara *sysPara)
{
m_sysPara = sysPara;
}
// 修复后
void BarcodeEditor::bindSysPara(SysPara *sysPara)
{
if (!sysPara) {
qWarning() << "BarcodeEditor::bindSysPara: null pointer passed";
return;
}
m_sysPara = sysPara;
}
```
### 模式 B:容器索引边界检查
```cpp
// 修复前
auto val = m_currCounterValues[0];
// 修复后
if (m_currCounterValues.size() < 2) {
qWarning() << "counterValues size insufficient:" << m_currCounterValues.size();
return QImage();
}
auto val = m_currCounterValues[0];
```
### 模式 C:Map 访问保护
```cpp
// 修复前
result.replace("{M}", model.monthsMap[month - 1]);
// 修复后
if (month - 1 >= 0 && month - 1 < model.monthsMap.size()) {
result.replace("{M}", model.monthsMap[month - 1]);
} else {
qWarning() << "monthsMap index out of range:" << month - 1;
}
```
### 模式 D:dynamic_cast 安全使用
```cpp
// 修复前
auto* textItem = dynamic_cast<PicTextItem*>(item);
textItem->setText(content);
// 修复后
auto* textItem = dynamic_cast<PicTextItem*>(item);
if (!textItem) {
qWarning() << "dynamic_cast to PicTextItem failed";
return;
}
textItem->setText(content);
```
### 模式 E:数值范围校验
```cpp
// 修复前
void ItemGeneralSettings::setWidth(int w)
{
item->setWidth(w);
}
// 修复后
void ItemGeneralSettings::setWidth(int w)
{
if (w <= 0 || w > MAX_ITEM_WIDTH) {
qWarning() << "Invalid width:" << w;
return;
}
item->setWidth(w);
}
```
---
## 实施计划
1. **第一阶段**:修复所有 P0 问题(防崩溃)
2. **第二阶段**:修复所有 P1 问题(防数据错误)
3. **第三阶段**:完成 P2 优化(代码质量提升)
每个阶段修复后需进行编译验证和基本功能回归测试。