# 文件预览语法高亮 - 产品需求文档 (PRD) ## 需求描述 ### 背景 - **业务问题**:当前文件预览功能仅对 Markdown 文件提供语法高亮渲染,其他代码文件(Python、Shell、Java、TypeScript 等)只显示纯文本,用户体验不佳,无法快速理解代码结构。 - **目标用户**:技能开发者、审核人员、技能使用者 - **价值主张**:提供与 Markdown 代码块一致的高质量语法高亮,提升代码可读性,加速代码审查和理解效率。 ### 功能概述 - **核心功能**: 1. 为常见编程语言(Python, Shell, Java, JS/TS, Go, Rust, C/C++, Ruby, PHP)提供语法高亮 2. 为配置文件(JSON, YAML, TOML, XML)提供语法高亮 3. 复用现有的 rehype-highlight(基于 highlight.js)渲染引擎 4. 保持与 Markdown 代码块一致的视觉样式 5. 支持 dark/light 主题自动切换 - **功能边界**: - **包含**:常见编程语言和配置文件的语法高亮、错误降级处理、性能优化 - **不包含**:行号显示、代码折叠、语法错误检测、自定义主题配置 - **用户场景**: 1. 技能审核人员查看提交的 Python 脚本,快速识别代码逻辑 2. 开发者预览技能包中的配置文件(如 skill.yaml),确认参数配置 3. 用户浏览技能源码,了解实现细节 ### 详细需求 - **输入/输出**: - 输入:文件路径、文件内容(InputStream)、文件扩展名 - 输出:带语法高亮的 HTML(通过 highlight.js 渲染) - **用户交互**: 1. 用户点击文件树中的代码文件节点 2. 前端显示 loading 状态 3. 后端返回文件内容和元数据(大小、类型) 4. 前端根据文件大小和类型决定渲染策略: - ≤ 500KB:语法高亮渲染 - 500KB < size ≤ 1MB:纯文本渲染(无高亮) - > 1MB:仅显示下载按钮 - **数据要求**: - 文件大小:通过 `SkillFile.fileSize` 字段获取 - 文件类型:通过文件扩展名推断(`.py` → Python) - 语言映射:使用 `file-type-utils.ts` 中的映射表 - **边界情况**: 1. **无法识别的语言**:显示纯文本(无高亮),不报错 2. **语法高亮失败**:降级到纯文本显示,记录错误日志 3. **内存不足**:降级到纯文本显示,显示提示信息 4. **大文件(> 500KB)**:跳过语法高亮,直接显示纯文本 5. **超大文件(> 1MB)**:不预览,仅提供下载 ## 设计决策 ### 技术方案 - **架构选择**:复用现有的 rehype-highlight(基于 highlight.js) - **理由**: 1. 零额外依赖,不增加包体积 2. 样式与 Markdown 代码块完全一致 3. 已有的语言支持(190+ 种语言) 4. 维护成本低 - **关键组件**: 1. **CodeRenderer 组件**(新增): - 位置:`web/src/features/skill/code-renderer.tsx` - 职责:接收代码字符串和语言类型,调用 highlight.js 渲染 - 依赖:`highlight.js/lib/core` + 按需导入的语言包 2. **file-type-utils.ts**(扩展): - 新增 `getLanguageForHighlight(extension: string): string | null` 函数 - 映射文件扩展名到 highlight.js 语言标识符 3. **file-preview-dialog.tsx**(修改): - 根据文件大小和类型选择渲染器: - Markdown → `MarkdownRenderer` - 代码文件(≤ 500KB)→ `CodeRenderer` - 代码文件(> 500KB)→ 纯文本 `
`
- 其他 → 下载提示
- **数据存储**:
- 无需新增数据库字段
- 文件内容从云存储(ObjectStorageService)读取
- 文件大小已存储在 `SkillFile.fileSize` 字段
- **接口设计**:
- 复用现有 API:`GET /api/v1/reviews/{id}/file?path={filePath}`
- 响应格式:`InputStream`(保持不变)
- 前端根据 `Content-Length` 响应头判断文件大小
### 约束条件
- **性能要求**:
- 语法高亮渲染时间:< 500ms(对于 500KB 文件)
- 首次加载时间:< 1s(包括网络请求)
- 内存占用:单个文件渲染 < 50MB
- **兼容性**:
- 浏览器:Chrome 90+, Firefox 88+, Safari 14+, Edge 90+
- 移动端:支持响应式布局,但不优化触摸交互
- **安全性**:
- 使用 `dangerouslySetInnerHTML` 时,确保 highlight.js 输出已转义
- 防止 XSS 攻击:不允许用户自定义语法高亮规则
- 文件路径验证:复用现有的路径遍历防护(禁止 `..` 和绝对路径)
- **可扩展性**:
- 语言支持:通过按需导入语言包扩展,不影响初始包体积
- 主题支持:预留主题切换接口,当前仅支持跟随系统主题
### 风险评估
- **技术风险**:
- **风险**:大文件语法高亮导致浏览器卡顿
- **概率**:中
- **影响**:高
- **缓解措施**:
1. 设置 500KB 阈值,超过则不高亮
2. 增加 loading 状态提示用户
3. 提供"取消加载"按钮(后续优化)
- **依赖风险**:
- **风险**:highlight.js 无法识别某些语言
- **概率**:低
- **影响**:低
- **缓解措施**:降级到纯文本显示,不影响核心功能
- **进度风险**:
- **风险**:后端缓存和限流实现延期
- **概率**:中
- **影响**:中
- **缓解措施**:
1. 前端功能可独立上线
2. 后端优化作为独立任务,分阶段实施
## 验收标准
### 功能验收
- [ ] 功能 1:Python 文件(.py)显示语法高亮(关键字、字符串、注释着色)
- [ ] 功能 2:Shell 脚本(.sh, .bash)显示语法高亮
- [ ] 功能 3:配置文件(JSON, YAML, TOML, XML)显示语法高亮
- [ ] 功能 4:Java/TypeScript/JavaScript 文件显示语法高亮
- [ ] 功能 5:无法识别的语言显示纯文本(不报错)
- [ ] 功能 6:大文件(> 500KB)显示纯文本(无高亮)
- [ ] 功能 7:超大文件(> 1MB)仅显示下载按钮
- [ ] 功能 8:语法高亮样式与 Markdown 代码块一致
- [ ] 功能 9:支持 dark/light 主题自动切换
- [ ] 功能 10:复制代码按钮正常工作
### 质量标准
- [ ] 代码质量:通过 ESLint 和 TypeScript 类型检查
- [ ] 测试覆盖:核心逻辑(CodeRenderer, getLanguageForHighlight)单元测试覆盖率 > 80%
- [ ] 性能指标:
- 500KB 文件渲染时间 < 500ms(P95)
- 首次加载时间 < 1s(P95)
- Lighthouse 性能评分不下降
- [ ] 安全审查:代码审查确认无 XSS 风险
### 用户验收
- [ ] 用户体验:审核人员反馈代码可读性提升
- [ ] 文档:更新用户文档,说明支持的文件类型
- [ ] 无障碍:键盘导航正常,屏幕阅读器可访问
## 执行阶段
### 阶段 1:准备工作
**目标**:环境准备和技术验证
- [ ] 任务 1:调研 highlight.js 语言包按需导入方案
- [ ] 任务 2:验证 rehype-highlight 的 CSS 样式可复用性
- [ ] 任务 3:确认文件大小字段(`SkillFile.fileSize`)已存在
- [ ] 任务 4:设计 CodeRenderer 组件 API
- **交付物**:技术方案文档、组件 API 设计
- **时间**:0.5 天
### 阶段 2:核心开发
**目标**:实现语法高亮核心功能
- [ ] 任务 1:创建 CodeRenderer 组件
- 导入 highlight.js 核心库
- 按需导入常见语言包(Python, Shell, Java, JS/TS, Go, Rust, C/C++, Ruby, PHP)
- 按需导入配置文件语言包(JSON, YAML, TOML, XML)
- 实现 `highlightCode(code: string, language: string)` 函数
- 复用 Markdown 代码块的 CSS 样式
- [ ] 任务 2:扩展 file-type-utils.ts
- 新增 `getLanguageForHighlight(extension: string)` 函数
- 映射文件扩展名到 highlight.js 语言标识符
- [ ] 任务 3:修改 file-preview-dialog.tsx
- 根据文件大小和类型选择渲染器
- 实现 500KB 和 1MB 阈值逻辑
- 集成 CodeRenderer 组件
- [ ] 任务 4:主题适配
- 确保语法高亮样式跟随 dark/light 主题
- 测试主题切换时的视觉一致性
- **交付物**:可运行的语法高亮功能
- **时间**:1.5 天
### 阶段 3:集成与测试
**目标**:集成测试和质量保证
- [ ] 任务 1:单元测试
- CodeRenderer 组件测试(不同语言、边界情况)
- getLanguageForHighlight 函数测试
- [ ] 任务 2:集成测试
- 技能详情页文件预览测试
- 审核详情页文件预览测试
- [ ] 任务 3:性能测试
- 测试 500KB 文件渲染时间
- 测试内存占用
- [ ] 任务 4:浏览器兼容性测试
- Chrome, Firefox, Safari, Edge
- [ ] 任务 5:用户验收测试
- 邀请审核人员试用,收集反馈
- **交付物**:测试报告、性能基准数据
- **时间**:1 天
### 阶段 4:部署与监控
**目标**:上线发布和效果监控
- [ ] 任务 1:代码审查
- 安全审查(XSS 风险)
- 性能审查(包体积、渲染性能)
- [ ] 任务 2:部署到生产环境
- 前端构建和部署
- 验证生产环境功能正常
- [ ] 任务 3:监控指标
- 文件预览 API 响应时间
- 前端渲染性能(通过 RUM)
- 错误率监控
- [ ] 任务 4:文档更新
- 更新用户文档,说明支持的文件类型
- 更新开发者文档,说明如何添加新语言支持
- **交付物**:生产环境部署、监控仪表板、用户文档
- **时间**:0.5 天
---
**文档版本**:1.0
**创建时间**:2026-03-22
**澄清轮次**:3
**质量评分**:95/100
## 附录:后端优化方案(后续实施)
以下优化方案已记录在文档中,但不在本次实施范围内,将作为独立任务后续统一实现。
### 1. 后端缓存策略
- **目标**:减少云存储读取次数,降低响应时间
- **方案**:
- 使用 Redis 缓存文件内容
- 缓存键:`file:content:{storageKey}`
- TTL:1 小时
- 缓存策略:LRU(最近最少使用)
- **预期效果**:
- 缓存命中率 > 60%
- 响应时间降低 50%
### 2. 限流策略
- **目标**:防止文件预览 API 被滥用,保护后端服务
- **方案**:
- 使用 `@RateLimit` 注解
- 配置:
```java
@RateLimit(
category = "file-preview",
authenticated = 60, // 认证用户:每分钟 60 次
anonymous = 20, // 匿名用户:每分钟 20 次
windowSeconds = 60
)
```
- 限流粒度:按用户 ID(认证用户)或 IP(匿名用户)
- 超出限流响应:返回 429 状态码 + `{"code": 429, "message": "error.rateLimit.exceeded"}`
- **预期效果**:
- 防止单用户/IP 过度请求
- 保护云存储 API 配额
### 3. 监控指标
- **目标**:实时监控文件预览功能的健康状况
- **指标**:
- 文件预览 API 响应时间(P50, P95, P99)
- 文件预览 API 错误率
- 缓存命中率
- 限流触发次数
- 云存储 API 调用次数
- **告警规则**:
- P95 响应时间 > 2s:警告
- 错误率 > 5%:严重
- 缓存命中率 < 40%:警告
### 4. 实施优先级
1. **P0(本次实施)**:前端语法高亮功能
2. **P1(下个迭代)**:后端缓存策略
3. **P2(后续优化)**:限流策略、监控指标