311 lines
11 KiB
Markdown
311 lines
11 KiB
Markdown
# 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 优化(代码质量提升)
|
||
|
||
每个阶段修复后需进行编译验证和基本功能回归测试。
|