# df-relay + df-tunnel + df-project + df-types + df-execute 代码走查报告

> 走查日期: 2026-08-02
> 范围: df-relay(5 文件, 30KB) + df-tunnel(3 文件, 32KB) + df-project(2 文件, 21KB) + df-types(3 文件, 42KB) + df-execute(2 文件, 34KB)

---

## 一、df-relay 云中继

### 问题汇总

| # | 等级 | 文件 | 类型 | 简述 |
|---|---|---|---|---|
| 1 | 🟡 P1 | relay.rs | risk | token 鉴权用 `env DF_RELAY_TOKEN` 全局常量，生产级应有独立 token 每 device |
| 2 | 🟡 P1 | relay.rs | risk | 注册表 `tokio::sync::Mutex` 锁粒度粗，高并发连接时瓶颈 |
| 3 | 🟢 P2 | conn.rs | smell | `ConnRegistry::route` 中 `BroadcastMessage` 字段 `kind`/`from`/`handle.kind` 三因子匹配，可读性差 |
| 4 | 🟢 P2 | broadcast.rs | smell | `ControlMessage` 定义了但 `handle_inbound_text` 中只处理 `ping`，其余控制消息未实现 |

### 正面评价

1. **架构清晰**: Event/Command/Control 三方向路由，语义明确
2. **Hello 握手**: 双因子校验(kind + token)，超时 10s，error 帧回显
3. **心跳处理**: 小程序 ping → relay pong 本地响应，不经过 device
4. **BroadcastMessage 全字段**: device_id/kind/source/from/payload/ts，路由信息完整

---

## 二、df-tunnel 隧道客户端

### 问题汇总

| # | 等级 | 文件 | 类型 | 简述 |
|---|---|---|---|---|
| 1 | 🟡 P1 | tunnel.rs | risk | 重连退避逻辑写了 `backoff_delay` 函数但未实现自动后台重连，需手动 connect |
| 2 | 🟡 P1 | tunnel.rs | risk | `cleanup_conn` 中 `select!` 500ms 超时后 abort task，可能丢最后一帧 |
| 3 | 🟢 P2 | tunnel.rs | smell | `WsTunnelClient` 持 3 个 `Mutex<Option<String>>`(server_url/device_id/token)，可合并为单 struct |
| 4 | 🟢 P2 | tunnel.rs | smell | `connect` 方法 130 行，逻辑过重，可拆为 `do_connect` + `handshake` + `start_loop` |

### 正面评价

1. **协议对齐**: 与 df-relay 的 Hello 握手协议一致
2. **connect_timeout**: 15s 超时防 TCP SYN 无响应时永久挂
3. **HANDSHAKE_PROBE**: 3s 窗口探测 relay 是否拒绝，防静默失败
4. **心跳间隔**: 25s 应用层 Ping，防 NAT 连接表超时回收
5. **CommandHandler**: `BoxFuture` 回调，不阻塞收发循环
6. **send_raw_event**: AiChatEvent 全 19 变体透传，不依赖强类型

---

## 三、df-project 项目管理

### 问题汇总

| # | 等级 | 文件 | 类型 | 简述 |
|---|---|---|---|---|
| 1 | 🟢 P2 | manager.rs | smell | `ProjectManager::create` 纯领域层工厂，但 `Project` 实体与 `ProjectRecord` 模型不同源 |
| 2 | 🟢 P2 | scan_helpers.rs | smell | 图片收集 `is_badge_image` 依赖域名+关键词列表，`is_pure_badge_line` 行级扫描逻辑复杂 |

### 正面评价

1. **状态机完整**: `can_transition` 矩阵清晰，`transition` 返回 `TerminalState`/`IllegalTransition` 区分
2. **scan_helpers 纯函数**: 无 IO 依赖，`strip_readme_noise` 处理 frontmatter/HTML注释/TOC/徽章
3. **否定前缀处理**: 单字(不/无/非/未/没/勿)+两字(没有/并非/毫无等)否定前缀，设计精细
4. **测试覆盖**: manager.rs 7 个测试，scan_helpers.rs 10 个测试

---

## 四、df-types 类型定义

### 问题汇总

| # | 等级 | 文件 | 类型 | 简述 |
|---|---|---|---|---|
| 1 | 🟢 P2 | augmentation.rs | smell | 21KB 单文件，MentionRef/Augmentation/ResolveError 三个模块混在一起 |
| 2 | 🟢 P2 | types.rs | smell | `ExecutionId`/`ToolCallType` newtype 大量 `From`/`PartialEq` 样板，可取 derive_more |

### 正面评价

1. **SanitizedPath newtype**: 脱敏路径强制类型化，防裸 String 误用
2. **MentionRef/Augmentation 双结构**: resolve 前/后分离，职责清晰
3. **serde tag=kind**: 四种变体统一 json 结构，前端可共用 chip 渲染
4. **TaskStatus 双源一致性**: `as_str()` const fn + `valid_values()` 测试锁定
5. **WorkflowEvent 兼容**: `#[serde(default)]` 向后兼容老事件

---

## 五、df-execute 命令执行

### 问题汇总

| # | 等级 | 文件 | 类型 | 简述 |
|---|---|---|---|---|
| 1 | 🟡 P1 | shell.rs | risk | `build_command` 中 `ShellType::default()` 依赖 `PWSH_CACHE` 异步填充，首次 `execute` 可能读到未初始化 |
| 2 | 🟡 P1 | env_snapshot.rs | risk | `probe_version` 内 `std::process::Command::output()` 无 timeout，Windows Store alias 卡死（已修复 5s timeout） |
| 3 | 🟢 P2 | shell.rs | smell | `execute_streaming` 的 `on_output` 回调在 `select!` 中调用，`FnMut` 不可重入 |

### 正面评价

1. **PWSH_CACHE 单源**: 异步 probe + 同步读取，死缓存已修复
2. **execute_streaming**: mpsc 通道解耦 + 逐行回调 + read_to_end 兜底
3. **kill_on_drop**: 超时后子进程不残留
4. **CREATE_NO_WINDOW**: Windows 后台静默执行不弹黑窗
5. **env_snapshot 超时兜底**: 5s timeout + spawn_blocking 防永久卡
6. **extract_version_token**: 鲁棒版本号提取，"2.41.0.windows.1" → "2.41.0"
7. **测试覆盖**: shell.rs 探测缓存测试，env_snapshot 7 个测试