# 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()` 取值,无类型校验 | 中 | 使用 `canConvert()` 先行判断 | | 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 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 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()` 判断 | | 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(item); textItem->setText(content); // 修复后 auto* textItem = dynamic_cast(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 优化(代码质量提升) 每个阶段修复后需进行编译验证和基本功能回归测试。