# docfill HOWTO 本文档说明如何使用 `docfill` 完成本地文档自动填充。文档分为两部分: - “最小实践”用于快速完成一次可验证运行。 - “详细说明”用于理解任务配置、运行变量、筛选、模板、输出、错误和日志。 ## 一、最小实践 ### 1. 进入项目目录 ```bash cd /Users/zhaoyilun/Documents/xls_doc ``` ### 2. 构建命令行工具 ```bash bash scripts/build.sh ``` 输出示例: ```text /Users/zhaoyilun/Documents/xls_doc/dist/docfill-darwin-arm64 ``` 构建后的可执行文件是: ```text dist/docfill-darwin-arm64 ``` ### 3. 准备测试素材 仓库中已经包含 `manual_tests/` 测试素材。如果需要重新生成,可执行: ```bash /Users/zhaoyilun/.cache/codex-runtimes/codex-primary-runtime/dependencies/python/bin/python3 scripts/make-manual-test-materials.py ``` 输出示例: ```text /Users/zhaoyilun/Documents/xls_doc/manual_tests ``` 测试素材包含四类任务: - `manual_tests/01_excel_to_word`: Excel 源数据生成 Word 日报。 - `manual_tests/02_sqlite_to_excel`: SQLite 源数据生成 Excel 汇总表。 - `manual_tests/03_excel_to_excel`: Excel 源数据生成 Excel 质检表。 - `manual_tests/04_filter_work_order_to_word`: 按日期和工单编号筛选一条工单,再生成 Word。 ### 4. 运行筛选工单案例 命令: ```bash dist/docfill-darwin-arm64 run -c manual_tests/04_filter_work_order_to_word/task.json --var date=2026-06-18 --var ticket_no=GD-002 --verbose ``` 输出示例: ```text log: load config path=manual_tests/04_filter_work_order_to_word/task.json log: read data type=excel path=/Users/zhaoyilun/Documents/xls_doc/manual_tests/04_filter_work_order_to_word/source_work_orders.xlsx log: read data type=excel path=/Users/zhaoyilun/Documents/xls_doc/manual_tests/04_filter_work_order_to_word/source_work_orders.xlsx rows=3 log: filter rows=3 matched=1 expect=one log: render row=1 output=/Users/zhaoyilun/Documents/xls_doc/manual_tests/04_filter_work_order_to_word/out/work_order_2026-06-18_GD-002.docx generated 1 file(s) - /Users/zhaoyilun/Documents/xls_doc/manual_tests/04_filter_work_order_to_word/out/work_order_2026-06-18_GD-002.docx ``` 查看输出目录: ```bash open manual_tests/04_filter_work_order_to_word/out ``` 输入 Excel 内容示例: ```text 日期 工单编号 客户名称 系统来源 处理结果 下一步 2026-06-18 GD-001 A 公司 PMS3.0 已派单 等待现场反馈 2026-06-18 GD-002 B 公司 D5000 已完成 归档并同步日报 2026-06-19 GD-001 C 公司 风控平台 待复核 补充截图 ``` 本次命令传入: ```text date = 2026-06-18 ticket_no = GD-002 ``` 所以工具会筛选: ```text 日期 = 2026-06-18,并且 工单编号 = GD-002 ``` 最终只生成 `GD-002` 对应的 Word 文件。 ## 二、详细说明 ### 1. task.json 与 --var 的关系 `task.json` 是某一种业务报表的固定任务说明书。它通常由开发人员或维护人员准备好,普通运行时不需要频繁修改。 命令行 `--var` 是本次运行传入的具体条件,例如日期、工单编号、人员、批次。 可以这样理解: ```text 不同业务 = 不同 task.json 本次运行的具体条件 = --var 真实业务数据 = Excel 或 SQLite 输出模板 = Word 或 Excel ``` 例如不同业务可以分别维护: ```text 工单日报.json 部门汇总.json 质检报告.json ``` 运行时只传本次变化的信息: ```bash dist/docfill-darwin-arm64 run -c 工单日报.json --var date=2026-06-18 --var ticket_no=GD-002 ``` 变量优先级: ```text 数据表里的字段 > 命令行 --var > task.json 里的 vars ``` 示例: ```json "vars": { "date": "2026-06-18" } ``` 如果运行时执行: ```bash dist/docfill-darwin-arm64 run -c task.json --var date=2026-06-19 ``` 实际使用的是: ```text date = 2026-06-19 ``` 也就是命令行 `--var` 会覆盖 `task.json` 中的同名默认变量。 ### 2. 命令格式 `docfill` 当前只提供一个核心命令: ```bash dist/docfill-darwin-arm64 run -c [--var key=value] [--verbose] ``` 参数说明: | 参数 | 作用 | 示例 | | --- | --- | --- | | `run` | 执行一次填充任务 | `dist/docfill-darwin-arm64 run -c task.json` | | `-c` | 指定任务配置文件 | `-c manual_tests/04_filter_work_order_to_word/task.json` | | `--config` | `-c` 的完整写法 | `--config task.json` | | `--var` | 传入运行时变量,可以重复使用 | `--var date=2026-06-18 --var ticket_no=GD-002` | | `--verbose` | 输出诊断日志到 stderr | `--verbose` | 命令行参数错误示例: ```bash dist/docfill-darwin-arm64 run --var date ``` 输出示例: ```text docfill: [CLI_USAGE] invalid value "date" for flag -var: invalid --var "date", want key=value hint: Check command flags and use key=value for --var. ``` ### 3. 任务配置结构 典型的筛选任务配置如下: ```json { "vars": { "date": "2026-06-18" }, "data": { "type": "excel", "path": "./source_work_orders.xlsx", "sheet": "工单", "header_row": 1 }, "filter": { "equals": { "日期": "{{date}}", "工单编号": "{{ticket_no}}" }, "expect": "one" }, "template": { "path": "./work_order_template.docx" }, "output": { "path": "./out/work_order_{{日期}}_{{工单编号}}.docx", "overwrite": true } } ``` 配置字段说明: | 字段 | 必填 | 说明 | | --- | --- | --- | | `vars` | 否 | 默认变量。适合放日期、批次、班次等默认值。 | | `data.type` | 是 | 数据源类型。支持 `excel`、`xlsx`、`sqlite`。 | | `data.path` | 是 | 数据源路径。支持 `.xlsx` 或 SQLite `.db`。 | | `data.sheet` | Excel 可选 | Excel 工作表名称。未填写时读取第一个工作表。 | | `data.header_row` | Excel 可选 | Excel 表头行号,从 `1` 开始。未填写时默认为 `1`。 | | `data.query` | SQLite 必填 | SQLite 查询语句。查询结果列名作为字段名。 | | `data.params` | SQLite 可选 | SQL `?` 参数列表,支持 `{{变量名}}`。 | | `filter.equals` | 否 | 等值筛选条件。左侧是数据字段,右侧是匹配值。 | | `filter.expect` | 否 | 筛选结果数量要求。支持 `one`、`many`,默认 `one`。 | | `template.path` | 是 | Word 或 Excel 模板路径。支持 `.docx`、`.xlsx`。 | | `output.path` | 是 | 输出文件路径模板。可以使用 `{{字段名}}`。 | | `output.overwrite` | 否 | 是否覆盖已存在文件。默认是 `false`。 | 相对路径规则: ```text task.json 所在目录就是相对路径的基准目录。 ``` ### 4. 业务筛选 filter `filter` 的作用是从数据表中筛选出本次要使用的数据行。它的目标不是提供数据库查询能力,而是提供符合表格使用习惯的简单筛选。 配置示例: ```json "filter": { "equals": { "日期": "{{date}}", "工单编号": "{{ticket_no}}" }, "expect": "one" } ``` 含义: ```text 找到 日期 等于 date,并且 工单编号 等于 ticket_no 的数据行。 ``` 命令: ```bash dist/docfill-darwin-arm64 run -c task.json --var date=2026-06-18 --var ticket_no=GD-002 ``` 实际筛选: ```text 日期 = 2026-06-18 工单编号 = GD-002 ``` 筛选规则: - `filter.equals` 左侧必须是 Excel 表头或 SQLite 查询结果列名。 - `filter.equals` 右侧可以写固定值,也可以写 `{{变量名}}`。 - 多个条件之间是“全部满足”关系。 - 比较前会去掉单元格值和筛选值两侧的空格。 - 只支持等值匹配。 - 不支持模糊匹配、范围查询、OR、排序表达式或复杂条件。 `filter.expect` 支持两个值: | 值 | 含义 | 适用场景 | | --- | --- | --- | | `one` | 必须只匹配一行 | 工单、单份日报、单个报表。 | | `many` | 允许匹配多行 | 按日期批量生成多个人或多个部门的报表。 | 未填写 `filter.expect` 时,默认按 `one` 处理。 如果没有配置 `filter`,工具会沿用旧行为:读取到的每一行都会生成一个输出文件。 复杂情况的处理建议: - 如果多个字段才能确定一条数据,就在 `filter.equals` 中写多个条件。 - 如果表格本身不容易筛选,建议在 Excel 中新增辅助列,例如 `填报批次`、`业务唯一标识`、`筛选日期`。 - 不建议把复杂业务规则写进 `docfill`,复杂规则应在数据准备阶段完成。 ### 5. Excel 数据源 Excel 数据源配置示例: ```json { "data": { "type": "excel", "path": "./source_work_orders.xlsx", "sheet": "工单", "header_row": 1 } } ``` 运行示例: ```bash dist/docfill-darwin-arm64 run -c manual_tests/04_filter_work_order_to_word/task.json --var date=2026-06-18 --var ticket_no=GD-002 ``` Excel 读取规则: - `header_row` 指定表头行,表头单元格内容就是字段名。 - 表头下方的每一行是候选数据。 - 配置了 `filter` 时,先筛选,再生成输出。 - 未配置 `filter` 时,每一行都会生成一个输出文件。 - 空行会被跳过。 - 空表头列会被忽略。 表头在第 1 行的输入示例: ```text 日期 工单编号 客户名称 系统来源 处理结果 下一步 2026-06-18 GD-001 A 公司 PMS3.0 已派单 等待现场反馈 2026-06-18 GD-002 B 公司 D5000 已完成 归档并同步日报 2026-06-19 GD-001 C 公司 风控平台 待复核 补充截图 ``` 表头在第 2 行的配置示例: ```json { "data": { "type": "excel", "path": "./source_audit_data.xlsx", "sheet": "Audit", "header_row": 2 } } ``` 对应输入示例: ```text 日报质检导出 employee date item score comment 王五 2026-06-18 日报完整性 92 字段齐全,说明清楚 赵六 2026-06-18 系统截图一致性 87 缺少 1 张截图,需要补充 ``` ### 6. SQLite 数据源 SQLite 数据源配置示例: ```json { "vars": { "date": "2026-06-18" }, "data": { "type": "sqlite", "path": "./source_summary.db", "query": "select department, report_date, total_count, abnormal_count, owner from department_summary order by department", "params": [] }, "filter": { "equals": { "report_date": "{{date}}" }, "expect": "many" } } ``` 运行示例: ```bash dist/docfill-darwin-arm64 run -c manual_tests/02_sqlite_to_excel/task.json --var date=2026-06-18 ``` SQLite 读取规则: - `data.query` 的查询结果列名就是模板字段名。 - `data.params` 按顺序对应 SQL 中的 `?`。 - `data.params` 可以引用 `vars` 或命令行 `--var`。 - 配置了 `filter` 时,SQL 查询结果还会再经过业务筛选。 - 查询结果的每一行或筛选后的每一行生成一个输出文件。 输入数据示例: ```text department report_date total_count abnormal_count owner 营销部 2026-06-17 115 2 张三 客服部 2026-06-18 76 0 李四 营销部 2026-06-18 128 3 张三 ``` SQLite 可以继续使用 SQL 参数: ```json "data": { "type": "sqlite", "path": "./source_summary.db", "query": "select department, report_date, total_count, abnormal_count, owner from department_summary where report_date = ? order by department", "params": ["{{date}}"] } ``` 这适合由开发人员维护的 SQLite 数据源。面向表格使用习惯时,优先使用 `filter.equals` 表达业务筛选。 ### 7. Word 模板 Word 模板使用 `.docx` 文件,模板中写入 `{{字段名}}`: ```text 日期:{{日期}} 工单编号:{{工单编号}} 客户名称:{{客户名称}} 系统来源:{{系统来源}} 处理结果:{{处理结果}} 下一步:{{下一步}} ``` 运行示例: ```bash dist/docfill-darwin-arm64 run -c manual_tests/04_filter_work_order_to_word/task.json --var date=2026-06-18 --var ticket_no=GD-002 ``` 生成结果示例: ```text 日期:2026-06-18 工单编号:GD-002 客户名称:B 公司 系统来源:D5000 处理结果:已完成 下一步:归档并同步日报 ``` Word 支持同一段落内被 Word 拆分的文本占位符。例如模板内部可能把 `{{name}}` 拆成相邻文本节点 `{{na` 和 `me}}`,工具会按同一段落进行最小兼容处理。 这种兼容会把同一段落内的拆分文本合并后再写回,渲染结果会写入该段落第一个文本节点的位置,后续拆分节点的独立格式不再体现。建议模板中每个占位符使用同一种字体、颜色和加粗状态,不要在 `{{`、字段名、`}}` 之间混用格式。 当前不支持复杂模板语法,例如表格循环、条件判断、图片插入、跨段落拼接占位符。 ### 8. Excel 模板 Excel 模板使用 `.xlsx` 文件,单元格中写入 `{{字段名}}`: ```text B2: {{department}} B3: {{report_date}} B4: {{owner}} B5: {{total_count}} B6: {{abnormal_count}} ``` 运行示例: ```bash dist/docfill-darwin-arm64 run -c manual_tests/02_sqlite_to_excel/task.json --var date=2026-06-18 ``` 生成结果示例: ```text B2: 营销部 B3: 2026-06-18 B4: 张三 B5: 128 B6: 3 ``` Excel 模板会遍历所有工作表,替换单元格文本中的占位符。当前不支持公式重算、透视表刷新、复杂样式模板语法。 ### 9. 输出路径 输出路径可以使用字段占位符: ```json { "output": { "path": "./out/work_order_{{日期}}_{{工单编号}}.docx", "overwrite": true } } ``` 运行示例: ```bash dist/docfill-darwin-arm64 run -c manual_tests/04_filter_work_order_to_word/task.json --var date=2026-06-18 --var ticket_no=GD-002 ``` 输出文件示例: ```text work_order_2026-06-18_GD-002.docx ``` 文件名清理规则: ```text / \ : * ? " < > | ``` 以上字符会在输出路径字段值中替换为 `_`。例如: ```text 张三/测试 -> 张三_测试 ``` 覆盖规则: - `output.overwrite` 未设置或为 `false` 时,输出文件已存在会失败。 - `output.overwrite` 为 `true` 时,允许覆盖同名输出文件。 ### 10. 日志、标准输出和退出码 成功时,stdout 输出生成文件清单: ```text generated 1 file(s) - /path/to/out/work_order_2026-06-18_GD-002.docx ``` 启用 `--verbose` 后,stderr 输出诊断日志: ```text log: load config path=task.json log: read data type=excel path=/path/data.xlsx log: read data type=excel path=/path/data.xlsx rows=3 log: filter rows=3 matched=1 expect=one log: render row=1 output=/path/out/work_order_2026-06-18_GD-002.docx ``` 退出码: | 退出码 | 含义 | 适用场景 | | --- | --- | --- | | `0` | 成功 | 可以读取 stdout 中的生成文件清单。 | | `1` | 执行失败 | 配置、数据、模板、输出等处理失败。 | | `2` | 命令行错误 | 参数缺失、命令错误、`--var` 格式错误。 | 数字员工或调度脚本建议: - 判断退出码确认任务是否成功。 - 成功时读取 stdout 获取生成文件路径。 - 失败时记录 stderr,用于定位错误码、提示和上下文。 - 仅在诊断或审计时启用 `--verbose`。 ### 11. 错误格式与错误码 错误输出格式: ```text docfill: [ERROR_CODE] message hint: repair suggestion context: key=value ``` 配置文件不存在示例: ```bash dist/docfill-darwin-arm64 run -c manual_tests/not-exist.json ``` 输出示例: ```text docfill: [CONFIG_READ] read config: open manual_tests/not-exist.json: no such file or directory hint: Check that the config file exists and is readable. context: config=manual_tests/not-exist.json ``` 筛选无匹配示例: ```bash dist/docfill-darwin-arm64 run -c manual_tests/04_filter_work_order_to_word/task.json --var date=2026-06-18 --var ticket_no=GD-999 --verbose ``` 输出示例: ```text log: load config path=manual_tests/04_filter_work_order_to_word/task.json log: read data type=excel path=/Users/zhaoyilun/Documents/xls_doc/manual_tests/04_filter_work_order_to_word/source_work_orders.xlsx log: read data type=excel path=/Users/zhaoyilun/Documents/xls_doc/manual_tests/04_filter_work_order_to_word/source_work_orders.xlsx rows=3 docfill: [DATA_FILTER_NO_MATCH] filter matched no rows hint: Check filter.equals, --var values, or source table contents. context: expect=one filter=工单编号=GD-999,日期=2026-06-18 ``` 错误码说明: | 错误码 | 含义 | 常见处理方式 | | --- | --- | --- | | `CLI_USAGE` | 命令行参数错误 | 检查命令、`-c`、`--var key=value`。 | | `CONFIG_READ` | 配置文件读取失败 | 检查路径、文件是否存在、读权限。 | | `CONFIG_PARSE` | 配置 JSON 解析失败 | 检查 JSON 语法。 | | `CONFIG_VALIDATE` | 配置字段缺失或不支持 | 检查 `data.type`、`data.path`、`template.path`、`output.path`、`filter.expect`。 | | `DATA_EXCEL` | Excel 数据读取失败 | 检查 `.xlsx` 文件、工作表名、表头行。 | | `DATA_SQLITE` | SQLite 数据读取或查询失败 | 检查 `.db` 文件、SQL、参数。 | | `DATA_EMPTY` | 数据源没有返回可生成的数据行 | 检查源数据是否为空。 | | `DATA_FILTER_FIELD` | 筛选字段不在数据中 | 检查 Excel 表头或 SQLite 查询列名。 | | `DATA_FILTER_VALUE` | 筛选条件引用的变量没有提供 | 补充 `--var` 或 `vars`。 | | `DATA_FILTER_NO_MATCH` | 筛选后没有符合条件的数据行 | 检查筛选条件,或整理源表。 | | `DATA_FILTER_MULTI_MATCH` | 期望一行但匹配多行 | 增加筛选条件,或整理重复数据。 | | `TEMPLATE_OPEN` | 模板读取失败 | 检查模板路径和文件格式。 | | `TEMPLATE_FIELD_MISSING` | 模板字段缺失 | 在数据源、`vars` 或 `--var` 中补充字段。 | | `TEMPLATE_UNRESOLVED` | 渲染后仍残留占位符 | 检查模板占位符拼写和字段值。 | | `OUTPUT_EXISTS` | 输出文件已存在 | 修改输出路径或设置 `output.overwrite: true`。 | | `OUTPUT_WRITE` | 输出写入失败 | 检查输出目录权限、文件占用状态。 | | `INTERNAL` | 未分类内部错误 | 保留 stderr 和输入文件,开启 `--verbose` 复现。 | ### 12. 自动化调用示例 以下示例展示数字员工或调度脚本如何调用 `docfill`。该脚本只做最小封装,不承担模板解析或业务逻辑。 ```bash #!/usr/bin/env bash set -euo pipefail DOCFILL="/Users/zhaoyilun/Documents/xls_doc/dist/docfill-darwin-arm64" CONFIG="/Users/zhaoyilun/Documents/xls_doc/manual_tests/04_filter_work_order_to_word/task.json" DATE="${1:?date is required, example: 2026-06-18}" TICKET_NO="${2:?ticket_no is required, example: GD-002}" "$DOCFILL" run -c "$CONFIG" --var date="$DATE" --var ticket_no="$TICKET_NO" --verbose ``` 执行: ```bash bash run-docfill.sh 2026-06-18 GD-002 ``` 预期结果: ```text generated 1 file(s) - /Users/zhaoyilun/Documents/xls_doc/manual_tests/04_filter_work_order_to_word/out/work_order_2026-06-18_GD-002.docx ``` 集成建议: - 把业务系统接口采集结果先落成 Excel 或 SQLite。 - 为每类报表维护一个 `task.json`。 - 数字员工只负责准备运行变量并执行命令。 - 复杂筛选优先通过整理 Excel 表解决,例如新增辅助列。 - 不要把账号密码、审批状态、业务系统访问逻辑写入 `docfill` 配置。 ### 13. 开发与验证 运行全部 Go 测试: ```bash go test -count=1 ./... ``` 运行冒烟测试: ```bash bash scripts/smoke.sh ``` 构建发布用本机二进制: ```bash bash scripts/build.sh ``` 重新生成手工测试素材: ```bash /Users/zhaoyilun/.cache/codex-runtimes/codex-primary-runtime/dependencies/python/bin/python3 scripts/make-manual-test-materials.py ``` 冒烟测试覆盖内容: - Go 单元测试。 - 临时构建 `docfill`。 - 生成临时 Excel、Word、SQLite 示例数据。 - 执行无筛选 Excel 到 Word 示例。 - 执行筛选 Excel 到 Word 示例。 - 执行 SQLite 到 Excel 示例。 - 检查输出文件是否生成。 ### 14. 当前边界 为了保持工具小型、稳定、便于自动化调用,当前明确不包含以下能力: - 不提供 Web 界面。 - 不提供模板设计器。 - 不直接登录业务系统。 - 不管理账号、密码或权限。 - 不实现审批流。 - 不实现 Word 表格循环或条件语法。 - 不实现 Excel 公式重算和透视表刷新。 - 不实现复杂筛选语法。 - 不实现复杂任务编排。 如需扩展,应优先通过外部脚本准备数据,再交给 `docfill` 执行单次文档填充。