Files
work-secretfile-selfcheck/docs/plans/2026-06-08-secret-file-selfcheck.md

25 KiB
Raw Blame History

涉密文件自检工具 实施计划

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: 在 Windows 上做一个单文件 GUI 程序对磁盘中文档doc/docx/pdf按可配置数量随机抽检借助本地 Umi-OCR HTTP 服务识别是否含有"机密"等敏感关键词,并产出截图+结论报告。

Architecture:

  • eframe + egui Material 风格单窗口 GUI暗/亮/跟随系统三主题),左侧设置、右侧主面板、底部状态栏;按钮 "开始检测" 启动任务。
  • 抽检分三层:
    • docxdocx-rs 解析段落 → 关键词匹配
    • pdf:调用 Windows 默认 PDF 关联程序 → 等待 → 模拟 Win+Shift+S 触发系统截图 → 用户在屏幕上手动框选 → 程序轮询剪贴板读取位图 → 调 Umi-OCR HTTP → 关键词匹配
    • doc:调用 <exe 同目录>/doclite.exe 打开 → 同上截图链路
  • OCR 客户端:reqwest 把 base64 图像 POST 到 http://127.0.0.1:1224/api/ocr,解析返回 data[].textbox
  • 默认全盘扫描UI 中加"白名单目录"列表(不扫的目录);启动时单次申请 UAC整会话复用。
  • 配置 serde + toml 持久化到 %APPDATA%\secret-file-selfcheck\config.toml
  • 资源(中文字体、图标)通过 include_bytes! 嵌入二进制;doclite.exe / Umi-OCR.exe 由程序在 exe 同目录自动发现并按需启动。
  • #![windows_subsystem = "windows"] 隐藏控制台,单 exe 交付。

Tech Stack:

  • 语言/工具链Rust stable-x86_64-pc-windows-gnuMSYS2 + MinGW
  • GUIeframe = "0.27"egui = "0.27"egui_extras,手写 Material token
  • 文本:docx-rsencoding_rschardetng
  • OCR 客户端:reqwest(阻塞 + JSON→ Umi-OCR
  • 截屏链路:windows cratekeybd_event 触发 Win+Shift+SOpenClipboard / GetClipboardData 读位图)
  • 进程/窗口:sysinfowindows crateCreateProcessWEnumWindowsWM_CLOSEtaskkill
  • 异步:tokio(轻量使用)
  • 权限:windows crateShellExecuteExW + runas 提升 UAC
  • 配置/日志:serdetomltracing + tracing-appender
  • 打包:embed-resource 嵌入图标 + #![windows_subsystem = "windows"] 隐藏控制台
  • 构建:cargo build --release --target x86_64-pc-windows-gnu

用户规则 2 规定 Python 才用 loguru本项目是 Rust统一使用 tracing


一、需求与决策记录

1.1 用户已确认的设计决策

# 决策
1 扫描范围 默认全盘扫描UI 维护"白名单目录"列表(不扫的目录)
2 排除目录语义 "白名单" = 不扫描的目录模板按钮系统目录、Program Files、回收站、临时目录
3 文件大小 不设上限(可加最小 KB 过滤以跳过空文件)
4 .doc 打开 <exe 同目录>/doclite.exe 自动探测,配置中可改
5 .pdf 打开 Windows 默认 PDF 关联程序SumatraPDF / Edge / Adobe可设置覆盖
6 启动后等待 设置项"截图前等待时间"毫秒(默认 1500ms
7 截图方式 模拟 Win+Shift+S 触发系统截图工具UI 提示用户在屏幕上手动框选;轮询剪贴板读位图
8 OCR Umi-OCR HTTPhttp://127.0.0.1:1224/api/ocrUmi-OCR.exe 放 exe 同目录自动启动
9 管理员权限 启动时单次申请 UAC(未以管理员运行时 ShellExecuteExW + runas 重启一次)
10 报告 本地留存HTML/JSON/PNG 组图)
11 关键词来源 不从网络拉取
12 UI 风格 Material圆角、阴影、规范化间距、Material 配色)
13 触发方式 "开始检测"按钮,无全局热键
14 关键词网络拉取 不做

1.2 进入设置页的可配置项

# 默认
15 关键词列表 / 导入导出 / 大小写 / 正则 / 整词 / 按文件类型覆盖 默认"机密/秘密/绝密/内部/Confidential/Secret"
16 抽检数量 10
17 抽样策略(随机 / 分层 / 类型配额) 完全随机
18 并发度 1人工参与截图串行更稳
19 单文件超时 120s
20 报告格式HTML/JSON/PNG 组图) HTML + JSON
21 首次启动向导 引导 Umi-OCR/doclite 路径、关键词、等待时间
22 中文字体(嵌入或自选) 嵌入 SourceHanSansSC
23 主题Light / Dark / Follow Follow
24 国际化(中/英) 中文
25 日志级别、保留天数、UI 实时) Info14 天
26 静默/托盘/开机自启 默认关闭
27 历史去重(同文件 N 天不重复) 7

二、目录结构

secret-file-selfcheck/
├── Cargo.toml
├── build.rs                       # embed-resource / winres
├── app.ico                        # 图标
├── app.rc                         # 资源脚本
├── README.md                      # 部署说明:与 doclite.exe / Umi-OCR.exe 同目录
├── assets/
│   ├── fonts/SourceHanSansSC-Regular.otf
│   └── templates/report.html
├── src/
│   ├── main.rs                    # eframe 入口 + UAC 提升 + 启动 Umi-OCR
│   ├── app.rs                     # 顶层 App struct + 页面路由
│   ├── privilege.rs               # 单次 UAC 提升ShellExecuteExW runas
│   ├── config/
│   │   ├── mod.rs
│   │   ├── model.rs               # AppConfig 强类型
│   │   └── persist.rs             # 读写 %APPDATA%/.../config.toml
│   ├── ui/
│   │   ├── settings.rs            # 设置页(多分组)
│   │   ├── home.rs                # 主面板(开始/进度/日志/截图引导)
│   │   ├── report.rs              # 报告查看
│   │   ├── material.rs            # Material 风格 token & 主题
│   │   └── widgets.rs             # 字体加载、托盘
│   ├── scan/
│   │   ├── walker.rs              # 全盘遍历 + 白名单(不扫的目录)
│   │   ├── sampler.rs             # 抽检算法
│   │   └── filter.rs              # 扩展名/隐藏/最小大小过滤
│   ├── inspect/
│   │   ├── mod.rs                 # Inspector trait + Finding
│   │   ├── docx_inspector.rs      # 文本解析docx-rs
│   │   ├── pdf_inspector.rs       # 外部查看器 + 截图 + OCR
│   │   ├── doc_inspector.rs       # doclite.exe + 截图 + OCR
│   │   ├── external.rs            # 启进程 / 关闭窗口 / 强杀
│   │   ├── screenshot.rs          # 触发 Win+Shift+S + 读剪贴板
│   │   └── umi_ocr.rs             # Umi-OCR HTTP 客户端
│   ├── matcher/
│   │   ├── keywords.rs            # 关键词匹配(正则、大小写、整词、按文件类型)
│   │   └── hash.rs                # 文件指纹 + 去重
│   ├── report/
│   │   ├── model.rs               # Report / Finding 结构
│   │   ├── html.rs                # 渲染 HTML
│   │   └── png.rs                 # 合并截图
│   └── utils/
│       ├── paths.rs               # APPDATA / exe 同目录 / 临时目录
│       └── logger.rs              # tracing 初始化
└── tests/
    ├── matcher_test.rs
    ├── sampler_test.rs
    └── fixtures/
        ├── sample.docx
        └── sample.pdf

三、关键 Cargo.toml 节选

[package]
name = "secret-file-selfcheck"
version = "0.1.0"
edition = "2021"

[dependencies]
eframe = { version = "0.27", default-features = false, features = ["default_fonts", "glow"] }
egui = "0.27"
egui_extras = { version = "0.27", features = ["all_loaders"] }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "time", "fs"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
toml = "0.8"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
tracing-appender = "0.2"
docx-rs = "0.4"
encoding_rs = "0.8"
chardetng = "0.1"
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
base64 = "0.22"
sysinfo = "0.31"
windows = { version = "0.58", features = [
    "Win32_UI_WindowsAndMessaging",
    "Win32_System_Threading",
    "Win32_Graphics_Gdi",
    "Win32_System_Console",
    "Win32_Foundation",
] }
sys-locale = "0.3"
dirs = "5"
walkdir = "2"
regex = "1"
aho-corasick = "1"
sha2 = "0.10"
anyhow = "1"
thiserror = "1"
chrono = { version = "0.4", features = ["serde"] }
once_cell = "1"
log = "0.4"
url = "2"
image = { version = "0.25", default-features = false, features = ["png"] }

[build-dependencies]
embed-resource = "2"

[profile.release]
opt-level = 3
lto = "thin"
codegen-units = 1
strip = true
panic = "abort"

四、设置页设计6 个分组60+ 控件)

设置页用 egui::ScrollArea + CollapsingHeader 分组,所有变更即时写入 config.toml

A. 常规

  • 启动时自动检测(开关)
  • 启动时最小化到托盘(开关)
  • 开机自启(开关 + 写入 HKCU\...\Run
  • 主题Light / Dark / Follow
  • 语言:中文 / English
  • 日志级别Error / Warn / Info / Debug / Trace
  • 日志保留天数(数字框)
  • 日志文件路径(只读 + 打开按钮)
  • 单实例锁(开关,避免多开)
  • 检查前清空临时目录(开关)
  • 检查完自动退出(开关 + 倒计时秒数)

B. 扫描范围

  • 扫描根目录:默认空(=全盘)
  • 白名单目录(不扫的目录,多行列表 + 增删 + 拖拽)
  • 模板按钮系统目录、Windows、Program Files、回收站、临时目录
  • 包含隐藏文件(开关)
  • 包含系统文件(开关)
  • 跟随符号链接(开关)
  • 文件最小大小 KB数字框默认 10 = 不限)
  • 最大遍历深度数字框0=无限)
  • 扩展名白名单(多选 + 自定义:默认 doc/docx/pdf
  • 修改时间范围(起始/结束日期)
  • 单次扫描超时分钟0=无限)

C. 抽检

  • 抽检数量(数字框)
  • 抽样策略(单选:完全随机 / 分层 / 类型配额)
  • 类型配额DOC / DOCX / PDF 各自数量
  • 抽检执行方式:串行(一个接一个打开,前一份处理完再开下一份,无并发选项
  • 单文件总超时(秒,默认 120
  • 同文件多少天内不重复(数字框,默认 7
  • 跳过锁定/无权限文件(开关)
  • 命中后是否继续后续文件(开关,默认继续;关闭则一旦命中就停)

D. 查看器与截图

  • .doc 查看器路径(默认 <exe 同目录>/doclite.exe,文件选择 + 探测)
  • .doc 启动参数模板(默认 {path} 占位)
  • .pdf 查看器路径(默认 = Windows 关联程序,文件选择 + "用默认"按钮)
  • .pdf 启动参数模板(默认 {path}
  • 截图前等待时间 ms默认 1500
  • 用户截图超时秒(默认 60超时跳过当前文件
  • 截图后是否自动关闭查看器(开关 + 关闭等待 ms + 强杀超时 ms
  • Umi-OCR HTTP 地址(默认 http://127.0.0.1:1224/api/ocr
  • Umi-OCR 启动路径(默认 <exe 同目录>/Umi-OCR.exe
  • Umi-OCR 启动后等待秒(默认 3
  • Umi-OCR 调用超时秒(默认 30
  • OCR 选项语言配置(默认 models/config_chinese.txt
  • OCR 启用文本方向校正(开关)
  • OCR 限制边长960/2880/4320/不压缩)

E. 关键词

  • 全局关键词列表(多行编辑 + 增删 + 一键清空)
  • 导入 / 导出 txt
  • 区分大小写(开关)
  • 使用正则(开关)
  • 整词匹配(开关)
  • 按文件类型追加DOC / DOCX / PDF 各自多行列表)
  • 误报白名单(列表:指纹 → 关键词,可清除)
  • 命中最低置信度(数字框 0-100
  • 高亮颜色(颜色选择器)

F. 报告

  • 输出目录(路径选择)
  • 输出格式多选HTML / JSON / PNG 组图)
  • 报告文件名前缀(默认 selfcheck-{date}
  • 包含截图(开关)
  • 敏感词高亮(开关)
  • 截图最大边长 px数字框默认 1600
  • 历史保留条数(数字框,默认 30
  • 检查完自动打开报告(开关)
  • 同时复制报告到剪贴板摘要(开关)

底部按钮:恢复默认 / 另存为模板 / 立即保存 / 打开配置文件


五、工作流程

[启动]
   ├─ IsUserAnAdmin? 否 → ShellExecuteExW "runas" 重启 → 退出当前进程
   ├─ 加载 config.toml不存在则首次启动向导
   ├─ 嵌入字体 set_fonts
   ├─ 启动 Umi-OCR.exe同目录 / 配置路径)→ 轮询 HTTP /api/ocr 是否可达
   └─ 启动单实例锁

[主面板] 用户点击 "开始检测"
   │
   ▼
[扫描阶段] walker 遍历全盘 → 白名单剔除 → 扩展名/大小/隐藏过滤 → 候选列表 Vec<PathBuf>
   │
   ▼
[抽样阶段] sampler 按策略(随机 / 分层 / 类型配额)选 N 个
   │
   ▼
[抽检阶段] 串行(默认 1 并发;并发时 buffer_unordered
   ├─ DOCXdocx-rs 读段落 → keywords 匹配
   ├─ DOC  spawn(doclite.exe "{path}") → 等待 wait_ms
   │           → keybd_event LWIN+LSHIFT+'S' 模拟 Win+Shift+S
   │           → UI 弹层 "请框选 doclite 窗口区域" + 倒计时
   │           → 轮询剪贴板OpenClipboard/GetClipboardData CF_BITMAP拿位图
   │           → base64 编码 → POST /api/ocr
   │           → 匹配关键词 → WM_CLOSE 关闭窗口 → 等 close_wait_ms → 强杀
   └─ PDF  spawn(pdf_viewer "{path}") → 等待 → 同上截图 → OCR → 匹配 → 关闭

[汇总] Finding 列表 → 写 HTML/JSON/PNG → 通知系统 → 弹窗(命中/未命中)

并发:用 tokio::sync::watch 通道发取消信号;进度用 Arc<AtomicUsize> 推进UI 在 update() 里非阻塞地拉取状态。


六、报告格式

HTML 报告(默认)

  • 顶部:检查时间、机器名、用户、抽检数量、命中数量、关键词列表
  • 表格:文件名、路径、类型、命中关键词、置信度、截图缩略图、状态
  • 截图:原图 + 敏感词高亮(红框)
  • 底部:原始 JSON 数据链接

JSON 报告

{
  "scan_id": "2026-06-08-153012-1234",
  "machine": "DESKTOP-XXX", "user": "alice",
  "started_at": "...", "finished_at": "...",
  "total": 20, "hit": 1,
  "findings": [
    { "path": "...", "type": "doc", "matched": ["机密"], "score": 0.92,
      "screenshot": "screenshots/001.png", "boxes": [[x1,y1,x2,y2]] }
  ]
}

PNG 组图:把每张截图拼成 A4 大图,便于一键分享。


七、关键技术点

1. MSYS2 + MinGW 编译要点

pacman -S mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake
rustup target add x86_64-pc-windows-gnu

.cargo/config.toml

[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
ar = "x86_64-w64-mingw32-ar"

2. 隐藏控制台 + UAC 单次提升

  • src/main.rs 顶部:#![windows_subsystem = "windows"]
  • privilege.rs
    • IsUserAnAdmin()false 时构造 ShellExecuteExW { lpVerb: "runas", lpFile: current_exe, nShow: SW_SHOWNORMAL }
    • 当前进程 ExitProcess(0);新进程以管理员启动
    • 第二次进入已经管理员,跳过
  • 入口处保证 IsUserAnAdmin() == true 后再 run()

3. 中文字体

  • assets/fonts/SourceHanSansSC-Regular.otf~16MB通过 include_bytes! 嵌入
  • eframe::NativeOptions 启动后 egui::Context::set_fonts
    let mut fonts = FontDefinitions::default();
    fonts.font_data.insert("cn".into(), FontData::from_static(include_bytes!("../assets/fonts/SourceHanSansSC-Regular.otf")));
    fonts.families.get_mut(&FontFamily::Proportional).unwrap().insert(0, "cn".into());
    ctx.set_fonts(fonts);
    

4. .doc / .pdf 截图流程(最复杂)

  1. CreateProcessW 启动 doclite.exe "{path}"(或 PDF 关联程序)
  2. 轮询 EnumWindows + GetWindowThreadProcessId 匹配 PID 获取 HWND
  3. SetForegroundWindow + ShowWindow(SW_RESTORE) 强制前台
  4. Sleep(wait_ms)
  5. keybd_event(VK_LWIN, 0, 0, 0); keybd_event(VK_LSHIFT, 0, 0, 0); keybd_event(0x53, 0, 0, 0)
    • 释放:keybd_event(..., KEYEVENTF_KEYUP, 0)
  6. UI 弹层 "请用系统截图工具框选窗口区域"(带倒计时)
  7. 启动 tokio::time::interval(200ms) 轮询剪贴板:OpenClipboardGetClipboardData(CF_BITMAP) → 转 DynamicImage
  8. 拿到位图 → base64::encodereqwest::Client::new().post(URL).json(...)
  9. 解析 {"code":100, "data":[{"text":"...","box":[[x1,y1],...]}]}
  10. keywords 匹配 → 写 Finding
  11. 关闭:先 PostMessage(hwnd, WM_CLOSE, 0, 0)Sleep(close_wait_ms)taskkill /F /PID xxx /T

5. Umi-OCR HTTP 客户端(关键代码片段)

// src/inspect/umi_ocr.rs
pub struct UmiOcrClient { base_url: String, client: reqwest::blocking::Client }

#[derive(Serialize)]
struct OcrRequest<'a> { base64: &'a str, options: OcrOptions }

#[derive(Serialize)]
struct OcrOptions { #[serde(rename = "ocr.language")] language: String,
                    #[serde(rename = "ocr.cls")] cls: bool,
                    #[serde(rename = "ocr.limit_side_len")] limit_side_len: u32,
                    #[serde(rename = "tbpu.parser")] parser: String }

#[derive(Deserialize)]
pub struct OcrResponse { pub code: u32, pub data: Vec<OcrItem>, pub time: f64 }
pub struct OcrItem { pub text: String, pub score: f32, pub box: Vec<[f64;2]> }

impl UmiOcrClient {
    pub fn recognize_png(&self, png: &[u8], lang: &str) -> anyhow::Result<OcrResponse> {
        let b64 = base64::engine::general_purpose::STANDARD.encode(png);
        let req = OcrRequest { base64: &b64, options: OcrOptions { language: lang.into(), cls: false, limit_side_len: 2880, parser: "multi_para".into() } };
        let resp = self.client.post(&self.base_url).json(&req).send()?.json::<OcrResponse>()?;
        if resp.code != 100 { anyhow::bail!("Umi-OCR 返回 code={}", resp.code); }
        Ok(resp)
    }
}

6. Umi-OCR 启动 + 健康检查

  • 启动时检查 <exe 同目录>/Umi-OCR.exe 存在 → 不存在则提示"请将 Umi-OCR.exe 放在 exe 同目录"
  • CreateProcessW 启动后,循环 GET http://127.0.0.1:1224/ 直到成功或超时(默认 3s
  • 失败时给出修复指引端口被占、Umi-OCR 未运行)

7. 关键词匹配

  • 关键词少:用 regex::RegexSet
  • 关键词多:用 aho_corasick::AhoCorasick(多模式 AC 自动机)
  • 命中位置记录 (text_index, char_offset, length) → 写回 box 坐标做红框高亮

8. 取消与超时

  • 全局 tokio::sync::watch::Sender<bool> 发取消信号
  • 每个 Inspector 用 tokio::time::timeout(总超时, ...) 包裹
  • UI 顶栏 "取消" 按钮 → 发送 true

9. 性能与稳定

  • 截图位图大于 screenshot_max_side 时先 image::imageops::resize
  • 临时文件清理:每次启动清 temp/secret-scan-*
  • 关闭查看器失败 → 二次 taskkill /F /T
  • doclite 进程残留 → 主程序退出前强制清

八、构建与发布

# 一次性
pacman -S mingw-w64-x86_64-toolchain mingw-w64-x86_64-cmake
rustup target add x86_64-pc-windows-gnu

# 编译
cargo build --release --target x86_64-pc-windows-gnu

# 产物
ls target/x86_64-pc-windows-gnu/release/secret-file-selfcheck.exe

发布物(同级目录):

secret-file-selfcheck.exe
Umi-OCR.exe
Umi-OCR-data/        # Umi-OCR 自带数据目录
doclite.exe
README.md

README 章节:构建步骤、运行方式、首次配置(放 Umi-OCR 与 doclite、常见问题OCR 不返回、doclite 启动失败、UAC 弹窗被禁用、AV 误报)。


九、测试策略

  • 单元测试
    • matcher::keywords:大小写、正则、整词、误报白名单
    • sampler:随机 / 分层 / 配额
    • filter:隐藏文件、扩展名白名单、最小大小
    • walker:白名单目录剔除、最大深度
  • 集成测试
    • inspect::umi_ocr 用一个 mock HTTP serverhttpmock / wiremock)验证请求体/响应解析
    • fixturesample.docx(含"机密")和"普通"两份 → 走完 Inspector
  • 手动测试
    • 在 doclite / SumatraPDF / Edge 下验证窗口截图与 OCR 准确度
    • 验证 Umi-OCR 中文识别率
    • 验证 UAC 弹窗仅出现一次
  • 做 GUI 自动化测试egui 暂无稳定 e2e 方案)

十、MVP 任务拆分

任务 1脚手架 + 隐藏控制台 + UAC

  1. Cargo.toml + 最小 main.rscargo build 通过
  2. build.rs + #![windows_subsystem = "windows"] 隐藏控制台
  3. privilege.rs 实现 ensure_admin(),未提权时 runas 重启
  4. 提交

任务 2egui 入口 + Material 主题 + 中文字体

  1. app.rs 骨架:左侧设置、右侧主面板、底部状态栏
  2. ui/material.rs:颜色 token + 圆角控件
  3. ui/widgets.rs:嵌入 SourceHanSansSC-Regular.otf
  4. UI 显示"涉密文件自检工具"中文正常
  5. 提交

任务 3配置模型与持久化

  1. config/model.rs 强类型 AppConfigserde + toml
  2. 启动加载、退出保存到 %APPDATA%\secret-file-selfcheck\config.toml
  3. 首次启动向导:检测 Umi-OCR/doclite 路径、关键词、等待时间
  4. 单元测试:序列化往返
  5. 提交

任务 4目录遍历 walker

  1. walkdir + 白名单剔除 + 扩展名白名单 + 隐藏/系统过滤 + 最小大小
  2. 单元测试:白名单、扩展名、深度
  3. 提交

任务 5抽检 sampler

  1. 完全随机 / 分层 / 类型配额 三种策略
  2. 单元测试
  3. 提交

任务 6关键词匹配

  1. keywords 模块:大小写、正则、整词、白名单
  2. aho-corasick 多模式
  3. 单元测试
  4. 提交

任务 7DOCX 文本抽检

  1. docx-rs 读段落
  2. 命中检测
  3. fixture 测试
  4. 提交

任务 8Umi-OCR HTTP 客户端

  1. umi_ocr.rs 阻塞 POST /api/ocr解析返回
  2. Umi-OCR 启动 + 健康检查
  3. 集成测试mock server
  4. 提交

任务 9截图链路

  1. external.rs 启进程 / 关闭窗口 / 强杀
  2. screenshot.rskeybd_event 模拟 Win+Shift+S + 剪贴板轮询读位图
  3. 单元测试 + 手动测试
  4. 提交

任务 10DOC 抽检

  1. doc_inspector.rs 拼装流程doclite.exe + 等待 + 截图 + OCR + 匹配 + 关闭
  2. 提交

任务 11PDF 抽检

  1. pdf_inspector.rs 拼装流程:关联程序 + 等待 + 截图 + OCR + 匹配 + 关闭
  2. 提交

任务 12并发调度

  1. tokio + buffer_unordered 调度;进度、取消、日志
  2. 提交

任务 13报告输出

  1. report::html + report::png + report::json
  2. 提交

任务 14设置页 UIA-F 六分组)

  1. 所有控件
  2. 即时写 config
  3. 提交

任务 15主面板 UI

  1. "开始检测" 按钮、进度条、实时日志
  2. 截图引导弹层
  3. 提交

任务 16打包

  1. release 构建
  2. 验证无控制台、UI 中文、抽检 10 份报告正确、UAC 仅一次
  3. README构建/部署/FAQ
  4. 提交

十一、风险与对策

风险 影响 对策
Umi-OCR 启动失败/端口占用 OCR 全失败 启动时健康检查 + 清晰错误提示;允许用户在设置中改端口
Win+Shift+S 触发失败(被其它程序抢占) 截不到图 增加"手动粘贴"兜底UI 提供"从剪贴板读取"按钮;再不行就跳过
doclite 启动后窗口未出现 截图空白 wait_ms 可调;增加"窗口标题探测"再截图
OCR 识别率低(中文/复杂排版) 漏报 提供 hi-reslimit_side_len=4320截图后预处理二值化作为后续
截图位图被 Word 起始页覆盖 漏报 OCR 后再次截图(用户配合二次框选),或启用 PrintWindow 抓窗口(兜底)
单 exe 体积 20-40MB LTO + strip + release 优化;提供"瘦身版"不内嵌字体
Anti-Virus 误报 启动被拦 README 提示加白名单;可选代码签名
全盘扫描耗时 几分钟到几十分钟 UI 实时进度 + 可取消;默认全盘但允许"白名单"缩小范围
UAC 弹窗被用户禁用 后续权限不足 首次启动检测 UAC 状态;告知用户
剪贴板被其它程序占用 读不到图 OpenClipboard 重试 + 退避;超过 N 次失败则放弃当前文件

十二、可后续追加(不在 MVP 范围)

  • 哈希去重在 SQLite 中持久化
  • 关键词 + 文件指纹的"已确认误报"列表
  • 隔离区(命中后复制到独立目录)
  • 开机自启 + 托盘
  • 报告 PDF 格式
  • 关键词网络下发
  • 抽检历史对比 / 趋势图

十三、最终确认(已通过)

  1. 截图三模式manual / auto_printwindow / auto_with_manual_fallback已纳入设计与实现。
  2. 抽检固定串行,无并发选项。
  3. 所有核心设计决策1.1 节 15 项)已锁定。

可进入实现阶段,按任务 1-16 顺序执行。