# 本机打印 API · API.md

组件开启后默认监听：`http://127.0.0.1:17891/`

配置入口：菜单 **API配置**  
配置文件：`%LocalAppData%\PrintComponent\api-config.json`

## 鉴权

```
Header: X-Print-Token: <Token>
```

Token 在「API配置 → 服务」中查看/复制。Token 不能为空且至少 16 个字符。

> 安全要求：Token 只允许放在 `X-Print-Token` Header，URL `?token=` 已禁用，避免凭据进入浏览器历史和代理日志。

## 多打印机通道（核心）

「服务」页每一行绑定：

| 本机打印机 | API 地址示例 |
|------------|----------------|
| HPRT N31 | `http://127.0.0.1:17891/api/print/label` |
| Godex | `http://127.0.0.1:17891/api/print/godex` |
| Microsoft Print to PDF | `http://127.0.0.1:17891/api/print/a4` |

网页对不同业务 POST 不同路径，组件自动选对应打印机与模版。

## 端点

| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/health` | 健康检查 |
| GET | `/designer` · `/designer.html` | 本机托管网页设计器（同源免 CORS） |
| GET | `/api/printers` | 本机打印机列表 |
| GET | `/api/profiles` | 打印机 Profile（DPI/偏移/旋转）列表 |
| GET | `/api/profiles/{device}` | 按设备名读取 Profile |
| PUT | `/api/profiles/{device}` | 保存校准参数（偏移/DPI/旋转等） |
| POST | `/api/profiles/{device}/calibrate` | 十字校准预览；`print=true` 试打，`save=true` 落盘 |
| GET | `/api/queues` | 按打印机队列快照（同机串行、跨机并行） |
| GET | `/api/channels` | 已配置通道 |
| GET | `/api/channels/{id}` | 通道详情（含绑定模版字段） |
| PATCH | `/api/channels/{id}` | 更新通道（如 TemplateId、打印机） |
| POST | `/api/channels/{id}/test` | 按通道试打 |
| GET | `/api/capabilities` | 能力发现（支持 `/api/v1/*` 别名） |
| GET | `/api/diagnostics` | 配置与打印机引用诊断（含 Profile 绑定与队列） |
| POST | `/api/printers/{name}/probe` | 探测打印机状态 |
| GET | `/api/templates` | 模版列表（含占位符字段） |
| GET | `/api/templates/{id}` | 模版详情（含 content、revision） |
| PUT | `/api/templates/{id}` | 新建或保存模板；内容变化时自动生成 revision |
| POST | `/api/templates/validate` | 校验 JSON 标签模板及样例数据 |
| POST | `/api/templates/preview` | 渲染 JSON 为 PNG；可带 printer/channel 用对应 Profile |
| POST | `/api/templates/{id}/rollback` | 回滚到历史 revision |
| GET | `/api/sites` | 网站绑定 |
| GET | `/api/jobs` | 最近打印作业（重启后仍可查） |
| GET | `/api/jobs/{jobId}` | 作业状态、Spooler 与事件 |
| POST | `/api/jobs/{jobId}/cancel` | 取消排队 / 尝试撤销 Spooler |
| POST | `/api/jobs/{jobId}/reprint` | 按快照重打（新 jobId） |
| GET | `/api/requests` | 最近打印请求数据记录（?limit=，默认 100，最多 200） |
| GET | `/api/requests/{id}` | 单条请求完整数据（含请求 JSON 原文） |
| DELETE | `/api/requests` | 清空请求记录 |
| POST | `/api/print/{channel}` | **按通道打印**（202 + jobId） |
| POST | `/api/print/{channel}/dry-run` | 只解析路由/模版，不入队、不出纸 |
| POST | `/api/print/{channel}/preview` | 渲染结果写入本机预览目录 |
| POST | `/api/print` | body 内带 `channel` |

## 打印请求示例

```http
POST /api/print/label HTTP/1.1
Host: 127.0.0.1:17891
Content-Type: application/json
X-Print-Token: YOUR_TOKEN
Idempotency-Key: SO20260720001

{
  "title": "发货单",
  "copies": 1,
  "data": {
    "title": "发货单",
    "orderNo": "SO20260720001",
    "barcode": "1234567890",
    "content": "莆阳打印测试"
  }
}
```

模版占位符：`{{orderNo}}`、`{{barcode}}`、`{{title}}`、`{{content}}`、`{{time}}` 等。

设计器可调用 `/api/v1/templates/preview`，请求体传入 `template`、`data` 和 `dpi`（72–1200），响应包含 `pngBase64`、像素尺寸和物理尺寸。该接口只渲染，不创建作业、不出纸。

设计器保存 JSON 模板：

```http
PUT /api/v1/templates/label-custom
Content-Type: application/json
X-Print-Token: YOUR_TOKEN

{
  "name": "自定义标签",
  "type": "json",
  "template": {
    "schemaVersion": 1,
    "widthMm": 50,
    "heightMm": 30,
    "elements": [
      { "type": "text", "xMm": 2, "yMm": 2, "widthMm": 30, "heightMm": 5, "value": "测试标签" }
    ]
  }
}
```

模板内容或类型发生变化时 revision 自动递增并保留最近 10 个历史版本；仅修改名称不会产生新 revision。

打印请求成功进入队列时返回 HTTP `202`：

```json
{
  "code": "ACCEPTED",
  "message": "queued",
  "jobId": "32位作业ID",
  "status": "等待中"
}
```

调用方应使用 `GET /api/jobs/{jobId}` 查询 `等待中 / 提交中 / 已提交 / 失败 / 已中断`。`已提交`表示 Windows 打印驱动已接受任务，不等同于纸张已经物理出纸。作业会持久化到本机，组件重启后仍可查询；重启时未完成作业会标为 `已中断` 且不会自动重打。

### dry-run / preview

```http
POST /api/print/label/dry-run
POST /api/print/label/preview
```

请求体与正式打印相同。`dry-run` 返回解析出的打印机、模版、字段与校验错误；`preview` 对 JSON 标签模板生成 PNG，并返回 `pngBase64`、像素尺寸、DPI 及本机预览文件路径。旧文本模板仍生成 `.txt`。

## JSON 标签模板（schemaVersion 1）

JSON 模板以毫米为物理单位，支持 `text`、`barcode`（Code 128）、`qr`、`rect`、`line`、`image`、`table`、`repeat`。预览与正式打印共用同一个 GDI Raster 渲染器。

`image` 的 `imageSource`（或 `value`）仅允许：

- `data:image/png|jpeg|gif|bmp|webp;base64,...`（解码后 ≤ 512KB）
- `%LocalAppData%\PrintComponent\templates\assets\` 下的相对路径（如 `logo.png`）

绝对路径、盘符路径与包含 `..` 的路径会被拒绝。

### Table / Repeat 集合数据

打印、校验和预览请求均可传 `collections`：

```json
{
  "data": { "orderNo": "SO-1001" },
  "collections": {
    "items": [
      { "name": "标签纸", "qty": "2" },
      { "name": "碳带", "qty": "1" }
    ]
  }
}
```

`table` 通过 `dataSource`、`columns`、`headerHeightMm`、`rowHeightMm` 描述表格；`repeat` 通过 `dataSource` 和相对坐标 `children` 重复渲染一组元素。两者支持 `maxRows`，`overflow` 可设为 `clip`（截断并警告）或 `error`（打印前拒绝）。

```json
{
  "schemaVersion": 1,
  "revision": 1,
  "widthMm": 50,
  "heightMm": 30,
  "fields": [
    { "key": "title", "label": "标题", "type": "text", "required": true },
    { "key": "barcode", "label": "条码", "type": "text", "required": true }
  ],
  "sampleData": {
    "title": "莆阳打印",
    "barcode": "1234567890"
  },
  "elements": [
    {
      "type": "text",
      "xMm": 2,
      "yMm": 2,
      "widthMm": 46,
      "heightMm": 6,
      "value": "{{title}}",
      "fontSizePt": 12,
      "bold": true
    },
    {
      "type": "barcode",
      "xMm": 3,
      "yMm": 12,
      "widthMm": 44,
      "heightMm": 12,
      "value": "{{barcode}}"
    }
  ]
}
```

校验请求：

```http
POST /api/templates/validate
X-Print-Token: YOUR_TOKEN
Content-Type: application/json

{
  "template": { "...": "上面的 TemplateDefinition" },
  "data": {
    "title": "正式数据",
    "barcode": "SO10001"
  }
}
```

模板越界、必填字段缺失、非法 Code 128 字符会返回 HTTP `422`，不会进入打印队列。打印机档案中的 DPI 与 X/Y 偏移会应用到 PNG 预览和正式打印。

## 网页注意

- 必须本机已运行打印组件
- 默认只允许「网站绑定」中配置的精确 Origin（scheme + host + port）
- 「允许任意网站 CORS」仅用于临时联调，不建议长期开启
- 单次请求体上限 1 MB；并发请求受限，避免打印服务被占满
- 默认禁止请求直接覆盖 `printer` / `templateId`，正式调用应使用已绑定通道
- 浏览器访问的是用户电脑的 `127.0.0.1`，不是公网服务器
