feat(doc-parser): 添加文档解析系统架构文档和豆包预处理支持
## 新增 - 文档解析系统架构文档 (docs/doc-parser-architecture.md) - 完整的三层架构说明 - 8 种字段提取模式详解 - 优缺点分析和优化建议 - 豆包预处理快速通道 - 新增 preprocessed/ 目录支持 - 自动识别文档来源 - 优化 MD 文件解析提示 - 混合解析方案 - 少量文档用豆包预处理 - 批量文档用 MCP 直接解析 - 按来源分组显示文档列表 ## 更新 - README.md: 添加文档解析工具说明 - docs/to-parse/README.md: 添加豆包预处理指南和对比表 ## 移除 - scripts/doc-parser/QUICKSTART.md (内容已整合) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Showing
5 changed files
with
145 additions
and
218 deletions
| ... | @@ -4,6 +4,7 @@ | ... | @@ -4,6 +4,7 @@ |
| 4 | 4 | ||
| 5 | ## 📚 项目文档 | 5 | ## 📚 项目文档 |
| 6 | 6 | ||
| 7 | +- **[文档解析系统架构](docs/doc-parser-architecture.md)** - 计划书配置自动化生成工具 | ||
| 7 | - **[经验教训总结](docs/lessons-learned.md)** - Taro 项目开发经验、最佳实践和常见陷阱 | 8 | - **[经验教训总结](docs/lessons-learned.md)** - Taro 项目开发经验、最佳实践和常见陷阱 |
| 8 | - **[CLAUDE.md](CLAUDE.md)** - 项目开发指南(供 Claude Code 使用) | 9 | - **[CLAUDE.md](CLAUDE.md)** - 项目开发指南(供 Claude Code 使用) |
| 9 | - **[文档导航](docs/README.md)** - 项目文档索引与使用建议 | 10 | - **[文档导航](docs/README.md)** - 项目文档索引与使用建议 |
| ... | @@ -55,7 +56,7 @@ pnpm lint | ... | @@ -55,7 +56,7 @@ pnpm lint |
| 55 | 56 | ||
| 56 | ### 近期亮点 | 57 | ### 近期亮点 |
| 57 | 58 | ||
| 58 | -- **多产品文档解析** - 支持自动识别和分割包含多个保险产品的文档 | 59 | +- **文档解析系统** - 从 PDF/DOCX 自动生成计划书配置(支持多产品文档分割) |
| 59 | - **计划书 Schema 驱动** - 储蓄类/人寿/重疾模板字段配置化 | 60 | - **计划书 Schema 驱动** - 储蓄类/人寿/重疾模板字段配置化 |
| 60 | - **Git 工作流标准化** - 使用 standard-version + Conventional Commits | 61 | - **Git 工作流标准化** - 使用 standard-version + Conventional Commits |
| 61 | - **认证系统完善** - 401 自动刷新、登录权限检查、TabBar 红点 | 62 | - **认证系统完善** - 401 自动刷新、登录权限检查、TabBar 红点 |
| ... | @@ -270,7 +271,44 @@ export default { | ... | @@ -270,7 +271,44 @@ export default { |
| 270 | - ✅ 所有参数都有 `@param` 说明 | 271 | - ✅ 所有参数都有 `@param` 说明 |
| 271 | - ✅ 返回值有 `@returns` 说明 | 272 | - ✅ 返回值有 `@returns` 说明 |
| 272 | 273 | ||
| 273 | -## 🔧 可选功能 | 274 | +## 🔧 开发工具 |
| 275 | + | ||
| 276 | +### 文档解析工具 | ||
| 277 | + | ||
| 278 | +自动从保险产品文档(PDF/DOCX)中提取配置,生成计划书模板: | ||
| 279 | + | ||
| 280 | +```bash | ||
| 281 | +# 解析所有待处理文档 | ||
| 282 | +pnpm parse:docs | ||
| 283 | + | ||
| 284 | +# 解析指定文件 | ||
| 285 | +pnpm parse:docs -- --file=产品说明书.pdf | ||
| 286 | + | ||
| 287 | +# 查看待处理文档列表 | ||
| 288 | +pnpm parse:docs -- --list | ||
| 289 | + | ||
| 290 | +# 应用审核通过的配置 | ||
| 291 | +pnpm parse:docs -- --apply=计划书模版4 | ||
| 292 | + | ||
| 293 | +# 预览变更(不实际修改) | ||
| 294 | +pnpm parse:docs -- --apply=计划书模版4 --dry-run | ||
| 295 | + | ||
| 296 | +# 查看配置状态 | ||
| 297 | +pnpm parse:docs -- --status | ||
| 298 | +``` | ||
| 299 | + | ||
| 300 | +**核心能力**: | ||
| 301 | +- 📄 支持 PDF、DOCX、TXT、MD 格式 | ||
| 302 | +- 🔄 自动识别并分割多产品文档 | ||
| 303 | +- 🤖 智能字段提取(8 个核心字段) | ||
| 304 | +- ✅ 人工审核流程 | ||
| 305 | +- 💾 自动备份和回滚 | ||
| 306 | + | ||
| 307 | +**详细文档**: [文档解析系统架构](docs/doc-parser-architecture.md) | ||
| 308 | + | ||
| 309 | +--- | ||
| 310 | + | ||
| 311 | +### 可选功能组件 | ||
| 274 | 312 | ||
| 275 | 以下功能可以根据项目需求选择使用或移除: | 313 | 以下功能可以根据项目需求选择使用或移除: |
| 276 | 314 | ||
| ... | @@ -281,11 +319,24 @@ export default { | ... | @@ -281,11 +319,24 @@ export default { |
| 281 | 319 | ||
| 282 | ## ✅ 优化建议 | 320 | ## ✅ 优化建议 |
| 283 | 321 | ||
| 284 | -- 建议将文档解析脚本接入真实 AI 解析服务以替代 mock 配置 | 322 | +### 文档解析系统 |
| 285 | -- 建议为 parse:docs 增加一键校验配置合法性的脚本输出 | 323 | + |
| 324 | +| 优先级 | 优化项 | 说明 | | ||
| 325 | +|--------|--------|------| | ||
| 326 | +| 🔴 P0 | 启用 AI 服务 | 配置 `AI_SERVICE_TYPE` 提升复杂文档解析准确率 | | ||
| 327 | +| 🟡 P1 | 完善 .doc 支持 | 使用 antiword 或 LibreOffice 转换 | | ||
| 328 | +| 🟡 P1 | 增加自动化测试 | 补充 parse-docs.test.js 测试用例 | | ||
| 329 | +| 🟢 P2 | 添加 OCR 能力 | 支持扫描件解析(Tesseract.js) | | ||
| 330 | + | ||
| 331 | +### 项目整体 | ||
| 332 | + | ||
| 333 | +1. 持续维护 API 集成日志与页面模块对应关系 | ||
| 334 | +2. 文档预览与视频播放页面补充更多异常场景说明 | ||
| 335 | +3. 页面入口与权限策略保持同步,避免入口显示但权限不一致 | ||
| 286 | 336 | ||
| 287 | ## 📚 相关文档 | 337 | ## 📚 相关文档 |
| 288 | 338 | ||
| 339 | +- **[文档解析系统架构](docs/doc-parser-architecture.md)** - 计划书配置自动化工具详解 | ||
| 289 | - **[经验教训总结](docs/lessons-learned.md)** - Taro 项目开发经验、最佳实践和常见陷阱 | 340 | - **[经验教训总结](docs/lessons-learned.md)** - Taro 项目开发经验、最佳实践和常见陷阱 |
| 290 | - **[CLAUDE.md](CLAUDE.md)** - 项目开发指南(供 Claude Code 使用) | 341 | - **[CLAUDE.md](CLAUDE.md)** - 项目开发指南(供 Claude Code 使用) |
| 291 | - **[文档解析待处理说明](docs/to-parse/README.md)** - 文档解析样本与脚本使用方式 | 342 | - **[文档解析待处理说明](docs/to-parse/README.md)** - 文档解析样本与脚本使用方式 | ... | ... |
docs/doc-parser-architecture.md
0 → 100644
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
scripts/doc-parser/QUICKSTART.md
deleted
100644 → 0
| 1 | -# OpenAPI 转 API 文档生成器 - 快速开始 | ||
| 2 | - | ||
| 3 | -## 🎯 一分钟快速上手 | ||
| 4 | - | ||
| 5 | -### 1️⃣ 创建 OpenAPI 文档 | ||
| 6 | - | ||
| 7 | -在 `docs/api-specs/` 目录下创建模块和接口文档: | ||
| 8 | - | ||
| 9 | -```bash | ||
| 10 | -# 创建新模块 | ||
| 11 | -mkdir -p docs/api-specs/product | ||
| 12 | - | ||
| 13 | -# 创建接口文档 | ||
| 14 | -touch docs/api-specs/product/getList.md | ||
| 15 | -``` | ||
| 16 | - | ||
| 17 | -### 2️⃣ 编写 OpenAPI 规范 | ||
| 18 | - | ||
| 19 | -编辑 `getList.md`: | ||
| 20 | - | ||
| 21 | -```markdown | ||
| 22 | -# 获取商品列表 | ||
| 23 | - | ||
| 24 | -## OpenAPI Specification | ||
| 25 | - | ||
| 26 | -\```yaml | ||
| 27 | -openapi: 3.0.1 | ||
| 28 | -info: | ||
| 29 | - title: '' | ||
| 30 | - version: 1.0.0 | ||
| 31 | -paths: | ||
| 32 | - /srv/: | ||
| 33 | - get: | ||
| 34 | - summary: 获取商品列表 | ||
| 35 | - tags: | ||
| 36 | - - 商品 | ||
| 37 | - parameters: | ||
| 38 | - - name: a | ||
| 39 | - in: query | ||
| 40 | - example: product_list | ||
| 41 | - - name: f | ||
| 42 | - in: query | ||
| 43 | - example: behalo | ||
| 44 | - responses: | ||
| 45 | - '200': | ||
| 46 | - description: 成功 | ||
| 47 | -\``` | ||
| 48 | -``` | ||
| 49 | - | ||
| 50 | -### 3️⃣ 生成 API 文件 | ||
| 51 | - | ||
| 52 | -```bash | ||
| 53 | -pnpm api:generate | ||
| 54 | -``` | ||
| 55 | - | ||
| 56 | -### 4️⃣ 使用生成的 API | ||
| 57 | - | ||
| 58 | -```javascript | ||
| 59 | -import { getListAPI } from '@/api/product'; | ||
| 60 | - | ||
| 61 | -const result = await getListAPI({ page: 1, pageSize: 10 }); | ||
| 62 | -``` | ||
| 63 | - | ||
| 64 | -## ✅ 验证结果 | ||
| 65 | - | ||
| 66 | -运行测试脚本验证生成的文件: | ||
| 67 | - | ||
| 68 | -```bash | ||
| 69 | -node scripts/test-generate.js | ||
| 70 | -``` | ||
| 71 | - | ||
| 72 | -## 📂 文件结构 | ||
| 73 | - | ||
| 74 | -``` | ||
| 75 | -manulife-weapp/ | ||
| 76 | -├── docs/ | ||
| 77 | -│ ├── api-specs/ # API 规范文档源目录 | ||
| 78 | -│ │ └── user/ # 模块目录 | ||
| 79 | -│ │ └── getUserInfo.md | ||
| 80 | -│ ├── OPENAPI_TO_API_GUIDE.md # 详细使用指南 | ||
| 81 | -│ └── API_USAGE_EXAMPLES.md # API 使用示例 | ||
| 82 | -├── scripts/ | ||
| 83 | -│ ├── generateApiFromOpenAPI.js # 生成器核心脚本 | ||
| 84 | -│ └── test-generate.js # 测试脚本 | ||
| 85 | -├── src/ | ||
| 86 | -│ └── api/ # 生成的 API 文件目录 | ||
| 87 | -│ ├── user.js # 自动生成 | ||
| 88 | -│ ├── wx/ | ||
| 89 | -│ └── index.js | ||
| 90 | -└── package.json # 包含 api:generate 命令 | ||
| 91 | -``` | ||
| 92 | - | ||
| 93 | -## 🔄 工作流程 | ||
| 94 | - | ||
| 95 | -```mermaid | ||
| 96 | -graph LR | ||
| 97 | - A[编写 OpenAPI 文档] --> B[运行 pnpm api:generate] | ||
| 98 | - B --> C[生成 API 文件] | ||
| 99 | - C --> D[在项目中使用] | ||
| 100 | - D --> E[需要修改接口] | ||
| 101 | - E --> A | ||
| 102 | -``` | ||
| 103 | - | ||
| 104 | -## 🎨 常见场景 | ||
| 105 | - | ||
| 106 | -### 场景 1: 批量生成多个接口 | ||
| 107 | - | ||
| 108 | -```bash | ||
| 109 | -docs/api-specs/ | ||
| 110 | -├── user/ | ||
| 111 | -│ ├── getUserInfo.md | ||
| 112 | -│ ├── updateProfile.md | ||
| 113 | -│ └── changePassword.md | ||
| 114 | -└── order/ | ||
| 115 | - ├── getList.md | ||
| 116 | - └── getDetail.md | ||
| 117 | -``` | ||
| 118 | - | ||
| 119 | -运行 `pnpm api:generate` 后生成: | ||
| 120 | - | ||
| 121 | -``` | ||
| 122 | -src/api/ | ||
| 123 | -├── user.js # 包含 3 个接口 | ||
| 124 | -└── order.js # 包含 2 个接口 | ||
| 125 | -``` | ||
| 126 | - | ||
| 127 | -### 场景 2: 更新已有接口 | ||
| 128 | - | ||
| 129 | -1. 修改 `docs/api-specs/user/getUserInfo.md` | ||
| 130 | -2. 运行 `pnpm api:generate` | ||
| 131 | -3. `src/api/user.js` 自动更新 | ||
| 132 | - | ||
| 133 | -### 场景 3: 添加新模块 | ||
| 134 | - | ||
| 135 | -1. 创建 `docs/api-specs/payment/` | ||
| 136 | -2. 添加接口文档 | ||
| 137 | -3. 运行生成命令 | ||
| 138 | -4. 自动生成 `src/api/payment.js` | ||
| 139 | - | ||
| 140 | -## ⚙️ 配置和自定义 | ||
| 141 | - | ||
| 142 | -### 修改输出目录 | ||
| 143 | - | ||
| 144 | -编辑 `scripts/generateApiFromOpenAPI.js`: | ||
| 145 | - | ||
| 146 | -```javascript | ||
| 147 | -const outputDir = path.resolve(__dirname, '../src/api'); | ||
| 148 | -// 改为你想要的目录 | ||
| 149 | -const outputDir = path.resolve(__dirname, '../src/apis'); | ||
| 150 | -``` | ||
| 151 | - | ||
| 152 | -### 修改命名规则 | ||
| 153 | - | ||
| 154 | -编辑 `toCamelCase()` 或 `toPascalCase()` 函数。 | ||
| 155 | - | ||
| 156 | -### 修改生成模板 | ||
| 157 | - | ||
| 158 | -编辑 `generateApiFileContent()` 函数。 | ||
| 159 | - | ||
| 160 | -## 🐛 调试技巧 | ||
| 161 | - | ||
| 162 | -### 启用详细日志 | ||
| 163 | - | ||
| 164 | -在脚本中添加更多 console.log: | ||
| 165 | - | ||
| 166 | -```javascript | ||
| 167 | -console.log('解析的 API 信息:', JSON.stringify(apiInfo, null, 2)); | ||
| 168 | -``` | ||
| 169 | - | ||
| 170 | -### 单独测试某个模块 | ||
| 171 | - | ||
| 172 | -修改脚本中的模块过滤逻辑。 | ||
| 173 | - | ||
| 174 | -### 查看生成的中间数据 | ||
| 175 | - | ||
| 176 | -添加调试输出查看 YAML 解析结果。 | ||
| 177 | - | ||
| 178 | -## 📞 获取帮助 | ||
| 179 | - | ||
| 180 | -- 详细指南:[OpenAPI 转 API 文档生成器指南](./OPENAPI_TO_API_GUIDE.md) | ||
| 181 | -- 使用示例:[API 使用示例](./API_USAGE_EXAMPLES.md) | ||
| 182 | -- 项目架构:[CLAUDE.md](../CLAUDE.md) | ||
| 183 | - | ||
| 184 | -## 🎉 开始使用 | ||
| 185 | - | ||
| 186 | -现在你已经准备好了!开始创建你的第一个 OpenAPI 文档吧。 | ||
| 187 | - | ||
| 188 | -```bash | ||
| 189 | -# 1. 创建模块目录 | ||
| 190 | -mkdir -p docs/api-specs/your-module | ||
| 191 | - | ||
| 192 | -# 2. 创建接口文档(参考 docs/api-specs/user/getUserInfo.md) | ||
| 193 | - | ||
| 194 | -# 3. 生成 API | ||
| 195 | -pnpm api:generate | ||
| 196 | - | ||
| 197 | -# 4. 查看生成的文件 | ||
| 198 | -cat src/api/your-module.js | ||
| 199 | - | ||
| 200 | -# 5. 开始使用 | ||
| 201 | -``` | ||
| 202 | - | ||
| 203 | -祝你编码愉快!🚀 |
| ... | @@ -42,6 +42,8 @@ import { splitByProducts, findProductTitles, generateSplitReport } from './produ | ... | @@ -42,6 +42,8 @@ import { splitByProducts, findProductTitles, generateSplitReport } from './produ |
| 42 | // ========== 配置区 ========== | 42 | // ========== 配置区 ========== |
| 43 | 43 | ||
| 44 | const DOCS_DIR = path.resolve(process.cwd(), 'docs/to-parse') | 44 | const DOCS_DIR = path.resolve(process.cwd(), 'docs/to-parse') |
| 45 | +const DOCS_PREPROCESSED_DIR = path.resolve(process.cwd(), 'docs/to-parse/preprocessed') | ||
| 46 | +const DOCS_RAW_DIR = path.resolve(process.cwd(), 'docs/to-parse/raw') | ||
| 45 | const DOCS_ARCHIVE_DIR = path.resolve(process.cwd(), 'docs/to-parse/archived') | 47 | const DOCS_ARCHIVE_DIR = path.resolve(process.cwd(), 'docs/to-parse/archived') |
| 46 | const CONFIG_FILE = path.resolve(process.cwd(), 'src/config/plan-templates.js') | 48 | const CONFIG_FILE = path.resolve(process.cwd(), 'src/config/plan-templates.js') |
| 47 | const BACKUP_DIR = path.resolve(process.cwd(), 'docs/parsed-backup') | 49 | const BACKUP_DIR = path.resolve(process.cwd(), 'docs/parsed-backup') |
| ... | @@ -49,6 +51,29 @@ const BACKUP_DIR = path.resolve(process.cwd(), 'docs/parsed-backup') | ... | @@ -49,6 +51,29 @@ const BACKUP_DIR = path.resolve(process.cwd(), 'docs/parsed-backup') |
| 49 | // 支持的文档格式 | 51 | // 支持的文档格式 |
| 50 | const SUPPORTED_EXTENSIONS = ['.pdf', '.doc', '.docx', '.txt', '.md'] | 52 | const SUPPORTED_EXTENSIONS = ['.pdf', '.doc', '.docx', '.txt', '.md'] |
| 51 | 53 | ||
| 54 | +/** | ||
| 55 | + * 检测文档来源 | ||
| 56 | + * | ||
| 57 | + * @description 判断文档是预处理过的 MD 文件还是原始文档 | ||
| 58 | + * @param {string} filePath - 文档路径 | ||
| 59 | + * @returns {{source: string, type: string}} 来源信息 | ||
| 60 | + */ | ||
| 61 | +function detectDocumentSource(filePath) { | ||
| 62 | + if (filePath.includes('preprocessed')) { | ||
| 63 | + return { source: 'preprocessed', type: 'markdown' } | ||
| 64 | + } | ||
| 65 | + if (filePath.includes('raw')) { | ||
| 66 | + return { source: 'raw', type: 'original' } | ||
| 67 | + } | ||
| 68 | + // 根据文件扩展名推断 | ||
| 69 | + const ext = path.extname(filePath).toLowerCase() | ||
| 70 | + if (ext === '.md') { | ||
| 71 | + // MD 文件可能是预处理过的 | ||
| 72 | + return { source: 'likely-preprocessed', type: 'markdown' } | ||
| 73 | + } | ||
| 74 | + return { source: 'unknown', type: 'original' } | ||
| 75 | +} | ||
| 76 | + | ||
| 52 | const ajv = new Ajv({ allErrors: true, strict: false }) | 77 | const ajv = new Ajv({ allErrors: true, strict: false }) |
| 53 | const parseConfigSchema = { | 78 | const parseConfigSchema = { |
| 54 | type: 'object', | 79 | type: 'object', |
| ... | @@ -214,23 +239,45 @@ function writeFile(filePath, content) { | ... | @@ -214,23 +239,45 @@ function writeFile(filePath, content) { |
| 214 | 239 | ||
| 215 | /** | 240 | /** |
| 216 | * 获取所有待处理的文档 | 241 | * 获取所有待处理的文档 |
| 242 | + * | ||
| 243 | + * @description 扫描多个目录获取待处理文档,按优先级排序 | ||
| 244 | + * @returns {Array<{name: string, fullPath: string, ext: string, size: number, source: string}>} 文档列表 | ||
| 217 | */ | 245 | */ |
| 218 | function getDocsToParse() { | 246 | function getDocsToParse() { |
| 219 | - if (!fs.existsSync(DOCS_DIR)) { | 247 | + const docs = [] |
| 220 | - console.log('📂 文档夹不存在:', DOCS_DIR) | 248 | + const directories = [ |
| 221 | - return [] | 249 | + { path: DOCS_DIR, source: 'root' }, |
| 250 | + { path: DOCS_PREPROCESSED_DIR, source: 'preprocessed' }, | ||
| 251 | + { path: DOCS_RAW_DIR, source: 'raw' } | ||
| 252 | + ] | ||
| 253 | + | ||
| 254 | + for (const dir of directories) { | ||
| 255 | + if (!fs.existsSync(dir.path)) { | ||
| 256 | + continue | ||
| 222 | } | 257 | } |
| 223 | 258 | ||
| 224 | - const files = fs.readdirSync(DOCS_DIR) | 259 | + const files = fs.readdirSync(dir.path) |
| 225 | - return files | 260 | + const dirDocs = files |
| 226 | .filter(file => SUPPORTED_EXTENSIONS.includes(path.extname(file).toLowerCase())) | 261 | .filter(file => SUPPORTED_EXTENSIONS.includes(path.extname(file).toLowerCase())) |
| 227 | .filter(file => file !== 'README.md') | 262 | .filter(file => file !== 'README.md') |
| 228 | .map(file => ({ | 263 | .map(file => ({ |
| 229 | name: file, | 264 | name: file, |
| 230 | - fullPath: path.join(DOCS_DIR, file), | 265 | + fullPath: path.join(dir.path, file), |
| 231 | ext: path.extname(file).toLowerCase(), | 266 | ext: path.extname(file).toLowerCase(), |
| 232 | - size: fs.statSync(path.join(DOCS_DIR, file)).size | 267 | + size: fs.statSync(path.join(dir.path, file)).size, |
| 268 | + source: dir.source | ||
| 233 | })) | 269 | })) |
| 270 | + | ||
| 271 | + docs.push(...dirDocs) | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + // 优先处理预处理的 MD 文件,然后是原始文档 | ||
| 275 | + docs.sort((a, b) => { | ||
| 276 | + const priorityOrder = { preprocessed: 1, root: 2, raw: 3 } | ||
| 277 | + return priorityOrder[a.source] - priorityOrder[b.source] | ||
| 278 | + }) | ||
| 279 | + | ||
| 280 | + return docs | ||
| 234 | } | 281 | } |
| 235 | 282 | ||
| 236 | /** | 283 | /** |
| ... | @@ -367,10 +414,15 @@ function formatSize(size) { | ... | @@ -367,10 +414,15 @@ function formatSize(size) { |
| 367 | */ | 414 | */ |
| 368 | async function parseDocumentWithMarkitdown(docPath) { | 415 | async function parseDocumentWithMarkitdown(docPath) { |
| 369 | const ext = path.extname(docPath).toLowerCase() | 416 | const ext = path.extname(docPath).toLowerCase() |
| 417 | + const sourceInfo = detectDocumentSource(docPath) | ||
| 370 | 418 | ||
| 371 | // MD 和 TXT 文件直接读取,不需要 markitdown | 419 | // MD 和 TXT 文件直接读取,不需要 markitdown |
| 372 | if (ext === '.md' || ext === '.txt') { | 420 | if (ext === '.md' || ext === '.txt') { |
| 421 | + if (sourceInfo.source === 'preprocessed' || sourceInfo.source === 'likely-preprocessed') { | ||
| 422 | + console.log(`⚡ 预处理 MD 文件,跳过 markitdown: ${path.basename(docPath)}`) | ||
| 423 | + } else { | ||
| 373 | console.log(`📄 直接读取文本文件: ${path.basename(docPath)}`) | 424 | console.log(`📄 直接读取文本文件: ${path.basename(docPath)}`) |
| 425 | + } | ||
| 374 | return buildExtractResult(docPath, fs.readFileSync(docPath, 'utf-8'), []) | 426 | return buildExtractResult(docPath, fs.readFileSync(docPath, 'utf-8'), []) |
| 375 | } | 427 | } |
| 376 | 428 | ||
| ... | @@ -707,8 +759,17 @@ function inferCurrency(content) { | ... | @@ -707,8 +759,17 @@ function inferCurrency(content) { |
| 707 | */ | 759 | */ |
| 708 | async function parseSingleFile(filePath) { | 760 | async function parseSingleFile(filePath) { |
| 709 | const fileName = path.basename(filePath) | 761 | const fileName = path.basename(filePath) |
| 762 | + const sourceInfo = detectDocumentSource(filePath) | ||
| 763 | + const sourceLabel = { | ||
| 764 | + preprocessed: '⚡ 预处理文档', | ||
| 765 | + raw: '📄 原始文档', | ||
| 766 | + root: '📂 根目录文档', | ||
| 767 | + 'likely-preprocessed': '⚡ MD 文档', | ||
| 768 | + unknown: '📄 文档' | ||
| 769 | + }[sourceInfo.source] || '📄 文档' | ||
| 770 | + | ||
| 710 | console.log("\n" + "=".repeat(60)) | 771 | console.log("\n" + "=".repeat(60)) |
| 711 | - console.log("📄 处理文件: " + fileName) | 772 | + console.log(`📄 ${sourceLabel}: ${fileName}`) |
| 712 | console.log("=".repeat(60)) | 773 | console.log("=".repeat(60)) |
| 713 | 774 | ||
| 714 | // 解析文档(可能返回单个 config 或 configs 数组) | 775 | // 解析文档(可能返回单个 config 或 configs 数组) |
| ... | @@ -1799,15 +1860,33 @@ async function main() { | ... | @@ -1799,15 +1860,33 @@ async function main() { |
| 1799 | applyAuditFile(auditFileName, applyOptions) | 1860 | applyAuditFile(auditFileName, applyOptions) |
| 1800 | } else if (listMode) { | 1861 | } else if (listMode) { |
| 1801 | // 列出模式 | 1862 | // 列出模式 |
| 1802 | - const docs = getDocsToParse() | ||
| 1803 | console.log("\n📋 待处理文档列表:") | 1863 | console.log("\n📋 待处理文档列表:") |
| 1804 | if (docs.length === 0) { | 1864 | if (docs.length === 0) { |
| 1805 | console.log(' (无文档)') | 1865 | console.log(' (无文档)') |
| 1806 | } else { | 1866 | } else { |
| 1807 | - docs.forEach((doc, index) => { | 1867 | + // 按来源分组显示 |
| 1808 | - console.log(" " + (index + 1) + ". " + doc.name + " (" + formatSize(doc.size) + ")") | 1868 | + const grouped = { |
| 1869 | + preprocessed: docs.filter(d => d.source === 'preprocessed'), | ||
| 1870 | + root: docs.filter(d => d.source === 'root'), | ||
| 1871 | + raw: docs.filter(d => d.source === 'raw') | ||
| 1872 | + } | ||
| 1873 | + | ||
| 1874 | + for (const [source, sourceDocs] of Object.entries(grouped)) { | ||
| 1875 | + if (sourceDocs.length === 0) continue | ||
| 1876 | + | ||
| 1877 | + const sourceLabel = { | ||
| 1878 | + preprocessed: '⚡ 预处理 (preprocessed/)', | ||
| 1879 | + root: '📂 根目录 (docs/to-parse/)', | ||
| 1880 | + raw: '📄 原始文档 (raw/)' | ||
| 1881 | + }[source] | ||
| 1882 | + | ||
| 1883 | + console.log(`\n${sourceLabel}`) | ||
| 1884 | + sourceDocs.forEach((doc, index) => { | ||
| 1885 | + const sourceTag = doc.ext === '.md' ? ' [MD]' : '' | ||
| 1886 | + console.log(` ${index + 1}. ${doc.name}${sourceTag} (${formatSize(doc.size)})`) | ||
| 1809 | }) | 1887 | }) |
| 1810 | } | 1888 | } |
| 1889 | + } | ||
| 1811 | } else if (fileMode) { | 1890 | } else if (fileMode) { |
| 1812 | // 单文件模式 | 1891 | // 单文件模式 |
| 1813 | const fileName = fileMode.split('=')[1] | 1892 | const fileName = fileMode.split('=')[1] | ... | ... |
-
Please register or login to post a comment