docs: 补充物料批量导入接口说明

- 覆盖范围表补一行分组入口,并注明已在 §1 展开(该表标题写的是「未展开」)
- 「1. 物料接口」末尾补 GET /material-import/template 与 POST /material-import:
  模板的两张工作表与 5 列定义、按列名解析的规则、逐行校验表、成功与失败的响应
  示例、row 是 Excel 真实行号、5MB 与 20000 行限制、任意一行失败整批不入库、
  导入记录只留汇总不落逐行明细
- 状态码旁列出真实的提示文案(含后端返回的原文),便于与响应逐字对照
This commit is contained in:
文睿 张 2026-10-10 16:41:40 +08:00
parent 2f0d978dbf
commit 356c8520ca

View File

@ -143,6 +143,7 @@
| 物料申请 | `/applications`、`/applications/my` | 需登录 |
| 操作日志 | `/logs`、`/logs/materials/:materialId` | 需登录 |
| 图片上传 | `POST /upload` | 需登录 |
| 物料批量导入 | `GET /material-import/template`、`POST /material-import` | 需登录,见 §1 |
---
@ -388,6 +389,91 @@
---
### GET /material-import/template
下载批量导入用的 xlsx 模板。模板随代码实时生成、不落盘,所以下载到的列定义永远与当前校验规则一致。
**请求:** 无参数。
**响应:** xlsx 文件流,`Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`,文件名为 `物料导入模板.xlsx`。
模板含两张工作表:
| 工作表 | 内容 |
|--------|------|
| 填写说明 | 字段、是否必填、示例、填写规则 |
| 物料数据 | 第 1 行是表头,数据从第 2 行开始 |
`物料数据` 的表头是 5 列:`物料名称`、`规格`、`单位`、`大类`、`小类`。解析时**按列名定位,不按列位置**:
- 列顺序可以随意调整;表头允许带首尾空格和末尾的 `*` 必填标记;不认识的列忽略;
- 必填列 `物料名称`、`大类`、`小类` 缺任何一列,判为模板不合法并返回「模板格式不正确,请重新下载最新模板」;
- 选填列 `规格`、`单位` 可以整列删掉,取值按空处理。
分类**按名称**填写,后端按「大类名 + 小类名」在本公司内组合匹配(小类名在一个公司内会重名,例如多个大类下都有「其他」,所以必须两级组合)。单位按名称匹配,同时兼容按编码匹配(如 `kg` 与 `千克`)。
### POST /material-import
上传填好的 xlsx,**校验通过即在同一个请求内入库**——没有「预览确认」环节,也没有暂存任务 ID。
**请求:** `multipart/form-data`,文件字段名 `file`。仅支持 `.xlsx`,单文件不超过 5 MB,数据区不超过 20,000 行(不含表头)。
**逐行校验规则:**
| 列 | 规则 |
|----|------|
| 物料名称 | 必填,去首尾空格后非空,≤ 200 字 |
| 规格 | 选填,≤ 200 字 |
| 单位 | 选填;留空时默认「个」。非空时必须在当前公司已启用单位中按名称或编码匹配到 |
| 大类 | 必填,必须是当前公司已启用的顶层分类名称 |
| 小类 | 必填,必须是该大类下已启用的小类名称 |
| 编码规则 | 该小类必须存在启用的编码规则(`code_rules.status = 'active'`) |
物料编码由系统按小类编码规则生成,**不接受模板里的编码列**。同一行可能产生多条原因,用中文分号连接。
**成功响应 `201 Created`:**
```json
{
"code": 0,
"message": "成功导入 200 条物料",
"data": { "total_rows": 200, "success_rows": 200, "failed_rows": 0, "errors": [] }
}
```
**校验失败响应 `400 Bad Request`**(HTTP 状态是 400,但 `data` 里带完整明细):
```json
{
"code": 400,
"message": "有 3 行校验未通过,本次未导入任何数据",
"data": {
"total_rows": 200, "success_rows": 0, "failed_rows": 3,
"errors": [
{ "row": 3, "data": { "name": "", "spec": "M8x30", "unit": "个", "major": "机械结构类", "minor": "标准件" }, "reason": "物料名称不能为空" }
]
}
}
```
`row` 是 **Excel 里的真实行号**(从 1 开始、含表头行),用户可以直接对着 Excel 定位;`data` 是该行的原始内容。
**状态码:**
- `201 Created` — 全部行通过并入库
- `400 Bad Request` — 存在校验未通过的行(整批不入库);模板不合法(「模板格式不正确,请重新下载最新模板」)、行数超限(「导入行数超过 20000 行上限」)、未选文件(「请选择要导入的 xlsx 文件」)、文件超过 5 MB(「文件超过 5MB 上限」)
- `500 Internal Server Error` — 入库失败,事务已回滚,整批未落库
**行为说明:**
- **任意一行校验失败,整批不入库**,响应中返回全部逐行错误;重新上传前请先修正后重试。
- 整批在**一个事务**内完成:按小类锁编码规则 → 分配编码 → 分批插入物料(每批 500 行)→ 写审计日志;任一步失败整体回滚。
- 每次导入(无论成功或失败)都会写一条 `material_import_logs` 记录:操作人、公司、时间、文件名、总行数、成功数、失败数、状态、失败原因摘要(前 5 行行号)。**逐行错误明细只随响应返回,不落库。**
- 首版只支持**新增**物料:不做更新,也不自动创建分类或单位。
- **不做重复检测**:同一份文件重复上传会真的导入两遍(生成不同的编码)。
---
## 2. 入库接口
入库是物料**进入库存视野**的唯一入口:物料的第一行库存记录和第一个预警阈值都在这里产生。入库成功后,服务端会通过 WebSocket 广播库存变更。