# 1. 资料包导入与目录汇总详细设计

## 1. 设计目标

本步骤是注册申报资料审核工作流的入口，目标是把用户上传的注册申报资料包转化为可供后续法规完整性核查、字段抽取、一致性检查和风险预警使用的结构化文档底座。

本步骤需要完成以下业务结果：

1. 支持单文件、多文件、文件夹和压缩包导入。
2. 支持 `zip`、`rar`、`7z` 压缩包解包，并保留压缩包内原始相对路径。
3. 为每个文件建立注册资料记录，包含文件名、类型、大小、原始路径、所属批次和处理状态。
4. 统计文件页数，`PDF` 和 `DOCX` 必须精确统计，`DOC` 无法精确统计时标记为待人工复核。
5. 初步识别章节点、资料名称、资料类别和目录类文档。
6. 生成资料目录汇总结果，供页面展示和 Agent Core 后续任务使用。

本步骤不负责最终法规完整性判定，不负责字段抽取，不负责 RAG 入库。但它需要产出足够稳定的文档主数据，作为后续所有审核任务的事实输入。

## 2. 所属模块与边界

### 2.1 Django Documents

`apps.documents` 负责接收上传请求、保存原始文件、创建资料包批次、维护文档记录和展示目录汇总。

本步骤中 Django 侧建议提供：

1. 上传页和资料包导入入口。
2. 资料包批次模型。
3. 文档主数据模型。
4. 文档处理状态字段。
5. 目录汇总服务。
6. 目录汇总页面。

### 2.2 Agent Core

`agent_core` 负责沉淀可复用的资料包处理 Skill 和 Tool Registry 注册项。

本步骤中 Agent Core 建议提供：

1. `资料包导入Skill`
2. `压缩包解包Skill`
3. `资料包扫描Skill`
4. `文档页数统计Skill`
5. `章节点识别Skill`
6. `目录汇总Skill`

这些 Skill 不直接依赖 Django View。Django View 只负责收集参数并调用服务层，服务层再调用 Agent Core 的 Skill 或 Tool。

### 2.3 Audit

`apps.audit` 在本步骤中只记录资料包导入过程，不做审核结论留痕。

建议记录：

1. 导入入口。
2. 导入文件数量。
3. 解包结果。
4. 页数统计结果。
5. 失败文件数量。
6. 待人工复核文件数量。

## 3. 输入输出

### 3.1 输入

用户可以通过以下方式导入资料：

1. 单文件上传。
2. 多文件批量上传。
3. 文件夹上传，前端传递每个文件的相对路径。
4. 压缩包上传，后端解压后还原相对路径。

支持文件类型：

1. `pdf`
2. `docx`
3. `doc`
4. `txt`
5. `md`
6. `zip`
7. `rar`
8. `7z`

### 3.2 输出

本步骤输出 `registration_overview_report` 的第一版结构，核心字段如下：

```json
{
  "batch_id": "SUB-20260603-001",
  "workflow_type": "registration",
  "source_role": "submission",
  "file_count": 8,
  "supported_file_count": 7,
  "failed_file_count": 1,
  "total_page_count": 36,
  "page_count_status": "partial_review_required",
  "chapter_summary": [
    {
      "chapter_code": "CH1.2",
      "chapter_name": "监管信息目录",
      "file_count": 1,
      "page_count": 3
    }
  ],
  "documents": [],
  "warnings": []
}
```

每个 `documents` 元素至少包含：

```json
{
  "document_id": 1001,
  "original_filename": "CH1.4 申请表.docx",
  "relative_path": "第1章 监管信息/CH1.4 申请表.docx",
  "file_type": "docx",
  "file_size": 1048576,
  "chapter_code": "CH1.4",
  "chapter_name": "申请表",
  "document_role": "application_form",
  "page_count": 5,
  "page_count_method": "docx_exact",
  "page_count_confidence": "exact",
  "processing_status": "summarized",
  "review_status": "normal",
  "needs_manual_review": false
}
```

## 4. 主工作流

```text
用户选择导入资料
-> 创建资料包批次
-> 校验上传文件
-> 判断是否压缩包
-> 解包或登记原始文件
-> 扫描文件树
-> 过滤不支持文件
-> 创建文档主数据
-> 统计页数
-> 初步识别章节点
-> 识别目录类/声明类/沟通类文件
-> 汇总目录与页数
-> 写入导入审计
-> 返回目录汇总页面
```

## 5. 节点详细设计

### 5.1 节点一：创建资料包批次

业务功能：

1. 为一次导入创建独立批次。
2. 记录导入来源、导入人、任务类型和资料来源角色。
3. 建立隔离存储目录，避免不同项目或不同批次文件混在一起。

建议模型：

```python
class SubmissionBatch(models.Model):
    batch_no = models.CharField(max_length=64, unique=True)
    name = models.CharField(max_length=255)
    workflow_type = models.CharField(max_length=32, default="registration")
    source_role = models.CharField(max_length=32, default="submission")
    import_status = models.CharField(max_length=32, default="created")
    created_by = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
    created_at = models.DateTimeField(auto_now_add=True)
```

使用技术：

1. Django ORM
2. Django Storage
3. `uuid` 或业务编号生成器

产生方法：

1. `create_submission_batch(params) -> SubmissionBatch`
2. `build_batch_storage_path(batch) -> Path`

对应 Skill：

1. `资料包导入Skill`

### 5.2 节点二：上传文件校验

业务功能：

1. 校验文件扩展名是否支持。
2. 校验文件大小是否超过配置限制。
3. 校验文件名是否包含危险路径。
4. 识别普通文件和压缩包。

使用技术：

1. Django uploaded file API
2. `pathlib.PurePath`
3. `python-magic` 或 `mimetypes`

校验规则：

1. 禁止绝对路径。
2. 禁止 `..` 路径穿越。
3. 禁止空文件。
4. 允许中文文件名。
5. 对扩展名和 MIME 不一致的文件标记为待复核。

产生方法：

1. `validate_uploaded_file(uploaded_file) -> FileValidationResult`
2. `detect_upload_kind(uploaded_file) -> Literal["document", "archive"]`

对应 Skill：

1. `资料包导入Skill`

### 5.3 节点三：压缩包解包

业务功能：

1. 支持 `zip`、`rar`、`7z`。
2. 解包到批次隔离目录。
3. 保留压缩包内多层相对路径。
4. 记录每个解包文件来自哪个压缩包。
5. 对空包、损坏包、加密包、嵌套异常给出业务状态。

使用技术：

1. `zipfile`
2. `rarfile` 或纯 Python rar 解析依赖
3. `py7zr`
4. `pathlib`
5. 文件路径安全检查函数

关键状态：

1. `extracted`
2. `empty_archive`
3. `encrypted_archive`
4. `unsupported_archive`
5. `extract_failed`
6. `path_rejected`

产生方法：

1. `extract_archive(archive_path, target_dir) -> ArchiveExtractionResult`
2. `safe_extract_member(member_name, target_dir) -> Path`
3. `normalize_relative_path(raw_path) -> str`

对应 Skill：

1. `压缩包解包Skill`

### 5.4 节点四：扫描文件树

业务功能：

1. 遍历本次批次目录。
2. 过滤临时文件、系统隐藏文件和不支持文件。
3. 生成待处理文件清单。
4. 保留相对于资料包根目录的路径。

使用技术：

1. `pathlib.Path.rglob`
2. 文件哈希 `hashlib.sha256`
3. 后缀白名单

过滤规则：

1. 跳过 `__MACOSX`。
2. 跳过 `.DS_Store`。
3. 跳过 Office 临时文件，如 `~$申请表.docx`。
4. 对 `.exe` 等不支持文件记录为 `unsupported`，不进入后续页数统计。

产生方法：

1. `scan_submission_package(root_dir) -> PackageScanResult`
2. `build_file_fingerprint(file_path) -> str`
3. `is_supported_document(file_path) -> bool`

对应 Skill：

1. `资料包扫描Skill`

### 5.5 节点五：创建文档主数据

业务功能：

1. 为每个文件创建文档记录。
2. 写入文件名、相对路径、文件类型、大小、哈希、来源压缩包。
3. 标记初始处理状态。
4. 区分业务申报资料和法规依据资料。

建议模型：

```python
class RegistrationDocument(models.Model):
    batch = models.ForeignKey(SubmissionBatch, on_delete=models.CASCADE)
    original_filename = models.CharField(max_length=255)
    relative_path = models.CharField(max_length=1024)
    file_type = models.CharField(max_length=32)
    file_size = models.PositiveBigIntegerField(default=0)
    file_hash = models.CharField(max_length=128, blank=True)
    source_archive_name = models.CharField(max_length=255, blank=True)
    source_role = models.CharField(max_length=32, default="submission")
    workflow_type = models.CharField(max_length=32, default="registration")
    processing_status = models.CharField(max_length=32, default="created")
```

使用技术：

1. Django ORM
2. 批量创建 `bulk_create`
3. 唯一性约束：`batch + relative_path`

产生方法：

1. `create_document_records(batch, scanned_files) -> list[RegistrationDocument]`
2. `mark_unsupported_file(batch, scanned_file, reason) -> RegistrationDocument`

对应 Skill：

1. `资料包导入Skill`
2. `资料包扫描Skill`

### 5.6 节点六：页数统计

业务功能：

1. 为每份文档统计页数。
2. 写入页数统计方法和可信度。
3. 对无法精确统计的文档标记待人工复核。

使用技术：

1. PDF：`pypdf` 或 `PyMuPDF`
2. DOCX：优先读取 Word 兼容统计结果，必要时通过 LibreOffice headless 转 PDF 后精确统计
3. DOC：尝试转换或解析，失败时标记 `manual_review_required`
4. TXT/MD：页数不作为强制指标，可按 `not_applicable` 或导出预览后统计

页数状态：

1. `exact`
2. `not_applicable`
3. `manual_review_required`
4. `failed`

产生方法：

1. `count_document_pages(document) -> PageCountResult`
2. `count_pdf_pages(path) -> PageCountResult`
3. `count_docx_pages(path) -> PageCountResult`
4. `count_doc_pages(path) -> PageCountResult`

对应 Skill：

1. `文档页数统计Skill`

### 5.7 节点七：章节点与资料名称识别

业务功能：

1. 从文件名、相对路径和标题文本中识别章节点。
2. 识别资料名称。
3. 标记目录类、声明类、申请表、产品列表、历史沟通说明等文档角色。
4. 对无法确认的文件标记待人工确认。

使用技术：

1. 正则表达式
2. 本地章节点规则 YAML
3. `python-docx` 抽取首页标题
4. 简单关键词规则

识别优先级：

1. 相对路径中的章名，如 `第1章 监管信息`
2. 文件名中的章节点编码，如 `CH1.4`
3. 文件名中的标准资料名称，如 `申请表`
4. 文档首页标题
5. 用户手动选择的章节点

产生方法：

1. `classify_chapter_node(document) -> ChapterClassificationResult`
2. `extract_chapter_code_from_path(relative_path) -> str | None`
3. `detect_document_role(filename, title_text) -> str`

对应 Skill：

1. `章节点识别Skill`

### 5.8 节点八：目录汇总生成

业务功能：

1. 汇总当前批次所有文档。
2. 计算文件数量、支持文件数量、失败数量、总页数。
3. 按章节点聚合文件和页数。
4. 输出待人工复核清单。
5. 生成页面展示和 Agent Core 输入使用的统一结构。

使用技术：

1. Django ORM 聚合查询
2. Python dataclass 或 Pydantic schema
3. JSONField 存储结构化结果

产生方法：

1. `build_directory_summary(batch_id) -> RegistrationOverviewReport`
2. `summarize_chapters(documents) -> list[ChapterSummary]`
3. `collect_import_warnings(documents) -> list[ImportWarning]`

对应 Skill：

1. `目录汇总Skill`

### 5.9 节点九：审计留痕

业务功能：

1. 记录资料包导入执行过程。
2. 记录失败文件和待人工复核文件。
3. 为后续完整性核查提供可追溯基础。

使用技术：

1. `apps.audit` 服务层
2. JSONField
3. 敏感信息脱敏函数

产生方法：

1. `record_import_audit(batch, overview_report) -> AuditLog`
2. `sanitize_import_context(context) -> dict`

对应 Skill：

1. 本步骤不单独产生 Audit Skill，由 Django 服务层调用 Audit 服务完成。

## 6. Skill 清单

本步骤产生以下 Skill 设计文档：

1. [资料包导入Skill](skill/资料包导入Skill.md)
2. [压缩包解包Skill](skill/压缩包解包Skill.md)
3. [资料包扫描Skill](skill/资料包扫描Skill.md)
4. [文档页数统计Skill](skill/文档页数统计Skill.md)
5. [章节点识别Skill](skill/章节点识别Skill.md)
6. [目录汇总Skill](skill/目录汇总Skill.md)

## 7. 数据状态设计

### 7.1 批次状态

| 状态 | 含义 |
|---|---|
| `created` | 已创建批次，尚未完成文件登记 |
| `importing` | 正在导入或解包 |
| `summarizing` | 正在统计页数和目录 |
| `completed` | 导入与目录汇总完成 |
| `partial_completed` | 部分文件失败或需复核 |
| `failed` | 整体导入失败 |

### 7.2 文档状态

| 状态 | 含义 |
|---|---|
| `created` | 已创建记录 |
| `unsupported` | 文件类型不支持 |
| `page_counted` | 已完成页数统计 |
| `classified` | 已完成章节点识别 |
| `summarized` | 已进入目录汇总 |
| `manual_review_required` | 需要人工复核 |
| `failed` | 处理失败 |

### 7.3 页数可信度

| 状态 | 含义 |
|---|---|
| `exact` | 精确页数 |
| `not_applicable` | 不适用 |
| `manual_review_required` | 无法精确统计，需人工复核 |
| `failed` | 统计失败 |

## 8. 异常处理

本步骤需要业务化处理以下异常：

1. 上传空文件：拒绝导入，提示文件为空。
2. 文件名路径穿越：拒绝该文件，记录安全拦截。
3. 压缩包损坏：批次标记为 `failed` 或 `partial_completed`。
4. 压缩包加密：标记为 `manual_review_required`。
5. 解包后无支持文件：批次标记为 `partial_completed`，页面提示未发现可处理资料。
6. DOC 页数无法精确统计：文档标记待人工复核，不阻断目录汇总。
7. DOCX 页数无法精确统计：文档标记失败或待复核，并在汇总结果中突出提示。
8. 文件内容与扩展名不一致：记录风险提示，仍可尝试解析。
9. 同名相对路径重复：保留第一份，后续文件加版本后缀或标记重复。
10. 混入不支持文件：记录为 `unsupported`，不进入页数统计。

## 9. 页面展示

资料包导入完成后，Documents 页面建议展示：

1. 批次名称和导入时间。
2. 导入文件总数。
3. 支持文件数量。
4. 页数合计。
5. 待人工复核数量。
6. 解包失败或不支持文件数量。
7. 按章节点聚合的目录表。
8. 文件明细表。

文件明细表字段：

1. 原始相对路径。
2. 文件名。
3. 文件类型。
4. 页数。
5. 页数可信度。
6. 章节点。
7. 资料名称。
8. 处理状态。
9. 是否需人工复核。

## 10. 与后续步骤的接口

后续法规完整性核查需要读取：

1. `batch_id`
2. `workflow_type`
3. `source_role`
4. `documents[].document_id`
5. `documents[].relative_path`
6. `documents[].chapter_code`
7. `documents[].document_role`
8. `documents[].page_count`
9. `documents[].processing_status`
10. `documents[].needs_manual_review`

后续 RAG 入库需要读取：

1. 文件路径。
2. 文件类型。
3. 章节点。
4. 资料名称。
5. 业务资料或法规资料角色。
6. 文档处理状态。

## 11. 测试设计

### 11.1 单元测试

1. 上传文件校验。
2. 路径安全校验。
3. 压缩包解包。
4. 文件扫描过滤。
5. 页数统计。
6. 章节点识别。
7. 目录汇总聚合。

### 11.2 服务层测试

1. 单文件导入生成文档记录。
2. 多文件导入生成同一批次。
3. 压缩包导入保留相对路径。
4. 解包失败时批次状态正确。
5. DOC 无法统计页数时标记待复核。
6. 目录汇总结果包含文件数量、页数、章节点和警告。

### 11.3 页面测试

1. 上传成功后跳转到目录汇总页。
2. 页面展示页数可信度。
3. 页面展示待人工复核提示。
4. 不支持文件有清晰提示。

## 12. V1 实现建议

V1 建议先完成以下最小闭环：

1. 支持单文件和多文件上传。
2. 支持 `zip` 解包，并预留 `rar`、`7z` 依赖接入点。
3. 支持 `PDF`、`DOCX` 页数统计。
4. `DOC` 文件先标记待人工复核。
5. 基于文件名和路径识别 `CH1.*` 章节点。
6. 生成目录汇总页面和结构化 `registration_overview_report`。

在 V1 后续增强中补齐：

1. `rar`、`7z` 纯 Python 解包。
2. DOC 转换后精确统计。
3. 目录类文档内容解析。
4. 文件夹上传前端体验。
5. 后台人工修正章节点和资料名称。
