init:代码初始化

This commit is contained in:
bai
2026-01-06 09:19:12 +08:00
parent 2dff90de4a
commit ba478d70cc
34 changed files with 11917 additions and 1 deletions

299
docs/IMPORT_API.md Normal file
View File

@@ -0,0 +1,299 @@
# 数据导入 API 文档
## POST /api/prices/import
批量导入钢材价格数据到数据库。
### 请求格式
**Content-Type:** `application/json`
**请求体:**
```json
{
"prices": [
{
"price_id": "2008067775216672769",
"goods_material": "HPB300",
"goods_spec": "Φ8",
"partsname_name": "螺纹钢",
"productarea_name": "昆钢",
"price_source": "我的钢铁",
"price_region": "昆明",
"pntree_name": "钢筋",
"price_date": "2026-01-05T10:30:00.000Z",
"make_price": 3800,
"hang_price": 3850,
"last_make_price": 3750,
"last_hang_price": 3800,
"make_price_updw": "↑50",
"hang_price_updw": "↑50",
"operator_code": "OP001",
"operator_name": "张三"
}
]
}
```
### 字段说明
#### 必填字段
| 字段名 | 类型 | 说明 | 示例 |
|--------|------|------|------|
| `goods_material` | string | 材质牌号 | `"HPB300"` |
| `goods_spec` | string | 规格型号 | `"Φ8"` |
| `partsname_name` | string | 品名 | `"螺纹钢"` |
| `productarea_name` | string | 产地/钢厂 | `"昆钢"` |
| `price_source` | string | 价格来源 | `"我的钢铁"` |
| `price_region` | string | 价格地区 | `"昆明"` |
| `price_date` | string | 价格日期ISO 8601 | `"2026-01-05T10:30:00.000Z"` |
| `hang_price` | number | 挂牌价(元/吨) | `3850` |
#### 可选字段
| 字段名 | 类型 | 说明 | 默认值 | 示例 |
|--------|------|------|--------|------|
| `price_id` | string | 价格唯一ID为空时自动生成 | `null` | `"2008067775216672769"` |
| `pntree_name` | string | 分类名称 | `"钢筋"` | `"钢筋"` |
| `make_price` | number | 钢厂价(元/吨) | `null` | `3800` |
| `last_make_price` | number | 上次钢厂价 | `null` | `3750` |
| `last_hang_price` | number | 上次挂牌价 | `null` | `3800` |
| `make_price_updw` | string | 钢厂价涨跌 | `null` | `"↑50"` |
| `hang_price_updw` | string | 挂牌价涨跌 | `null` | `"↑50"` |
| `operator_code` | string | 操作员代码 | `null` | `"OP001"` |
| `operator_name` | string | 操作员名称 | `null` | `"张三"` |
#### 自动生成字段
以下字段由系统自动生成,无需传入:
- `id` - 自增主键
- `created_at` - 创建时间
- `updated_at` - 更新时间
### price_id 自动生成规则
如果传入的数据中 `price_id` 为空或不存在,系统会基于以下字段生成 MD5 哈希值:
```
{goods_material}-{goods_spec}-{price_region}-{price_source}-{price_date}
```
**示例:**
```
HPB300-Φ8-昆明-我的钢铁-2026-01-05T10:30:00.000Z
↓ MD5 哈希
a1b2c3d4e5f6...32 位十六进制字符串)
```
这样可以确保相同业务数据不会重复插入。
### 响应格式
#### 成功响应
**HTTP Status:** `200 OK`
```json
{
"success": true,
"message": "成功导入 100 条数据",
"data": {
"imported": 100,
"total": 105,
"validCount": 100,
"errorCount": 5,
"errors": [
{
"index": 3,
"data": { ... },
"reasons": {
"missing": ["hang_price"],
"invalid": [
{ "field": "price_date", "expected": "valid Date", "received": "invalid-date" }
]
}
}
]
}
}
```
#### 失败响应
**HTTP Status:** `200 OK` (业务失败)
```json
{
"success": false,
"message": "没有有效的数据可以导入",
"data": {
"total": 10,
"imported": 0,
"errors": [
{
"index": 0,
"data": { ... },
"reasons": {
"missing": ["goods_material", "hang_price"],
"invalid": []
}
}
]
}
}
```
#### 服务器错误
**HTTP Status:** `500 Internal Server Error`
```json
{
"success": false,
"error": "数据库连接失败"
}
```
### 数据验证规则
#### 必填字段验证
- 所有必填字段不能为空、null 或 undefined
- 字符串字段长度不能为 0
#### 数据类型验证
| 字段 | 允许的类型 | 验证规则 |
|------|-----------|----------|
| `hang_price` | `number` | 必须是有效数字,不能为 NaN |
| `make_price` | `number` \| `null` | 如果存在,必须是有效数字 |
| `last_make_price` | `number` \| `null` | 如果存在,必须是有效数字 |
| `last_hang_price` | `number` \| `null` | 如果存在,必须是有效数字 |
| `price_date` | `string` \| `Date` | 必须是有效的日期格式 |
### 错误处理
#### 数据格式错误
如果传入的数据不符合格式要求:
```json
{
"success": false,
"message": "无效的数据格式",
"error": "数据必须是数组格式"
}
```
#### 缺少必填字段
如果某条记录缺少必填字段,该记录会被跳过,错误信息会返回在响应中:
```json
{
"success": true,
"message": "成功导入 95 条数据5 条数据因格式错误被跳过",
"data": {
"imported": 95,
"total": 100,
"validCount": 95,
"errorCount": 5,
"errors": [
{
"index": 10,
"data": { "goods_material": "HPB300" },
"reasons": {
"missing": ["goods_spec", "hang_price", "price_date"],
"invalid": []
}
}
]
}
}
```
### 使用示例
#### JavaScript (Fetch API)
```javascript
const response = await fetch('http://localhost:3000/api/prices/import', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
prices: [
{
goods_material: 'HPB300',
goods_spec: 'Φ8',
partsname_name: '螺纹钢',
productarea_name: '昆钢',
price_source: '我的钢铁',
price_region: '昆明',
price_date: '2026-01-05T10:30:00.000Z',
hang_price: 3850
}
]
})
});
const result = await response.json();
console.log(result);
```
#### cURL
```bash
curl -X POST http://localhost:3000/api/prices/import \
-H "Content-Type: application/json" \
-d '{
"prices": [
{
"goods_material": "HPB300",
"goods_spec": "Φ8",
"partsname_name": "螺纹钢",
"productarea_name": "昆钢",
"price_source": "我的钢铁",
"price_region": "昆明",
"price_date": "2026-01-05T10:30:00.000Z",
"hang_price": 3850
}
]
}'
```
### 注意事项
1. **批量限制**:建议单次导入不超过 1000 条记录
2. **重复数据处理**:使用 `ON DUPLICATE KEY UPDATE` 机制自动更新已存在的记录
3. **事务处理**:每次批量插入是一个事务,失败会回滚
4. **错误详情**:响应中最多返回前 10 个错误详情
5. **price_id 唯一性**:系统会自动确保 price_id 的唯一性,无需手动处理
### 完整字段对照表
| 数据库字段 | JSON 字段 | 必填 | 类型 | 说明 |
|-----------|-----------|------|------|------|
| `price_id` | `price_id` | ❌ | string | 价格唯一ID |
| `goods_material` | `goods_material` | ✅ | string | 材质牌号 |
| `goods_spec` | `goods_spec` | ✅ | string | 规格型号 |
| `partsname_name` | `partsname_name` | ✅ | string | 品名 |
| `productarea_name` | `productarea_name` | ✅ | string | 产地/钢厂 |
| `price_source` | `price_source` | ✅ | string | 价格来源 |
| `price_region` | `price_region` | ✅ | string | 价格地区 |
| `pntree_name` | `pntree_name` | ❌ | string | 分类名称 |
| `price_date` | `price_date` | ✅ | string | 价格日期 |
| `make_price` | `make_price` | ❌ | number | 钢厂价 |
| `hang_price` | `hang_price` | ✅ | number | 挂牌价 |
| `last_make_price` | `last_make_price` | ❌ | number | 上次钢厂价 |
| `last_hang_price` | `last_hang_price` | ❌ | number | 上次挂牌价 |
| `make_price_updw` | `make_price_updw` | ❌ | string | 钢厂价涨跌 |
| `hang_price_updw` | `hang_price_updw` | ❌ | string | 挂牌价涨跌 |
| `operator_code` | `operator_code` | ❌ | string | 操作员代码 |
| `operator_name` | `operator_name` | ❌ | string | 操作员名称 |
| `created_at` | - | - | - | 自动生成 |
| `updated_at` | - | - | - | 自动生成 |