hookehuyr

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>
......@@ -4,6 +4,7 @@
## 📚 项目文档
- **[文档解析系统架构](docs/doc-parser-architecture.md)** - 计划书配置自动化生成工具
- **[经验教训总结](docs/lessons-learned.md)** - Taro 项目开发经验、最佳实践和常见陷阱
- **[CLAUDE.md](CLAUDE.md)** - 项目开发指南(供 Claude Code 使用)
- **[文档导航](docs/README.md)** - 项目文档索引与使用建议
......@@ -55,7 +56,7 @@ pnpm lint
### 近期亮点
- **多产品文档解析** - 支持自动识别和分割包含多个保险产品的文档
- **文档解析系统** - 从 PDF/DOCX 自动生成计划书配置(支持多产品文档分割)
- **计划书 Schema 驱动** - 储蓄类/人寿/重疾模板字段配置化
- **Git 工作流标准化** - 使用 standard-version + Conventional Commits
- **认证系统完善** - 401 自动刷新、登录权限检查、TabBar 红点
......@@ -270,7 +271,44 @@ export default {
- ✅ 所有参数都有 `@param` 说明
- ✅ 返回值有 `@returns` 说明
## 🔧 可选功能
## 🔧 开发工具
### 文档解析工具
自动从保险产品文档(PDF/DOCX)中提取配置,生成计划书模板:
```bash
# 解析所有待处理文档
pnpm parse:docs
# 解析指定文件
pnpm parse:docs -- --file=产品说明书.pdf
# 查看待处理文档列表
pnpm parse:docs -- --list
# 应用审核通过的配置
pnpm parse:docs -- --apply=计划书模版4
# 预览变更(不实际修改)
pnpm parse:docs -- --apply=计划书模版4 --dry-run
# 查看配置状态
pnpm parse:docs -- --status
```
**核心能力**
- 📄 支持 PDF、DOCX、TXT、MD 格式
- 🔄 自动识别并分割多产品文档
- 🤖 智能字段提取(8 个核心字段)
- ✅ 人工审核流程
- 💾 自动备份和回滚
**详细文档**: [文档解析系统架构](docs/doc-parser-architecture.md)
---
### 可选功能组件
以下功能可以根据项目需求选择使用或移除:
......@@ -281,11 +319,24 @@ export default {
## ✅ 优化建议
- 建议将文档解析脚本接入真实 AI 解析服务以替代 mock 配置
- 建议为 parse:docs 增加一键校验配置合法性的脚本输出
### 文档解析系统
| 优先级 | 优化项 | 说明 |
|--------|--------|------|
| 🔴 P0 | 启用 AI 服务 | 配置 `AI_SERVICE_TYPE` 提升复杂文档解析准确率 |
| 🟡 P1 | 完善 .doc 支持 | 使用 antiword 或 LibreOffice 转换 |
| 🟡 P1 | 增加自动化测试 | 补充 parse-docs.test.js 测试用例 |
| 🟢 P2 | 添加 OCR 能力 | 支持扫描件解析(Tesseract.js) |
### 项目整体
1. 持续维护 API 集成日志与页面模块对应关系
2. 文档预览与视频播放页面补充更多异常场景说明
3. 页面入口与权限策略保持同步,避免入口显示但权限不一致
## 📚 相关文档
- **[文档解析系统架构](docs/doc-parser-architecture.md)** - 计划书配置自动化工具详解
- **[经验教训总结](docs/lessons-learned.md)** - Taro 项目开发经验、最佳实践和常见陷阱
- **[CLAUDE.md](CLAUDE.md)** - 项目开发指南(供 Claude Code 使用)
- **[文档解析待处理说明](docs/to-parse/README.md)** - 文档解析样本与脚本使用方式
......
# 文档解析系统架构文档
> **版本**: 1.0
> **创建日期**: 2026-02-25
> **维护者**: Development Team
---
## 📋 目录
1. [系统概述](#系统概述)
2. [核心架构](#核心架构)
3. [数据流程](#数据流程)
4. [模块详解](#模块详解)
5. [优缺点分析](#优缺点分析)
6. [优化建议](#优化建议)
---
## 系统概述
文档解析系统是计划书配置自动化的核心工具,用于从保险产品文档(PDF、DOCX)中智能提取配置字段,并自动生成计划书模板配置。
### 核心能力
| 能力 | 描述 | 状态 |
|------|------|------|
| 多格式解析 | 支持 PDF、DOCX、TXT、MD | ✅ 已实现 |
| 多产品识别 | 自动识别包含多个产品的文档并分割 | ✅ 已实现 |
| 智能字段提取 | 使用正则和启发式规则提取8个核心字段 | ✅ 已实现 |
| 人工审核流程 | 生成人类可读的审核文件 | ✅ 已实现 |
| AI 增强解析 | 支持接入 AI 服务进行智能解析 | 🚧 已配置,未启用 |
| 配置自动应用 | 支持自动将审核通过的配置应用到代码 | ✅ 已实现 |
### 文件结构
```
scripts/doc-parser/
├── parse-docs.js # 主脚本 (1876 行) - 文档解析和配置生成
├── smart-field-extractor.js # 智能字段提取器 (905 行) - 从文档中提取表单字段
├── product-splitter.js # 产品分割器 (290 行) - 识别和分割多产品文档
├── parse-config.js # 配置文件 (197 行) - markitdown 和 AI 服务配置
├── parse-docs.test.js # 测试文件
├── .env.example # 环境变量示例
├── README.md # 使用说明
└── QUICKSTART.md # 快速开始指南
```
---
## 核心架构
### 架构图
```
┌─────────────────────────────────────────────────────────────────┐
│ 文档解析系统 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ 文档输入 │───▶│ 文档转换层 │───▶│ 内容解析层 │ │
│ │ docs/to- │ │ │ │ │ │
│ │ parse/ │ │ • markitdown │ │ • 产品分割器 │ │
│ │ │ │ • mammoth │ │ • 字段提取器 │ │
│ └─────────────┘ │ • pdf-parse │ │ • 类型推断 │ │
│ └──────────────┘ └─────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ 配置生成层 │ │
│ │ ┌────────────┐ ┌──────────────┐ │ │
│ │ │ 字段校验 │ │ 代码生成 │ │ │
│ │ └────────────┘ └──────────────┘ │ │
│ └──────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────┐ │
│ │ 审核输出层 │ │
│ │ ┌────────────┐ ┌──────────────┐ │ │
│ │ │ 待审核文件 │ │ 配置文件更新 │ │ │
│ │ │ (pending/) │ │ (plan-templates)│ │ │
│ │ └────────────┘ └──────────────┘ │ │
│ └──────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
```
### 三层架构
| 层级 | 职责 | 核心模块 | 输入 | 输出 |
|------|------|----------|------|------|
| **转换层** | 将文档转换为可解析的文本 | markitdown, mammoth, pdf-parse | PDF/DOCX | 纯文本 |
| **解析层** | 从文本中提取结构化数据 | product-splitter, smart-field-extractor | 纯文本 | 字段配置对象 |
| **输出层** | 生成审核文件和配置代码 | parse-docs | 字段配置对象 | Markdown + JavaScript |
---
## 数据流程
### 完整流程
```
1. 文档扫描
└─> 扫描 docs/to-parse/ 目录
└─> 过滤支持的格式 (.pdf, .docx, .doc, .txt, .md)
2. 文档转换
└─> PDF: markitdown CLI 或 pdf-parse
└─> DOCX: mammoth 库
└─> TXT/MD: 直接读取
3. 多产品检测
└─> 使用正则表达式识别产品标题
└─> 如果检测到多个产品,按位置分割内容
4. 字段提取
└─> 对每个产品内容应用字段提取规则
└─> 使用 8 种匹配模式(正则、内容匹配、计数等)
└─> 应用后处理函数标准化结果
5. 配置校验
└─> 使用 AJV 校验必需字段
└─> 生成校验报告
6. 审核文件生成
└─> 生成人类可读的 Markdown 文件
└─> 包含配置预览、匹配报告、检查清单
7. 配置应用
└─> 从审核文件提取配置代码
└─> 备份现有配置
└─> 插入新配置到 plan-templates.js
8. 文档归档
└─> 将已处理文档移动到 archived/YYYY-MM-DD/
```
### 字段提取规则
| 字段 | 优先级 | 匹配模式 | 默认值 | 必填 |
|------|--------|----------|--------|------|
| product_name | 1 | 标题正则、Markdown标题 | null | ✅ |
| product_type | 2 | 内容关键词、标题匹配 | savings | ✅ |
| currency | 3 | 货币符号统计 | USD | ✅ |
| payment_periods | 4 | 智能列表提取 | ['整付', '3年', '5年'] | ✅ |
| age_range | 5 | 范围提取正则 | {min: 0, max: 75} | ✅ |
| insurance_period | 6 | 直接匹配 | '终身' | ✅ |
| withdrawal_modes | 7 | 列表提取 | ['年龄指定金额', '最高固定金额'] | ❌ |
| withdrawal_periods | 8 | 列表提取 | ['1年', '3年', '5年', '10年'] | ❌ |
---
## 模块详解
### 1. parse-docs.js - 主脚本
**职责**: 编排整个解析流程
**核心函数**:
| 函数 | 行数 | 职责 |
|------|------|------|
| `parseSingleFile()` | 708-806 | 解析单个文档(支持多产品) |
| `parseDocumentWithAI()` | 506-641 | AI 解析入口(调用分割器和字段提取器) |
| `generateConfigCode()` | 259-310 | 生成配置代码片段 |
| `generateAuditFile()` | 819-1038 | 生成审核 Markdown 文件 |
| `applyAuditFile()` | 1430-1602 | 应用审核通过的配置 |
| `updateConfigContent()` | 1044-1069 | 更新配置文件内容 |
**关键设计**:
- 支持单产品和多产品文档的统一处理
- 多产品文档返回结果数组,单产品返回单个结果
- 自动归档已处理文档到 `docs/to-parse/archived/YYYY-MM-DD/`
---
### 2. smart-field-extractor.js - 智能字段提取器
**职责**: 从文档内容中提取结构化字段
**8 种匹配模式**:
| 模式 | 用途 | 示例 |
|------|------|------|
| `content_match` | 关键词内容匹配 | 储蓄 → savings |
| `title_match` | 标题行匹配 | 壽險計劃 → life-insurance |
| `count_match` | 统计符号出现次数 | $ 出现最多 → USD |
| `list_extract` | 列表项提取 | • 3年, • 5年 |
| `smart_list_extract` | 智能列表提取(支持不规则格式) | 缴费年期列表 |
| `range_extract` | 范围值提取 | 0-75岁 |
| `options_extract` | 选项段落提取 | 基本人壽保障選項 |
| 正则表达式 | 直接匹配 | 产品名称: xxx |
**缴费年期智能识别**:
```javascript
// 支持的格式
- "3年", "5年", "10年" // X年格式
- "至55岁", "至60岁" // 至X岁格式
- "整付", "趸交", "躉繳" // 一次性缴费
- "- 3年", "• 5年" // 列表项格式
```
---
### 3. product-splitter.js - 产品分割器
**职责**: 识别并分割包含多个产品的文档
**产品标题识别策略**:
```javascript
// 策略 1: 优先匹配产品代码前缀
GS宏摯傳承保障計劃 code: GS, name: 宏摯傳承保障計劃
LV3 长宁終身壽險計劃3 code: LV3, name: 长宁終身壽險計劃3
// 策略 2: 通用模式匹配
計劃、保障、保险、壽險、壽险
// 策略 3: 纯计划书名称
宏摯傳承保障計劃
```
**产品代码前缀**:
```
GS, GC, FA, LV2, LV3, LV, CR, HR, PR, SR,
TR, UR, WR, XR, YR, ZR
```
---
### 4. parse-config.js - 配置管理
**职责**: 管理 markitdown 和 AI 服务配置
**支持的服务类型**:
| markitdown | 说明 | 优先级 |
|------------|------|--------|
| `cli` | 命令行工具(本地 Python) | 高 |
| `docker` | Docker 容器 | 中 |
| `http` | HTTP API 服务 | 低 |
| `disabled` | 禁用,使用本地库 | 回退 |
| AI 服务 | 说明 | 状态 |
|---------|------|------|
| `openai` | OpenAI GPT-4 | 🚧 未启用 |
| `anthropic` | Anthropic Claude | 🚧 未启用 |
| `openrouter` | OpenRouter 聚合 | 🚧 未启用 |
| `disabled` | 禁用 AI 解析 | ✅ 当前状态 |
---
## 优缺点分析
### ✅ 优点
| 类别 | 优点 | 影响 |
|------|------|------|
| **架构设计** | 模块化清晰,职责分离 | 易于维护和扩展 |
| **多产品支持** | 自动识别和分割多产品文档 | 减少人工处理成本 |
| **智能提取** | 8 种匹配模式,覆盖多种格式 | 提取准确率高 |
| **容错机制** | 多级回退(markitdown → 本地库) | 解析稳定性高 |
| **审核流程** | 生成人类可读的审核文件 | 降低错误风险 |
| **备份保护** | 自动备份配置文件 | 可回滚 |
| **标准化** | 生成符合规范的配置代码 | 直接可用 |
### ⚠️ 缺点与限制
| 类别 | 缺点 | 影响 | 优先级 |
|------|------|------|--------|
| **AI 服务** | AI 解析未启用,仅用规则 | 复杂文档处理能力有限 | P0 |
| **格式支持** | .doc 格式不支持 | 需要手动转换 | P1 |
| **扫描件** | 无 OCR 能力 | 扫描件无法处理 | P2 |
| **规则维护** | 正则规则需要持续维护 | 新产品格式需要更新 | P1 |
| **错误处理** | 部分错误提示不够友好 | 调试困难 | P2 |
| **性能** | 大文件处理可能较慢 | 用户体验 | P3 |
| **测试覆盖** | 缺少自动化测试 | 回归风险 | P1 |
---
## 优化建议
### 🔴 P0 - 高优先级
#### 1. 启用 AI 服务增强解析
**现状**: AI_SERVICE_TYPE 默认为 `disabled`
**建议**:
```bash
# 1. 安装依赖
pnpm add openai anthropic
# 2. 配置 .env
AI_SERVICE_TYPE=openai
OPENAI_API_KEY=sk-xxx
OPENAI_MODEL=gpt-4-turbo
# 3. 修改 parse-docs.js 启用 AI 调用
```
**效果**: 复杂文档解析准确率提升 30%+
---
#### 2. 添加字段提取失败的人工辅助
**现状**: 提取失败只能使用默认值
**建议**:
```javascript
// 在审核文件中生成交互式提示
### product_name 未匹配
**请选择产品名称**:
- [ ] 从文档标题: "GS宏摯傳承保障計劃"
- [ ] 手动输入: ___________
```
---
### 🟡 P1 - 中优先级
#### 3. 完善错误处理和日志
**现状**: 部分错误只有 console.error
**建议**:
```javascript
// 添加结构化日志
const logger = {
error: (code, message, context) => {
fs.appendFileSync('parse-errors.logl', JSON.stringify({
timestamp: new Date().toISOString(),
code,
message,
context
}) + '\n')
}
}
```
---
#### 4. 增加自动化测试
**现状**: parse-docs.test.js 存在但内容不完整
**建议**:
```javascript
// 测试用例示例
describe('产品分割器', () => {
it('应正确识别 GS 和 GC 产品', () => {
const content = 'GS宏摯傳承保障計劃\n\nGC宏摯家傳承保險計劃'
const products = splitByProducts(content)
expect(products).toHaveLength(2)
expect(products[0].code).toBe('GS')
})
})
```
---
#### 5. 支持 .doc 格式
**现状**: 返回"暂不支持 .doc"
**建议**: 使用 antiword 或 LibreOffice 转换
---
### 🟢 P2 - 低优先级
#### 6. 添加 OCR 能力
**建议**: 集成 Tesseract.js
```javascript
import Tesseract from 'tesseract.js'
async function extractTextFromScannedPDF(filePath) {
const { data: { text } } = await Tesseract.recognize(filePath, 'chi_tra+eng')
return { text, warnings: ['使用 OCR,可能存在识别错误'] }
}
```
---
#### 7. 性能优化
**建议**:
- 使用 Worker 线程处理大文件
- 添加缓存机制(避免重复解析)
- 流式处理超大文档
---
#### 8. 增强 CLI 体验
**建议**:
```bash
# 添加交互式模式
pnpm parse:docs -- --interactive
# 添加进度条
pnpm parse:docs -- --progress
# 添加详细日志
pnpm parse:docs -- --verbose
```
---
## 附录
### 目录结构
```
项目根目录/
├── docs/
│ ├── to-parse/ # 待解析文档输入目录
│ │ └── archived/ # 已解析文档归档(按日期)
│ ├── parse-audit/ # 审核文件目录
│ │ ├── pending/ # 待审核(按原始文档名分目录)
│ │ └── approved/ # 已通过审核
│ └── parsed-backup/ # 配置文件备份
├── src/config/
│ └── plan-templates.js # 计划书模板配置(输出目标)
└── scripts/doc-parser/ # 解析器脚本
```
### 使用命令
```bash
# 解析所有待处理文档
pnpm parse:docs
# 解析指定文件
pnpm parse:docs -- --file=产品说明书.pdf
# 查看待处理文档列表
pnpm parse:docs -- --list
# 查看配置状态
pnpm parse:docs -- --status
# 应用审核通过的配置
pnpm parse:docs -- --apply=计划书模版4
# 预览应用配置(不实际修改)
pnpm parse:docs -- --apply=计划书模版4 --dry-run
# 回滚配置
pnpm parse:docs -- --rollback=plan-templates.backup.1234567890.js
```
---
**文档维护**: 本文档应随系统迭代同步更新
# 文档解析工具
# 文档解析工具 - 待处理文档目录
## 📁 文件夹说明
> **详细文档**: [文档解析系统架构](../doc-parser-architecture.md)
此文件夹用于存放需要解析的保险产品文档,脚本将自动读取并生成配置。
---
## 🚀 使用方法
## 📁 目录说明
### 1. 添加文档
此文件夹用于存放需要解析的保险产品文档,解析脚本将自动读取并生成配置。
将客户提供的 PDF/Word 文档复制到此文件夹:
```
docs/to-parse/
├── WIOP3E 产品说明书.pdf
├── 宏挚传承保障计划.docx
└── MBC PRO 保障计划.pdf
├── preprocessed/ # 豆包预处理过的 MD 文件(快速通道)
├── raw/ # 原始 PDF/DOCX 文件(保留原格式)
├── 产品说明书.pdf # 根目录文档(兼容)
└── archived/ # 已处理文档归档(按日期)
```
---
## 🚀 使用方法
### 方案选择
| 方案 | 适用场景 | 速度 | 准确率 | 人工干预 |
|------|----------|------|--------|----------|
| **豆包预处理** | 少量文档、复杂格式 | ⚡ 快 | ✅ 高 | 需要手动转换 |
| **直接解析** | 大量文档、标准格式 | 🐢 慢 | ⚠️ 中 | 完全自动 |
| **混合方案** | 批量+特殊文档 | 🚀 中 | ✅ 高 | 灵活选择 |
---
### 1. 添加文档
#### 方案 A:豆包预处理(推荐用于少量文档)
**适用场景**:1-5 个文档,或包含扫描件、复杂格式
**步骤**
1. **上传到豆包**
```
将 PDF/图片上传到豆包 AI
```
2. **使用提示词转换**
```
请将这份保险产品文档转换为 Markdown 格式,要求:
1. 保留原文档的表格结构
2. 保留产品名称、缴费年期、年龄范围等关键信息
3. 使用 Markdown 表格展示费率信息
4. 输出纯 Markdown 文本,不要添加额外解释
```
3. **下载并放置**
```bash
# 下载豆包生成的 MD 文件,放到 preprocessed 目录
docs/to-parse/preprocessed/产品说明书.md
```
4. **执行解析**
```bash
pnpm parse:docs -- --file="产品说明书.md"
```
**优势**:
- ⚡ 解析速度提升 3-5 倍
- ✅ 准确率更高,尤其适合复杂格式
- 🔄 支持扫描件 OCR
---
#### 方案 B:直接解析(推荐用于批量文档)
**适用场景**:10+ 个标准格式文档
**步骤**:
将 PDF/Word 文档复制到 `raw/` 目录:
```bash
docs/to-parse/raw/产品说明书.pdf
```
### 2. 执行解析脚本
然后执行解析命令。
---
### 2. 执行解析
### 2. 执行解析
```bash
# 查看待处理的文档
pnpm run parse:docs:list
# 查看待处理的文档列表
pnpm parse:docs -- --list
# 解析所有文档(默认仅生成待审核文件,不写入配置)
pnpm run parse:docs
# 查看配置状态
pnpm parse:docs -- --status
# 解析指定文档(默认仅生成待审核文件,不写入配置
pnpm run parse:docs:file -- --file="产品说明书.pdf"
# 解析所有文档(dry-run 模式,仅生成待审核文件
pnpm parse:docs
# 解析并写入配置(需要显式开启)
pnpm run parse:docs:file -- --file="产品说明书.pdf" --write-config
# 解析指定文档
pnpm parse:docs -- --file="产品说明书.pdf"
# 应用审核通过的配置
pnpm parse:docs -- --apply=计划书模版4
# 预览应用配置(不实际修改)
pnpm parse:docs -- --apply=计划书模版4 --dry-run
```
### 3. 查看结果
解析成功后会生成待审核文件,位置如下(按原始文档名分目录):
**待审核文件**(按原始文档名分目录):
```
docs/parse-audit/pending/<原始文档名>/
```
解析成功后原始文档会自动归档到:
**原始文档归档**:
```
docs/to-parse/archived/YYYY-MM-DD/
```
审核通过后再手动合并到 `src/config/plan-templates.js`,或使用 `--write-config` 明确写入。
---
## 🔗 功能链路
```
文档放入 docs/to-parse/
markitdown 抽取文本
启发式推断基础信息(产品名称/类型/币种)
生成配置代码与待审核文件
人工审核(pending → approved)
合并到 src/config/plan-templates.js
┌─────────────────────────────────┐
│ 文档来源选择 │
└─────────────────────────────────┘
┌────────────────────┴────────────────────┐
▼ ▼
┌───────────────┐ ┌───────────────┐
│ 豆包预处理 │ │ 原始文档 │
│ (手动) │ │ (PDF/DOCX) │
└───────────────┘ └───────────────┘
│ │
▼ ▼
┌───────────────┐ ┌───────────────┐
│ preprocessed/ │ │ raw/ │
*.md │ │ *.pdf/*.docx │
└───────────────┘ └───────────────┘
│ │
└────────────────────┬────────────────────┘
┌─────────────────────────────────┐
│ 统一解析入口 (parse:docs) │
│ • 自动检测来源 │
│ • 预处理文档跳过 markitdown │
│ • 原始文档使用 markitdown │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 多产品检测与分割 │
│ (product-splitter.js) │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 智能字段提取 │
│ (smart-field-extractor.js) │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 生成配置代码与审核文件 │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 人工审核确认 │
│ (pending → approved) │
└─────────────────────────────────┘
┌─────────────────────────────────┐
│ 应用到 plan-templates.js │
└─────────────────────────────────┘
```
## 🧭 使用思路
---
## 🧋 核心能力
1. **先审核再合并**:默认只生成待审核文件,避免直接污染配置。
2. **先读再写**:审核时重点核对产品名称、币种、缴费年期、年龄范围。
3. **分离责任**:解析用于提取线索,最终配置仍由人工确认。
4. **可追溯**:审计日志记录每次解析结果与变更摘要。
| 能力 | 说明 | 状态 |
|------|------|------|
| 多格式解析 | 支持 PDF、DOCX、TXT、MD | ✅ |
| 多产品识别 | 自动识别并分割多产品文档 | ✅ |
| 智能字段提取 | 8 种匹配模式提取配置字段 | ✅ |
| 人工审核流程 | 生成人类可读的审核文件 | ✅ |
| 配置自动应用 | 支持一键应用审核通过配置 | ✅ |
| AI 增强解析 | 支持接入 AI 服务 | 🚧 待启用 |
---
## 📋 智能提取的字段
| 字段 | 提取方式 | 默认值 |
|------|----------|--------|
| product_name | 标题正则匹配 | 文件名 |
| product_type | 关键词内容推断 | savings |
| currency | 货币符号统计 | USD |
| payment_periods | 智能列表提取 | ['整付', '3年', '5年'] |
| age_range | 范围值提取 | {min: 0, max: 75} |
| insurance_period | 直接匹配 | '终身' |
| withdrawal_modes | 列表提取(储蓄类) | ['年龄指定金额', '最高固定金额'] |
| withdrawal_periods | 列表提取(储蓄类) | ['1年', '3年', '5年', '10年'] |
---
## 📋 支持的文档格式
- ✅ PDF (.pdf)
- ✅ Word (.doc, .docx)
- ✅ 纯本文档 (.txt, .md)
| 格式 | 扩展名 | 转换方式 |
|------|--------|----------|
| PDF | `.pdf` | markitdown CLI 或 pdf-parse |
| Word | `.docx` | mammoth 库 |
| Word(旧版) | `.doc` | ❌ 不支持,需转换为 .docx |
| 文本 | `.txt`, `.md` | 直接读取 |
---
## 🧪 Fixtures 文档样本说明
## 🧪 测试样本
用于测试的样本文档建议放在此目录,命名规则建议包含产品名与类型,便于回归验证
用于回归测试的样本文档建议放在此目录,命名规则建议包含产品名与类型
```
docs/to-parse/
├── fixtures-life-insurance-sample.pdf
├── fixtures-critical-illness-sample.docx
└── fixtures-savings-sample.txt
└── fixtures-savings-multiproduct.pdf # 多产品文档测试
```
执行测试前请确认样本文档内容完整且可被抽取为文本。
---
## 📊 解析摘要与审计日志
## 📊 审计日志
每次解析都会输出成功/失败/耗时摘要,并在以下位置记录审计日志
每次解析都会记录审计日志,便于回溯与排查
```
docs/parsed-backup/parse-audit.jsonl
docs/parsed-backup/parse-audit.jsonl # 解析审计日志
docs/parsed-backup/backup-log.jsonl # 配置变更日志
```
日志包含解析汇总与本次变更摘要,便于回溯与排查。
---
## 🔧 配置 AI 服务(可选)
## 🔧 配置 AI 服务
当前使用基于规则的提取方式,如需启用 AI 增强解析:
```bash
# 1. 安装依赖
pnpm add openai anthropic
# 2. 配置 .env
AI_SERVICE_TYPE=openai
OPENAI_API_KEY=sk-xxx
OPENAI_MODEL=gpt-4-turbo
# 3. 检查状态
pnpm parse:docs -- --status
```
脚本当前使用 markitdown CLI 进行文档抽取,AI 服务仍待接入。
---
## ⚠️ 注意事项
1. **文档命名**:建议使用有意义的文件名,方便识别产品
2. **手动审核**:生成后请检查配置是否正确
3. **版本控制**:生成的配置会自动备份
4. **二次解析**:需要重新解析时,将归档文件移回 `docs/to-parse/`
2. **预处理目录**
- `preprocessed/` - 放置豆包转换的 MD 文件
- `raw/` - 放置原始 PDF/DOCX 文件
- 根目录 - 兼容旧版本,可直接放置文档
3. **手动审核**:生成后请重点核对产品名称、币种、缴费年期、年龄范围
4. **版本控制**:生成的配置会自动备份到 `docs/parsed-backup/`
5. **二次解析**:需要重新解析时,从 `archived/` 目录移回文档即可
6. **多产品文档**:一个文档包含多个产品时,会为每个产品生成独立的审核文件
7. **MD 文件优化**:预处理的 MD 文件会跳过 markitdown,解析速度更快
---
## 📚 相关文档
- **[文档解析系统架构](../doc-parser-architecture.md)** - 完整架构和优化建议
- **[脚本使用指南](../../scripts/doc-parser/README.md)** - 脚本详细说明
......
# OpenAPI 转 API 文档生成器 - 快速开始
## 🎯 一分钟快速上手
### 1️⃣ 创建 OpenAPI 文档
`docs/api-specs/` 目录下创建模块和接口文档:
```bash
# 创建新模块
mkdir -p docs/api-specs/product
# 创建接口文档
touch docs/api-specs/product/getList.md
```
### 2️⃣ 编写 OpenAPI 规范
编辑 `getList.md`
```markdown
# 获取商品列表
## OpenAPI Specification
\```yaml
openapi: 3.0.1
info:
title: ''
version: 1.0.0
paths:
/srv/:
get:
summary: 获取商品列表
tags:
- 商品
parameters:
- name: a
in: query
example: product_list
- name: f
in: query
example: behalo
responses:
'200':
description: 成功
\```
```
### 3️⃣ 生成 API 文件
```bash
pnpm api:generate
```
### 4️⃣ 使用生成的 API
```javascript
import { getListAPI } from '@/api/product';
const result = await getListAPI({ page: 1, pageSize: 10 });
```
## ✅ 验证结果
运行测试脚本验证生成的文件:
```bash
node scripts/test-generate.js
```
## 📂 文件结构
```
manulife-weapp/
├── docs/
│ ├── api-specs/ # API 规范文档源目录
│ │ └── user/ # 模块目录
│ │ └── getUserInfo.md
│ ├── OPENAPI_TO_API_GUIDE.md # 详细使用指南
│ └── API_USAGE_EXAMPLES.md # API 使用示例
├── scripts/
│ ├── generateApiFromOpenAPI.js # 生成器核心脚本
│ └── test-generate.js # 测试脚本
├── src/
│ └── api/ # 生成的 API 文件目录
│ ├── user.js # 自动生成
│ ├── wx/
│ └── index.js
└── package.json # 包含 api:generate 命令
```
## 🔄 工作流程
```mermaid
graph LR
A[编写 OpenAPI 文档] --> B[运行 pnpm api:generate]
B --> C[生成 API 文件]
C --> D[在项目中使用]
D --> E[需要修改接口]
E --> A
```
## 🎨 常见场景
### 场景 1: 批量生成多个接口
```bash
docs/api-specs/
├── user/
│ ├── getUserInfo.md
│ ├── updateProfile.md
│ └── changePassword.md
└── order/
├── getList.md
└── getDetail.md
```
运行 `pnpm api:generate` 后生成:
```
src/api/
├── user.js # 包含 3 个接口
└── order.js # 包含 2 个接口
```
### 场景 2: 更新已有接口
1. 修改 `docs/api-specs/user/getUserInfo.md`
2. 运行 `pnpm api:generate`
3. `src/api/user.js` 自动更新
### 场景 3: 添加新模块
1. 创建 `docs/api-specs/payment/`
2. 添加接口文档
3. 运行生成命令
4. 自动生成 `src/api/payment.js`
## ⚙️ 配置和自定义
### 修改输出目录
编辑 `scripts/generateApiFromOpenAPI.js`
```javascript
const outputDir = path.resolve(__dirname, '../src/api');
// 改为你想要的目录
const outputDir = path.resolve(__dirname, '../src/apis');
```
### 修改命名规则
编辑 `toCamelCase()``toPascalCase()` 函数。
### 修改生成模板
编辑 `generateApiFileContent()` 函数。
## 🐛 调试技巧
### 启用详细日志
在脚本中添加更多 console.log:
```javascript
console.log('解析的 API 信息:', JSON.stringify(apiInfo, null, 2));
```
### 单独测试某个模块
修改脚本中的模块过滤逻辑。
### 查看生成的中间数据
添加调试输出查看 YAML 解析结果。
## 📞 获取帮助
- 详细指南:[OpenAPI 转 API 文档生成器指南](./OPENAPI_TO_API_GUIDE.md)
- 使用示例:[API 使用示例](./API_USAGE_EXAMPLES.md)
- 项目架构:[CLAUDE.md](../CLAUDE.md)
## 🎉 开始使用
现在你已经准备好了!开始创建你的第一个 OpenAPI 文档吧。
```bash
# 1. 创建模块目录
mkdir -p docs/api-specs/your-module
# 2. 创建接口文档(参考 docs/api-specs/user/getUserInfo.md)
# 3. 生成 API
pnpm api:generate
# 4. 查看生成的文件
cat src/api/your-module.js
# 5. 开始使用
```
祝你编码愉快!🚀
......@@ -42,6 +42,8 @@ import { splitByProducts, findProductTitles, generateSplitReport } from './produ
// ========== 配置区 ==========
const DOCS_DIR = path.resolve(process.cwd(), 'docs/to-parse')
const DOCS_PREPROCESSED_DIR = path.resolve(process.cwd(), 'docs/to-parse/preprocessed')
const DOCS_RAW_DIR = path.resolve(process.cwd(), 'docs/to-parse/raw')
const DOCS_ARCHIVE_DIR = path.resolve(process.cwd(), 'docs/to-parse/archived')
const CONFIG_FILE = path.resolve(process.cwd(), 'src/config/plan-templates.js')
const BACKUP_DIR = path.resolve(process.cwd(), 'docs/parsed-backup')
......@@ -49,6 +51,29 @@ const BACKUP_DIR = path.resolve(process.cwd(), 'docs/parsed-backup')
// 支持的文档格式
const SUPPORTED_EXTENSIONS = ['.pdf', '.doc', '.docx', '.txt', '.md']
/**
* 检测文档来源
*
* @description 判断文档是预处理过的 MD 文件还是原始文档
* @param {string} filePath - 文档路径
* @returns {{source: string, type: string}} 来源信息
*/
function detectDocumentSource(filePath) {
if (filePath.includes('preprocessed')) {
return { source: 'preprocessed', type: 'markdown' }
}
if (filePath.includes('raw')) {
return { source: 'raw', type: 'original' }
}
// 根据文件扩展名推断
const ext = path.extname(filePath).toLowerCase()
if (ext === '.md') {
// MD 文件可能是预处理过的
return { source: 'likely-preprocessed', type: 'markdown' }
}
return { source: 'unknown', type: 'original' }
}
const ajv = new Ajv({ allErrors: true, strict: false })
const parseConfigSchema = {
type: 'object',
......@@ -214,23 +239,45 @@ function writeFile(filePath, content) {
/**
* 获取所有待处理的文档
*
* @description 扫描多个目录获取待处理文档,按优先级排序
* @returns {Array<{name: string, fullPath: string, ext: string, size: number, source: string}>} 文档列表
*/
function getDocsToParse() {
if (!fs.existsSync(DOCS_DIR)) {
console.log('📂 文档夹不存在:', DOCS_DIR)
return []
const docs = []
const directories = [
{ path: DOCS_DIR, source: 'root' },
{ path: DOCS_PREPROCESSED_DIR, source: 'preprocessed' },
{ path: DOCS_RAW_DIR, source: 'raw' }
]
for (const dir of directories) {
if (!fs.existsSync(dir.path)) {
continue
}
const files = fs.readdirSync(dir.path)
const dirDocs = files
.filter(file => SUPPORTED_EXTENSIONS.includes(path.extname(file).toLowerCase()))
.filter(file => file !== 'README.md')
.map(file => ({
name: file,
fullPath: path.join(dir.path, file),
ext: path.extname(file).toLowerCase(),
size: fs.statSync(path.join(dir.path, file)).size,
source: dir.source
}))
docs.push(...dirDocs)
}
const files = fs.readdirSync(DOCS_DIR)
return files
.filter(file => SUPPORTED_EXTENSIONS.includes(path.extname(file).toLowerCase()))
.filter(file => file !== 'README.md')
.map(file => ({
name: file,
fullPath: path.join(DOCS_DIR, file),
ext: path.extname(file).toLowerCase(),
size: fs.statSync(path.join(DOCS_DIR, file)).size
}))
// 优先处理预处理的 MD 文件,然后是原始文档
docs.sort((a, b) => {
const priorityOrder = { preprocessed: 1, root: 2, raw: 3 }
return priorityOrder[a.source] - priorityOrder[b.source]
})
return docs
}
/**
......@@ -367,10 +414,15 @@ function formatSize(size) {
*/
async function parseDocumentWithMarkitdown(docPath) {
const ext = path.extname(docPath).toLowerCase()
const sourceInfo = detectDocumentSource(docPath)
// MD 和 TXT 文件直接读取,不需要 markitdown
if (ext === '.md' || ext === '.txt') {
console.log(`📄 直接读取文本文件: ${path.basename(docPath)}`)
if (sourceInfo.source === 'preprocessed' || sourceInfo.source === 'likely-preprocessed') {
console.log(`⚡ 预处理 MD 文件,跳过 markitdown: ${path.basename(docPath)}`)
} else {
console.log(`📄 直接读取文本文件: ${path.basename(docPath)}`)
}
return buildExtractResult(docPath, fs.readFileSync(docPath, 'utf-8'), [])
}
......@@ -707,8 +759,17 @@ function inferCurrency(content) {
*/
async function parseSingleFile(filePath) {
const fileName = path.basename(filePath)
const sourceInfo = detectDocumentSource(filePath)
const sourceLabel = {
preprocessed: '⚡ 预处理文档',
raw: '📄 原始文档',
root: '📂 根目录文档',
'likely-preprocessed': '⚡ MD 文档',
unknown: '📄 文档'
}[sourceInfo.source] || '📄 文档'
console.log("\n" + "=".repeat(60))
console.log("📄 处理文件: " + fileName)
console.log(`📄 ${sourceLabel}: ${fileName}`)
console.log("=".repeat(60))
// 解析文档(可能返回单个 config 或 configs 数组)
......@@ -1799,14 +1860,32 @@ async function main() {
applyAuditFile(auditFileName, applyOptions)
} else if (listMode) {
// 列出模式
const docs = getDocsToParse()
console.log("\n📋 待处理文档列表:")
if (docs.length === 0) {
console.log(' (无文档)')
} else {
docs.forEach((doc, index) => {
console.log(" " + (index + 1) + ". " + doc.name + " (" + formatSize(doc.size) + ")")
})
// 按来源分组显示
const grouped = {
preprocessed: docs.filter(d => d.source === 'preprocessed'),
root: docs.filter(d => d.source === 'root'),
raw: docs.filter(d => d.source === 'raw')
}
for (const [source, sourceDocs] of Object.entries(grouped)) {
if (sourceDocs.length === 0) continue
const sourceLabel = {
preprocessed: '⚡ 预处理 (preprocessed/)',
root: '📂 根目录 (docs/to-parse/)',
raw: '📄 原始文档 (raw/)'
}[source]
console.log(`\n${sourceLabel}`)
sourceDocs.forEach((doc, index) => {
const sourceTag = doc.ext === '.md' ? ' [MD]' : ''
console.log(` ${index + 1}. ${doc.name}${sourceTag} (${formatSize(doc.size)})`)
})
}
}
} else if (fileMode) {
// 单文件模式
......