6月29日崩溃前缓存
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
# PrintSetting 崩溃风险分析报告
|
||||
|
||||
## 涉及文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| [Printsetting.h](../Printsetting.h) | `PrintSettingModel` 结构体 + `PrintSetting` 类声明 |
|
||||
| [Printsetting.cpp](../Printsetting.cpp) | `PrintSetting` 类实现 + `PrintSettingModel` 静态方法实现 |
|
||||
| [testprintwidget.cpp](../testprintwidget.cpp) | `TestPrintWidget` 中调用 `m_printSetting` 的代码 |
|
||||
| [InkJetPrinter.cpp](../InkJetPrinter.cpp) | `InkJetPrinter` 中使用 `m_PrintSetting` 的代码 |
|
||||
|
||||
---
|
||||
|
||||
## 🔴 高风险(必定崩溃)
|
||||
|
||||
### 1. `applyPrintSettingsToSysPara()` — `sysPara->devOp` 在 if 保护块外被访问
|
||||
**文件**: [Printsetting.cpp:600-609](../Printsetting.cpp#L600-L609)
|
||||
```cpp
|
||||
void PrintSetting::applyPrintSettingsToSysPara(bool showTip)
|
||||
{
|
||||
updateModelFromUI();
|
||||
PrintSettingModel::applyToSysPara(m_model, sysPara); // L600: 无 null 检查
|
||||
|
||||
if (sysPara) { // L602: 有检查
|
||||
sysPara->devOp->setHighVoltageAmplitudetoSTM32();
|
||||
sysPara->devOp->writeFpgaCalandar(0);
|
||||
sysPara->devOp->writeFlightCompCtrl(...);
|
||||
}
|
||||
|
||||
if(sysPara->devOp->writeFpgaPrintPara(true) != -1 && showTip) { // L609: ❌ 在 if(sysPara) 之外!
|
||||
QMessageBox box(...);
|
||||
box.exec();
|
||||
}
|
||||
|
||||
if(sysPara)
|
||||
sysPara->saveParaToFile();
|
||||
}
|
||||
```
|
||||
**问题**:
|
||||
- L600 调用 `PrintSettingModel::applyToSysPara(m_model, sysPara)` 时未检查 `sysPara` 是否为 null。
|
||||
- L609 的 `sysPara->devOp->writeFpgaPrintPara(true)` 在 `if(sysPara)` 保护块**之外**,如果 `sysPara` 为 null,直接崩溃。
|
||||
- L602-L606 虽在 `if(sysPara)` 内,但 `sysPara->devOp` 本身也可能为 null。
|
||||
|
||||
**触发场景**:`bindSysPara()` 未被调用或 `sysPara` 被意外置空时,用户点击"应用"按钮。
|
||||
|
||||
---
|
||||
|
||||
### 2. `PrintSettingModel::applyToSysPara()` — `sysPara` 参数无 null 检查
|
||||
**文件**: [Printsetting.cpp:743-799](../Printsetting.cpp#L743-L799)
|
||||
```cpp
|
||||
void PrintSettingModel::applyToSysPara(const PrintSettingModel &model, SysPara *sysPara)
|
||||
{
|
||||
sysPara->para.leftRightInverse = model.leftRightInverse; // ❌ 无 null 检查
|
||||
sysPara->para.upDownInverse = model.upDownInverse;
|
||||
// ... 后续约 30 行全部直接访问 sysPara->para.xxx
|
||||
}
|
||||
```
|
||||
**问题**:整个函数体没有对 `sysPara` 做 null 检查,直接解引用。如果传入 null,立即崩溃。
|
||||
|
||||
**调用链**:
|
||||
- `PrintSetting::applyPrintSettingsToSysPara()` → `PrintSettingModel::applyToSysPara(m_model, sysPara)`
|
||||
- `PrintSetting::bindSysPara()` → `PrintSettingModel::fromSysPara(sysPara)` (同样无 null 检查,见下文)
|
||||
|
||||
---
|
||||
|
||||
### 3. `PrintSettingModel::fromSysPara()` — `sysPara` 参数无 null 检查
|
||||
**文件**: [Printsetting.cpp:802-845](../Printsetting.cpp#L802-L845)
|
||||
```cpp
|
||||
PrintSettingModel PrintSettingModel::fromSysPara(SysPara *sysPara)
|
||||
{
|
||||
PrintSettingModel model;
|
||||
model.leftRightInverse = sysPara->para.leftRightInverse; // ❌ 无 null 检查
|
||||
model.upDownInverse = sysPara->para.upDownInverse;
|
||||
// ... 后续约 30 行全部直接访问 sysPara->para.xxx
|
||||
}
|
||||
```
|
||||
**问题**:与 `applyToSysPara()` 相同,整个函数体没有对 `sysPara` 做 null 检查。
|
||||
|
||||
**调用链**:
|
||||
- `PrintSetting::bindSysPara()` → `PrintSettingModel::fromSysPara(sysPara)`
|
||||
|
||||
---
|
||||
|
||||
### 4. `on_pushButtonApplyPulseFix_clicked()` — `sysPara` 可能为 null
|
||||
**文件**: [Printsetting.cpp:287-290](../Printsetting.cpp#L287-L290)
|
||||
```cpp
|
||||
void PrintSetting::on_pushButtonApplyPulseFix_clicked()
|
||||
{
|
||||
pulseNumFix->bindSysPara(sysPara); // ❌ sysPara 可能为 null
|
||||
pulseNumFix->hide();
|
||||
pulseNumFix->show();
|
||||
}
|
||||
```
|
||||
**问题**:未检查 `sysPara` 是否为 null,直接传给 `pulseNumFix->bindSysPara()`。
|
||||
|
||||
---
|
||||
|
||||
### 5. TestPrintWidget 中 `m_printSetting` 空指针解引用(3处)
|
||||
|
||||
已在 [TestPrintWidget 崩溃风险分析报告](./TestPrintWidget_Crash_Risk_Analysis.md) 中详述,此处汇总:
|
||||
|
||||
| 位置 | 代码 | 问题 |
|
||||
|------|------|------|
|
||||
| [testprintwidget.cpp:435](../testprintwidget.cpp#L435) | `PrintSettingModel::saveToXml(m_printSetting->model(), writer)` | `m_printSetting` 可能为 null |
|
||||
| [testprintwidget.cpp:621-622](../testprintwidget.cpp#L621-L622) | `m_printSetting->setModel(model)` / `m_printSetting->applyPrintSettingsToSysPara(false)` | `m_printSetting` 可能为 null |
|
||||
| [testprintwidget.cpp:1122](../testprintwidget.cpp#L1122) | `m_printSetting->setModel(defModel)` | `m_printSetting` 可能为 null |
|
||||
|
||||
---
|
||||
|
||||
## 🟡 中风险(特定条件下崩溃)
|
||||
|
||||
### 6. `applyPrintSettingsToSysPara()` — `sysPara->devOp` 可能为 null
|
||||
**文件**: [Printsetting.cpp:603-606](../Printsetting.cpp#L603-L606)
|
||||
```cpp
|
||||
if (sysPara) {
|
||||
sysPara->devOp->setHighVoltageAmplitudetoSTM32(); // devOp 可能为 null
|
||||
sysPara->devOp->writeFpgaCalandar(0); // devOp 可能为 null
|
||||
sysPara->devOp->writeFlightCompCtrl(...); // devOp 可能为 null
|
||||
}
|
||||
```
|
||||
**问题**:虽然检查了 `sysPara`,但 `sysPara->devOp` 指针本身可能为 null(取决于 `SysPara` 的初始化时机)。
|
||||
|
||||
---
|
||||
|
||||
### 7. `on_checkEyeTriggerSynPrintEn_clicked()` — `sysPara` 有检查但 `comboBoxSyncActType` 可能越界
|
||||
**文件**: [Printsetting.cpp:302-340](../Printsetting.cpp#L302-L340)
|
||||
```cpp
|
||||
void PrintSetting::on_checkEyeTriggerSynPrintEn_clicked(bool checked)
|
||||
{
|
||||
if(sysPara) // ✅ 有 null 检查
|
||||
{
|
||||
// ...
|
||||
if(ui->comboBoxSyncActType->currentIndex() >= ui->comboBoxSyncActType->count()
|
||||
|| ui->comboBoxSyncActType->currentIndex() == -1)
|
||||
{
|
||||
ui->comboBoxSyncActType->setCurrentIndex(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
**问题**:`comboBoxSyncActType` 的 `currentIndex()` 值为 -1 时的处理是正确的,但如果 comboBox 未正确初始化(count() 为 0),`setCurrentIndex(0)` 可能无效,但不会崩溃。此处理论上安全,标注为中风险仅供参考。
|
||||
|
||||
---
|
||||
|
||||
## 🟢 低风险(逻辑瑕疵,不太可能崩溃)
|
||||
|
||||
### 8. `PrintSettingModel::toString()` — 格式说明符类型不匹配
|
||||
**文件**: [Printsetting.cpp:849-856](../Printsetting.cpp#L849-L856)
|
||||
```cpp
|
||||
QString PrintSettingModel::toString(const PrintSettingModel &model)
|
||||
{
|
||||
QString str = QString::asprintf("LRInverse: %d, ... triggerInterval: %d, ... eleEyeRepeatCnt: %d, ...",
|
||||
model.triggerInterval, ... model.eleEyeRepeatCnt, ...);
|
||||
}
|
||||
```
|
||||
**问题**:`model.triggerInterval` 和 `model.eleEyeRepeatCnt` 是 `unsigned` 类型,但格式说明符用了 `%d`(有符号)。不会崩溃,但可能输出异常值(如大数字显示为负数)。
|
||||
|
||||
---
|
||||
|
||||
### 9. `PrintSettingModel::defPrintSettingModel()` — 文件不存在时静默返回默认值
|
||||
**文件**: [Printsetting.cpp:860-873](../Printsetting.cpp#L860-L873)
|
||||
```cpp
|
||||
PrintSettingModel PrintSettingModel::defPrintSettingModel()
|
||||
{
|
||||
QFile file(DEF_PRINT_SETTINGS_PATH);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
return PrintSettingModel(); // 静默返回默认构造
|
||||
}
|
||||
// ...
|
||||
}
|
||||
```
|
||||
**问题**:文件打开失败时没有日志输出,不利于调试。不会崩溃。
|
||||
|
||||
---
|
||||
|
||||
### 10. `updateModelFromUI()` — 无返回值校验
|
||||
**文件**: [Printsetting.cpp:114-170](../Printsetting.cpp#L114-L170)
|
||||
```cpp
|
||||
m_model.encoderPulsePerMm = ui->lineEditTriggerPulsePerMm->text().toFloat();
|
||||
```
|
||||
**问题**:`toFloat()` 在输入为空时返回 0.0,不会崩溃。但如果用户输入了非法字符(虽然有 `QDoubleValidator`),结果可能不符合预期。
|
||||
|
||||
---
|
||||
|
||||
### 11. `on_radioButtonEleEyeLevelTrigger_pressed()` 等函数中 `setEnableSynTriggerSetting` 的判断逻辑
|
||||
**文件**: [Printsetting.cpp:310-325](../Printsetting.cpp#L310-L325)
|
||||
```cpp
|
||||
void PrintSetting::on_radioButtonEleEyeEdgetTrigger_pressed()
|
||||
{
|
||||
setTriggerUI(PRINT_TRIGGER_ELEC_EYE_EDGE);
|
||||
if(ui->radioButtonSynTriggerExternal->isChecked())
|
||||
setEnableSynTriggerSetting(true);
|
||||
else
|
||||
setEnableSynTriggerSetting(false);
|
||||
}
|
||||
```
|
||||
**问题**:这些函数未检查 `sysPara` 是否为 null 就调用 `setTriggerUI`,但 `setTriggerUI` 只操作 `m_model` 和 UI 控件,不依赖 `sysPara`,所以安全。
|
||||
|
||||
---
|
||||
|
||||
## 📋 修复建议汇总
|
||||
|
||||
| 优先级 | 位置 | 问题 | 修复方案 |
|
||||
|--------|------|------|----------|
|
||||
| **P0** | `applyPrintSettingsToSysPara()` L600 | `sysPara` 未检查就传给 `applyToSysPara` | 在 L600 前加 `if(!sysPara) return;` |
|
||||
| **P0** | `applyPrintSettingsToSysPara()` L609 | `sysPara->devOp` 在 if 块外访问 | 将 L609-L614 移入 `if(sysPara)` 块内 |
|
||||
| **P0** | `PrintSettingModel::applyToSysPara()` L743 | `sysPara` 无 null 检查 | 函数开头加 `if(!sysPara) return;` |
|
||||
| **P0** | `PrintSettingModel::fromSysPara()` L802 | `sysPara` 无 null 检查 | 函数开头加 `if(!sysPara) return PrintSettingModel();` |
|
||||
| **P0** | `on_pushButtonApplyPulseFix_clicked()` L287 | `sysPara` 未检查 | 加 `if(!sysPara) return;` |
|
||||
| **P1** | `applyPrintSettingsToSysPara()` L603 | `sysPara->devOp` 可能 null | 加 `if(sysPara->devOp)` 判断 |
|
||||
| **P1** | `testprintwidget.cpp` 多处 | `m_printSetting` 可能 null | 加 `if(m_printSetting)` 判断(共3处) |
|
||||
|
||||
---
|
||||
|
||||
## 崩溃调用链总结
|
||||
|
||||
```
|
||||
用户点击"应用"按钮
|
||||
→ PrintSetting::on_pushButtonApply_pressed()
|
||||
→ PrintSetting::applyPrintSettingsToSysPara(true)
|
||||
→ PrintSettingModel::applyToSysPara(m_model, sysPara) ← ❌ sysPara 可能 null
|
||||
→ sysPara->devOp->writeFpgaPrintPara(true) ← ❌ 在 if(sysPara) 外
|
||||
```
|
||||
|
||||
```
|
||||
用户点击"脉冲修正"按钮
|
||||
→ PrintSetting::on_pushButtonApplyPulseFix_clicked()
|
||||
→ pulseNumFix->bindSysPara(sysPara) ← ❌ sysPara 可能 null
|
||||
```
|
||||
|
||||
```
|
||||
加载/新建文件
|
||||
→ TestPrintWidget::saveFile() / parseVbfile() / on_newfileButton_pressed()
|
||||
→ m_printSetting->model() / setModel() / applyPrintSettingsToSysPara() ← ❌ m_printSetting 可能 null
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 生成时间
|
||||
2026-06-29
|
||||
@@ -0,0 +1,150 @@
|
||||
# TestPrintWidget 崩溃风险分析报告
|
||||
|
||||
## 🔴 高风险(必定崩溃)
|
||||
|
||||
### 1. `saveFile()` — `m_printSetting` 空指针解引用
|
||||
**文件**: [testprintwidget.cpp:435](../testprintwidget.cpp#L435)
|
||||
```cpp
|
||||
PrintSettingModel::saveToXml(m_printSetting->model(), writer);
|
||||
```
|
||||
`m_printSetting` 在 `setPrintSettingWidget()` 调用前为 `nullptr`。如果用户在 `setPrintSettingWidget()` 被调用之前就触发了保存,程序会崩溃。虽然 `InkJetPrinter.cpp:104` 在构造时调用了它,但如果构造顺序出现问题或 `m_PrintSetting` 尚未初始化,就会崩溃。
|
||||
|
||||
### 2. `saveFile()` — `qgraphicsitem_cast` 返回 null 后解引用
|
||||
**文件**: [testprintwidget.cpp:406](../testprintwidget.cpp#L406)
|
||||
```cpp
|
||||
PicTextItem *picText = qgraphicsitem_cast<PicTextItem*>(item);
|
||||
writer.writeTextElement(..., QString::fromLocal8Bit("%1").arg(picText->pos().x()));
|
||||
```
|
||||
`qgraphicsitem_cast` 可能返回 null(当 item 不是 PicTextItem 类型时),紧接着调用 `picText->pos().x()` 就会崩溃。
|
||||
|
||||
### 3. `onFileOpen()` — `sysPara->devOp` 空指针解引用
|
||||
**文件**: [testprintwidget.cpp:477](../testprintwidget.cpp#L477)
|
||||
```cpp
|
||||
sysPara->devOp->fpgaWr(0xa4b0, (unsigned char *)&SpecialVarEnable, 1);
|
||||
```
|
||||
- `sysPara` 可能为 null(`bindSysPara()` 未被调用时)
|
||||
- `sysPara->devOp` 也可能为 null
|
||||
|
||||
### 4. `on_newfileButton_pressed()` — `m_printSetting` 空指针解引用
|
||||
**文件**: [testprintwidget.cpp:1122](../testprintwidget.cpp#L1122)
|
||||
```cpp
|
||||
m_printSetting->setModel(defModel);
|
||||
```
|
||||
同样的问题,`m_printSetting` 可能为 null。
|
||||
|
||||
### 5. `getShowSelectItemText()` — `dynamic_cast` 后无空检查
|
||||
**文件**: [testprintwidget.cpp:312-317](../testprintwidget.cpp#L312-L317)
|
||||
```cpp
|
||||
PicTextItem* txtItem = dynamic_cast<PicTextItem*>(item);
|
||||
if(txtItem->sourceType() != SourceType::IMAGE && txtItem->sourceType() != SourceType::CODE ...) {
|
||||
lastSelectedTxtItem = txtItem;
|
||||
}
|
||||
```
|
||||
如果场景中包含非 `PicTextItem` 派生类的 `QGraphicsItem`,`dynamic_cast` 返回 null,直接解引用 `txtItem->sourceType()` 崩溃。
|
||||
|
||||
### 6. `onBtnItemConfigClicked()` — `dynamic_cast` 后无空检查
|
||||
**文件**: [testprintwidget.cpp:1042-1043](../testprintwidget.cpp#L1042-L1043)
|
||||
```cpp
|
||||
auto item = dynamic_cast<PicTextItem*>(selectedItems[0]);
|
||||
if(item->sourceType() == SourceType::COUNTER) {
|
||||
```
|
||||
如果选中的 item 不是 `PicTextItem` 类型,`dynamic_cast` 返回 null,直接崩溃。
|
||||
|
||||
### 7. `addDateTimeItem()` / `addScheduleShiftsItem()` — `dynamic_cast` 后无空检查
|
||||
**文件**: [testprintwidget.cpp:1310-1312](../testprintwidget.cpp#L1310-L1312)
|
||||
```cpp
|
||||
auto item = dynamic_cast<PicTextItem*>(selectItems[0]);
|
||||
if(item->sourceType() == SourceType::DATETIME) {
|
||||
```
|
||||
同样的问题,`testprintwidget.cpp:1338` 也存在。
|
||||
|
||||
### 8. `onEditItemDone()` — C风格强转,无空检查
|
||||
**文件**: [testprintwidget.cpp:287](../testprintwidget.cpp#L287)
|
||||
```cpp
|
||||
PicTextItem *pTextItem = (PicTextItem *)item;
|
||||
if(pTextItem->sourceType() == SourceType::TEXT)
|
||||
pTextItem->setSource(content);
|
||||
```
|
||||
C风格强转不做任何类型检查,如果 item 不是 PicTextItem,访问 `sourceType()` 是未定义行为。
|
||||
|
||||
### 9. `addCounterItem()` — `dynamic_cast` 后无空检查
|
||||
**文件**: [testprintwidget.cpp:1380](../testprintwidget.cpp#L1380)
|
||||
```cpp
|
||||
auto item = dynamic_cast<PicTextItem*>(items[index]);
|
||||
font = f ? *f : item->font();
|
||||
```
|
||||
如果选中的 item 不是 PicTextItem,`item->font()` 崩溃。
|
||||
|
||||
---
|
||||
|
||||
## 🟡 中风险(特定条件下崩溃)
|
||||
|
||||
### 10. `onItemEditorAccepted()` — `currentItem()` 可能返回 null
|
||||
**文件**: [testprintwidget.cpp:1095](../testprintwidget.cpp#L1095)
|
||||
```cpp
|
||||
if(itemEditor->currentItem()->source() == SourceType::UNKNOWN) {
|
||||
```
|
||||
`currentItem()` 可能返回 null 导致崩溃。
|
||||
|
||||
### 11. `parseVbfile()` 加载 XML 时 `m_printSetting` 空指针
|
||||
**文件**: [testprintwidget.cpp:621-622](../testprintwidget.cpp#L621-L622)
|
||||
```cpp
|
||||
m_printSetting->setModel(model);
|
||||
m_printSetting->applyPrintSettingsToSysPara(false);
|
||||
```
|
||||
当加载带有 PrintSettings 的 XML 文件时,`loadPrintSetting` 为 true 才会执行,但如果 `m_printSetting` 为 null,会崩溃。
|
||||
|
||||
### 12. `on_cbResolution_currentIndexChanged()` — 数组越界后的修复逻辑不完整
|
||||
**文件**: [testprintwidget.cpp:1274-1280](../testprintwidget.cpp#L1274-L1280)
|
||||
```cpp
|
||||
if(index < 0 || int(ARRAY_SIZE(IdxToFontFamily)) <= index) {
|
||||
index = std::max(std::min(int(ARRAY_SIZE(IdxToFontFamily) - 1), index), 0);
|
||||
...
|
||||
}
|
||||
auto selectedItems = pPicScene->selectedItems();
|
||||
...
|
||||
qInfo() << "Selected font family: " << IdxToFontFamily[index];
|
||||
```
|
||||
虽然有边界修正,但 `IdxToFontFamily` 是一个局部 `constexpr` 数组,如果 `ARRAY_SIZE` 宏未正确定义,可能导致越界访问。
|
||||
|
||||
---
|
||||
|
||||
## 🟢 低风险(逻辑问题,不太可能崩溃)
|
||||
|
||||
### 13. `pasteCopiedLayer()` — 临时对象传入 nullptr
|
||||
```cpp
|
||||
PicTextItem tempSrc(nullptr, nullptr, m_copiedSourceType);
|
||||
```
|
||||
用 `nullptr` 作为 parent 和 scene 创建临时对象,仅用于类型判断。如果 PicTextItem 构造函数中对 nullptr 做了安全处理,则没问题。
|
||||
|
||||
### 14. `onSceneFocusedItemPosChanged()` — `QString::asprintf` 格式字符串不安全
|
||||
**文件**: [testprintwidget.cpp:1112-1113](../testprintwidget.cpp#L1112-L1113)
|
||||
```cpp
|
||||
ui.m_lblX->setText(QString::asprintf("X:%d", p.x()));
|
||||
ui.m_lblY->setText(QString::asprintf("Y:%d", p.y()));
|
||||
```
|
||||
使用 `asprintf` 而非 `QString::number()`,虽然不会崩溃,但不是最佳实践。
|
||||
|
||||
---
|
||||
|
||||
## 📋 修复建议汇总
|
||||
|
||||
| 优先级 | 位置 | 问题 | 修复方案 |
|
||||
|--------|------|------|----------|
|
||||
| **P0** | `saveFile()` L435 | `m_printSetting` 空指针 | 加 `if(m_printSetting)` 判断 |
|
||||
| **P0** | `saveFile()` L406 | `qgraphicsitem_cast` 返回 null | 加 null 检查,跳过非 PicTextItem |
|
||||
| **P0** | `onFileOpen()` L477 | `sysPara->devOp` 空指针 | 加 `if(sysPara && sysPara->devOp)` |
|
||||
| **P0** | `getShowSelectItemText()` L312 | `dynamic_cast` 无空检查 | 加 `if(txtItem)` |
|
||||
| **P0** | `onBtnItemConfigClicked()` L1042 | `dynamic_cast` 无空检查 | 加 `if(item)` |
|
||||
| **P0** | `addDateTimeItem()` L1310 | `dynamic_cast` 无空检查 | 加 `if(item)` |
|
||||
| **P0** | `addScheduleShiftsItem()` L1338 | `dynamic_cast` 无空检查 | 加 `if(item)` |
|
||||
| **P0** | `onEditItemDone()` L287 | C风格强转无检查 | 改用 `dynamic_cast` + null 检查 |
|
||||
| **P0** | `addCounterItem()` L1380 | `dynamic_cast` 无空检查 | 加 `if(item)` |
|
||||
| **P0** | `on_newfileButton_pressed()` L1122 | `m_printSetting` 空指针 | 加 `if(m_printSetting)` |
|
||||
| **P1** | `parseVbfile()` L621 | `m_printSetting` 空指针 | 加 `if(m_printSetting)` |
|
||||
| **P1** | `onItemEditorAccepted()` L1095 | `currentItem()` 可能 null | 加 null 检查 |
|
||||
|
||||
---
|
||||
|
||||
## 生成时间
|
||||
2026-06-29
|
||||
Reference in New Issue
Block a user