Compare commits
No commits in common. "main" and "stable" have entirely different histories.
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"extends": "./node_modules/mwts/",
|
"extends": "./node_modules/mwts/",
|
||||||
"ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings", "scripts"],
|
"ignorePatterns": ["node_modules", "dist", "test", "jest.config.js", "typings"],
|
||||||
"env": {
|
"env": {
|
||||||
"jest": true
|
"jest": true
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,4 @@ run/
|
||||||
yarn.lock
|
yarn.lock
|
||||||
**/config.prod.ts
|
**/config.prod.ts
|
||||||
**/config.local.ts
|
**/config.local.ts
|
||||||
container
|
container
|
||||||
scripts
|
|
||||||
tmp_uploads/
|
|
||||||
.trae
|
|
||||||
docs
|
|
||||||
23
Dockerfile
23
Dockerfile
|
|
@ -1,23 +0,0 @@
|
||||||
# 使用 Node.js 作为基础镜像
|
|
||||||
FROM node:22-alpine
|
|
||||||
|
|
||||||
# 设置工作目录
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
# 复制 package.json 和 package-lock.json
|
|
||||||
COPY package*.json ./
|
|
||||||
|
|
||||||
# 安装依赖
|
|
||||||
RUN npm install --production
|
|
||||||
|
|
||||||
# 复制源代码
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# 构建项目
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
# 暴露端口
|
|
||||||
EXPOSE 7001
|
|
||||||
|
|
||||||
# 启动服务
|
|
||||||
CMD ["npm", "run", "prod"]
|
|
||||||
254
area-api-doc.md
254
area-api-doc.md
|
|
@ -1,254 +0,0 @@
|
||||||
# Area 区域管理 API 文档
|
|
||||||
|
|
||||||
## 概述
|
|
||||||
|
|
||||||
本文档详细描述了区域管理相关的API接口,包括增删改查操作以及新增的坐标功能.
|
|
||||||
|
|
||||||
## API 接口列表
|
|
||||||
|
|
||||||
### 1. 创建区域
|
|
||||||
|
|
||||||
**请求信息**
|
|
||||||
- URL: `/api/area`
|
|
||||||
- Method: `POST`
|
|
||||||
- Headers: `Authorization: Bearer {token}`
|
|
||||||
|
|
||||||
**请求体 (JSON)**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "欧洲",
|
|
||||||
"latitude": 48.8566,
|
|
||||||
"longitude": 2.3522
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**参数说明**
|
|
||||||
- `name`: 区域名称 (必填)
|
|
||||||
- `latitude`: 纬度 (-90 到 90 之间,可选)
|
|
||||||
- `longitude`: 经度 (-180 到 180 之间,可选)
|
|
||||||
|
|
||||||
**响应示例**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "创建成功",
|
|
||||||
"data": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "欧洲",
|
|
||||||
"latitude": 48.8566,
|
|
||||||
"longitude": 2.3522,
|
|
||||||
"createdAt": "2024-01-01T12:00:00Z",
|
|
||||||
"updatedAt": "2024-01-01T12:00:00Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. 更新区域
|
|
||||||
|
|
||||||
**请求信息**
|
|
||||||
- URL: `/api/area/{id}`
|
|
||||||
- Method: `PUT`
|
|
||||||
- Headers: `Authorization: Bearer {token}`
|
|
||||||
|
|
||||||
**请求体 (JSON)**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "欧洲区域",
|
|
||||||
"latitude": 48.8566,
|
|
||||||
"longitude": 2.3522
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**参数说明**
|
|
||||||
- `name`: 区域名称 (可选)
|
|
||||||
- `latitude`: 纬度 (-90 到 90 之间,可选)
|
|
||||||
- `longitude`: 经度 (-180 到 180 之间,可选)
|
|
||||||
|
|
||||||
**响应示例**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "更新成功",
|
|
||||||
"data": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "欧洲区域",
|
|
||||||
"latitude": 48.8566,
|
|
||||||
"longitude": 2.3522,
|
|
||||||
"createdAt": "2024-01-01T12:00:00Z",
|
|
||||||
"updatedAt": "2024-01-01T12:30:00Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 删除区域
|
|
||||||
|
|
||||||
**请求信息**
|
|
||||||
- URL: `/api/area/{id}`
|
|
||||||
- Method: `DELETE`
|
|
||||||
- Headers: `Authorization: Bearer {token}`
|
|
||||||
|
|
||||||
**响应示例**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "删除成功",
|
|
||||||
"data": null
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 获取区域列表(分页)
|
|
||||||
|
|
||||||
**请求信息**
|
|
||||||
- URL: `/api/area`
|
|
||||||
- Method: `GET`
|
|
||||||
- Headers: `Authorization: Bearer {token}`
|
|
||||||
- Query Parameters:
|
|
||||||
- `currentPage`: 当前页码 (默认 1)
|
|
||||||
- `pageSize`: 每页数量 (默认 10)
|
|
||||||
- `name`: 区域名称(可选,用于搜索)
|
|
||||||
|
|
||||||
**响应示例**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "查询成功",
|
|
||||||
"data": {
|
|
||||||
"list": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "欧洲",
|
|
||||||
"latitude": 48.8566,
|
|
||||||
"longitude": 2.3522,
|
|
||||||
"createdAt": "2024-01-01T12:00:00Z",
|
|
||||||
"updatedAt": "2024-01-01T12:00:00Z"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"total": 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. 获取所有区域
|
|
||||||
|
|
||||||
**请求信息**
|
|
||||||
- URL: `/api/area/all`
|
|
||||||
- Method: `GET`
|
|
||||||
- Headers: `Authorization: Bearer {token}`
|
|
||||||
|
|
||||||
**响应示例**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "查询成功",
|
|
||||||
"data": [
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"name": "欧洲",
|
|
||||||
"latitude": 48.8566,
|
|
||||||
"longitude": 2.3522,
|
|
||||||
"createdAt": "2024-01-01T12:00:00Z",
|
|
||||||
"updatedAt": "2024-01-01T12:00:00Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 2,
|
|
||||||
"name": "亚洲",
|
|
||||||
"latitude": 35.6762,
|
|
||||||
"longitude": 139.6503,
|
|
||||||
"createdAt": "2024-01-01T12:10:00Z",
|
|
||||||
"updatedAt": "2024-01-01T12:10:00Z"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. 根据ID获取区域详情
|
|
||||||
|
|
||||||
**请求信息**
|
|
||||||
- URL: `/api/area/{id}`
|
|
||||||
- Method: `GET`
|
|
||||||
- Headers: `Authorization: Bearer {token}`
|
|
||||||
|
|
||||||
**响应示例**
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"code": 0,
|
|
||||||
"message": "查询成功",
|
|
||||||
"data": {
|
|
||||||
"id": 1,
|
|
||||||
"name": "欧洲",
|
|
||||||
"latitude": 48.8566,
|
|
||||||
"longitude": 2.3522,
|
|
||||||
"createdAt": "2024-01-01T12:00:00Z",
|
|
||||||
"updatedAt": "2024-01-01T12:00:00Z"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 世界地图实现建议
|
|
||||||
|
|
||||||
对于前端实现世界地图并显示区域坐标,推荐以下方案:
|
|
||||||
|
|
||||||
### 1. 使用开源地图库
|
|
||||||
|
|
||||||
- **Leaflet.js**: 轻量级开源地图库,易于集成
|
|
||||||
- **Mapbox**: 提供丰富的地图样式和交互功能
|
|
||||||
- **Google Maps API**: 功能强大但需要API密钥
|
|
||||||
|
|
||||||
### 2. 实现步骤
|
|
||||||
|
|
||||||
1. **获取区域数据**:
|
|
||||||
使用 `/api/area/all` 接口获取所有包含坐标信息的区域
|
|
||||||
|
|
||||||
2. **初始化地图**:
|
|
||||||
```javascript
|
|
||||||
// Leaflet示例
|
|
||||||
const map = L.map('map').setView([0, 0], 2);
|
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
||||||
attribution: '© OpenStreetMap contributors'
|
|
||||||
}).addTo(map);
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **添加区域标记**:
|
|
||||||
```javascript
|
|
||||||
// 假设areas是从API获取的数据
|
|
||||||
areas.forEach(area => {
|
|
||||||
if (area.latitude && area.longitude) {
|
|
||||||
const marker = L.marker([area.latitude, area.longitude]).addTo(map);
|
|
||||||
marker.bindPopup(`<b>${area.name}</b>`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **添加交互功能**:
|
|
||||||
- 点击标记显示区域详情
|
|
||||||
- 搜索和筛选功能
|
|
||||||
- 编辑坐标功能(调用更新API)
|
|
||||||
|
|
||||||
### 3. 坐标输入建议
|
|
||||||
|
|
||||||
在区域管理界面,可以添加以下功能来辅助坐标输入:
|
|
||||||
|
|
||||||
1. 提供搜索框,根据地点名称自动获取坐标
|
|
||||||
2. 集成小型地图,允许用户点击选择位置
|
|
||||||
3. 添加验证,确保输入的坐标在有效范围内
|
|
||||||
|
|
||||||
## 数据模型说明
|
|
||||||
|
|
||||||
### Area 实体
|
|
||||||
|
|
||||||
| 字段名 | 类型 | 描述 | 是否必填 |
|
|
||||||
|--------|------|------|----------|
|
|
||||||
| id | number | 区域ID | 否(自动生成) |
|
|
||||||
| name | string | 区域名称 | 是 |
|
|
||||||
| latitude | number | 纬度(范围:-90 到 90) | 否 |
|
|
||||||
| longitude | number | 经度(范围:-180 到 180) | 否 |
|
|
||||||
| createdAt | Date | 创建时间 | 否(自动生成) |
|
|
||||||
| updatedAt | Date | 更新时间 | 否(自动生成) |
|
|
||||||
|
|
||||||
## 错误处理
|
|
||||||
|
|
||||||
API可能返回的错误信息:
|
|
||||||
|
|
||||||
- `区域名称已存在`: 当尝试创建或更新区域名称与现有名称重复时
|
|
||||||
- `区域不存在`: 当尝试更新或删除不存在的区域时
|
|
||||||
- `权限错误`: 当请求缺少有效的授权令牌时
|
|
||||||
File diff suppressed because one or more lines are too long
24
output.log
24
output.log
|
|
@ -1,24 +0,0 @@
|
||||||
|
|
||||||
> my-midway-project@1.0.0 dev
|
|
||||||
> cross-env NODE_ENV=local mwtsc --watch --run @midwayjs/mock/app.js
|
|
||||||
|
|
||||||
[2J[3J[H
|
|
||||||
[[90m10:37:17 AM[0m] Starting compilation in watch mode...
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[[90m10:37:19 AM[0m] Found 0 errors. Watching for file changes.
|
|
||||||
|
|
||||||
2025-12-01 10:37:20.106 INFO 58678 [SyncProductJob] start job SyncProductJob
|
|
||||||
2025-12-01 10:37:20.106 INFO 58678 [SyncShipmentJob] start job SyncShipmentJob
|
|
||||||
2025-12-01 10:37:20.109 INFO 58678 [SyncProductJob] complete job SyncProductJob
|
|
||||||
|
|
||||||
[32mNode.js server[0m [2mstarted in[0m 732 ms
|
|
||||||
|
|
||||||
[32m➜[0m Local: [36mhttp://127.0.0.1:[1m7001[0m[36m/[0m[0m
|
|
||||||
[32m➜[0m [2mNetwork: http://192.168.5.100:7001/ [0m
|
|
||||||
|
|
||||||
2025-12-01 10:37:20.110 INFO 58678 [SyncShipmentJob] complete job SyncShipmentJob
|
|
||||||
File diff suppressed because it is too large
Load Diff
28
package.json
28
package.json
|
|
@ -15,26 +15,16 @@
|
||||||
"@midwayjs/logger": "^3.1.0",
|
"@midwayjs/logger": "^3.1.0",
|
||||||
"@midwayjs/swagger": "^3.20.2",
|
"@midwayjs/swagger": "^3.20.2",
|
||||||
"@midwayjs/typeorm": "^3.20.0",
|
"@midwayjs/typeorm": "^3.20.0",
|
||||||
"@midwayjs/upload": "^3.20.16",
|
|
||||||
"@midwayjs/validate": "^3.20.2",
|
"@midwayjs/validate": "^3.20.2",
|
||||||
"@woocommerce/woocommerce-rest-api": "^1.0.2",
|
"@woocommerce/woocommerce-rest-api": "^1.0.2",
|
||||||
"axios": "^1.13.2",
|
"axios": "^1.7.9",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"csv-parse": "^6.1.0",
|
|
||||||
"dayjs": "^1.11.13",
|
"dayjs": "^1.11.13",
|
||||||
"eta": "^4.4.1",
|
"mysql2": "^3.11.5",
|
||||||
"i18n-iso-countries": "^7.14.0",
|
|
||||||
"mysql2": "^3.15.3",
|
|
||||||
"nodemailer": "^7.0.5",
|
"nodemailer": "^7.0.5",
|
||||||
"npm-check-updates": "^19.1.2",
|
|
||||||
"qs": "^6.14.0",
|
|
||||||
"sharp": "^0.33.3",
|
|
||||||
"swagger-ui-dist": "^5.18.2",
|
"swagger-ui-dist": "^5.18.2",
|
||||||
"typeorm": "^0.3.27",
|
"typeorm": "^0.3.20",
|
||||||
"typeorm-extension": "^3.7.2",
|
|
||||||
"wpapi": "^1.2.2",
|
|
||||||
"xlsx": "^0.18.5",
|
|
||||||
"xml2js": "^0.6.2"
|
"xml2js": "^0.6.2"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
@ -46,15 +36,10 @@
|
||||||
"dev": "cross-env NODE_ENV=local mwtsc --watch --run @midwayjs/mock/app.js",
|
"dev": "cross-env NODE_ENV=local mwtsc --watch --run @midwayjs/mock/app.js",
|
||||||
"test": "cross-env NODE_ENV=unittest jest",
|
"test": "cross-env NODE_ENV=unittest jest",
|
||||||
"cov": "jest --coverage",
|
"cov": "jest --coverage",
|
||||||
"lint": "mwtsc check",
|
"lint": "mwts check",
|
||||||
"lint:fix": "mwtsc fix",
|
"lint:fix": "mwts fix",
|
||||||
"ci": "npm run cov",
|
"ci": "npm run cov",
|
||||||
"build": "mwtsc --cleanOutDir",
|
"build": "mwtsc --cleanOutDir"
|
||||||
"seed": "ts-node src/db/seed/index.ts",
|
|
||||||
"seed:run": "ts-node ./node_modules/typeorm-extension/bin/cli.cjs seed:run -d src/db/datasource.ts",
|
|
||||||
"typeorm": "ts-node ./node_modules/typeorm/cli.js",
|
|
||||||
"migration:generate": "npm run typeorm -- -d src/db/datasource.ts migration:generate",
|
|
||||||
"migration:run": "npm run typeorm -- migration:run -d src/db/datasource.ts"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
@ -66,7 +51,6 @@
|
||||||
"@midwayjs/mock": "^3.20.11",
|
"@midwayjs/mock": "^3.20.11",
|
||||||
"cross-env": "^10.1.0",
|
"cross-env": "^10.1.0",
|
||||||
"mwtsc": "^1.15.2",
|
"mwtsc": "^1.15.2",
|
||||||
"tsx": "^4.20.6",
|
|
||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
365
pnpm-lock.yaml
365
pnpm-lock.yaml
|
|
@ -65,18 +65,12 @@ importers:
|
||||||
nodemailer:
|
nodemailer:
|
||||||
specifier: ^7.0.5
|
specifier: ^7.0.5
|
||||||
version: 7.0.10
|
version: 7.0.10
|
||||||
npm-check-updates:
|
|
||||||
specifier: ^19.1.2
|
|
||||||
version: 19.1.2
|
|
||||||
swagger-ui-dist:
|
swagger-ui-dist:
|
||||||
specifier: ^5.18.2
|
specifier: ^5.18.2
|
||||||
version: 5.30.2
|
version: 5.30.2
|
||||||
typeorm:
|
typeorm:
|
||||||
specifier: ^0.3.20
|
specifier: ^0.3.20
|
||||||
version: 0.3.27(mysql2@3.15.3)(reflect-metadata@0.2.2)
|
version: 0.3.27(mysql2@3.15.3)(reflect-metadata@0.2.2)
|
||||||
xlsx:
|
|
||||||
specifier: ^0.18.5
|
|
||||||
version: 0.18.5
|
|
||||||
xml2js:
|
xml2js:
|
||||||
specifier: ^0.6.2
|
specifier: ^0.6.2
|
||||||
version: 0.6.2
|
version: 0.6.2
|
||||||
|
|
@ -90,9 +84,6 @@ importers:
|
||||||
mwtsc:
|
mwtsc:
|
||||||
specifier: ^1.15.2
|
specifier: ^1.15.2
|
||||||
version: 1.15.2
|
version: 1.15.2
|
||||||
tsx:
|
|
||||||
specifier: ^4.20.6
|
|
||||||
version: 4.20.6
|
|
||||||
typescript:
|
typescript:
|
||||||
specifier: ^5.9.3
|
specifier: ^5.9.3
|
||||||
version: 5.9.3
|
version: 5.9.3
|
||||||
|
|
@ -106,162 +97,6 @@ packages:
|
||||||
'@epic-web/invariant@1.0.0':
|
'@epic-web/invariant@1.0.0':
|
||||||
resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==}
|
resolution: {integrity: sha512-lrTPqgvfFQtR/eY/qkIzp98OGdNJu0m5ji3q/nJI8v3SXkRKEnWiOxMmbvcSoAIzv/cGiuvRy57k4suKQSAdwA==}
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [ppc64]
|
|
||||||
os: [aix]
|
|
||||||
|
|
||||||
'@esbuild/android-arm64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [android]
|
|
||||||
|
|
||||||
'@esbuild/android-arm@0.25.12':
|
|
||||||
resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [arm]
|
|
||||||
os: [android]
|
|
||||||
|
|
||||||
'@esbuild/android-x64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [android]
|
|
||||||
|
|
||||||
'@esbuild/darwin-arm64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [darwin]
|
|
||||||
|
|
||||||
'@esbuild/darwin-x64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [darwin]
|
|
||||||
|
|
||||||
'@esbuild/freebsd-arm64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [freebsd]
|
|
||||||
|
|
||||||
'@esbuild/freebsd-x64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [freebsd]
|
|
||||||
|
|
||||||
'@esbuild/linux-arm64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@esbuild/linux-arm@0.25.12':
|
|
||||||
resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [arm]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@esbuild/linux-ia32@0.25.12':
|
|
||||||
resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [ia32]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@esbuild/linux-loong64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [loong64]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@esbuild/linux-mips64el@0.25.12':
|
|
||||||
resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [mips64el]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@esbuild/linux-ppc64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [ppc64]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@esbuild/linux-riscv64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [riscv64]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@esbuild/linux-s390x@0.25.12':
|
|
||||||
resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [s390x]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@esbuild/linux-x64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [linux]
|
|
||||||
|
|
||||||
'@esbuild/netbsd-arm64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [netbsd]
|
|
||||||
|
|
||||||
'@esbuild/netbsd-x64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [netbsd]
|
|
||||||
|
|
||||||
'@esbuild/openbsd-arm64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [openbsd]
|
|
||||||
|
|
||||||
'@esbuild/openbsd-x64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [openbsd]
|
|
||||||
|
|
||||||
'@esbuild/openharmony-arm64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [openharmony]
|
|
||||||
|
|
||||||
'@esbuild/sunos-x64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [sunos]
|
|
||||||
|
|
||||||
'@esbuild/win32-arm64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [arm64]
|
|
||||||
os: [win32]
|
|
||||||
|
|
||||||
'@esbuild/win32-ia32@0.25.12':
|
|
||||||
resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [ia32]
|
|
||||||
os: [win32]
|
|
||||||
|
|
||||||
'@esbuild/win32-x64@0.25.12':
|
|
||||||
resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
cpu: [x64]
|
|
||||||
os: [win32]
|
|
||||||
|
|
||||||
'@hapi/bourne@3.0.0':
|
'@hapi/bourne@3.0.0':
|
||||||
resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
|
resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
|
||||||
|
|
||||||
|
|
@ -479,10 +314,6 @@ packages:
|
||||||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
adler-32@1.3.1:
|
|
||||||
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
ansi-regex@5.0.1:
|
ansi-regex@5.0.1:
|
||||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
@ -584,10 +415,6 @@ packages:
|
||||||
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
|
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
cfb@1.2.2:
|
|
||||||
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
chokidar@3.6.0:
|
chokidar@3.6.0:
|
||||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||||
engines: {node: '>= 8.10.0'}
|
engines: {node: '>= 8.10.0'}
|
||||||
|
|
@ -619,10 +446,6 @@ packages:
|
||||||
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
||||||
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
|
||||||
|
|
||||||
codepage@1.15.0:
|
|
||||||
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
engines: {node: '>=7.0.0'}
|
engines: {node: '>=7.0.0'}
|
||||||
|
|
@ -665,11 +488,6 @@ packages:
|
||||||
core-util-is@1.0.3:
|
core-util-is@1.0.3:
|
||||||
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==}
|
||||||
|
|
||||||
crc-32@1.2.2:
|
|
||||||
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
create-hash@1.2.0:
|
create-hash@1.2.0:
|
||||||
resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
|
resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
|
||||||
|
|
||||||
|
|
@ -788,11 +606,6 @@ packages:
|
||||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
esbuild@0.25.12:
|
|
||||||
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
|
|
||||||
engines: {node: '>=18'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
escalade@3.2.0:
|
escalade@3.2.0:
|
||||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
@ -838,10 +651,6 @@ packages:
|
||||||
formidable@2.1.5:
|
formidable@2.1.5:
|
||||||
resolution: {integrity: sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==}
|
resolution: {integrity: sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==}
|
||||||
|
|
||||||
frac@1.1.2:
|
|
||||||
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
fresh@0.5.2:
|
fresh@0.5.2:
|
||||||
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
@ -1152,11 +961,6 @@ packages:
|
||||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
npm-check-updates@19.1.2:
|
|
||||||
resolution: {integrity: sha512-FNeFCVgPOj0fz89hOpGtxP2rnnRHR7hD2E8qNU8SMWfkyDZXA/xpgjsL3UMLSo3F/K13QvJDnbxPngulNDDo/g==}
|
|
||||||
engines: {node: '>=20.0.0', npm: '>=8.12.1'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
oauth-1.0a@2.2.6:
|
oauth-1.0a@2.2.6:
|
||||||
resolution: {integrity: sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ==}
|
resolution: {integrity: sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ==}
|
||||||
|
|
||||||
|
|
@ -1356,10 +1160,6 @@ packages:
|
||||||
resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
|
resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
ssf@0.11.2:
|
|
||||||
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
statuses@1.5.0:
|
statuses@1.5.0:
|
||||||
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
|
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
@ -1428,11 +1228,6 @@ packages:
|
||||||
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
|
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
|
||||||
engines: {node: '>=0.6.x'}
|
engines: {node: '>=0.6.x'}
|
||||||
|
|
||||||
tsx@4.20.6:
|
|
||||||
resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==}
|
|
||||||
engines: {node: '>=18.0.0'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
type-is@1.6.18:
|
type-is@1.6.18:
|
||||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
@ -1532,14 +1327,6 @@ packages:
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
wmf@1.0.2:
|
|
||||||
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
word@0.3.0:
|
|
||||||
resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
|
|
||||||
wrap-ansi@7.0.0:
|
wrap-ansi@7.0.0:
|
||||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
@ -1551,11 +1338,6 @@ packages:
|
||||||
wrappy@1.0.2:
|
wrappy@1.0.2:
|
||||||
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
|
||||||
|
|
||||||
xlsx@0.18.5:
|
|
||||||
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
|
|
||||||
engines: {node: '>=0.8'}
|
|
||||||
hasBin: true
|
|
||||||
|
|
||||||
xml2js@0.6.2:
|
xml2js@0.6.2:
|
||||||
resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
|
resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
|
||||||
engines: {node: '>=4.0.0'}
|
engines: {node: '>=4.0.0'}
|
||||||
|
|
@ -1591,84 +1373,6 @@ snapshots:
|
||||||
|
|
||||||
'@epic-web/invariant@1.0.0': {}
|
'@epic-web/invariant@1.0.0': {}
|
||||||
|
|
||||||
'@esbuild/aix-ppc64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/android-arm64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/android-arm@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/android-x64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/darwin-arm64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/darwin-x64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/freebsd-arm64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/freebsd-x64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/linux-arm64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/linux-arm@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/linux-ia32@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/linux-loong64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/linux-mips64el@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/linux-ppc64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/linux-riscv64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/linux-s390x@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/linux-x64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/netbsd-arm64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/netbsd-x64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/openbsd-arm64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/openbsd-x64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/openharmony-arm64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/sunos-x64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/win32-arm64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/win32-ia32@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@esbuild/win32-x64@0.25.12':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@hapi/bourne@3.0.0': {}
|
'@hapi/bourne@3.0.0': {}
|
||||||
|
|
||||||
'@hapi/hoek@9.3.0': {}
|
'@hapi/hoek@9.3.0': {}
|
||||||
|
|
@ -1941,8 +1645,6 @@ snapshots:
|
||||||
mime-types: 2.1.35
|
mime-types: 2.1.35
|
||||||
negotiator: 0.6.3
|
negotiator: 0.6.3
|
||||||
|
|
||||||
adler-32@1.3.1: {}
|
|
||||||
|
|
||||||
ansi-regex@5.0.1: {}
|
ansi-regex@5.0.1: {}
|
||||||
|
|
||||||
ansi-regex@6.2.2: {}
|
ansi-regex@6.2.2: {}
|
||||||
|
|
@ -2033,11 +1735,6 @@ snapshots:
|
||||||
call-bind-apply-helpers: 1.0.2
|
call-bind-apply-helpers: 1.0.2
|
||||||
get-intrinsic: 1.3.0
|
get-intrinsic: 1.3.0
|
||||||
|
|
||||||
cfb@1.2.2:
|
|
||||||
dependencies:
|
|
||||||
adler-32: 1.3.1
|
|
||||||
crc-32: 1.2.2
|
|
||||||
|
|
||||||
chokidar@3.6.0:
|
chokidar@3.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
anymatch: 3.1.3
|
anymatch: 3.1.3
|
||||||
|
|
@ -2082,8 +1779,6 @@ snapshots:
|
||||||
|
|
||||||
co@4.6.0: {}
|
co@4.6.0: {}
|
||||||
|
|
||||||
codepage@1.15.0: {}
|
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name: 1.1.4
|
color-name: 1.1.4
|
||||||
|
|
@ -2117,8 +1812,6 @@ snapshots:
|
||||||
|
|
||||||
core-util-is@1.0.3: {}
|
core-util-is@1.0.3: {}
|
||||||
|
|
||||||
crc-32@1.2.2: {}
|
|
||||||
|
|
||||||
create-hash@1.2.0:
|
create-hash@1.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
cipher-base: 1.0.7
|
cipher-base: 1.0.7
|
||||||
|
|
@ -2226,35 +1919,6 @@ snapshots:
|
||||||
has-tostringtag: 1.0.2
|
has-tostringtag: 1.0.2
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
|
|
||||||
esbuild@0.25.12:
|
|
||||||
optionalDependencies:
|
|
||||||
'@esbuild/aix-ppc64': 0.25.12
|
|
||||||
'@esbuild/android-arm': 0.25.12
|
|
||||||
'@esbuild/android-arm64': 0.25.12
|
|
||||||
'@esbuild/android-x64': 0.25.12
|
|
||||||
'@esbuild/darwin-arm64': 0.25.12
|
|
||||||
'@esbuild/darwin-x64': 0.25.12
|
|
||||||
'@esbuild/freebsd-arm64': 0.25.12
|
|
||||||
'@esbuild/freebsd-x64': 0.25.12
|
|
||||||
'@esbuild/linux-arm': 0.25.12
|
|
||||||
'@esbuild/linux-arm64': 0.25.12
|
|
||||||
'@esbuild/linux-ia32': 0.25.12
|
|
||||||
'@esbuild/linux-loong64': 0.25.12
|
|
||||||
'@esbuild/linux-mips64el': 0.25.12
|
|
||||||
'@esbuild/linux-ppc64': 0.25.12
|
|
||||||
'@esbuild/linux-riscv64': 0.25.12
|
|
||||||
'@esbuild/linux-s390x': 0.25.12
|
|
||||||
'@esbuild/linux-x64': 0.25.12
|
|
||||||
'@esbuild/netbsd-arm64': 0.25.12
|
|
||||||
'@esbuild/netbsd-x64': 0.25.12
|
|
||||||
'@esbuild/openbsd-arm64': 0.25.12
|
|
||||||
'@esbuild/openbsd-x64': 0.25.12
|
|
||||||
'@esbuild/openharmony-arm64': 0.25.12
|
|
||||||
'@esbuild/sunos-x64': 0.25.12
|
|
||||||
'@esbuild/win32-arm64': 0.25.12
|
|
||||||
'@esbuild/win32-ia32': 0.25.12
|
|
||||||
'@esbuild/win32-x64': 0.25.12
|
|
||||||
|
|
||||||
escalade@3.2.0: {}
|
escalade@3.2.0: {}
|
||||||
|
|
||||||
escape-html@1.0.3: {}
|
escape-html@1.0.3: {}
|
||||||
|
|
@ -2303,8 +1967,6 @@ snapshots:
|
||||||
once: 1.4.0
|
once: 1.4.0
|
||||||
qs: 6.14.0
|
qs: 6.14.0
|
||||||
|
|
||||||
frac@1.1.2: {}
|
|
||||||
|
|
||||||
fresh@0.5.2: {}
|
fresh@0.5.2: {}
|
||||||
|
|
||||||
fsevents@2.3.3:
|
fsevents@2.3.3:
|
||||||
|
|
@ -2651,8 +2313,6 @@ snapshots:
|
||||||
|
|
||||||
normalize-path@3.0.0: {}
|
normalize-path@3.0.0: {}
|
||||||
|
|
||||||
npm-check-updates@19.1.2: {}
|
|
||||||
|
|
||||||
oauth-1.0a@2.2.6: {}
|
oauth-1.0a@2.2.6: {}
|
||||||
|
|
||||||
object-inspect@1.13.4: {}
|
object-inspect@1.13.4: {}
|
||||||
|
|
@ -2834,10 +2494,6 @@ snapshots:
|
||||||
|
|
||||||
sqlstring@2.3.3: {}
|
sqlstring@2.3.3: {}
|
||||||
|
|
||||||
ssf@0.11.2:
|
|
||||||
dependencies:
|
|
||||||
frac: 1.1.2
|
|
||||||
|
|
||||||
statuses@1.5.0: {}
|
statuses@1.5.0: {}
|
||||||
|
|
||||||
statuses@2.0.1: {}
|
statuses@2.0.1: {}
|
||||||
|
|
@ -2926,13 +2582,6 @@ snapshots:
|
||||||
|
|
||||||
tsscmp@1.0.6: {}
|
tsscmp@1.0.6: {}
|
||||||
|
|
||||||
tsx@4.20.6:
|
|
||||||
dependencies:
|
|
||||||
esbuild: 0.25.12
|
|
||||||
get-tsconfig: 4.13.0
|
|
||||||
optionalDependencies:
|
|
||||||
fsevents: 2.3.3
|
|
||||||
|
|
||||||
type-is@1.6.18:
|
type-is@1.6.18:
|
||||||
dependencies:
|
dependencies:
|
||||||
media-typer: 0.3.0
|
media-typer: 0.3.0
|
||||||
|
|
@ -2998,10 +2647,6 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
|
|
||||||
wmf@1.0.2: {}
|
|
||||||
|
|
||||||
word@0.3.0: {}
|
|
||||||
|
|
||||||
wrap-ansi@7.0.0:
|
wrap-ansi@7.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
|
|
@ -3016,16 +2661,6 @@ snapshots:
|
||||||
|
|
||||||
wrappy@1.0.2: {}
|
wrappy@1.0.2: {}
|
||||||
|
|
||||||
xlsx@0.18.5:
|
|
||||||
dependencies:
|
|
||||||
adler-32: 1.3.1
|
|
||||||
cfb: 1.2.2
|
|
||||||
codepage: 1.15.0
|
|
||||||
crc-32: 1.2.2
|
|
||||||
ssf: 0.11.2
|
|
||||||
wmf: 1.0.2
|
|
||||||
word: 0.3.0
|
|
||||||
|
|
||||||
xml2js@0.6.2:
|
xml2js@0.6.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
sax: 1.4.3
|
sax: 1.4.3
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,8 @@
|
||||||
import { MidwayConfig } from '@midwayjs/core';
|
import { MidwayConfig } from '@midwayjs/core';
|
||||||
import { join } from 'path';
|
|
||||||
import { Product } from '../entity/product.entity';
|
import { Product } from '../entity/product.entity';
|
||||||
|
import { Category } from '../entity/category.entity';
|
||||||
|
import { WpProduct } from '../entity/wp_product.entity';
|
||||||
|
import { Variation } from '../entity/variation.entity';
|
||||||
import { User } from '../entity/user.entity';
|
import { User } from '../entity/user.entity';
|
||||||
import { PurchaseOrder } from '../entity/purchase_order.entity';
|
import { PurchaseOrder } from '../entity/purchase_order.entity';
|
||||||
import { PurchaseOrderItem } from '../entity/purchase_order_item.entity';
|
import { PurchaseOrderItem } from '../entity/purchase_order_item.entity';
|
||||||
|
|
@ -9,14 +11,13 @@ import { StockPoint } from '../entity/stock_point.entity';
|
||||||
import { StockRecord } from '../entity/stock_record.entity';
|
import { StockRecord } from '../entity/stock_record.entity';
|
||||||
import { Order } from '../entity/order.entity';
|
import { Order } from '../entity/order.entity';
|
||||||
import { OrderItem } from '../entity/order_item.entity';
|
import { OrderItem } from '../entity/order_item.entity';
|
||||||
import { OrderCoupon } from '../entity/order_coupon.entity';
|
import { OrderCoupon } from '../entity/order_copon.entity';
|
||||||
import { OrderFee } from '../entity/order_fee.entity';
|
import { OrderFee } from '../entity/order_fee.entity';
|
||||||
import { OrderRefund } from '../entity/order_refund.entity';
|
import { OrderRefund } from '../entity/order_refund.entity';
|
||||||
import { OrderRefundItem } from '../entity/order_refund_item.entity';
|
import { OrderRefundItem } from '../entity/order_retund_item.entity';
|
||||||
import { OrderSale } from '../entity/order_sale.entity';
|
import { OrderSale } from '../entity/order_sale.entity';
|
||||||
import { OrderItemOriginal } from '../entity/order_item_original.entity';
|
import { OrderSaleOriginal } from '../entity/order_item_original.entity';
|
||||||
import { OrderShipping } from '../entity/order_shipping.entity';
|
import { OrderShipping } from '../entity/order_shipping.entity';
|
||||||
import { OrderFulfillment } from '../entity/order_fulfillment.entity';
|
|
||||||
import { Service } from '../entity/service.entity';
|
import { Service } from '../entity/service.entity';
|
||||||
import { ShippingAddress } from '../entity/shipping_address.entity';
|
import { ShippingAddress } from '../entity/shipping_address.entity';
|
||||||
import { OrderNote } from '../entity/order_note.entity';
|
import { OrderNote } from '../entity/order_note.entity';
|
||||||
|
|
@ -25,22 +26,14 @@ import { Shipment } from '../entity/shipment.entity';
|
||||||
import { ShipmentItem } from '../entity/shipment_item.entity';
|
import { ShipmentItem } from '../entity/shipment_item.entity';
|
||||||
import { Transfer } from '../entity/transfer.entity';
|
import { Transfer } from '../entity/transfer.entity';
|
||||||
import { TransferItem } from '../entity/transfer_item.entity';
|
import { TransferItem } from '../entity/transfer_item.entity';
|
||||||
|
import { Strength } from '../entity/strength.entity';
|
||||||
|
import { Flavors } from '../entity/flavors.entity';
|
||||||
import { CustomerTag } from '../entity/customer_tag.entity';
|
import { CustomerTag } from '../entity/customer_tag.entity';
|
||||||
import { Customer } from '../entity/customer.entity';
|
import { Customer } from '../entity/customer.entity';
|
||||||
import { DeviceWhitelist } from '../entity/device_whitelist';
|
import { DeviceWhitelist } from '../entity/device_whitelist';
|
||||||
import { AuthCode } from '../entity/auth_code';
|
import { AuthCode } from '../entity/auth_code';
|
||||||
import { Subscription } from '../entity/subscription.entity';
|
import { Subscription } from '../entity/subscription.entity';
|
||||||
import { Site } from '../entity/site.entity';
|
import { Site } from '../entity/site.entity';
|
||||||
import { Dict } from '../entity/dict.entity';
|
|
||||||
import { DictItem } from '../entity/dict_item.entity';
|
|
||||||
import { Template } from '../entity/template.entity';
|
|
||||||
import { Area } from '../entity/area.entity';
|
|
||||||
import { ProductStockComponent } from '../entity/product_stock_component.entity';
|
|
||||||
import { CategoryAttribute } from '../entity/category_attribute.entity';
|
|
||||||
import { Category } from '../entity/category.entity';
|
|
||||||
import DictSeeder from '../db/seeds/dict.seeder';
|
|
||||||
import CategorySeeder from '../db/seeds/category.seeder';
|
|
||||||
import CategoryAttributeSeeder from '../db/seeds/category_attribute.seeder';
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// use for cookie sign key, should change to your own and keep security
|
// use for cookie sign key, should change to your own and keep security
|
||||||
|
|
@ -49,7 +42,11 @@ export default {
|
||||||
default: {
|
default: {
|
||||||
entities: [
|
entities: [
|
||||||
Product,
|
Product,
|
||||||
ProductStockComponent,
|
Category,
|
||||||
|
Strength,
|
||||||
|
Flavors,
|
||||||
|
WpProduct,
|
||||||
|
Variation,
|
||||||
User,
|
User,
|
||||||
PurchaseOrder,
|
PurchaseOrder,
|
||||||
PurchaseOrderItem,
|
PurchaseOrderItem,
|
||||||
|
|
@ -63,12 +60,11 @@ export default {
|
||||||
OrderRefund,
|
OrderRefund,
|
||||||
OrderRefundItem,
|
OrderRefundItem,
|
||||||
OrderSale,
|
OrderSale,
|
||||||
OrderItemOriginal,
|
OrderSaleOriginal,
|
||||||
OrderShipment,
|
OrderShipment,
|
||||||
ShipmentItem,
|
ShipmentItem,
|
||||||
Shipment,
|
Shipment,
|
||||||
OrderShipping,
|
OrderShipping,
|
||||||
OrderFulfillment,
|
|
||||||
Service,
|
Service,
|
||||||
ShippingAddress,
|
ShippingAddress,
|
||||||
OrderNote,
|
OrderNote,
|
||||||
|
|
@ -80,22 +76,15 @@ export default {
|
||||||
AuthCode,
|
AuthCode,
|
||||||
Subscription,
|
Subscription,
|
||||||
Site,
|
Site,
|
||||||
Dict,
|
|
||||||
DictItem,
|
|
||||||
Template,
|
|
||||||
Area,
|
|
||||||
CategoryAttribute,
|
|
||||||
Category,
|
|
||||||
],
|
],
|
||||||
synchronize: true,
|
synchronize: true,
|
||||||
logging: false,
|
logging: false,
|
||||||
seeders: [DictSeeder, CategorySeeder, CategoryAttributeSeeder],
|
|
||||||
},
|
},
|
||||||
dataSource: {
|
dataSource: {
|
||||||
default: {
|
default: {
|
||||||
type: 'mysql',
|
type: 'mysql',
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
port: 10014,
|
port: 3306,
|
||||||
username: 'root',
|
username: 'root',
|
||||||
password: 'root',
|
password: 'root',
|
||||||
database: 'inventory',
|
database: 'inventory',
|
||||||
|
|
@ -106,12 +95,23 @@ export default {
|
||||||
// origin: '*', // 允许所有来源跨域请求
|
// origin: '*', // 允许所有来源跨域请求
|
||||||
// allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 允许的 HTTP 方法
|
// allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 允许的 HTTP 方法
|
||||||
// allowHeaders: ['Content-Type', 'Authorization'], // 允许的自定义请求头
|
// allowHeaders: ['Content-Type', 'Authorization'], // 允许的自定义请求头
|
||||||
// credentials: true, // 允许携带凭据(cookies等)
|
// credentials: true, // 允许携带凭据(cookies等)
|
||||||
// },
|
// },
|
||||||
// jwt: {
|
// jwt: {
|
||||||
// secret: 'YOONE2024!@abc',
|
// secret: 'YOONE2024!@abc',
|
||||||
// expiresIn: '7d',
|
// expiresIn: '7d',
|
||||||
// },
|
// },
|
||||||
|
// wpSite: [
|
||||||
|
// {
|
||||||
|
// id: '2',
|
||||||
|
// wpApiUrl: 'http://localhost:10004',
|
||||||
|
// consumerKey: 'ck_dc9e151e9048c8ed3e27f35ac79d2bf7d6840652',
|
||||||
|
// consumerSecret: 'cs_d05d625d7b0ac05c6d765671d8417f41d9477e38',
|
||||||
|
// siteName: 'Local',
|
||||||
|
// email: 'tom@yoonevape.com',
|
||||||
|
// emailPswd: '',
|
||||||
|
// },
|
||||||
|
// ],
|
||||||
swagger: {
|
swagger: {
|
||||||
auth: {
|
auth: {
|
||||||
name: 'authorization',
|
name: 'authorization',
|
||||||
|
|
@ -119,15 +119,6 @@ export default {
|
||||||
description: 'Bearer Auth',
|
description: 'Bearer Auth',
|
||||||
addSecurityRequirements: true,
|
addSecurityRequirements: true,
|
||||||
},
|
},
|
||||||
// 配置 Swagger 支持嵌套查询参数
|
|
||||||
options: {
|
|
||||||
// 设置查询参数风格为 deepObject
|
|
||||||
// 这会告诉 Swagger 使用 JSON 格式来序列化嵌套的查询参数
|
|
||||||
query: {
|
|
||||||
style: 'deepObject',
|
|
||||||
explode: false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
mailer: {
|
mailer: {
|
||||||
host: 'smtphz.qiye.163.com',
|
host: 'smtphz.qiye.163.com',
|
||||||
|
|
@ -137,16 +128,5 @@ export default {
|
||||||
user: 'info@canpouches.com',
|
user: 'info@canpouches.com',
|
||||||
pass: 'WWqQ4aZq4Jrm9uwz',
|
pass: 'WWqQ4aZq4Jrm9uwz',
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
upload: {
|
|
||||||
// mode: 'file', // 默认为file,即上传到服务器临时目录,可以配置为 stream
|
|
||||||
mode: 'file',
|
|
||||||
fileSize: '10mb', // 最大支持的文件大小,默认为 10mb
|
|
||||||
whitelist: ['.csv', '.xlsx'], // 支持的文件后缀
|
|
||||||
tmpdir: join(__dirname, '../../tmp_uploads'),
|
|
||||||
cleanTimeout: 5 * 60 * 1000,
|
|
||||||
},
|
|
||||||
koa: {
|
|
||||||
queryParseMode: 'extended',
|
|
||||||
},
|
|
||||||
} as MidwayConfig;
|
} as MidwayConfig;
|
||||||
|
|
|
||||||
|
|
@ -7,25 +7,17 @@ export default {
|
||||||
// dataSource: {
|
// dataSource: {
|
||||||
// default: {
|
// default: {
|
||||||
// host: '13.212.62.127',
|
// host: '13.212.62.127',
|
||||||
// port: '3306',
|
|
||||||
// username: 'root',
|
// username: 'root',
|
||||||
// password: 'Yoone!@.2025',
|
// password: 'Yoone!@.2025',
|
||||||
// database: 'inventory_v2',
|
|
||||||
// synchronize: true,
|
|
||||||
// logging: true,
|
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
typeorm: {
|
typeorm: {
|
||||||
dataSource: {
|
dataSource: {
|
||||||
default: {
|
default: {
|
||||||
host: '13.212.62.127',
|
host: 'localhost',
|
||||||
port: "3306",
|
|
||||||
username: 'root',
|
username: 'root',
|
||||||
password: 'Yoone!@.2025',
|
password: '12345678',
|
||||||
database: 'inventory_v2',
|
|
||||||
synchronize: true,
|
|
||||||
logging: true,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -33,12 +25,46 @@ export default {
|
||||||
origin: '*', // 允许所有来源跨域请求
|
origin: '*', // 允许所有来源跨域请求
|
||||||
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 允许的 HTTP 方法
|
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // 允许的 HTTP 方法
|
||||||
allowHeaders: ['Content-Type', 'Authorization'], // 允许的自定义请求头
|
allowHeaders: ['Content-Type', 'Authorization'], // 允许的自定义请求头
|
||||||
credentials: true, // 允许携带凭据(cookies等)
|
credentials: true, // 允许携带凭据(cookies等)
|
||||||
},
|
},
|
||||||
jwt: {
|
jwt: {
|
||||||
secret: 'YOONE2024!@abc',
|
secret: 'YOONE2024!@abc',
|
||||||
expiresIn: '7d',
|
expiresIn: '7d',
|
||||||
},
|
},
|
||||||
|
wpSite: [
|
||||||
|
{
|
||||||
|
id: '-1',
|
||||||
|
siteName: 'Admin',
|
||||||
|
email: '2469687281@qq.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
wpApiUrl: 'http://t2-shop.local/',
|
||||||
|
consumerKey: 'ck_a369473a6451dbaec63d19cbfd74a074b2c5f742',
|
||||||
|
consumerSecret: 'cs_0946bbbeea1bfefff08a69e817ac62a48412df8c',
|
||||||
|
siteName: 'Local',
|
||||||
|
email: '2469687281@qq.com',
|
||||||
|
emailPswd: 'lulin91.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
wpApiUrl: 'http://t1-shop.local/',
|
||||||
|
consumerKey: 'ck_a369473a6451dbaec63d19cbfd74a074b2c5f742',
|
||||||
|
consumerSecret: 'cs_0946bbbeea1bfefff08a69e817ac62a48412df8c',
|
||||||
|
siteName: 'Local-test-2',
|
||||||
|
email: '2469687281@qq.com',
|
||||||
|
emailPswd: 'lulin91.',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// id: '2',
|
||||||
|
// wpApiUrl: 'http://localhost:10004',
|
||||||
|
// consumerKey: 'ck_dc9e151e9048c8ed3e27f35ac79d2bf7d6840652',
|
||||||
|
// consumerSecret: 'cs_d05d625d7b0ac05c6d765671d8417f41d9477e38',
|
||||||
|
// siteName: 'Local',
|
||||||
|
// email: 'tom@yoonevape.com',
|
||||||
|
// emailPswd: 'lulin91.',
|
||||||
|
// },
|
||||||
|
],
|
||||||
freightcom: {
|
freightcom: {
|
||||||
url: 'https://customer-external-api.ssd-test.freightcom.com',
|
url: 'https://customer-external-api.ssd-test.freightcom.com',
|
||||||
token: '6zGj1qPTL1jIkbLmgaiYc6SwHUIXJ2t25htUF8uuFYiCg8ILCY6xnBEbvrX1p79L',
|
token: '6zGj1qPTL1jIkbLmgaiYc6SwHUIXJ2t25htUF8uuFYiCg8ILCY6xnBEbvrX1p79L',
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,19 @@ import {
|
||||||
App,
|
App,
|
||||||
Inject,
|
Inject,
|
||||||
MidwayDecoratorService,
|
MidwayDecoratorService,
|
||||||
Logger,
|
|
||||||
Config,
|
|
||||||
} from '@midwayjs/core';
|
} from '@midwayjs/core';
|
||||||
import * as koa from '@midwayjs/koa';
|
import * as koa from '@midwayjs/koa';
|
||||||
import * as validate from '@midwayjs/validate';
|
import * as validate from '@midwayjs/validate';
|
||||||
import * as info from '@midwayjs/info';
|
import * as info from '@midwayjs/info';
|
||||||
import * as orm from '@midwayjs/typeorm';
|
import * as orm from '@midwayjs/typeorm';
|
||||||
import { DataSource } from 'typeorm';
|
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { DefaultErrorFilter } from './filter/default.filter';
|
// import { DefaultErrorFilter } from './filter/default.filter';
|
||||||
import { NotFoundFilter } from './filter/notfound.filter';
|
// import { NotFoundFilter } from './filter/notfound.filter';
|
||||||
import { ReportMiddleware } from './middleware/report.middleware';
|
import { ReportMiddleware } from './middleware/report.middleware';
|
||||||
import { QueryNormalizeMiddleware } from './middleware/query-normalize.middleware';
|
|
||||||
import * as swagger from '@midwayjs/swagger';
|
import * as swagger from '@midwayjs/swagger';
|
||||||
import * as crossDomain from '@midwayjs/cross-domain';
|
import * as crossDomain from '@midwayjs/cross-domain';
|
||||||
import * as cron from '@midwayjs/cron';
|
import * as cron from '@midwayjs/cron';
|
||||||
import * as jwt from '@midwayjs/jwt';
|
import * as jwt from '@midwayjs/jwt';
|
||||||
import * as upload from '@midwayjs/upload';
|
|
||||||
import { USER_KEY } from './decorator/user.decorator';
|
import { USER_KEY } from './decorator/user.decorator';
|
||||||
import { SiteService } from './service/site.service';
|
import { SiteService } from './service/site.service';
|
||||||
import { AuthMiddleware } from './middleware/auth.middleware';
|
import { AuthMiddleware } from './middleware/auth.middleware';
|
||||||
|
|
@ -38,7 +33,6 @@ import { AuthMiddleware } from './middleware/auth.middleware';
|
||||||
crossDomain,
|
crossDomain,
|
||||||
cron,
|
cron,
|
||||||
jwt,
|
jwt,
|
||||||
upload,
|
|
||||||
],
|
],
|
||||||
importConfigs: [join(__dirname, './config')],
|
importConfigs: [join(__dirname, './config')],
|
||||||
})
|
})
|
||||||
|
|
@ -55,23 +49,11 @@ export class MainConfiguration {
|
||||||
@Inject()
|
@Inject()
|
||||||
siteService: SiteService;
|
siteService: SiteService;
|
||||||
|
|
||||||
@Logger()
|
|
||||||
logger; // 注入 Logger 实例
|
|
||||||
|
|
||||||
@Config('typeorm.dataSource.default')
|
|
||||||
typeormDataSourceConfig; // 注入 TypeORM 数据源配置
|
|
||||||
|
|
||||||
async onConfigLoad() {
|
|
||||||
// 在组件初始化之前,先检查并创建数据库
|
|
||||||
await this.initializeDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
async onReady() {
|
async onReady() {
|
||||||
|
|
||||||
// add middleware
|
// add middleware
|
||||||
this.app.useMiddleware([QueryNormalizeMiddleware, ReportMiddleware, AuthMiddleware]);
|
this.app.useMiddleware([ReportMiddleware, AuthMiddleware]);
|
||||||
// add filter
|
// add filter
|
||||||
this.app.useFilter([NotFoundFilter, DefaultErrorFilter]);
|
// this.app.useFilter([NotFoundFilter, DefaultErrorFilter]);
|
||||||
|
|
||||||
this.decoratorService.registerParameterHandler(
|
this.decoratorService.registerParameterHandler(
|
||||||
USER_KEY,
|
USER_KEY,
|
||||||
|
|
@ -97,69 +79,4 @@ export class MainConfiguration {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 初始化数据库(如果不存在则创建)
|
|
||||||
*/
|
|
||||||
private async initializeDatabase(): Promise<void> {
|
|
||||||
// 使用注入的数据库配置
|
|
||||||
const typeormConfig = this.typeormDataSourceConfig;
|
|
||||||
|
|
||||||
// 创建一个临时的 DataSource,不指定数据库,用于创建数据库
|
|
||||||
const tempDataSource = new DataSource({
|
|
||||||
type: 'mysql',
|
|
||||||
host: typeormConfig.host,
|
|
||||||
port: typeormConfig.port,
|
|
||||||
username: typeormConfig.username,
|
|
||||||
password: typeormConfig.password,
|
|
||||||
database: undefined, // 不指定数据库
|
|
||||||
synchronize: true,
|
|
||||||
logging: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.logger.info(`正在检查数据库是否存在...`+ JSON.stringify(typeormConfig));
|
|
||||||
// 初始化临时数据源
|
|
||||||
await tempDataSource.initialize();
|
|
||||||
|
|
||||||
// 创建查询运行器
|
|
||||||
const queryRunner = tempDataSource.createQueryRunner();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// 检查数据库是否存在
|
|
||||||
const databases = await queryRunner.query(
|
|
||||||
`SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = ?`,
|
|
||||||
[typeormConfig.database]
|
|
||||||
);
|
|
||||||
|
|
||||||
const databaseExists = Array.isArray(databases) && databases.length > 0;
|
|
||||||
|
|
||||||
if (!databaseExists) {
|
|
||||||
this.logger.info(`数据库 ${typeormConfig.database} 不存在,正在创建...`);
|
|
||||||
|
|
||||||
// 创建数据库
|
|
||||||
await queryRunner.query(
|
|
||||||
`CREATE DATABASE \`${typeormConfig.database}\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`
|
|
||||||
);
|
|
||||||
|
|
||||||
this.logger.info(`数据库 ${typeormConfig.database} 创建成功`);
|
|
||||||
} else {
|
|
||||||
this.logger.info(`数据库 ${typeormConfig.database} 已存在`);
|
|
||||||
}
|
|
||||||
|
|
||||||
} finally {
|
|
||||||
// 释放查询运行器
|
|
||||||
await queryRunner.release();
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error('数据库初始化失败:', error);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
// 关闭临时数据源
|
|
||||||
if (tempDataSource.isInitialized) {
|
|
||||||
await tempDataSource.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,118 +0,0 @@
|
||||||
|
|
||||||
import { Body, Context, Controller, Del, Get, Inject, Param, Post, Put, Query } from '@midwayjs/core';
|
|
||||||
import {
|
|
||||||
ApiBearerAuth,
|
|
||||||
ApiBody,
|
|
||||||
ApiExtension,
|
|
||||||
ApiOkResponse,
|
|
||||||
ApiOperation,
|
|
||||||
ApiTags,
|
|
||||||
} from '@midwayjs/swagger';
|
|
||||||
import { AreaService } from '../service/area.service';
|
|
||||||
import { CreateAreaDTO, QueryAreaDTO, UpdateAreaDTO } from '../dto/area.dto';
|
|
||||||
import { errorResponse, successResponse } from '../utils/response.util';
|
|
||||||
import { Area } from '../entity/area.entity';
|
|
||||||
import * as countries from 'i18n-iso-countries';
|
|
||||||
|
|
||||||
@ApiBearerAuth()
|
|
||||||
@ApiTags('Area')
|
|
||||||
@Controller('/area')
|
|
||||||
export class AreaController {
|
|
||||||
@Inject()
|
|
||||||
ctx: Context;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
areaService: AreaService;
|
|
||||||
|
|
||||||
@ApiOperation({ summary: '获取国家列表' })
|
|
||||||
@ApiOkResponse({ description: '国家列表' })
|
|
||||||
@Get('/countries')
|
|
||||||
async getCountries() {
|
|
||||||
try {
|
|
||||||
// 注册中文语言包
|
|
||||||
countries.registerLocale(require('i18n-iso-countries/langs/zh.json'));
|
|
||||||
// 获取所有国家的中文名称
|
|
||||||
const countryNames = countries.getNames('zh', { select: 'official' });
|
|
||||||
// 格式化为 { code, name } 的数组
|
|
||||||
const countryList = Object.keys(countryNames).map(code => ({
|
|
||||||
code,
|
|
||||||
name: countryNames[code],
|
|
||||||
}));
|
|
||||||
return successResponse(countryList, '查询成功');
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation({ summary: '创建区域' })
|
|
||||||
@ApiBody({ type: CreateAreaDTO })
|
|
||||||
@ApiOkResponse({ type: Area, description: '成功创建的区域' })
|
|
||||||
@Post('/')
|
|
||||||
async createArea(@Body() area: CreateAreaDTO) {
|
|
||||||
try {
|
|
||||||
const newArea = await this.areaService.createArea(area);
|
|
||||||
return successResponse(newArea, '创建成功');
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation({ summary: '更新区域' })
|
|
||||||
@ApiBody({ type: UpdateAreaDTO })
|
|
||||||
@ApiOkResponse({ type: Area, description: '成功更新的区域' })
|
|
||||||
@Put('/:id')
|
|
||||||
async updateArea(@Param('id') id: number, @Body() area: UpdateAreaDTO) {
|
|
||||||
try {
|
|
||||||
const updatedArea = await this.areaService.updateArea(id, area);
|
|
||||||
return successResponse(updatedArea, '更新成功');
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation({ summary: '删除区域' })
|
|
||||||
@ApiOkResponse({ description: '删除成功' })
|
|
||||||
@Del('/:id')
|
|
||||||
async deleteArea(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
await this.areaService.deleteArea(id);
|
|
||||||
return successResponse(null, '删除成功');
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation({ summary: '获取区域列表(分页)' })
|
|
||||||
@ApiOkResponse({ type: [Area], description: '区域列表' })
|
|
||||||
@ApiExtension('x-pagination', { currentPage: 1, pageSize: 10, total: 100 })
|
|
||||||
@Get('/')
|
|
||||||
async getAreaList(@Query() query: QueryAreaDTO) {
|
|
||||||
try {
|
|
||||||
const { list, total } = await this.areaService.getAreaList(query);
|
|
||||||
return successResponse({ list, total }, '查询成功');
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOperation({ summary: '根据ID获取区域详情' })
|
|
||||||
@ApiOkResponse({ type: Area, description: '区域详情' })
|
|
||||||
@Get('/:id')
|
|
||||||
async getAreaById(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
const area = await this.areaService.getAreaById(id);
|
|
||||||
if (!area) {
|
|
||||||
return errorResponse('区域不存在');
|
|
||||||
}
|
|
||||||
return successResponse(area, '查询成功');
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,99 +0,0 @@
|
||||||
import { Controller, Get, Post, Put, Del, Body, Query, Inject, Param } from '@midwayjs/core';
|
|
||||||
import { CategoryService } from '../service/category.service';
|
|
||||||
import { successResponse, errorResponse } from '../utils/response.util';
|
|
||||||
import { ApiOkResponse } from '@midwayjs/swagger';
|
|
||||||
import { CreateCategoryDTO, UpdateCategoryDTO } from '../dto/product.dto';
|
|
||||||
|
|
||||||
@Controller('/category')
|
|
||||||
export class CategoryController {
|
|
||||||
@Inject()
|
|
||||||
categoryService: CategoryService;
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Get('/all')
|
|
||||||
async getAll() {
|
|
||||||
try {
|
|
||||||
const data = await this.categoryService.getAll();
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Get('/')
|
|
||||||
async getList(@Query('current') current = 1, @Query('pageSize') pageSize = 10, @Query('name') name?: string) {
|
|
||||||
try {
|
|
||||||
const data = await this.categoryService.getList({ current, pageSize }, name);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Post('/')
|
|
||||||
async create(@Body() body: CreateCategoryDTO) {
|
|
||||||
try {
|
|
||||||
const data = await this.categoryService.create(body);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Put('/:id')
|
|
||||||
async update(@Param('id') id: number, @Body() body: UpdateCategoryDTO) {
|
|
||||||
try {
|
|
||||||
const data = await this.categoryService.update(id, body);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Del('/:id')
|
|
||||||
async delete(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
await this.categoryService.delete(id);
|
|
||||||
return successResponse(null);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Get('/attribute/:categoryId')
|
|
||||||
async getCategoryAttributes(@Param('categoryId') categoryId: number) {
|
|
||||||
try {
|
|
||||||
const data = await this.categoryService.getCategoryAttributes(categoryId);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Post('/attribute')
|
|
||||||
async createCategoryAttribute(@Body() body: { categoryId: number, attributeDictIds: number[] }) {
|
|
||||||
try {
|
|
||||||
const data = await this.categoryService.createCategoryAttribute(body.categoryId, body.attributeDictIds);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Del('/attribute/:id')
|
|
||||||
async deleteCategoryAttribute(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
await this.categoryService.deleteCategoryAttribute(id);
|
|
||||||
return successResponse(null);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,250 +1,82 @@
|
||||||
import { Controller, Get, Post, Inject, Query, Body, Put, Del, Param } from '@midwayjs/core';
|
import {
|
||||||
import { successResponse, errorResponse, ApiResponse } from '../utils/response.util';
|
Body,
|
||||||
|
Context,
|
||||||
|
Controller,
|
||||||
|
Del,
|
||||||
|
Get,
|
||||||
|
Inject,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
Query,
|
||||||
|
} from '@midwayjs/core';
|
||||||
import { CustomerService } from '../service/customer.service';
|
import { CustomerService } from '../service/customer.service';
|
||||||
import { CustomerQueryParamsDTO, CreateCustomerDTO, UpdateCustomerDTO, GetCustomerDTO, BatchCreateCustomerDTO, BatchUpdateCustomerDTO, BatchDeleteCustomerDTO, SyncCustomersDTO } from '../dto/customer.dto';
|
import { errorResponse, successResponse } from '../utils/response.util';
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { ApiOkResponse } from '@midwayjs/swagger';
|
import { ApiOkResponse } from '@midwayjs/swagger';
|
||||||
import { UnifiedPaginationDTO } from '../dto/api.dto';
|
import { BooleanRes } from '../dto/reponse.dto';
|
||||||
|
import { CustomerTagDTO, QueryCustomerListDTO } from '../dto/customer.dto';
|
||||||
export class CustomerTagDTO {
|
|
||||||
@ApiProperty({ description: '客户邮箱' })
|
|
||||||
email: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '标签名称' })
|
|
||||||
tag: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Controller('/customer')
|
@Controller('/customer')
|
||||||
export class CustomerController {
|
export class CustomerController {
|
||||||
|
@Inject()
|
||||||
|
ctx: Context;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
customerService: CustomerService;
|
customerService: CustomerService;
|
||||||
|
|
||||||
@ApiOkResponse({ type: ApiResponse<UnifiedPaginationDTO<GetCustomerDTO>> })
|
@ApiOkResponse()
|
||||||
@Get('/list')
|
@Get('/list')
|
||||||
async getCustomerList(@Query() query: CustomerQueryParamsDTO) {
|
async getCustomerList(@Query() param: QueryCustomerListDTO) {
|
||||||
try {
|
try {
|
||||||
const result = await this.customerService.getCustomerList(query)
|
const data = await this.customerService.getCustomerList(param);
|
||||||
return successResponse(result);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error.message);
|
console.log(error)
|
||||||
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse({ type: Object })
|
@ApiOkResponse({ type: BooleanRes })
|
||||||
@Get('/statistic/list')
|
@Post('/tag/add')
|
||||||
async getCustomerStatisticList(@Query() query: CustomerQueryParamsDTO) {
|
async addTag(@Body() dto: CustomerTagDTO) {
|
||||||
try {
|
try {
|
||||||
const result = await this.customerService.getCustomerStatisticList(query as any);
|
await this.customerService.addTag(dto.email, dto.tag);
|
||||||
return successResponse(result);
|
return successResponse(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error.message);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse({ type: Object })
|
@ApiOkResponse({ type: BooleanRes })
|
||||||
@Post('/addtag')
|
@Del('/tag/del')
|
||||||
async addTag(@Body() body: CustomerTagDTO) {
|
async delTag(@Body() dto: CustomerTagDTO) {
|
||||||
try {
|
try {
|
||||||
const result = await this.customerService.addTag(body.email, body.tag);
|
await this.customerService.delTag(dto.email, dto.tag);
|
||||||
return successResponse(result);
|
return successResponse(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error.message);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse({ type: Object })
|
@ApiOkResponse()
|
||||||
@Post('/deltag')
|
@Get('/tags')
|
||||||
async delTag(@Body() body: CustomerTagDTO) {
|
|
||||||
try {
|
|
||||||
const result = await this.customerService.delTag(body.email, body.tag);
|
|
||||||
return successResponse(result);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: Object })
|
|
||||||
@Get('/gettags')
|
|
||||||
async getTags() {
|
async getTags() {
|
||||||
try {
|
try {
|
||||||
const result = await this.customerService.getTags();
|
const data = await this.customerService.getTags();
|
||||||
return successResponse(result);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error.message);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse({ type: Object })
|
|
||||||
@Post('/setrate')
|
@ApiOkResponse({ type: BooleanRes })
|
||||||
async setRate(@Body() body: { id: number; rate: number }) {
|
@Put('/rate')
|
||||||
|
async setRate(@Body() params: { id: number; rate: number }) {
|
||||||
try {
|
try {
|
||||||
const result = await this.customerService.setRate({ id: body.id, rate: body.rate });
|
await this.customerService.setRate(params);
|
||||||
return successResponse(result);
|
return successResponse(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error.message);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
/**
|
|
||||||
* 同步客户数据
|
|
||||||
* 从指定站点获取客户数据并保存到本地数据库
|
|
||||||
* 支持通过where和orderBy参数筛选和排序要同步的客户数据
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: Object })
|
|
||||||
@Post('/sync')
|
|
||||||
async syncCustomers(@Body() body: SyncCustomersDTO) {
|
|
||||||
try {
|
|
||||||
const { siteId, params = {} } = body;
|
|
||||||
|
|
||||||
// 调用service层的同步方法,所有业务逻辑都在service中处理
|
|
||||||
const syncResult = await this.customerService.syncCustomersFromSite(siteId, params);
|
|
||||||
|
|
||||||
return successResponse(syncResult);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== 单个客户CRUD操作 ======================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建单个客户
|
|
||||||
* 使用CreateCustomerDTO进行数据验证
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: GetCustomerDTO })
|
|
||||||
@Post('/')
|
|
||||||
async createCustomer(@Body() body: CreateCustomerDTO) {
|
|
||||||
try {
|
|
||||||
// 调用service层的upsertCustomer方法
|
|
||||||
const result = await this.customerService.upsertCustomer({...body, origin_id: String(body.origin_id)});
|
|
||||||
return successResponse(result.customer);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据ID获取单个客户
|
|
||||||
* 返回GetCustomerDTO格式的客户信息
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: GetCustomerDTO })
|
|
||||||
@Get('/:id')
|
|
||||||
async getCustomerById(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
const customer = await this.customerService.customerModel.findOne({ where: { id } });
|
|
||||||
if (!customer) {
|
|
||||||
return errorResponse('客户不存在');
|
|
||||||
}
|
|
||||||
return successResponse(customer);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新单个客户
|
|
||||||
* 使用UpdateCustomerDTO进行数据验证
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: GetCustomerDTO })
|
|
||||||
@Put('/:id')
|
|
||||||
async updateCustomer(@Param('id') id: number, @Body() body: UpdateCustomerDTO) {
|
|
||||||
try {
|
|
||||||
const customer = await this.customerService.updateCustomer(id, body);
|
|
||||||
if (!customer) {
|
|
||||||
return errorResponse('客户不存在');
|
|
||||||
}
|
|
||||||
return successResponse(customer);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除单个客户
|
|
||||||
* 根据客户ID删除客户记录
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: Object })
|
|
||||||
@Del('/:id')
|
|
||||||
async deleteCustomer(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
// 先检查客户是否存在
|
|
||||||
const customer = await this.customerService.customerModel.findOne({ where: { id } });
|
|
||||||
if (!customer) {
|
|
||||||
return errorResponse('客户不存在');
|
|
||||||
}
|
|
||||||
// 删除客户
|
|
||||||
await this.customerService.customerModel.delete(id);
|
|
||||||
return successResponse({ message: '删除成功' });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== 批量客户操作 ======================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量创建客户
|
|
||||||
* 使用BatchCreateCustomerDTO进行数据验证
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: Object })
|
|
||||||
@Post('/batch')
|
|
||||||
async batchCreateCustomers(@Body() body: BatchCreateCustomerDTO) {
|
|
||||||
try {
|
|
||||||
const result = await this.customerService.upsertManyCustomers(body.customers.map(c => ({ ...c, origin_id: String(c.origin_id) })));
|
|
||||||
return successResponse(result);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量更新客户
|
|
||||||
* 使用BatchUpdateCustomerDTO进行数据验证
|
|
||||||
* 每个客户可以有独立的更新字段,支持统一化修改或分别更新
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: Object })
|
|
||||||
@Put('/batch')
|
|
||||||
async batchUpdateCustomers(@Body() body: BatchUpdateCustomerDTO) {
|
|
||||||
try {
|
|
||||||
const { customers } = body;
|
|
||||||
|
|
||||||
// 调用service层的批量更新方法
|
|
||||||
const result = await this.customerService.batchUpdateCustomers(customers);
|
|
||||||
|
|
||||||
return successResponse({
|
|
||||||
total: result.total,
|
|
||||||
updated: result.updated,
|
|
||||||
processed: result.processed,
|
|
||||||
errors: result.errors,
|
|
||||||
message: `成功更新${result.updated}个客户,共处理${result.processed}个`
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量删除客户
|
|
||||||
* 使用BatchDeleteCustomerDTO进行数据验证
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: Object })
|
|
||||||
@Del('/batch')
|
|
||||||
async batchDeleteCustomers(@Body() body: BatchDeleteCustomerDTO) {
|
|
||||||
try {
|
|
||||||
const { ids } = body;
|
|
||||||
|
|
||||||
// 调用service层的批量删除方法
|
|
||||||
const result = await this.customerService.batchDeleteCustomers(ids);
|
|
||||||
|
|
||||||
return successResponse({
|
|
||||||
total: result.total,
|
|
||||||
updated: result.updated,
|
|
||||||
processed: result.processed,
|
|
||||||
errors: result.errors,
|
|
||||||
message: `成功删除${result.updated}个客户,共处理${result.processed}个`
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,239 +0,0 @@
|
||||||
|
|
||||||
import { Inject, Controller, Get, Post, Put, Del, Query, Body, Param, Files, Fields, ContentType } from '@midwayjs/core';
|
|
||||||
import { DictService } from '../service/dict.service';
|
|
||||||
import { CreateDictDTO, UpdateDictDTO, CreateDictItemDTO, UpdateDictItemDTO } from '../dto/dict.dto';
|
|
||||||
import { Validate } from '@midwayjs/validate';
|
|
||||||
import { Context } from '@midwayjs/koa';
|
|
||||||
import { successResponse, errorResponse, ApiResponse } from '../utils/response.util';
|
|
||||||
import { ApiOkResponse } from '@midwayjs/swagger';
|
|
||||||
import { BatchOperationResult } from '../dto/api.dto';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 字典管理
|
|
||||||
* @decorator Controller
|
|
||||||
*/
|
|
||||||
@Controller('/dict')
|
|
||||||
export class DictController {
|
|
||||||
@Inject()
|
|
||||||
dictService: DictService;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
ctx: Context;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量导入字典
|
|
||||||
* @param files 上传的文件
|
|
||||||
*/
|
|
||||||
@Post('/import')
|
|
||||||
@Validate()
|
|
||||||
async importDicts(@Files() files: any) {
|
|
||||||
// 从上传的文件列表中获取第一个文件
|
|
||||||
const file = files[0];
|
|
||||||
// 调用服务层方法处理XLSX文件
|
|
||||||
const result = await this.dictService.importDictsFromTable(file.data);
|
|
||||||
// 返回导入结果
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 下载字典XLSX模板
|
|
||||||
*/
|
|
||||||
@Get('/template')
|
|
||||||
@ContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
|
||||||
async downloadDictTemplate() {
|
|
||||||
// 设置下载文件的名称
|
|
||||||
this.ctx.set('Content-Disposition', 'attachment; filename=dict-template.xlsx');
|
|
||||||
// 返回XLSX模板内容
|
|
||||||
return this.dictService.getDictXLSXTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取单个字典及其所有字典项
|
|
||||||
* @param id 字典ID
|
|
||||||
*/
|
|
||||||
@Get('/:id')
|
|
||||||
async getDict(@Param('id') id: number) {
|
|
||||||
// 调用服务层方法,并关联查询字典项
|
|
||||||
return this.dictService.getDict({ id }, ['items']);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取字典列表
|
|
||||||
* @param title 字典标题 (模糊查询)
|
|
||||||
* @param name 字典名称 (模糊查询)
|
|
||||||
*/
|
|
||||||
@Get('/list')
|
|
||||||
async getDicts(@Query('title') title?: string, @Query('name') name?: string) {
|
|
||||||
// 调用服务层方法
|
|
||||||
return this.dictService.getDicts({ title, name });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建新字典
|
|
||||||
* @param createDictDTO 字典数据
|
|
||||||
*/
|
|
||||||
@Post('/')
|
|
||||||
@Validate()
|
|
||||||
async createDict(@Body() createDictDTO: CreateDictDTO) {
|
|
||||||
try {
|
|
||||||
// 调用服务层方法
|
|
||||||
const result = await this.dictService.createDict(createDictDTO);
|
|
||||||
return successResponse(result, '字典创建成功');
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '字典创建失败', error?.code || 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新字典
|
|
||||||
* @param id 字典ID
|
|
||||||
* @param updateDictDTO 待更新的字典数据
|
|
||||||
*/
|
|
||||||
@Put('/:id')
|
|
||||||
@Validate()
|
|
||||||
async updateDict(@Param('id') id: number, @Body() updateDictDTO: UpdateDictDTO) {
|
|
||||||
try {
|
|
||||||
// 调用服务层方法
|
|
||||||
const result = await this.dictService.updateDict(id, updateDictDTO);
|
|
||||||
return successResponse(result, '字典更新成功');
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '字典更新失败', error?.code || 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除字典
|
|
||||||
* @param id 字典ID
|
|
||||||
*/
|
|
||||||
@Del('/:id')
|
|
||||||
async deleteDict(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
// 调用服务层方法
|
|
||||||
const result = await this.dictService.deleteDict(id);
|
|
||||||
return successResponse(result, '字典删除成功');
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '字典删除失败', error?.code || 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量导入字典项
|
|
||||||
* @param files 上传的文件
|
|
||||||
* @param fields FormData中的字段
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({type:ApiResponse<BatchOperationResult>})
|
|
||||||
@Post('/item/import')
|
|
||||||
@Validate()
|
|
||||||
async importDictItems(@Files() files: any, @Fields() fields: { dictId: number }) {
|
|
||||||
// 获取第一个文件
|
|
||||||
const file = files[0];
|
|
||||||
// 从fields中获取字典ID
|
|
||||||
const dictId = fields.dictId;
|
|
||||||
// 调用服务层方法
|
|
||||||
const result = await this.dictService.importDictItemsFromXLSX(file.data, dictId);
|
|
||||||
// 返回结果
|
|
||||||
return successResponse(result)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 下载字典项XLSX模板
|
|
||||||
*/
|
|
||||||
@Get('/item/template')
|
|
||||||
@ContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
|
||||||
async downloadDictItemTemplate() {
|
|
||||||
// 设置下载文件名
|
|
||||||
this.ctx.set('Content-Disposition', 'attachment; filename=dict-item-template.xlsx');
|
|
||||||
// 返回模板内容
|
|
||||||
return this.dictService.getDictItemXLSXTemplate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 导出字典项为 XLSX 文件
|
|
||||||
* @param dictId 字典ID
|
|
||||||
*/
|
|
||||||
@Get('/item/export')
|
|
||||||
@ContentType('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
|
||||||
async exportDictItems(@Query('dictId') dictId: number) {
|
|
||||||
// 设置下载文件名
|
|
||||||
this.ctx.set('Content-Disposition', 'attachment; filename=dict-items.xlsx');
|
|
||||||
// 返回导出的 XLSX 文件内容
|
|
||||||
return this.dictService.exportDictItemsToXLSX(dictId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取字典项列表
|
|
||||||
* @param dictId 字典ID (可选)
|
|
||||||
*/
|
|
||||||
@Get('/items')
|
|
||||||
async getDictItems(
|
|
||||||
@Query('dictId') dictId?: number,
|
|
||||||
@Query('name') name?: string,
|
|
||||||
@Query('title') title?: string,
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
// 调用服务层方法
|
|
||||||
const result = await this.dictService.getDictItems({ dictId, name, title });
|
|
||||||
return successResponse(result, '获取字典项列表成功');
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '获取字典项列表失败', error?.code || 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建新字典项
|
|
||||||
* @param createDictItemDTO 字典项数据
|
|
||||||
*/
|
|
||||||
@Post('/item')
|
|
||||||
@Validate()
|
|
||||||
async createDictItem(@Body() createDictItemDTO: CreateDictItemDTO) {
|
|
||||||
try {
|
|
||||||
// 调用服务层方法
|
|
||||||
const result = await this.dictService.createDictItem(createDictItemDTO);
|
|
||||||
return successResponse(result, '字典项创建成功');
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '字典项创建失败', error?.code || 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新字典项
|
|
||||||
* @param id 字典项ID
|
|
||||||
* @param updateDictItemDTO 待更新的字典项数据
|
|
||||||
*/
|
|
||||||
@Put('/item/:id')
|
|
||||||
@Validate()
|
|
||||||
async updateDictItem(@Param('id') id: number, @Body() updateDictItemDTO: UpdateDictItemDTO) {
|
|
||||||
try {
|
|
||||||
// 调用服务层方法
|
|
||||||
const result = await this.dictService.updateDictItem(id, updateDictItemDTO);
|
|
||||||
return successResponse(result, '字典项更新成功');
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '字典项更新失败', error?.code || 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除字典项
|
|
||||||
* @param id 字典项ID
|
|
||||||
*/
|
|
||||||
@Del('/item/:id')
|
|
||||||
async deleteDictItem(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
// 调用服务层方法
|
|
||||||
const result = await this.dictService.deleteDictItem(id);
|
|
||||||
return successResponse(result, '字典项删除成功');
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '字典项删除失败', error?.code || 500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据字典名称获取字典项列表
|
|
||||||
* @param name 字典名称
|
|
||||||
*/
|
|
||||||
@Get('/items-by-name')
|
|
||||||
async getDictItemsByDictName(@Query('name') name: string) {
|
|
||||||
// 调用服务层方法
|
|
||||||
return this.dictService.getDictItemsByDictName(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
|
|
||||||
import { Controller, Get, Inject, Param } from '@midwayjs/core';
|
|
||||||
import { DictService } from '../service/dict.service';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 国际化语言包
|
|
||||||
*/
|
|
||||||
@Controller('/locales')
|
|
||||||
export class LocaleController {
|
|
||||||
@Inject()
|
|
||||||
dictService: DictService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据语言获取所有翻译项
|
|
||||||
* @param lang 语言代码,例如 zh-CN, en-US
|
|
||||||
* @returns 返回一个包含所有翻译键值对的 JSON 对象
|
|
||||||
*/
|
|
||||||
@Get('/:lang')
|
|
||||||
async getLocale(@Param('lang') lang: string) {
|
|
||||||
// 根据语言代码查找对应的字典
|
|
||||||
const dict = await this.dictService.getDict({ name: lang }, ['items']);
|
|
||||||
|
|
||||||
// 如果字典不存在,则返回空对象
|
|
||||||
if (!dict) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将字典项转换为 key-value 对象
|
|
||||||
const locale = dict.items.reduce((acc, item) => {
|
|
||||||
acc[item.name] = item.title;
|
|
||||||
return acc;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
return locale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -9,7 +9,6 @@ import {
|
||||||
Put,
|
Put,
|
||||||
Query,
|
Query,
|
||||||
} from '@midwayjs/core';
|
} from '@midwayjs/core';
|
||||||
import { SyncOperationResult } from '../dto/api.dto';
|
|
||||||
import { ApiOkResponse } from '@midwayjs/swagger';
|
import { ApiOkResponse } from '@midwayjs/swagger';
|
||||||
import {
|
import {
|
||||||
BooleanRes,
|
BooleanRes,
|
||||||
|
|
@ -36,11 +35,11 @@ export class OrderController {
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
type: BooleanRes,
|
type: BooleanRes,
|
||||||
})
|
})
|
||||||
@Post('/sync/:siteId')
|
@Post('/syncOrder/:siteId')
|
||||||
async syncOrders(@Param('siteId') siteId: number, @Body() params: Record<string, any>) {
|
async syncOrder(@Param('siteId') siteId: string) {
|
||||||
try {
|
try {
|
||||||
const result = await this.orderService.syncOrders(siteId, params);
|
await this.orderService.syncOrders(siteId);
|
||||||
return successResponse(result);
|
return successResponse(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return errorResponse('同步失败');
|
return errorResponse('同步失败');
|
||||||
|
|
@ -48,16 +47,16 @@ export class OrderController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
type: SyncOperationResult,
|
type: BooleanRes,
|
||||||
})
|
})
|
||||||
@Post('/syncOrder/:siteId/order/:orderId')
|
@Post('/syncOrder/:siteId/order/:orderId')
|
||||||
async syncOrderById(
|
async syncOrderById(
|
||||||
@Param('siteId') siteId: number,
|
@Param('siteId') siteId: string,
|
||||||
@Param('orderId') orderId: string
|
@Param('orderId') orderId: string
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const result = await this.orderService.syncOrderById(siteId, orderId);
|
await this.orderService.syncOrderById(siteId, orderId);
|
||||||
return successResponse(result);
|
return successResponse(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
return errorResponse('同步失败');
|
return errorResponse('同步失败');
|
||||||
|
|
@ -253,15 +252,4 @@ export class OrderController {
|
||||||
return errorResponse(error?.message || '获取失败');
|
return errorResponse(error?.message || '获取失败');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Post('/export')
|
|
||||||
async exportOrder(@Body('ids') ids: number[]) {
|
|
||||||
try {
|
|
||||||
const csvContent = await this.orderService.exportOrder(ids);
|
|
||||||
return successResponse(csvContent);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '导出失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,46 @@
|
||||||
import {
|
import {
|
||||||
Body,
|
|
||||||
ContentType,
|
|
||||||
Controller,
|
|
||||||
Del,
|
|
||||||
Files,
|
|
||||||
Get,
|
|
||||||
Inject,
|
Inject,
|
||||||
Param,
|
|
||||||
Post,
|
Post,
|
||||||
Put,
|
Put,
|
||||||
|
Get,
|
||||||
|
Body,
|
||||||
|
Param,
|
||||||
|
Del,
|
||||||
Query,
|
Query,
|
||||||
|
Controller,
|
||||||
} from '@midwayjs/core';
|
} from '@midwayjs/core';
|
||||||
import { Context } from '@midwayjs/koa';
|
|
||||||
import { ILogger } from '@midwayjs/logger';
|
|
||||||
import { ApiOkResponse } from '@midwayjs/swagger';
|
|
||||||
import { UnifiedSearchParamsDTO } from '../dto/api.dto';
|
|
||||||
import { SyncOperationResultDTO } from '../dto/batch.dto';
|
|
||||||
import { BatchDeleteProductDTO, BatchUpdateProductDTO, CreateCategoryDTO, CreateProductDTO, ProductWhereFilter, UpdateCategoryDTO, UpdateProductDTO } from '../dto/product.dto';
|
|
||||||
import { BooleanRes, ProductListRes, ProductRes, ProductsRes } from '../dto/reponse.dto';
|
|
||||||
import { BatchSyncProductToSiteDTO, SyncProductToSiteDTO, SyncProductToSiteResultDTO } from '../dto/site-sync.dto';
|
|
||||||
import { ProductService } from '../service/product.service';
|
import { ProductService } from '../service/product.service';
|
||||||
import { errorResponse, successResponse } from '../utils/response.util';
|
import { errorResponse, successResponse } from '../utils/response.util';
|
||||||
|
import {
|
||||||
|
BatchSetSkuDTO,
|
||||||
|
CreateCategoryDTO,
|
||||||
|
CreateFlavorsDTO,
|
||||||
|
CreateProductDTO,
|
||||||
|
CreateStrengthDTO,
|
||||||
|
QueryCategoryDTO,
|
||||||
|
QueryFlavorsDTO,
|
||||||
|
QueryProductDTO,
|
||||||
|
QueryStrengthDTO,
|
||||||
|
UpdateCategoryDTO,
|
||||||
|
UpdateFlavorsDTO,
|
||||||
|
UpdateProductDTO,
|
||||||
|
UpdateStrengthDTO,
|
||||||
|
} from '../dto/product.dto';
|
||||||
|
import { ApiOkResponse } from '@midwayjs/swagger';
|
||||||
|
import {
|
||||||
|
BooleanRes,
|
||||||
|
ProductCatListRes,
|
||||||
|
ProductCatRes,
|
||||||
|
ProductListRes,
|
||||||
|
ProductRes,
|
||||||
|
ProductsRes,
|
||||||
|
} from '../dto/reponse.dto';
|
||||||
|
|
||||||
@Controller('/product')
|
@Controller('/product')
|
||||||
export class ProductController {
|
export class ProductController {
|
||||||
@Inject()
|
@Inject()
|
||||||
productService: ProductService;
|
productService: ProductService;
|
||||||
|
ProductRes;
|
||||||
@Inject()
|
|
||||||
ctx: Context;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
logger: ILogger;
|
|
||||||
|
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
description: '通过name搜索产品',
|
description: '通过name搜索产品',
|
||||||
|
|
@ -68,182 +77,72 @@ export class ProductController {
|
||||||
})
|
})
|
||||||
@Get('/list')
|
@Get('/list')
|
||||||
async getProductList(
|
async getProductList(
|
||||||
@Query() query: UnifiedSearchParamsDTO<ProductWhereFilter>
|
@Query() query: QueryProductDTO
|
||||||
): Promise<ProductListRes> {
|
): Promise<ProductListRes> {
|
||||||
|
const { current = 1, pageSize = 10, name, categoryId } = query;
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.getProductList(query);
|
const data = await this.productService.getProductList(
|
||||||
|
{ current, pageSize },
|
||||||
|
name,
|
||||||
|
categoryId
|
||||||
|
);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.logger.error('获取产品列表失败', error);
|
console.log(error);
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
description: '成功返回分组后的产品列表',
|
type: ProductRes,
|
||||||
schema: {
|
|
||||||
type: 'object',
|
|
||||||
additionalProperties: {
|
|
||||||
type: 'array',
|
|
||||||
items: {
|
|
||||||
$ref: '#/components/schemas/Product',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
@Get('/list/grouped')
|
|
||||||
async getProductListGrouped(
|
|
||||||
@Query() query: UnifiedSearchParamsDTO<ProductWhereFilter>
|
|
||||||
): Promise<any> {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.getProductListGrouped(query);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error('获取分组产品列表失败', error);
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: ProductRes })
|
|
||||||
@Post('/')
|
@Post('/')
|
||||||
async createProduct(@Body() productData: CreateProductDTO) {
|
async createProduct(@Body() productData: CreateProductDTO) {
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.createProduct(productData);
|
const data = this.productService.createProduct(productData);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出所有产品 CSV
|
@ApiOkResponse({
|
||||||
@ApiOkResponse()
|
type: ProductRes,
|
||||||
@Get('/export')
|
})
|
||||||
@ContentType('text/csv')
|
|
||||||
async exportProductsCSV() {
|
|
||||||
try {
|
|
||||||
const csv = await this.productService.exportProductsCSV();
|
|
||||||
// 设置下载文件名(附件形式)
|
|
||||||
const date = new Date();
|
|
||||||
const pad = (n: number) => String(n).padStart(2, '0');
|
|
||||||
const name = `products-${date.getFullYear()}${pad(date.getMonth() + 1)}${pad(date.getDate())}.csv`;
|
|
||||||
this.ctx.set('Content-Disposition', `attachment; filename=${name}`);
|
|
||||||
return csv;
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 导入产品(CSV 文件)
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Post('/import')
|
|
||||||
async importProductsCSV(@Files() files: any) {
|
|
||||||
try {
|
|
||||||
// 条件判断:确保存在文件
|
|
||||||
const file = files?.[0];
|
|
||||||
if (!file) return errorResponse('未接收到上传文件');
|
|
||||||
|
|
||||||
const result = await this.productService.importProductsFromTable(file);
|
|
||||||
return successResponse(result);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: ProductRes })
|
|
||||||
@Put('/:id')
|
@Put('/:id')
|
||||||
async updateProduct(@Param('id') id: number, @Body() productData: UpdateProductDTO) {
|
async updateProduct(
|
||||||
|
@Param('id') id: number,
|
||||||
|
@Body() productData: UpdateProductDTO
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.updateProduct(id, productData);
|
const data = this.productService.updateProduct(id, productData);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
@ApiOkResponse({
|
||||||
@Put('/batch-update')
|
type: ProductRes,
|
||||||
async batchUpdateProduct(@Body() batchUpdateProductDTO: BatchUpdateProductDTO) {
|
})
|
||||||
try {
|
|
||||||
await this.productService.batchUpdateProduct(batchUpdateProductDTO);
|
|
||||||
return successResponse(true);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
|
||||||
@Post('/batch-delete')
|
|
||||||
async batchDeleteProduct(@Body() body: BatchDeleteProductDTO) {
|
|
||||||
try {
|
|
||||||
const result = await this.productService.batchDeleteProduct(body.ids);
|
|
||||||
if (result.failed > 0) {
|
|
||||||
return errorResponse(`成功删除 ${result.success} 个,失败 ${result.failed} 个。首个错误: ${result.errors[0]}`);
|
|
||||||
}
|
|
||||||
return successResponse(true);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: ProductRes })
|
|
||||||
@Put('updateNameCn/:id/:nameCn')
|
@Put('updateNameCn/:id/:nameCn')
|
||||||
async updatenameCn(@Param('id') id: number, @Param('nameCn') nameCn: string) {
|
async updateProductNameCn(
|
||||||
|
@Param('id') id: number,
|
||||||
|
@Param('nameCn') nameCn: string
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.updatenameCn(id, nameCn);
|
const data = this.productService.updateProductNameCn(id, nameCn);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据站点SKU查询产品
|
|
||||||
@ApiOkResponse({ type: ProductRes })
|
|
||||||
@Get('/site-sku/:siteSku')
|
|
||||||
async getProductBySiteSku(@Param('siteSku') siteSku: string) {
|
|
||||||
try {
|
|
||||||
const product = await this.productService.findProductBySiteSku(siteSku);
|
|
||||||
return successResponse(product);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message || '获取数据失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取产品的站点SKU绑定
|
@ApiOkResponse({
|
||||||
@ApiOkResponse()
|
type: BooleanRes,
|
||||||
@Get('/:id/site-skus')
|
})
|
||||||
async getProductSiteSkus(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.getProductSiteSkus(id);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 覆盖式绑定产品的站点SKU列表
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Post('/:id/site-skus')
|
|
||||||
async bindProductSiteSkus(@Param('id') id: number, @Body() body: { codes: string[] }) {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.bindSiteSkus(id, body?.codes || []);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: ProductRes })
|
|
||||||
@Get('/:id')
|
|
||||||
async getProductById(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
const product = await this.productService.getProductById(id);
|
|
||||||
return successResponse(product);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message || '获取数据失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
|
||||||
@Del('/:id')
|
@Del('/:id')
|
||||||
async deleteProduct(@Param('id') id: number) {
|
async deleteProduct(@Param('id') id: number) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -254,43 +153,14 @@ export class ProductController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取产品的库存组成
|
@ApiOkResponse({
|
||||||
@ApiOkResponse()
|
type: ProductCatListRes,
|
||||||
@Get('/:id/components')
|
})
|
||||||
async getProductComponents(@Param('id') id: number) {
|
@Get('/categories')
|
||||||
|
async getCategories(@Query() query: QueryCategoryDTO) {
|
||||||
|
const { current = 1, pageSize = 10, name } = query;
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.getProductComponents(id);
|
let data = await this.productService.getCategoryList(
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 根据 SKU 自动绑定组成(匹配所有相同 SKU 的库存)
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Post('/:id/components/auto')
|
|
||||||
async autoBindComponents(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.autoBindComponentsBySku(id);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 通用属性接口:分页列表
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Get('/attribute')
|
|
||||||
async getAttributeList(
|
|
||||||
@Query('dictName') dictName: string,
|
|
||||||
@Query('current') current = 1,
|
|
||||||
@Query('pageSize') pageSize = 10,
|
|
||||||
@Query('name') name?: string
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.getAttributeList(
|
|
||||||
dictName,
|
|
||||||
{ current, pageSize },
|
{ current, pageSize },
|
||||||
name
|
name
|
||||||
);
|
);
|
||||||
|
|
@ -300,138 +170,92 @@ export class ProductController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通用属性接口:全部列表
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Get('/attributeAll')
|
@Get('/categorieAll')
|
||||||
async getAttributeAll(@Query('dictName') dictName: string) {
|
async getCategorieAll() {
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.getAttributeAll(dictName);
|
let data = await this.productService.getCategoryAll();
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通用属性接口:创建
|
@ApiOkResponse({
|
||||||
@ApiOkResponse()
|
type: ProductCatRes,
|
||||||
@Post('/attribute')
|
})
|
||||||
async createAttribute(
|
@Post('/category')
|
||||||
@Query('dictName') dictName: string,
|
async createCategory(@Body() categoryData: CreateCategoryDTO) {
|
||||||
@Body() body: { title: string; name: string }
|
|
||||||
) {
|
|
||||||
try {
|
try {
|
||||||
// 调用 getOrCreateAttribute 方法,如果不存在则创建,如果存在则返回
|
const hasCategory = await this.productService.hasCategory(
|
||||||
const data = await this.productService.getOrCreateAttribute(dictName, body.title, body.name);
|
categoryData.name
|
||||||
|
);
|
||||||
|
if (hasCategory) {
|
||||||
|
return errorResponse('分类已存在');
|
||||||
|
}
|
||||||
|
let data = await this.productService.createCategory(categoryData);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通用属性接口:更新
|
@ApiOkResponse({
|
||||||
@ApiOkResponse()
|
type: ProductCatRes,
|
||||||
@Put('/attribute/:id')
|
})
|
||||||
async updateAttribute(
|
@Put('/category/:id')
|
||||||
|
async updateCategory(
|
||||||
@Param('id') id: number,
|
@Param('id') id: number,
|
||||||
@Query('dictName') dictName: string,
|
@Body() categoryData: UpdateCategoryDTO
|
||||||
@Body() body: { title?: string; name?: string }
|
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
if (body?.name) {
|
const hasCategory = await this.productService.hasCategory(
|
||||||
const hasItem = await this.productService.hasAttribute(
|
categoryData.name
|
||||||
dictName,
|
);
|
||||||
body.name,
|
if (hasCategory) {
|
||||||
id
|
return errorResponse('分类已存在');
|
||||||
);
|
|
||||||
if (hasItem) return errorResponse('字典项已存在');
|
|
||||||
}
|
}
|
||||||
const data = await this.productService.updateAttribute(id, body);
|
const data = this.productService.updateCategory(id, categoryData);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通用属性接口:删除
|
@ApiOkResponse({
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
type: BooleanRes,
|
||||||
@Del('/attribute/:id')
|
})
|
||||||
async deleteAttribute(@Param('id') id: number) {
|
@Del('/category/:id')
|
||||||
|
async deleteCategory(@Param('id') id: number) {
|
||||||
try {
|
try {
|
||||||
await this.productService.deleteAttribute(id);
|
const hasProducts = await this.productService.hasProductsInCategory(id);
|
||||||
return successResponse(true);
|
if (hasProducts) throw new Error('该分类下有商品,无法删除');
|
||||||
} catch (error) {
|
const data = await this.productService.deleteCategory(id);
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容旧接口:品牌
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Get('/brandAll')
|
|
||||||
async compatBrandAll() {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.getAttributeAll('brand'); // 返回所有品牌字典项
|
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse()
|
@Post('/batchSetSku')
|
||||||
@Get('/brands')
|
@ApiOkResponse({
|
||||||
async compatBrands(@Query('current') current = 1, @Query('pageSize') pageSize = 10, @Query('name') name?: string) {
|
description: '批量设置 sku 的响应结果',
|
||||||
|
type: BooleanRes,
|
||||||
|
})
|
||||||
|
async batchSetSku(@Body() body: BatchSetSkuDTO) {
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.getAttributeList('brand', { current, pageSize }, name); // 分页品牌列表
|
const result = await this.productService.batchSetSku(body.skus);
|
||||||
return successResponse(data);
|
return successResponse(result, '批量设置 sku 成功');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error.message, 400);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Post('/brand')
|
|
||||||
async compatCreateBrand(@Body() body: { title: string; name: string; image?: string; shortName?: string }) {
|
|
||||||
try {
|
|
||||||
const has = await this.productService.hasAttribute('brand', body.name); // 唯一性校验
|
|
||||||
if (has) return errorResponse('品牌已存在');
|
|
||||||
const data = await this.productService.createAttribute('brand', body); // 创建品牌字典项
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Put('/brand/:id')
|
|
||||||
async compatUpdateBrand(@Param('id') id: number, @Body() body: { title?: string; name?: string; image?: string; shortName?: string }) {
|
|
||||||
try {
|
|
||||||
if (body?.name) {
|
|
||||||
const has = await this.productService.hasAttribute('brand', body.name, id); // 唯一性校验(排除自身)
|
|
||||||
if (has) return errorResponse('品牌已存在');
|
|
||||||
}
|
|
||||||
const data = await this.productService.updateAttribute(id, body); // 更新品牌字典项
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
|
||||||
@Del('/brand/:id')
|
|
||||||
async compatDeleteBrand(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
await this.productService.deleteAttribute(id); // 删除品牌字典项
|
|
||||||
return successResponse(true);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容旧接口:口味
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Get('/flavorsAll')
|
@Get('/flavorsAll')
|
||||||
async compatFlavorsAll() {
|
async getFlavorsAll() {
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.getAttributeAll('flavor');
|
let data = await this.productService.getFlavorsAll();
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
|
|
@ -440,9 +264,13 @@ export class ProductController {
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Get('/flavors')
|
@Get('/flavors')
|
||||||
async compatFlavors(@Query('current') current = 1, @Query('pageSize') pageSize = 10, @Query('name') name?: string) {
|
async getFlavors(@Query() query: QueryFlavorsDTO) {
|
||||||
|
const { current = 1, pageSize = 10, name } = query;
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.getAttributeList('flavor', { current, pageSize }, name);
|
let data = await this.productService.getFlavorsList(
|
||||||
|
{ current, pageSize },
|
||||||
|
name
|
||||||
|
);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
|
|
@ -451,11 +279,13 @@ export class ProductController {
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Post('/flavors')
|
@Post('/flavors')
|
||||||
async compatCreateFlavors(@Body() body: { title: string; name: string; image?: string; shortName?: string }) {
|
async createFlavors(@Body() flavorsData: CreateFlavorsDTO) {
|
||||||
try {
|
try {
|
||||||
const has = await this.productService.hasAttribute('flavor', body.name);
|
const hasFlavors = await this.productService.hasFlavors(flavorsData.name);
|
||||||
if (has) return errorResponse('口味已存在');
|
if (hasFlavors) {
|
||||||
const data = await this.productService.createAttribute('flavor', body);
|
return errorResponse('分类已存在');
|
||||||
|
}
|
||||||
|
let data = await this.productService.createFlavors(flavorsData);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
|
|
@ -464,36 +294,42 @@ export class ProductController {
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Put('/flavors/:id')
|
@Put('/flavors/:id')
|
||||||
async compatUpdateFlavors(@Param('id') id: number, @Body() body: { title?: string; name?: string; image?: string; shortName?: string }) {
|
async updateFlavors(
|
||||||
|
@Param('id') id: number,
|
||||||
|
@Body() flavorsData: UpdateFlavorsDTO
|
||||||
|
) {
|
||||||
try {
|
try {
|
||||||
if (body?.name) {
|
const hasFlavors = await this.productService.hasFlavors(flavorsData.name);
|
||||||
const has = await this.productService.hasAttribute('flavor', body.name, id);
|
if (hasFlavors) {
|
||||||
if (has) return errorResponse('口味已存在');
|
return errorResponse('分类已存在');
|
||||||
}
|
}
|
||||||
const data = await this.productService.updateAttribute(id, body);
|
const data = this.productService.updateFlavors(id, flavorsData);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
@ApiOkResponse({
|
||||||
|
type: BooleanRes,
|
||||||
|
})
|
||||||
@Del('/flavors/:id')
|
@Del('/flavors/:id')
|
||||||
async compatDeleteFlavors(@Param('id') id: number) {
|
async deleteFlavors(@Param('id') id: number) {
|
||||||
try {
|
try {
|
||||||
await this.productService.deleteAttribute(id);
|
const hasProducts = await this.productService.hasProductsInFlavors(id);
|
||||||
return successResponse(true);
|
if (hasProducts) throw new Error('该分类下有商品,无法删除');
|
||||||
|
const data = await this.productService.deleteFlavors(id);
|
||||||
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 兼容旧接口:规格
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Get('/strengthAll')
|
@Get('/strengthAll')
|
||||||
async compatStrengthAll() {
|
async getStrengthAll() {
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.getAttributeAll('strength');
|
let data = await this.productService.getStrengthAll();
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
|
|
@ -502,9 +338,13 @@ export class ProductController {
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Get('/strength')
|
@Get('/strength')
|
||||||
async compatStrength(@Query('current') current = 1, @Query('pageSize') pageSize = 10, @Query('name') name?: string) {
|
async getStrength(@Query() query: QueryStrengthDTO) {
|
||||||
|
const { current = 1, pageSize = 10, name } = query;
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.getAttributeList('strength', { current, pageSize }, name);
|
let data = await this.productService.getStrengthList(
|
||||||
|
{ current, pageSize },
|
||||||
|
name
|
||||||
|
);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
|
|
@ -513,11 +353,15 @@ export class ProductController {
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Post('/strength')
|
@Post('/strength')
|
||||||
async compatCreateStrength(@Body() body: { title: string; name: string; image?: string; shortName?: string }) {
|
async createStrength(@Body() strengthData: CreateStrengthDTO) {
|
||||||
try {
|
try {
|
||||||
const has = await this.productService.hasAttribute('strength', body.name);
|
const hasStrength = await this.productService.hasStrength(
|
||||||
if (has) return errorResponse('规格已存在');
|
strengthData.name
|
||||||
const data = await this.productService.createAttribute('strength', body);
|
);
|
||||||
|
if (hasStrength) {
|
||||||
|
return errorResponse('分类已存在');
|
||||||
|
}
|
||||||
|
let data = await this.productService.createStrength(strengthData);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
|
|
@ -526,277 +370,33 @@ export class ProductController {
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Put('/strength/:id')
|
@Put('/strength/:id')
|
||||||
async compatUpdateStrength(@Param('id') id: number, @Body() body: { title?: string; name?: string; image?: string; shortName?: string }) {
|
async updateStrength(
|
||||||
try {
|
@Param('id') id: number,
|
||||||
if (body?.name) {
|
@Body() strengthData: UpdateStrengthDTO
|
||||||
const has = await this.productService.hasAttribute('strength', body.name, id);
|
|
||||||
if (has) return errorResponse('规格已存在');
|
|
||||||
}
|
|
||||||
const data = await this.productService.updateAttribute(id, body);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
|
||||||
@Del('/strength/:id')
|
|
||||||
async compatDeleteStrength(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
await this.productService.deleteAttribute(id);
|
|
||||||
return successResponse(true);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 兼容旧接口:尺寸
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Get('/sizeAll')
|
|
||||||
async compatSizeAll() {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.getAttributeAll('size');
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Get('/size')
|
|
||||||
async compatSize(@Query('current') current = 1, @Query('pageSize') pageSize = 10, @Query('name') name?: string) {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.getAttributeList('size', { current, pageSize }, name);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Post('/size')
|
|
||||||
async compatCreateSize(@Body() body: { title: string; name: string; image?: string; shortName?: string }) {
|
|
||||||
try {
|
|
||||||
const has = await this.productService.hasAttribute('size', body.name);
|
|
||||||
if (has) return errorResponse('尺寸已存在');
|
|
||||||
const data = await this.productService.createAttribute('size', body);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse()
|
|
||||||
@Put('/size/:id')
|
|
||||||
async compatUpdateSize(@Param('id') id: number, @Body() body: { title?: string; name?: string; image?: string; shortName?: string }) {
|
|
||||||
try {
|
|
||||||
if (body?.name) {
|
|
||||||
const has = await this.productService.hasAttribute('size', body.name, id);
|
|
||||||
if (has) return errorResponse('尺寸已存在');
|
|
||||||
}
|
|
||||||
const data = await this.productService.updateAttribute(id, body);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
|
||||||
@Del('/size/:id')
|
|
||||||
async compatDeleteSize(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
await this.productService.deleteAttribute(id);
|
|
||||||
return successResponse(true);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取所有分类
|
|
||||||
@ApiOkResponse({ description: '获取所有分类' })
|
|
||||||
@Get('/categories/all')
|
|
||||||
async getCategoriesAll() {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.getCategoriesAll();
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取分类下的属性配置
|
|
||||||
@ApiOkResponse({ description: '获取分类下的属性配置' })
|
|
||||||
@Get('/category/:id/attributes')
|
|
||||||
async getCategoryAttributes(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.getCategoryAttributes(id);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建分类
|
|
||||||
@ApiOkResponse({ description: '创建分类' })
|
|
||||||
@Post('/category')
|
|
||||||
async createCategory(@Body() body: CreateCategoryDTO) {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.createCategory(body);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新分类
|
|
||||||
@ApiOkResponse({ description: '更新分类' })
|
|
||||||
@Put('/category/:id')
|
|
||||||
async updateCategory(@Param('id') id: number, @Body() body: UpdateCategoryDTO) {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.updateCategory(id, body);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除分类
|
|
||||||
@ApiOkResponse({ description: '删除分类' })
|
|
||||||
@Del('/category/:id')
|
|
||||||
async deleteCategory(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
await this.productService.deleteCategory(id);
|
|
||||||
return successResponse(true);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建分类属性
|
|
||||||
@ApiOkResponse({ description: '创建分类属性' })
|
|
||||||
@Post('/category/attribute')
|
|
||||||
async createCategoryAttribute(@Body() body: { categoryId: number; dictId: number }) {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.createCategoryAttribute(body);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除分类属性
|
|
||||||
@ApiOkResponse({ description: '删除分类属性' })
|
|
||||||
@Del('/category/attribute/:id')
|
|
||||||
async deleteCategoryAttribute(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
await this.productService.deleteCategoryAttribute(id);
|
|
||||||
return successResponse(true);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 同步库存 SKU 到产品单品
|
|
||||||
@ApiOkResponse({ description: '同步库存 SKU 到产品单品' })
|
|
||||||
@Post('/sync-stock')
|
|
||||||
async syncStockToProduct() {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.syncStockToProduct();
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 同步单个产品到站点
|
|
||||||
@ApiOkResponse({ description: '同步单个产品到站点', type: SyncProductToSiteResultDTO })
|
|
||||||
@Post('/sync-to-site')
|
|
||||||
async syncToSite(@Body() body: SyncProductToSiteDTO) {
|
|
||||||
try {
|
|
||||||
const result = await this.productService.syncToSite(body);
|
|
||||||
return successResponse(result);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 从站点同步产品到本地
|
|
||||||
@ApiOkResponse({ description: '从站点同步产品到本地', type: ProductRes })
|
|
||||||
@Post('/sync-from-site')
|
|
||||||
async syncProductFromSite(@Body() body: { siteId: number; siteProductId: string | number ,sku: string}) {
|
|
||||||
try {
|
|
||||||
const { siteId, siteProductId, sku } = body;
|
|
||||||
const product = await this.productService.syncProductFromSite(siteId, siteProductId, sku);
|
|
||||||
return successResponse(product);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量从站点同步产品到本地
|
|
||||||
@ApiOkResponse({ description: '批量从站点同步产品到本地', type: SyncOperationResultDTO })
|
|
||||||
@Post('/batch-sync-from-site')
|
|
||||||
async batchSyncFromSite(@Body() body: { siteId: number; siteProductIds: (string | number)[] }) {
|
|
||||||
try {
|
|
||||||
throw new Error('批量同步产品到本地暂未实现');
|
|
||||||
// const { siteId, siteProductIds } = body;
|
|
||||||
// const result = await this.productService.batchSyncFromSite(siteId, siteProductIds.map((id) => ({ siteProductId: id, sku: '' })));
|
|
||||||
// // 将服务层返回的结果转换为统一格式
|
|
||||||
// const errors = result.errors.map((error: string) => {
|
|
||||||
// // 提取产品ID部分作为标识符
|
|
||||||
// const match = error.match(/站点产品ID (\d+) /);
|
|
||||||
// const identifier = match ? match[1] : 'unknown';
|
|
||||||
// return {
|
|
||||||
// identifier: identifier,
|
|
||||||
// error: error
|
|
||||||
// };
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return successResponse({
|
|
||||||
// total: siteProductIds.length,
|
|
||||||
// processed: result.synced + errors.length,
|
|
||||||
// synced: result.synced,
|
|
||||||
// errors: errors
|
|
||||||
// });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量同步产品到站点
|
|
||||||
@ApiOkResponse({ description: '批量同步产品到站点', type: SyncOperationResultDTO })
|
|
||||||
@Post('/batch-sync-to-site')
|
|
||||||
async batchSyncToSite(@Body() body: BatchSyncProductToSiteDTO) {
|
|
||||||
try {
|
|
||||||
const { siteId, data } = body;
|
|
||||||
const result = await this.productService.batchSyncToSite(siteId, data);
|
|
||||||
return successResponse(result);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取所有产品,支持按品牌过滤
|
|
||||||
@ApiOkResponse({ description: '获取所有产品', type: ProductListRes })
|
|
||||||
@Get('/all')
|
|
||||||
async getAllProducts(@Query('brand') brand?: string) {
|
|
||||||
try {
|
|
||||||
const data = await this.productService.getAllProducts(brand);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取按属性分组的产品,默认按强度划分
|
|
||||||
@ApiOkResponse({ description: '获取按属性分组的产品' })
|
|
||||||
@Get('/grouped')
|
|
||||||
async getGroupedProducts(
|
|
||||||
@Query('brand') brand?: string,
|
|
||||||
@Query('attribute') attribute: string = 'strength'
|
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
const data = await this.productService.getProductsGroupedByAttribute(brand, attribute);
|
const hasStrength = await this.productService.hasStrength(
|
||||||
|
strengthData.name
|
||||||
|
);
|
||||||
|
if (hasStrength) {
|
||||||
|
return errorResponse('分类已存在');
|
||||||
|
}
|
||||||
|
const data = this.productService.updateStrength(id, strengthData);
|
||||||
|
return successResponse(data);
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error?.message || error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOkResponse({
|
||||||
|
type: BooleanRes,
|
||||||
|
})
|
||||||
|
@Del('/strength/:id')
|
||||||
|
async deleteStrength(@Param('id') id: number) {
|
||||||
|
try {
|
||||||
|
const hasProducts = await this.productService.hasProductsInStrength(id);
|
||||||
|
if (hasProducts) throw new Error('该分类下有商品,无法删除');
|
||||||
|
const data = await this.productService.deleteStrength(id);
|
||||||
return successResponse(data);
|
return successResponse(data);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || error);
|
return errorResponse(error?.message || error);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
||||||
import { Body, Controller, Get, Inject, Param, Put, Post, Query } from '@midwayjs/core';
|
import { Body, Controller, Get, Inject, Param, Put, Post, Query } from '@midwayjs/core';
|
||||||
import { ApiOkResponse } from '@midwayjs/swagger';
|
import { ApiOkResponse } from '@midwayjs/swagger';
|
||||||
import { SitesResponse } from '../dto/reponse.dto';
|
import { WpSitesResponse } from '../dto/reponse.dto';
|
||||||
import { errorResponse, successResponse } from '../utils/response.util';
|
import { errorResponse, successResponse } from '../utils/response.util';
|
||||||
import { SiteService } from '../service/site.service';
|
import { SiteService } from '../service/site.service';
|
||||||
import { CreateSiteDTO, DisableSiteDTO, QuerySiteDTO, UpdateSiteDTO } from '../dto/site.dto';
|
import { CreateSiteDTO, DisableSiteDTO, QuerySiteDTO, UpdateSiteDTO } from '../dto/site.dto';
|
||||||
|
|
@ -10,13 +10,12 @@ export class SiteController {
|
||||||
@Inject()
|
@Inject()
|
||||||
siteService: SiteService;
|
siteService: SiteService;
|
||||||
|
|
||||||
@ApiOkResponse({ description: '关联网站', type: SitesResponse })
|
@ApiOkResponse({ description: '关联网站', type: WpSitesResponse })
|
||||||
@Get('/all')
|
@Get('/all')
|
||||||
async all() {
|
async all() {
|
||||||
try {
|
try {
|
||||||
const { items } = await this.siteService.list({ current: 1, pageSize: 1000, isDisabled: false });
|
const { items } = await this.siteService.list({ current: 1, pageSize: 1000, isDisabled: false });
|
||||||
// 返回完整的站点对象,包括 skuPrefix 等字段
|
return successResponse(items.map((v: any) => ({ id: v.id, siteName: v.siteName })));
|
||||||
return successResponse(items);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || '获取失败');
|
return errorResponse(error?.message || '获取失败');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ export class StatisticsController {
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Get('/orderSource')
|
@Get('/orderSource')
|
||||||
async getOrderSource(@Query() params) {
|
async getOrderSorce(@Query() params) {
|
||||||
try {
|
try {
|
||||||
return successResponse(await this.statisticsService.getOrderSorce(params));
|
return successResponse(await this.statisticsService.getOrderSorce(params));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
||||||
|
|
@ -176,21 +176,9 @@ export class StockController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查某个 SKU 是否有库存(任一仓库数量大于 0)
|
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
|
||||||
@Get('/has/:sku')
|
|
||||||
async hasStock(@Param('sku') sku: string) {
|
|
||||||
try {
|
|
||||||
const data = await this.stockService.hasStockBySku(sku);
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '查询失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@ApiOkResponse({
|
@ApiOkResponse({
|
||||||
type: BooleanRes,
|
type: BooleanRes,
|
||||||
description: '更新库存(入库,出库,调整)',
|
description: '更新库存(入库、出库、调整)',
|
||||||
})
|
})
|
||||||
@Post('/update')
|
@Post('/update')
|
||||||
async updateStock(@Body() body: UpdateStockDTO) {
|
async updateStock(@Body() body: UpdateStockDTO) {
|
||||||
|
|
|
||||||
|
|
@ -10,19 +10,19 @@ export class SubscriptionController {
|
||||||
@Inject()
|
@Inject()
|
||||||
subscriptionService: SubscriptionService;
|
subscriptionService: SubscriptionService;
|
||||||
|
|
||||||
// 同步订阅:根据站点 ID 拉取并更新本地订阅数据
|
// 同步订阅:根据站点 ID 拉取并更新本地订阅数据
|
||||||
@ApiOkResponse({ type: BooleanRes })
|
@ApiOkResponse({ type: BooleanRes })
|
||||||
@Post('/sync/:siteId')
|
@Post('/sync/:siteId')
|
||||||
async sync(@Param('siteId') siteId: number) {
|
async sync(@Param('siteId') siteId: string) {
|
||||||
try {
|
try {
|
||||||
const result = await this.subscriptionService.syncSubscriptions(siteId);
|
await this.subscriptionService.syncSubscriptions(siteId);
|
||||||
return successResponse(result);
|
return successResponse(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse(error?.message || '同步失败');
|
return errorResponse(error?.message || '同步失败');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 订阅列表:分页 + 筛选
|
// 订阅列表:分页 + 筛选
|
||||||
@ApiOkResponse({ type: SubscriptionListRes })
|
@ApiOkResponse({ type: SubscriptionListRes })
|
||||||
@Get('/list')
|
@Get('/list')
|
||||||
async list(@Query() query: QuerySubscriptionDTO) {
|
async list(@Query() query: QuerySubscriptionDTO) {
|
||||||
|
|
|
||||||
|
|
@ -1,168 +0,0 @@
|
||||||
import { Inject, Controller, Get, Post, Put, Del, Body, Param, Query } from '@midwayjs/core';
|
|
||||||
import { TemplateService } from '../service/template.service';
|
|
||||||
import { successResponse, errorResponse } from '../utils/response.util';
|
|
||||||
import { CreateTemplateDTO, UpdateTemplateDTO, RenderTemplateDTO } from '../dto/template.dto';
|
|
||||||
import { ApiOkResponse, ApiTags } from '@midwayjs/swagger';
|
|
||||||
import { Template } from '../entity/template.entity';
|
|
||||||
import { BooleanRes } from '../dto/reponse.dto';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @controller TemplateController 模板管理
|
|
||||||
*/
|
|
||||||
@ApiTags('Template')
|
|
||||||
@Controller('/template')
|
|
||||||
export class TemplateController {
|
|
||||||
@Inject()
|
|
||||||
templateService: TemplateService;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary 获取模板列表
|
|
||||||
* @description 获取所有可用模板的列表
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: [Template], description: '成功获取模板列表' })
|
|
||||||
@Get('/list')
|
|
||||||
async getTemplateList(@Query() params: any) {
|
|
||||||
// 调用服务层获取列表
|
|
||||||
return this.templateService.getTemplateList(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary 根据名称获取模板
|
|
||||||
* @description 通过模板的唯一名称查找特定模板
|
|
||||||
* @param name 模板名称
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: Template, description: '成功获取模板' })
|
|
||||||
@Get('/:name')
|
|
||||||
async getTemplateByName(@Param('name') name: string) {
|
|
||||||
try {
|
|
||||||
// 调用服务层获取单个模板
|
|
||||||
const data = await this.templateService.getTemplateByName(name);
|
|
||||||
// 返回成功响应
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
// 返回错误响应
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary 创建新模板
|
|
||||||
* @description 创建一个新的模板,用于后续的字符串生成
|
|
||||||
* @param templateData 模板数据
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: Template, description: '成功创建模板' })
|
|
||||||
@Post('/')
|
|
||||||
async createTemplate(@Body() templateData: CreateTemplateDTO) {
|
|
||||||
try {
|
|
||||||
// 调用服务层创建模板
|
|
||||||
const data = await this.templateService.createTemplate(templateData);
|
|
||||||
// 返回成功响应
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
// 返回错误响应
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary 更新现有模板
|
|
||||||
* @description 根据模板 ID 更新一个现有模板的内容
|
|
||||||
* @param id 模板 ID
|
|
||||||
* @param templateData 模板数据
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: Template, description: '成功更新模板' })
|
|
||||||
@Put('/:id')
|
|
||||||
async updateTemplate(
|
|
||||||
@Param('id') id: number,
|
|
||||||
@Body() templateData: UpdateTemplateDTO
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
// 调用服务层更新模板
|
|
||||||
const data = await this.templateService.updateTemplate(id, templateData);
|
|
||||||
// 返回成功响应
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
// 返回错误响应
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary 删除模板
|
|
||||||
* @description 根据模板 ID 删除一个模板
|
|
||||||
* @param id 模板 ID
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: BooleanRes, description: '成功删除模板' })
|
|
||||||
@Del('/:id')
|
|
||||||
async deleteTemplate(@Param('id') id: number) {
|
|
||||||
try {
|
|
||||||
// 调用服务层删除模板
|
|
||||||
const data = await this.templateService.deleteTemplate(id);
|
|
||||||
// 返回成功响应
|
|
||||||
return successResponse(data);
|
|
||||||
} catch (error) {
|
|
||||||
// 返回错误响应
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary 渲染模板
|
|
||||||
* @description 根据模板名称和输入的数据渲染最终字符串
|
|
||||||
* @param name 模板名称
|
|
||||||
* @param data 渲染数据
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: String, description: '成功渲染模板' })
|
|
||||||
@Post('/render/:name')
|
|
||||||
async renderTemplate(
|
|
||||||
@Param('name') name: string,
|
|
||||||
@Body() data: Record<string, any>
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
// 调用服务层渲染模板
|
|
||||||
const renderedString = await this.templateService.render(name, data);
|
|
||||||
// 返回成功响应
|
|
||||||
return successResponse(renderedString);
|
|
||||||
} catch (error) {
|
|
||||||
// 返回错误响应
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary 回填缺失的测试数据
|
|
||||||
* @description 扫描数据库中所有模板,为缺失 testData 的记录生成并保存测试数据
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: Number, description: '成功回填的数量' })
|
|
||||||
@Post('/backfill-testdata')
|
|
||||||
async backfillTestData() {
|
|
||||||
try {
|
|
||||||
const count = await this.templateService.backfillMissingTestData();
|
|
||||||
return successResponse({ updated: count });
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @summary 直接渲染模板内容
|
|
||||||
* @description 直接传入模板内容和数据渲染最终字符串,无需保存模板到数据库
|
|
||||||
* @param renderData 包含模板内容和渲染数据的对象
|
|
||||||
*/
|
|
||||||
@ApiOkResponse({ type: String, description: '成功渲染模板' })
|
|
||||||
@Post('/render-direct')
|
|
||||||
async renderTemplateDirect(@Body() renderData: RenderTemplateDTO) {
|
|
||||||
try {
|
|
||||||
// 调用服务层渲染模板内容
|
|
||||||
const renderedString = await this.templateService.renderWithTemplate(
|
|
||||||
renderData.template,
|
|
||||||
renderData.data
|
|
||||||
);
|
|
||||||
// 返回成功响应
|
|
||||||
return successResponse(renderedString);
|
|
||||||
} catch (error) {
|
|
||||||
// 返回错误响应
|
|
||||||
return errorResponse(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -34,17 +34,16 @@ export class UserController {
|
||||||
})
|
})
|
||||||
@Post('/logout')
|
@Post('/logout')
|
||||||
async logout() {
|
async logout() {
|
||||||
// 可选:在这里处理服务端缓存的 token 或 session
|
// 可选:在这里处理服务端缓存的 token 或 session
|
||||||
|
|
||||||
return successResponse(true);
|
return successResponse(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('/add')
|
@Post('/add')
|
||||||
async addUser(@Body() body: { username: string; password: string; email?: string; remark?: string }) {
|
async addUser(@Body() body: { username: string; password: string }) {
|
||||||
const { username, password, email, remark } = body;
|
const { username, password } = body;
|
||||||
try {
|
try {
|
||||||
// 新增用户 支持邮箱与备注
|
await this.userService.addUser(username, password);
|
||||||
await this.userService.addUser(username, password, remark, email);
|
|
||||||
return successResponse(true);
|
return successResponse(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
|
@ -53,91 +52,21 @@ export class UserController {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('/list')
|
@Get('/list')
|
||||||
async listUsers(
|
async listUsers(@Query() query: { current: number; pageSize: number }) {
|
||||||
@Query()
|
const { current = 1, pageSize = 10 } = query;
|
||||||
query: {
|
return successResponse(await this.userService.listUsers(current, pageSize));
|
||||||
current: number;
|
|
||||||
pageSize: number;
|
|
||||||
remark?: string;
|
|
||||||
username?: string;
|
|
||||||
email?: string;
|
|
||||||
isActive?: string;
|
|
||||||
isSuper?: string;
|
|
||||||
isAdmin?: string;
|
|
||||||
sortField?: string;
|
|
||||||
sortOrder?: string;
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
const { current = 1, pageSize = 10, remark, username, email, isActive, isSuper, isAdmin, sortField, sortOrder } = query;
|
|
||||||
// 将字符串布尔转换为真实布尔
|
|
||||||
const toBool = (v?: string) => (v === undefined ? undefined : v === 'true');
|
|
||||||
// 处理排序方向
|
|
||||||
const order = (sortOrder === 'ascend' || sortOrder === 'ASC') ? 'ASC' : 'DESC';
|
|
||||||
|
|
||||||
// 列表移除密码字段
|
|
||||||
const { items, total } = await this.userService.listUsers(
|
|
||||||
current,
|
|
||||||
pageSize,
|
|
||||||
{
|
|
||||||
remark,
|
|
||||||
username,
|
|
||||||
email,
|
|
||||||
isActive: toBool(isActive),
|
|
||||||
isSuper: toBool(isSuper),
|
|
||||||
isAdmin: toBool(isAdmin),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
field: sortField,
|
|
||||||
order,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
const safeItems = (items || []).map((it: any) => {
|
|
||||||
const { password, ...rest } = it || {};
|
|
||||||
return rest;
|
|
||||||
});
|
|
||||||
return successResponse({ items: safeItems, total, current, pageSize });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('/toggleActive')
|
@Post('/toggleActive')
|
||||||
async toggleActive(@Body() body: { userId: number; isActive: boolean }) {
|
async toggleActive(@Body() body: { userId: number; isActive: boolean }) {
|
||||||
try {
|
return this.userService.toggleUserActive(body.userId, body.isActive);
|
||||||
// 调用服务层更新启用状态
|
|
||||||
const data = await this.userService.toggleUserActive(body.userId, body.isActive);
|
|
||||||
// 移除密码字段,保证安全
|
|
||||||
const { password, ...safe } = data as any;
|
|
||||||
return successResponse(safe);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '操作失败');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新用户(支持用户名/密码/权限/角色更新)
|
|
||||||
@Post('/update/:id')
|
|
||||||
async updateUser(
|
|
||||||
@Body() body: { username?: string; password?: string; email?: string; isSuper?: boolean; isAdmin?: boolean; permissions?: string[]; remark?: string },
|
|
||||||
@Query('id') id?: number
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
// 条件判断:优先从路径参数获取 ID(兼容生成的 API 文件为 POST /user/update/:id)
|
|
||||||
const userId = Number((this.ctx?.params?.id ?? id));
|
|
||||||
if (!userId) throw new Error('缺少用户ID');
|
|
||||||
const data = await this.userService.updateUser(userId, body);
|
|
||||||
// 移除密码字段,保证安全
|
|
||||||
const { password, ...safe } = data as any;
|
|
||||||
return successResponse(safe);
|
|
||||||
} catch (error) {
|
|
||||||
return errorResponse(error?.message || '更新失败');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ApiOkResponse()
|
@ApiOkResponse()
|
||||||
@Get()
|
@Get()
|
||||||
async getUser(@User() user) {
|
async getUser(@User() user) {
|
||||||
try {
|
try {
|
||||||
// 详情移除密码字段
|
return successResponse(await this.userService.getUser(user.id));
|
||||||
const data = await this.userService.getUser(user.id);
|
|
||||||
const { password, ...safe } = (data as any) || {};
|
|
||||||
return successResponse(safe);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return errorResponse('获取失败');
|
return errorResponse('获取失败');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { HttpStatus, ILogger, Inject, Logger } from '@midwayjs/core';
|
import { HttpStatus, Inject } from '@midwayjs/core';
|
||||||
import {
|
import {
|
||||||
Controller,
|
Controller,
|
||||||
Post,
|
Post,
|
||||||
|
|
@ -9,18 +9,20 @@ import {
|
||||||
} from '@midwayjs/decorator';
|
} from '@midwayjs/decorator';
|
||||||
import { Context } from '@midwayjs/koa';
|
import { Context } from '@midwayjs/koa';
|
||||||
import * as crypto from 'crypto';
|
import * as crypto from 'crypto';
|
||||||
|
import { WpProductService } from '../service/wp_product.service';
|
||||||
|
import { WPService } from '../service/wp.service';
|
||||||
import { SiteService } from '../service/site.service';
|
import { SiteService } from '../service/site.service';
|
||||||
import { OrderService } from '../service/order.service';
|
import { OrderService } from '../service/order.service';
|
||||||
import { SiteApiService } from '../service/site-api.service';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Controller('/webhook')
|
@Controller('/webhook')
|
||||||
export class WebhookController {
|
export class WebhookController {
|
||||||
private secret = 'YOONE24kd$kjcdjflddd';
|
private secret = 'YOONE24kd$kjcdjflddd';
|
||||||
|
|
||||||
// 平台服务保留按需注入
|
@Inject()
|
||||||
|
private readonly wpProductService: WpProductService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private readonly wpApiService: WPService;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private readonly orderService: OrderService;
|
private readonly orderService: OrderService;
|
||||||
|
|
@ -28,37 +30,29 @@ export class WebhookController {
|
||||||
@Inject()
|
@Inject()
|
||||||
ctx: Context;
|
ctx: Context;
|
||||||
|
|
||||||
@Logger()
|
|
||||||
logger: ILogger;
|
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private readonly siteService: SiteService;
|
private readonly siteService: SiteService;
|
||||||
@Inject()
|
|
||||||
private readonly siteApiService: SiteApiService;
|
|
||||||
|
|
||||||
// 移除配置中的站点数组,来源统一改为数据库
|
// 移除配置中的站点数组,来源统一改为数据库
|
||||||
|
|
||||||
@Get('/')
|
@Get('/')
|
||||||
async test() {
|
async test() {
|
||||||
return 'webhook';
|
return 'webhook';
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO 这里得检查一下是否对 SHOPYY有效,否则得另外书写
|
|
||||||
@Post('/woocommerce')
|
@Post('/woocommerce')
|
||||||
async handleWooWebhook(
|
async handleWooWebhook(
|
||||||
@Body() body: any,
|
@Body() body: any,
|
||||||
@Query('siteId') siteIdStr: string,
|
@Query('siteId') siteId: string,
|
||||||
@Headers() header: any
|
@Headers() header: any
|
||||||
) {
|
) {
|
||||||
console.log(`webhook woocommerce`, siteIdStr, body, header)
|
|
||||||
const signature = header['x-wc-webhook-signature'];
|
const signature = header['x-wc-webhook-signature'];
|
||||||
const topic = header['x-wc-webhook-topic'];
|
const topic = header['x-wc-webhook-topic'];
|
||||||
const source = header['x-wc-webhook-source'];
|
const source = header['x-wc-webhook-source'];
|
||||||
const siteId = Number(siteIdStr);
|
|
||||||
// 从数据库获取站点配置
|
// 从数据库获取站点配置
|
||||||
const site = await this.siteService.get(siteId, true);
|
const site = await this.siteService.get(Number(siteId), true);
|
||||||
|
|
||||||
if (!site || !source?.includes(site.apiUrl)) {
|
if (!site || !source.includes(site.apiUrl)) {
|
||||||
console.log('domain not match');
|
console.log('domain not match');
|
||||||
return {
|
return {
|
||||||
code: HttpStatus.BAD_REQUEST,
|
code: HttpStatus.BAD_REQUEST,
|
||||||
|
|
@ -80,94 +74,42 @@ export class WebhookController {
|
||||||
.update(rawBody)
|
.update(rawBody)
|
||||||
.digest('base64');
|
.digest('base64');
|
||||||
try {
|
try {
|
||||||
if (hash !== signature) {
|
if (hash === signature) {
|
||||||
return {
|
|
||||||
code: 403,
|
|
||||||
success: false,
|
|
||||||
message: 'Webhook verification failed',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const adapter = await this.siteApiService.getAdapter(siteId);
|
|
||||||
switch (topic) {
|
|
||||||
case 'product.created':
|
|
||||||
case 'product.updated':
|
|
||||||
// 不再写入本地,平台事件仅确认接收
|
|
||||||
break;
|
|
||||||
case 'product.deleted':
|
|
||||||
// 不再写入本地,平台事件仅确认接收
|
|
||||||
break;
|
|
||||||
case 'order.created':
|
|
||||||
case 'order.updated':
|
|
||||||
const order = adapter.mapPlatformToUnifiedOrder(body)
|
|
||||||
await this.orderService.syncSingleOrder(siteId, order);
|
|
||||||
break;
|
|
||||||
case 'order.deleted':
|
|
||||||
break;
|
|
||||||
case 'customer.created':
|
|
||||||
break;
|
|
||||||
case 'customer.updated':
|
|
||||||
break;
|
|
||||||
case 'customer.deleted':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log('Unhandled event:', body.event);
|
|
||||||
|
|
||||||
return {
|
|
||||||
code: 200,
|
|
||||||
success: true,
|
|
||||||
message: 'Webhook processed successfully',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Post('/shoppy')
|
|
||||||
async handleShoppyWebhook(
|
|
||||||
@Body() body: any,
|
|
||||||
@Query('siteId') siteIdStr: string,
|
|
||||||
@Query('signature') signature: string,
|
|
||||||
@Headers() header: any
|
|
||||||
) {
|
|
||||||
console.log(`webhook shoppy`, siteIdStr, body, header)
|
|
||||||
const topic = header['x-oemsaas-event-type'];
|
|
||||||
// const source = header['x-oemsaas-shop-domain'];
|
|
||||||
const siteId = Number(siteIdStr);
|
|
||||||
|
|
||||||
if (!signature) {
|
|
||||||
return {
|
|
||||||
code: HttpStatus.BAD_REQUEST,
|
|
||||||
success: false,
|
|
||||||
message: 'Signature missing',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//shopyy 无法提供加密字段校验,注释校验逻辑
|
|
||||||
// const rawBody = this.ctx.request.rawBody;
|
|
||||||
// const hash = crypto
|
|
||||||
// .createHmac('sha256', this.secret)
|
|
||||||
// .update(rawBody)
|
|
||||||
// .digest('base64');
|
|
||||||
const adapter = await this.siteApiService.getAdapter(siteId);
|
|
||||||
try {
|
|
||||||
if (this.secret === signature) {
|
|
||||||
switch (topic) {
|
switch (topic) {
|
||||||
case 'product.created':
|
case 'product.created':
|
||||||
case 'product.updated':
|
case 'product.updated':
|
||||||
// 不再写入本地,平台事件仅确认接收
|
// 变体更新
|
||||||
|
if (body.type === 'variation') {
|
||||||
|
const variation = await this.wpApiService.getVariation(
|
||||||
|
site,
|
||||||
|
body.parent_id,
|
||||||
|
body.id
|
||||||
|
);
|
||||||
|
this.wpProductService.syncVariation(
|
||||||
|
siteId,
|
||||||
|
body.parent_id,
|
||||||
|
variation
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const variations =
|
||||||
|
body.type === 'variable'
|
||||||
|
? await this.wpApiService.getVariations(site, body.id)
|
||||||
|
: [];
|
||||||
|
await this.wpProductService.syncProductAndVariations(
|
||||||
|
String(site.id),
|
||||||
|
body,
|
||||||
|
variations
|
||||||
|
);
|
||||||
break;
|
break;
|
||||||
case 'product.deleted':
|
case 'product.deleted':
|
||||||
// 不再写入本地,平台事件仅确认接收
|
await this.wpProductService.delWpProduct(String(site.id), body.id);
|
||||||
break;
|
break;
|
||||||
case 'orders/create':
|
case 'order.created':
|
||||||
case 'orders/update':
|
case 'order.updated':
|
||||||
const order = adapter.mapPlatformToUnifiedOrder(body)
|
await this.orderService.syncSingleOrder(siteId, body);
|
||||||
await this.orderService.syncSingleOrder(siteId, order);
|
|
||||||
break;
|
break;
|
||||||
case 'orders/delete':
|
case 'order.deleted':
|
||||||
break;
|
break;
|
||||||
case 'customer.created':
|
case 'customer.created':
|
||||||
break;
|
break;
|
||||||
|
|
@ -176,18 +118,23 @@ export class WebhookController {
|
||||||
case 'customer.deleted':
|
case 'customer.deleted':
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
console.log('Unhandled event:', topic);
|
console.log('Unhandled event:', body.event);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code: 200,
|
code: 200,
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Webhook processed successfully',
|
message: 'Webhook processed successfully',
|
||||||
};
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
code: 403,
|
||||||
|
success: false,
|
||||||
|
message: 'Webhook verification failed',
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,200 @@
|
||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
Inject,
|
||||||
|
Get,
|
||||||
|
Query,
|
||||||
|
Put,
|
||||||
|
Body,
|
||||||
|
} from '@midwayjs/core';
|
||||||
|
import { WpProductService } from '../service/wp_product.service';
|
||||||
|
import { errorResponse, successResponse } from '../utils/response.util';
|
||||||
|
import { ApiOkResponse } from '@midwayjs/swagger';
|
||||||
|
import { BooleanRes, WpProductListRes } from '../dto/reponse.dto';
|
||||||
|
import {
|
||||||
|
QueryWpProductDTO,
|
||||||
|
SetConstitutionDTO,
|
||||||
|
UpdateVariationDTO,
|
||||||
|
UpdateWpProductDTO,
|
||||||
|
} from '../dto/wp_product.dto';
|
||||||
|
import { WPService } from '../service/wp.service';
|
||||||
|
import { SiteService } from '../service/site.service';
|
||||||
|
import {
|
||||||
|
ProductsRes,
|
||||||
|
} from '../dto/reponse.dto';
|
||||||
|
@Controller('/wp_product')
|
||||||
|
export class WpProductController {
|
||||||
|
// 移除控制器内的配置站点引用,统一由服务层处理站点数据
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private readonly wpProductService: WpProductService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private readonly wpApiService: WPService;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private readonly siteService: SiteService;
|
||||||
|
|
||||||
|
@ApiOkResponse({
|
||||||
|
type: BooleanRes,
|
||||||
|
})
|
||||||
|
@Post('/sync/:siteId')
|
||||||
|
async syncProducts(@Param('siteId') siteId: string) {
|
||||||
|
try {
|
||||||
|
await this.wpProductService.syncSite(siteId);
|
||||||
|
return successResponse(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return errorResponse('同步失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOkResponse({
|
||||||
|
type: WpProductListRes,
|
||||||
|
})
|
||||||
|
@Get('/list')
|
||||||
|
async getWpProducts(@Query() query: QueryWpProductDTO) {
|
||||||
|
try {
|
||||||
|
const data = await this.wpProductService.getProductList(query);
|
||||||
|
return successResponse(data);
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOkResponse({
|
||||||
|
type: BooleanRes,
|
||||||
|
})
|
||||||
|
@Put('/:id/constitution')
|
||||||
|
async setConstitution(
|
||||||
|
@Param('id') id: number,
|
||||||
|
@Body()
|
||||||
|
body: SetConstitutionDTO
|
||||||
|
) {
|
||||||
|
const { isProduct, constitution } = body;
|
||||||
|
try {
|
||||||
|
await this.wpProductService.setConstitution(id, isProduct, constitution);
|
||||||
|
return successResponse(true);
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOkResponse({
|
||||||
|
type: BooleanRes
|
||||||
|
})
|
||||||
|
@Post('/updateState/:id')
|
||||||
|
async updateWPProductState(
|
||||||
|
@Param('id') id: number,
|
||||||
|
@Body() body: any, // todo
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const res = await this.wpProductService.updateProductStatus(id, body?.status, body?.stock_status);
|
||||||
|
return successResponse(res);
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新产品接口
|
||||||
|
* @param productId 产品 ID
|
||||||
|
* @param body 更新数据
|
||||||
|
*/
|
||||||
|
@ApiOkResponse({
|
||||||
|
type: BooleanRes,
|
||||||
|
})
|
||||||
|
@Put('/siteId/:siteId/products/:productId')
|
||||||
|
async updateProduct(
|
||||||
|
@Param('siteId') siteId: string,
|
||||||
|
@Param('productId') productId: string,
|
||||||
|
@Body() body: UpdateWpProductDTO
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const isDuplicate = await this.wpProductService.isSkuDuplicate(
|
||||||
|
body.sku,
|
||||||
|
siteId,
|
||||||
|
productId
|
||||||
|
);
|
||||||
|
if (isDuplicate) {
|
||||||
|
return errorResponse('SKU已存在');
|
||||||
|
}
|
||||||
|
const site = await this.siteService.get(Number(siteId), true);
|
||||||
|
const result = await this.wpApiService.updateProduct(
|
||||||
|
site,
|
||||||
|
productId,
|
||||||
|
body
|
||||||
|
);
|
||||||
|
if (result) {
|
||||||
|
this.wpProductService.updateWpProduct(siteId, productId, body);
|
||||||
|
return successResponse(result, '产品更新成功');
|
||||||
|
}
|
||||||
|
return errorResponse('产品更新失败');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新产品失败:', error);
|
||||||
|
return errorResponse(error.message || '产品更新失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新变体接口
|
||||||
|
* @param productId 产品 ID
|
||||||
|
* @param variationId 变体 ID
|
||||||
|
* @param body 更新数据
|
||||||
|
*/
|
||||||
|
@Put('/siteId/:siteId/products/:productId/variations/:variationId')
|
||||||
|
async updateVariation(
|
||||||
|
@Param('siteId') siteId: string,
|
||||||
|
@Param('productId') productId: string,
|
||||||
|
@Param('variationId') variationId: string,
|
||||||
|
@Body() body: UpdateVariationDTO
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const isDuplicate = await this.wpProductService.isSkuDuplicate(
|
||||||
|
body.sku,
|
||||||
|
siteId,
|
||||||
|
productId,
|
||||||
|
variationId
|
||||||
|
);
|
||||||
|
if (isDuplicate) {
|
||||||
|
return errorResponse('SKU已存在');
|
||||||
|
}
|
||||||
|
const site = await this.siteService.get(Number(siteId), true);
|
||||||
|
const result = await this.wpApiService.updateVariation(
|
||||||
|
site,
|
||||||
|
productId,
|
||||||
|
variationId,
|
||||||
|
body
|
||||||
|
);
|
||||||
|
if (result) {
|
||||||
|
this.wpProductService.updateWpProductVaritation(
|
||||||
|
siteId,
|
||||||
|
productId,
|
||||||
|
variationId,
|
||||||
|
body
|
||||||
|
);
|
||||||
|
return successResponse(result, '产品变体更新成功');
|
||||||
|
}
|
||||||
|
return errorResponse('变体更新失败');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新变体失败:', error);
|
||||||
|
return errorResponse(error.message || '产品变体更新失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOkResponse({
|
||||||
|
description: '通过name搜索产品/订单',
|
||||||
|
type: ProductsRes,
|
||||||
|
})
|
||||||
|
@Get('/search')
|
||||||
|
async searchProducts(@Query('name') name: string) {
|
||||||
|
try {
|
||||||
|
// 调用服务获取产品数据
|
||||||
|
const products = await this.wpProductService.findProductsByName(name);
|
||||||
|
return successResponse(products);
|
||||||
|
} catch (error) {
|
||||||
|
return errorResponse(error.message || '获取数据失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import { DataSource, DataSourceOptions } from 'typeorm';
|
|
||||||
import { SeederOptions } from 'typeorm-extension';
|
|
||||||
|
|
||||||
|
|
||||||
const options: DataSourceOptions & SeederOptions = {
|
|
||||||
type: 'mysql',
|
|
||||||
host: '127.0.0.1',
|
|
||||||
port: 23306,
|
|
||||||
username: 'root',
|
|
||||||
password: '12345678',
|
|
||||||
database: 'inventory',
|
|
||||||
synchronize: true,
|
|
||||||
logging: true,
|
|
||||||
entities: [__dirname + '/../entity/*.ts'],
|
|
||||||
migrations: ['src/db/migrations/**/*.ts'],
|
|
||||||
seeds: ['src/db/seeds/**/*.ts'],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const AppDataSource = new DataSource(options);
|
|
||||||
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
|
|
||||||
import { Seeder, SeederFactoryManager } from 'typeorm-extension';
|
|
||||||
import { DataSource } from 'typeorm';
|
|
||||||
import { Area } from '../../entity/area.entity';
|
|
||||||
|
|
||||||
export default class AreaSeeder implements Seeder {
|
|
||||||
public async run(
|
|
||||||
dataSource: DataSource,
|
|
||||||
factoryManager: SeederFactoryManager
|
|
||||||
): Promise<any> {
|
|
||||||
const areaRepository = dataSource.getRepository(Area);
|
|
||||||
|
|
||||||
const areas = [
|
|
||||||
{ name: 'Australia', code: 'AU' },
|
|
||||||
{ name: 'Canada', code: 'CA' },
|
|
||||||
{ name: 'United States', code: 'US' },
|
|
||||||
{ name: 'Germany', code: 'DE' },
|
|
||||||
{ name: 'Poland', code: 'PL' },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const areaData of areas) {
|
|
||||||
const existingArea = await areaRepository.findOne({
|
|
||||||
where: [
|
|
||||||
{ name: areaData.name },
|
|
||||||
{ code: areaData.code }
|
|
||||||
]
|
|
||||||
});
|
|
||||||
if (!existingArea) {
|
|
||||||
const newArea = areaRepository.create(areaData);
|
|
||||||
await areaRepository.save(newArea);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
import { Seeder } from 'typeorm-extension';
|
|
||||||
import { DataSource } from 'typeorm';
|
|
||||||
import { Category } from '../../entity/category.entity';
|
|
||||||
|
|
||||||
export default class CategorySeeder implements Seeder {
|
|
||||||
public async run(
|
|
||||||
dataSource: DataSource,
|
|
||||||
): Promise<any> {
|
|
||||||
const repository = dataSource.getRepository(Category);
|
|
||||||
|
|
||||||
const categories = [
|
|
||||||
{
|
|
||||||
name: 'nicotine-pouches',
|
|
||||||
title: 'Nicotine Pouches',
|
|
||||||
titleCN: '尼古丁袋',
|
|
||||||
sort: 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'vape',
|
|
||||||
title: 'vape',
|
|
||||||
titleCN: '电子烟',
|
|
||||||
sort: 2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'pouches-can',
|
|
||||||
title: 'Pouches Can',
|
|
||||||
titleCN: '口含烟盒',
|
|
||||||
sort: 3
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const cat of categories) {
|
|
||||||
const existing = await repository.findOne({ where: { name: cat.name } });
|
|
||||||
if (!existing) {
|
|
||||||
await repository.save(cat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
import { Seeder } from 'typeorm-extension';
|
|
||||||
import { DataSource } from 'typeorm';
|
|
||||||
import { Dict } from '../../entity/dict.entity';
|
|
||||||
import { Category } from '../../entity/category.entity';
|
|
||||||
import { CategoryAttribute } from '../../entity/category_attribute.entity';
|
|
||||||
|
|
||||||
export default class CategoryAttributeSeeder implements Seeder {
|
|
||||||
public async run(
|
|
||||||
dataSource: DataSource,
|
|
||||||
): Promise<any> {
|
|
||||||
const dictRepository = dataSource.getRepository(Dict);
|
|
||||||
const categoryRepository = dataSource.getRepository(Category);
|
|
||||||
const categoryAttributeRepository = dataSource.getRepository(CategoryAttribute);
|
|
||||||
|
|
||||||
// 1. 确保属性字典存在
|
|
||||||
const attributeNames = ['brand', 'strength', 'flavor', 'size', 'humidity'];
|
|
||||||
const attributeDicts: Dict[] = [];
|
|
||||||
|
|
||||||
for (const name of attributeNames) {
|
|
||||||
let dict = await dictRepository.findOne({ where: { name } });
|
|
||||||
if (!dict) {
|
|
||||||
dict = new Dict();
|
|
||||||
dict.name = name;
|
|
||||||
dict.title = name.charAt(0).toUpperCase() + name.slice(1);
|
|
||||||
dict.deletable = false;
|
|
||||||
dict = await dictRepository.save(dict);
|
|
||||||
console.log(`Created Dict: ${name}`);
|
|
||||||
}
|
|
||||||
attributeDicts.push(dict);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 获取 'nicotine-pouches' 分类 (由 CategorySeeder 创建)
|
|
||||||
const nicotinePouchesCategory = await categoryRepository.findOne({
|
|
||||||
where: {
|
|
||||||
name: 'nicotine-pouches'
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!nicotinePouchesCategory) {
|
|
||||||
console.warn('Category "nicotine-pouches" not found. Skipping attribute linking. Please ensure CategorySeeder runs first.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. 绑定属性到 'nicotine-pouches' 分类
|
|
||||||
for (const attrDict of attributeDicts) {
|
|
||||||
const existing = await categoryAttributeRepository.findOne({
|
|
||||||
where: {
|
|
||||||
category: { id: nicotinePouchesCategory.id },
|
|
||||||
attributeDict: { id: attrDict.id }
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!existing) {
|
|
||||||
const link = new CategoryAttribute();
|
|
||||||
link.category = nicotinePouchesCategory;
|
|
||||||
link.attributeDict = attrDict;
|
|
||||||
await categoryAttributeRepository.save(link);
|
|
||||||
console.log(`Linked ${attrDict.name} to ${nicotinePouchesCategory.name}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,878 +0,0 @@
|
||||||
import { Seeder } from 'typeorm-extension';
|
|
||||||
import { DataSource } from 'typeorm';
|
|
||||||
import { Dict } from '../../entity/dict.entity';
|
|
||||||
import { DictItem } from '../../entity/dict_item.entity';
|
|
||||||
|
|
||||||
export default class DictSeeder implements Seeder {
|
|
||||||
/**
|
|
||||||
* 格式化名称为 kebab-case
|
|
||||||
* @param name 需要格式化的名称
|
|
||||||
* @returns 格式化后的名称
|
|
||||||
*/
|
|
||||||
private formatName(name: string): string {
|
|
||||||
// 只替换空格和下划线
|
|
||||||
return String(name).replace(/[\_\s]+/g, '-').toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async run(
|
|
||||||
dataSource: DataSource,
|
|
||||||
): Promise<any> {
|
|
||||||
const dictRepository = dataSource.getRepository(Dict);
|
|
||||||
const dictItemRepository = dataSource.getRepository(DictItem);
|
|
||||||
|
|
||||||
// 初始化语言字典
|
|
||||||
const locales = [
|
|
||||||
{ name: 'zh-cn', title: '简体中文', titleCn: '简体中文', shortName: 'CN' },
|
|
||||||
{ name: 'en-us', title: 'English', titleCn: '英文', shortName: 'EN' },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const locale of locales) {
|
|
||||||
await this.createOrFindDict(dictRepository, locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加示例翻译条目
|
|
||||||
const zhDict = await dictRepository.findOne({ where: { name: 'zh-cn' } });
|
|
||||||
const enDict = await dictRepository.findOne({ where: { name: 'en-us' } });
|
|
||||||
|
|
||||||
const translations = [
|
|
||||||
{ name: 'common-save', zh: '保存', en: 'Save' },
|
|
||||||
{ name: 'common-cancel', zh: '取消', en: 'Cancel' },
|
|
||||||
{ name: 'common-success', zh: '操作成功', en: 'Success' },
|
|
||||||
{ name: 'common-failure', zh: '操作失败', en: 'Failure' },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const t of translations) {
|
|
||||||
// 添加中文翻译
|
|
||||||
let item = await dictItemRepository.findOne({ where: { name: t.name, dict: { id: zhDict.id } } });
|
|
||||||
if (!item) {
|
|
||||||
await dictItemRepository.save({ name: t.name, title: t.zh, titleCn: t.zh, shortName: t.zh.substring(0, 2).toUpperCase(), dict: zhDict });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加英文翻译
|
|
||||||
item = await dictItemRepository.findOne({ where: { name: t.name, dict: { id: enDict.id } } });
|
|
||||||
if (!item) {
|
|
||||||
await dictItemRepository.save({ name: t.name, title: t.en, titleCn: t.en, shortName: t.en.substring(0, 2).toUpperCase(), dict: enDict });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const brandDict = await this.createOrFindDict(dictRepository, { name: 'brand', title: '品牌', titleCn: '品牌', shortName: 'BR' });
|
|
||||||
const flavorDict = await this.createOrFindDict(dictRepository, { name: 'flavor', title: '口味', titleCn: '口味', shortName: 'FL' });
|
|
||||||
const strengthDict = await this.createOrFindDict(dictRepository, { name: 'strength', title: '强度', titleCn: '强度', shortName: 'ST' });
|
|
||||||
|
|
||||||
// 遍历品牌数据
|
|
||||||
await this.seedDictItems(dictItemRepository, brandDict, brandsData);
|
|
||||||
|
|
||||||
// 遍历口味数据
|
|
||||||
await this.seedDictItems(dictItemRepository, flavorDict, flavorsData);
|
|
||||||
|
|
||||||
// 遍历强度数据
|
|
||||||
await this.seedDictItems(dictItemRepository, strengthDict, strengthsData);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建或查找字典
|
|
||||||
* @param repo DictRepository
|
|
||||||
* @param dictInfo 字典信息
|
|
||||||
* @returns Dict 实例
|
|
||||||
*/
|
|
||||||
private async createOrFindDict(repo: any, dictInfo: { name: string; title: string; titleCn: string; shortName: string }): Promise<Dict> {
|
|
||||||
// 格式化 name
|
|
||||||
const formattedName = this.formatName(dictInfo.name);
|
|
||||||
let dict = await repo.findOne({ where: { name: formattedName } });
|
|
||||||
if (!dict) {
|
|
||||||
// 如果字典不存在,则使用格式化后的 name 创建新字典
|
|
||||||
dict = await repo.save({ name: formattedName, title: dictInfo.title, titleCn: dictInfo.titleCn, shortName: dictInfo.shortName });
|
|
||||||
}
|
|
||||||
return dict;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 填充字典项
|
|
||||||
* @param repo DictItemRepository
|
|
||||||
* @param dict 字典实例
|
|
||||||
* @param items 字典项数组
|
|
||||||
*/
|
|
||||||
private async seedDictItems(repo: any, dict: Dict, items: { name: string; title: string; titleCn: string; shortName: string }[]): Promise<void> {
|
|
||||||
for (const item of items) {
|
|
||||||
// 格式化 name
|
|
||||||
const formattedName = this.formatName(item.name);
|
|
||||||
const existingItem = await repo.findOne({ where: { name: formattedName, dict: { id: dict.id } } });
|
|
||||||
if (!existingItem) {
|
|
||||||
// 如果字典项不存在,则使用格式化后的 name 创建新字典项
|
|
||||||
await repo.save({ name: formattedName, title: item.title, titleCn: item.titleCn, shortName: item.shortName, dict });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 口味数据
|
|
||||||
const flavorsData = [
|
|
||||||
{ name: 'all-white', title: 'all white', titleCn: '全白', shortName: 'AL' },
|
|
||||||
{ name: 'amazing-apple-blackcurrant', title: 'amazing apple blackcurrant', titleCn: '惊艳苹果黑加仑', shortName: 'AM' },
|
|
||||||
{ name: 'apple-&-mint', title: 'apple & mint', titleCn: '苹果薄荷', shortName: 'AP' },
|
|
||||||
{ name: 'applemint', title: 'applemint', titleCn: '苹果薄荷混合', shortName: 'AP' },
|
|
||||||
{ name: 'apple-berry-ice', title: 'apple berry ice', titleCn: '苹果莓冰', shortName: 'AP' },
|
|
||||||
{ name: 'apple-bomb', title: 'apple bomb', titleCn: '苹果炸弹', shortName: 'AP' },
|
|
||||||
{ name: 'apple-kiwi-melon-ice', title: 'apple kiwi melon ice', titleCn: '苹果奇异瓜冰', shortName: 'AP' },
|
|
||||||
{ name: 'apple-mango-pear', title: 'apple mango pear', titleCn: '苹果芒果梨', shortName: 'AP' },
|
|
||||||
{ name: 'apple-melon-ice', title: 'apple melon ice', titleCn: '苹果瓜冰', shortName: 'AP' },
|
|
||||||
{ name: 'apple-mint', title: 'apple mint', titleCn: '苹果薄荷', shortName: 'AP' },
|
|
||||||
{ name: 'apple-peach', title: 'apple peach', titleCn: '苹果桃子', shortName: 'AP' },
|
|
||||||
{ name: 'apple-peach-pear', title: 'apple peach pear', titleCn: '苹果桃梨', shortName: 'AP' },
|
|
||||||
{ name: 'apple-peach-strawww', title: 'apple peach strawww', titleCn: '苹果桃草莓', shortName: 'AP' },
|
|
||||||
{ name: 'apple-pom-passion-ice', title: 'apple pom passion ice', titleCn: '苹果石榴激情冰', shortName: 'AP' },
|
|
||||||
{ name: 'arctic-banana-glaze', title: 'arctic banana glaze', titleCn: '北极香蕉釉', shortName: 'AR' },
|
|
||||||
{ name: 'arctic-grapefruit', title: 'arctic grapefruit', titleCn: '北极葡萄柚', shortName: 'AR' },
|
|
||||||
{ name: 'arctic-mint', title: 'arctic mint', titleCn: '北极薄荷', shortName: 'AR' },
|
|
||||||
{ name: 'baddie-blueberries', title: 'baddie blueberries', titleCn: '时髦蓝莓', shortName: 'BA' },
|
|
||||||
{ name: 'banana', title: 'banana', titleCn: '香蕉', shortName: 'BA' },
|
|
||||||
{ name: 'banana-(solid)', title: 'banana (solid)', titleCn: '香蕉(固体)', shortName: 'BA' },
|
|
||||||
{ name: 'banana-berry', title: 'banana berry', titleCn: '香蕉莓果', shortName: 'BA' },
|
|
||||||
{ name: 'banana-berry-melon-ice', title: 'banana berry melon ice', titleCn: '香蕉莓果瓜冰', shortName: 'BA' },
|
|
||||||
{ name: 'banana-blackberry', title: 'banana blackberry', titleCn: '香蕉黑莓', shortName: 'BA' },
|
|
||||||
{ name: 'banana-ice', title: 'banana ice', titleCn: '香蕉冰', shortName: 'BA' },
|
|
||||||
{ name: 'banana-milkshake', title: 'banana milkshake', titleCn: '香蕉奶昔', shortName: 'BA' },
|
|
||||||
{ name: 'banana-pnck-dude', title: 'banana pnck dude', titleCn: '香蕉粉红小子', shortName: 'BA' },
|
|
||||||
{ name: 'banana-pomegranate-cherry-ice', title: 'banana pomegranate cherry ice', titleCn: '香蕉石榴樱桃冰', shortName: 'BA' },
|
|
||||||
{ name: 'bangin-blood-orange-iced', title: 'bangin blood orange iced', titleCn: '爆炸血橙冰', shortName: 'BA' },
|
|
||||||
{ name: 'berries-in-the-6ix', title: 'berries in the 6ix', titleCn: '多伦多莓果', shortName: 'BE' },
|
|
||||||
{ name: 'berry-burst', title: 'berry burst', titleCn: '浆果爆发', shortName: 'BE' },
|
|
||||||
{ name: 'berry-burst-(thermal)', title: 'berry burst (thermal)', titleCn: '浆果爆发(热感)', shortName: 'BE' },
|
|
||||||
{ name: 'berry-ice', title: 'berry ice', titleCn: '浆果冰', shortName: 'BE' },
|
|
||||||
{ name: 'berry-lime-ice', title: 'berry lime ice', titleCn: '浆果青柠冰', shortName: 'BE' },
|
|
||||||
{ name: 'berry-trio-ice', title: 'berry trio ice', titleCn: '三重浆果冰', shortName: 'BE' },
|
|
||||||
{ name: 'black', title: 'black', titleCn: '黑色', shortName: 'BL' },
|
|
||||||
{ name: 'black-cherry', title: 'black cherry', titleCn: '黑樱桃', shortName: 'BL' },
|
|
||||||
{ name: 'blackcherry', title: 'blackcherry', titleCn: '黑樱桃混合', shortName: 'BL' },
|
|
||||||
{ name: 'blackcurrant-ice', title: 'blackcurrant ice', titleCn: '黑加仑冰', shortName: 'BL' },
|
|
||||||
{ name: 'black-currant-ice', title: 'black currant ice', titleCn: '黑加仑冰(空格版)', shortName: 'BL' },
|
|
||||||
{ name: 'black-licorice', title: 'black licorice', titleCn: '黑甘草', shortName: 'BL' },
|
|
||||||
{ name: 'black-tea', title: 'black tea', titleCn: '红茶', shortName: 'BL' },
|
|
||||||
{ name: 'blackberry-ice', title: 'blackberry ice', titleCn: '黑莓冰', shortName: 'BL' },
|
|
||||||
{ name: 'blackberry-raspberry-lemon', title: 'blackberry raspberry lemon', titleCn: '黑莓覆盆子柠檬', shortName: 'BL' },
|
|
||||||
{ name: 'blackcurrant-lychee-berries', title: 'blackcurrant lychee berries', titleCn: '黑加仑荔枝莓', shortName: 'BL' },
|
|
||||||
{ name: 'blackcurrant-pineapple-ice', title: 'blackcurrant pineapple ice', titleCn: '黑加仑菠萝冰', shortName: 'BL' },
|
|
||||||
{ name: 'blackcurrant-quench-ice', title: 'blackcurrant quench ice', titleCn: '黑加仑清爽冰', shortName: 'BL' },
|
|
||||||
{ name: 'blastin-banana-mango-iced', title: 'blastin banana mango iced', titleCn: '香蕉芒果爆炸冰', shortName: 'BL' },
|
|
||||||
{ name: 'blazin-banana-blackberry-iced', title: 'blazin banana blackberry iced', titleCn: '香蕉黑莓火焰冰', shortName: 'BL' },
|
|
||||||
{ name: 'blessed-blueberry-mint-iced', title: 'blessed blueberry mint iced', titleCn: '蓝莓薄荷冰', shortName: 'BL' },
|
|
||||||
{ name: 'bliss-iced', title: 'bliss iced', titleCn: '极乐冰', shortName: 'BL' },
|
|
||||||
{ name: 'blood-orange', title: 'blood orange', titleCn: '血橙', shortName: 'BL' },
|
|
||||||
{ name: 'blood-orange-ice', title: 'blood orange ice', titleCn: '血橙冰', shortName: 'BL' },
|
|
||||||
{ name: 'blue-dragon-fruit-peach', title: 'blue dragon fruit peach', titleCn: '蓝色龙果桃', shortName: 'BL' },
|
|
||||||
{ name: 'blue-lemon', title: 'blue lemon', titleCn: '蓝柠檬', shortName: 'BL' },
|
|
||||||
{ name: 'blue-raspberry', title: 'blue raspberry', titleCn: '蓝覆盆子', shortName: 'BL' },
|
|
||||||
{ name: 'blue-raspberry-apple', title: 'blue raspberry apple', titleCn: '蓝覆盆子苹果', shortName: 'BL' },
|
|
||||||
{ name: 'blue-raspberry-lemon', title: 'blue raspberry lemon', titleCn: '蓝覆盆子柠檬', shortName: 'BL' },
|
|
||||||
{ name: 'blue-raspberry-magic-cotton-ice', title: 'blue raspberry magic cotton ice', titleCn: '蓝覆盆子魔法棉花糖冰', shortName: 'BL' },
|
|
||||||
{ name: 'blue-razz', title: 'blue razz', titleCn: '蓝覆盆子', shortName: 'BL' },
|
|
||||||
{ name: 'blue-razz-hype', title: 'blue razz hype', titleCn: '蓝覆盆子热情', shortName: 'BL' },
|
|
||||||
{ name: 'blue-razz-ice', title: 'blue razz ice', titleCn: '蓝覆盆子冰', shortName: 'BL' },
|
|
||||||
{ name: 'blue-razz-ice-(solid)', title: 'blue razz ice (solid)', titleCn: '蓝覆盆子冰(固体)', shortName: 'BL' },
|
|
||||||
{ name: 'blue-razz-ice-glace', title: 'blue razz ice glace', titleCn: '蓝覆盆子冰格', shortName: 'BL' },
|
|
||||||
{ name: 'blue-razz-lemon-ice', title: 'blue razz lemon ice', titleCn: '蓝覆盆子柠檬冰', shortName: 'BL' },
|
|
||||||
{ name: 'blue-razz-lemonade', title: 'blue razz lemonade', titleCn: '蓝覆盆子柠檬水', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry', title: 'blueberry', titleCn: '蓝莓', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-banana', title: 'blueberry banana', titleCn: '蓝莓香蕉', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-cloudz', title: 'blueberry cloudz', titleCn: '蓝莓云', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-ice', title: 'blueberry ice', titleCn: '蓝莓冰', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-kiwi-ice', title: 'blueberry kiwi ice', titleCn: '蓝莓奇异果冰', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-lemon', title: 'blueberry lemon', titleCn: '蓝莓柠檬', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-lemon-ice', title: 'blueberry lemon ice', titleCn: '蓝莓柠檬冰', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-mint', title: 'blueberry mint', titleCn: '蓝莓薄荷', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-pear', title: 'blueberry pear', titleCn: '蓝莓梨', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-razz-cc', title: 'blueberry razz cc', titleCn: '蓝莓覆盆子混合', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-sour-raspberry', title: 'blueberry sour raspberry', titleCn: '蓝莓酸覆盆子', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-storm', title: 'blueberry storm', titleCn: '蓝莓风暴', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-swirl-ice', title: 'blueberry swirl ice', titleCn: '蓝莓漩涡冰', shortName: 'BL' },
|
|
||||||
{ name: 'blueberry-watermelon', title: 'blueberry watermelon', titleCn: '蓝莓西瓜', shortName: 'BL' },
|
|
||||||
{ name: 'bold-tobacco', title: 'bold tobacco', titleCn: '浓烈烟草', shortName: 'BO' },
|
|
||||||
{ name: 'bomb-blue-razz', title: 'bomb blue razz', titleCn: '蓝覆盆子炸弹', shortName: 'BO' },
|
|
||||||
{ name: 'boss-blueberry-iced', title: 'boss blueberry iced', titleCn: '老板蓝莓冰', shortName: 'BO' },
|
|
||||||
{ name: 'boss-blueberry-lced', title: 'boss blueberry lced', titleCn: '老板蓝莓冷饮', shortName: 'BO' },
|
|
||||||
{ name: 'bright-peppermint', title: 'bright peppermint', titleCn: '清爽薄荷', shortName: 'BR' },
|
|
||||||
{ name: 'bright-spearmint', title: 'bright spearmint', titleCn: '清爽留兰香', shortName: 'BR' },
|
|
||||||
{ name: 'brisky-classic-red', title: 'brisky classic red', titleCn: '经典红色烈酒', shortName: 'BR' },
|
|
||||||
{ name: 'bumpin-blackcurrant-iced', title: 'bumpin blackcurrant iced', titleCn: '黑加仑热烈冰', shortName: 'BU' },
|
|
||||||
{ name: 'burst-ice', title: 'burst ice', titleCn: '爆炸冰', shortName: 'BU' },
|
|
||||||
{ name: 'bussin-banana-iced', title: 'bussin banana iced', titleCn: '香蕉热烈冰', shortName: 'BU' },
|
|
||||||
{ name: 'bussin-banana-iced', title: 'bussin banana iced', titleCn: '香蕉热烈冰(重复)', shortName: 'BU' },
|
|
||||||
{ name: 'california-cherry', title: 'california cherry', titleCn: '加州樱桃', shortName: 'CA' },
|
|
||||||
{ name: 'cantaloupe-mango-banana', title: 'cantaloupe mango banana', titleCn: '香瓜芒果香蕉', shortName: 'CA' },
|
|
||||||
{ name: 'caramel', title: 'caramel', titleCn: '焦糖', shortName: 'CA' },
|
|
||||||
{ name: 'caribbean-spirit', title: 'caribbean spirit', titleCn: '加勒比风情', shortName: 'CA' },
|
|
||||||
{ name: 'caribbean-white', title: 'caribbean white', titleCn: '加勒比白', shortName: 'CA' },
|
|
||||||
{ name: 'cherry', title: 'cherry', titleCn: '樱桃', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-blast-ice', title: 'cherry blast ice', titleCn: '樱桃爆炸冰', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-classic-cola', title: 'cherry classic cola', titleCn: '樱桃经典可乐', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-classic-red', title: 'cherry classic red', titleCn: '樱桃经典红', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-cola-ice', title: 'cherry cola ice', titleCn: '樱桃可乐冰', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-ice', title: 'cherry ice', titleCn: '樱桃冰', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-lemon', title: 'cherry lemon', titleCn: '樱桃柠檬', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-lime-classic', title: 'cherry lime classic', titleCn: '樱桃青柠经典', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-lime-ice', title: 'cherry lime ice', titleCn: '樱桃青柠冰', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-lychee', title: 'cherry lychee', titleCn: '樱桃荔枝', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-peach-lemon', title: 'cherry peach lemon', titleCn: '樱桃桃子柠檬', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-red-classic', title: 'cherry red classic', titleCn: '红樱桃经典', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-strazz', title: 'cherry strazz', titleCn: '樱桃草莓', shortName: 'CH' },
|
|
||||||
{ name: 'cherry-watermelon', title: 'cherry watermelon', titleCn: '樱桃西瓜', shortName: 'CH' },
|
|
||||||
{ name: 'chill', title: 'chill', titleCn: '冰爽', shortName: 'CH' },
|
|
||||||
{ name: 'chilled-classic-red', title: 'chilled classic red', titleCn: '冰镇经典红', shortName: 'CH' },
|
|
||||||
{ name: 'chillin-coffee-iced', title: 'chillin coffee iced', titleCn: '冰镇咖啡', shortName: 'CH' },
|
|
||||||
{ name: 'chilly-jiggle-b', title: 'chilly jiggle b', titleCn: '清凉果冻 B', shortName: 'CH' },
|
|
||||||
{ name: 'churned-peanut', title: 'churned peanut', titleCn: '搅拌花生', shortName: 'CH' },
|
|
||||||
{ name: 'cinnamon', title: 'cinnamon', titleCn: '肉桂', shortName: 'CI' },
|
|
||||||
{ name: 'cinnamon-flame', title: 'cinnamon flame', titleCn: '肉桂火焰', shortName: 'CI' },
|
|
||||||
{ name: 'cinnamon-roll', title: 'cinnamon roll', titleCn: '肉桂卷', shortName: 'CI' },
|
|
||||||
{ name: 'circle-of-life', title: 'circle of life', titleCn: '生命循环', shortName: 'CI' },
|
|
||||||
{ name: 'citrus', title: 'citrus', titleCn: '柑橘', shortName: 'CI' },
|
|
||||||
{ name: 'citrus-burst-ice', title: 'citrus burst ice', titleCn: '柑橘爆发冰', shortName: 'CI' },
|
|
||||||
{ name: 'citrus-chill', title: 'citrus chill', titleCn: '柑橘清凉', shortName: 'CI' },
|
|
||||||
{ name: 'citrus-smash-ice', title: 'citrus smash ice', titleCn: '柑橘冲击冰', shortName: 'CI' },
|
|
||||||
{ name: 'citrus-sunrise', title: 'citrus sunrise', titleCn: '柑橘日出', shortName: 'CI' },
|
|
||||||
{ name: 'citrus-sunrise-(thermal)', title: 'citrus sunrise (thermal)', titleCn: '柑橘日出(热感)', shortName: 'CI' },
|
|
||||||
{ name: 'classic', title: 'classic', titleCn: '经典', shortName: 'CL' },
|
|
||||||
{ name: 'classic-ice', title: 'classic ice', titleCn: '经典冰', shortName: 'CL' },
|
|
||||||
{ name: 'classic-mint-ice', title: 'classic mint ice', titleCn: '经典薄荷冰', shortName: 'CL' },
|
|
||||||
{ name: 'classic-tobacco', title: 'classic tobacco', titleCn: '经典烟草', shortName: 'CL' },
|
|
||||||
{ name: 'classical-tobacco', title: 'classical tobacco', titleCn: '古典烟草', shortName: 'CL' },
|
|
||||||
{ name: 'coconut-ice', title: 'coconut ice', titleCn: '椰子冰', shortName: 'CO' },
|
|
||||||
{ name: 'coconut-water-ice', title: 'coconut water ice', titleCn: '椰子水冰', shortName: 'CO' },
|
|
||||||
{ name: 'coffee', title: 'coffee', titleCn: '咖啡', shortName: 'CO' },
|
|
||||||
{ name: 'coffee-stout', title: 'coffee stout', titleCn: '咖啡烈酒', shortName: 'CO' },
|
|
||||||
{ name: 'cola', title: 'cola', titleCn: '可乐', shortName: 'CO' },
|
|
||||||
{ name: 'cola-&-cherry', title: 'cola & cherry', titleCn: '可乐樱桃', shortName: 'CO' },
|
|
||||||
{ name: 'cola-&-vanilla', title: 'cola & vanilla', titleCn: '可乐香草', shortName: 'CO' },
|
|
||||||
{ name: 'cola-ice', title: 'cola ice', titleCn: '可乐冰', shortName: 'CO' },
|
|
||||||
{ name: 'cool-frost', title: 'cool frost', titleCn: '酷霜', shortName: 'CO' },
|
|
||||||
{ name: 'cool-mint', title: 'cool mint', titleCn: '酷薄荷', shortName: 'CO' },
|
|
||||||
{ name: 'cool-mint-ice', title: 'cool mint ice', titleCn: '酷薄荷冰', shortName: 'CO' },
|
|
||||||
{ name: 'cool-storm', title: 'cool storm', titleCn: '酷风暴', shortName: 'CO' },
|
|
||||||
{ name: 'cool-tropical', title: 'cool tropical', titleCn: '酷热带', shortName: 'CO' },
|
|
||||||
{ name: 'cool-watermelon', title: 'cool watermelon', titleCn: '酷西瓜', shortName: 'CO' },
|
|
||||||
{ name: 'cotton-clouds', title: 'cotton clouds', titleCn: '棉花云', shortName: 'CO' },
|
|
||||||
{ name: 'cranberry-blackcurrant', title: 'cranberry blackcurrant', titleCn: '蔓越莓黑加仑', shortName: 'CR' },
|
|
||||||
{ name: 'cranberry-lemon', title: 'cranberry lemon', titleCn: '蔓越莓柠檬', shortName: 'CR' },
|
|
||||||
{ name: 'cranberry-lemon-ice', title: 'cranberry lemon ice', titleCn: '蔓越莓柠檬冰', shortName: 'CR' },
|
|
||||||
{ name: 'creamy-maple', title: 'creamy maple', titleCn: '奶香枫糖', shortName: 'CR' },
|
|
||||||
{ name: 'creamy-vanilla', title: 'creamy vanilla', titleCn: '奶香香草', shortName: 'CR' },
|
|
||||||
{ name: 'crispy-peppermint', title: 'crispy peppermint', titleCn: '脆薄荷', shortName: 'CR' },
|
|
||||||
{ name: 'cuban-tobacco', title: 'cuban tobacco', titleCn: '古巴烟草', shortName: 'CU' },
|
|
||||||
{ name: 'cucumber-lime', title: 'cucumber lime', titleCn: '黄瓜青柠', shortName: 'CU' },
|
|
||||||
{ name: 'dark-blackcurrant', title: 'dark blackcurrant', titleCn: '深黑加仑', shortName: 'DA' },
|
|
||||||
{ name: 'dark-forest', title: 'dark forest', titleCn: '深林', shortName: 'DA' },
|
|
||||||
{ name: 'deep-freeze', title: 'deep freeze', titleCn: '极冻', shortName: 'DE' },
|
|
||||||
{ name: 'dope-double-kiwi-iced', title: 'dope double kiwi iced', titleCn: '双奇异果冰', shortName: 'DO' },
|
|
||||||
{ name: 'dope-double-kiwi-lced', title: 'dope double kiwi lced', titleCn: '双奇异果冷饮', shortName: 'DO' },
|
|
||||||
{ name: 'double-apple', title: 'double apple', titleCn: '双苹果', shortName: 'DO' },
|
|
||||||
{ name: 'double-apple-ice', title: 'double apple ice', titleCn: '双苹果冰', shortName: 'DO' },
|
|
||||||
{ name: 'double-berry-twist-ice', title: 'double berry twist ice', titleCn: '双浆果扭曲冰', shortName: 'DO' },
|
|
||||||
{ name: 'double-ice', title: 'double ice', titleCn: '双冰', shortName: 'DO' },
|
|
||||||
{ name: 'double-mango', title: 'double mango', titleCn: '双芒果', shortName: 'DO' },
|
|
||||||
{ name: 'double-mint', title: 'double mint', titleCn: '双薄荷', shortName: 'DO' },
|
|
||||||
{ name: 'double-mocha', title: 'double mocha', titleCn: '双摩卡', shortName: 'DO' },
|
|
||||||
{ name: 'double-shot-espresso', title: 'double shot espresso', titleCn: '双份浓缩咖啡', shortName: 'DO' },
|
|
||||||
{ name: 'dragon-berry-mango-ice', title: 'dragon berry mango ice', titleCn: '龙莓芒果冰', shortName: 'DR' },
|
|
||||||
{ name: 'dragon-fruit', title: 'dragon fruit', titleCn: '龙果', shortName: 'DR' },
|
|
||||||
{ name: 'dragon-fruit-lychee-ice', title: 'dragon fruit lychee ice', titleCn: '龙果荔枝冰', shortName: 'DR' },
|
|
||||||
{ name: 'dragon-fruit-strawberry-ice', title: 'dragon fruit strawberry ice', titleCn: '龙果草莓冰', shortName: 'DR' },
|
|
||||||
{ name: 'dragon-fruit-strawnana', title: 'dragon fruit strawnana', titleCn: '龙果香蕉', shortName: 'DR' },
|
|
||||||
{ name: 'dragon-melon-ice', title: 'dragon melon ice', titleCn: '龙瓜冰', shortName: 'DR' },
|
|
||||||
{ name: 'dragonfruit-lychee', title: 'dragonfruit lychee', titleCn: '龙果荔枝', shortName: 'DR' },
|
|
||||||
{ name: 'dreamy-dragonfruit-lychee-iced', title: 'dreamy dragonfruit lychee iced', titleCn: '梦幻龙果荔枝冰', shortName: 'DR' },
|
|
||||||
{ name: 'dub-dub', title: 'dub dub', titleCn: '双重', shortName: 'DU' },
|
|
||||||
{ name: 'durian', title: 'durian', titleCn: '榴莲', shortName: 'DU' },
|
|
||||||
{ name: 'electric-fruit-blast', title: 'electric fruit blast', titleCn: '电果爆炸', shortName: 'EL' },
|
|
||||||
{ name: 'electric-orange', title: 'electric orange', titleCn: '电橙', shortName: 'EL' },
|
|
||||||
{ name: 'energy-drink', title: 'energy drink', titleCn: '能量饮料', shortName: 'EN' },
|
|
||||||
{ name: 'epic-apple', title: 'epic apple', titleCn: '极致苹果', shortName: 'EP' },
|
|
||||||
{ name: 'epic-apple-peach', title: 'epic apple peach', titleCn: '极致苹果桃', shortName: 'EP' },
|
|
||||||
{ name: 'epic-banana', title: 'epic banana', titleCn: '极致香蕉', shortName: 'EP' },
|
|
||||||
{ name: 'epic-berry-swirl', title: 'epic berry swirl', titleCn: '极致浆果旋风', shortName: 'EP' },
|
|
||||||
{ name: 'epic-blue-razz', title: 'epic blue razz', titleCn: '极致蓝覆盆子', shortName: 'EP' },
|
|
||||||
{ name: 'epic-fruit-bomb', title: 'epic fruit bomb', titleCn: '极致水果炸弹', shortName: 'EP' },
|
|
||||||
{ name: 'epic-grape', title: 'epic grape', titleCn: '极致葡萄', shortName: 'EP' },
|
|
||||||
{ name: 'epic-honeydew-blackcurrant', title: 'epic honeydew blackcurrant', titleCn: '极致蜜瓜黑加仑', shortName: 'EP' },
|
|
||||||
{ name: 'epic-kiwi-mango', title: 'epic kiwi mango', titleCn: '极致奇异果芒果', shortName: 'EP' },
|
|
||||||
{ name: 'epic-peach-mango', title: 'epic peach mango', titleCn: '极致桃芒果', shortName: 'EP' },
|
|
||||||
{ name: 'epic-peppermint', title: 'epic peppermint', titleCn: '极致薄荷', shortName: 'EP' },
|
|
||||||
{ name: 'epic-sour-berries', title: 'epic sour berries', titleCn: '极致酸浆果', shortName: 'EP' },
|
|
||||||
{ name: 'epic-strawberry', title: 'epic strawberry', titleCn: '极致草莓', shortName: 'EP' },
|
|
||||||
{ name: 'epic-strawberry-watermelon', title: 'epic strawberry watermelon', titleCn: '极致草莓西瓜', shortName: 'EP' },
|
|
||||||
{ name: 'epic-watermelon-kiwi', title: 'epic watermelon kiwi', titleCn: '极致西瓜奇异果', shortName: 'EP' },
|
|
||||||
{ name: 'exotic-mango', title: 'exotic mango', titleCn: '异国芒果', shortName: 'EX' },
|
|
||||||
{ name: 'extreme-chill-mint', title: 'extreme chill mint', titleCn: '极寒薄荷', shortName: 'EX' },
|
|
||||||
{ name: 'extreme-cinnamon', title: 'extreme cinnamon', titleCn: '极寒肉桂', shortName: 'EX' },
|
|
||||||
{ name: 'extreme-mint', title: 'extreme mint', titleCn: '极寒薄荷', shortName: 'EX' },
|
|
||||||
{ name: 'extreme-mint-iced', title: 'extreme mint iced', titleCn: '极寒薄荷冰', shortName: 'EX' },
|
|
||||||
{ name: 'famous-fruit-ko-iced', title: 'famous fruit ko iced', titleCn: '知名水果 KO 冰', shortName: 'FA' },
|
|
||||||
{ name: 'famous-fruit-ko-lced', title: 'famous fruit ko lced', titleCn: '知名水果 KO 冷饮', shortName: 'FA' },
|
|
||||||
{ name: 'fizzy', title: 'fizzy', titleCn: '汽水', shortName: 'FI' },
|
|
||||||
{ name: 'flavourless', title: 'flavourless', titleCn: '无味', shortName: 'FL' },
|
|
||||||
{ name: 'flippin-fruit-flash', title: 'flippin fruit flash', titleCn: '翻转水果闪电', shortName: 'FL' },
|
|
||||||
{ name: 'flippin-fruit-flash-(rainbow-burst)', title: 'flippin fruit flash (rainbow burst)', titleCn: '翻转水果闪电(彩虹爆发)', shortName: 'FL' },
|
|
||||||
{ name: 'forest-fruits', title: 'forest fruits', titleCn: '森林水果', shortName: 'FO' },
|
|
||||||
{ name: 'fragrant-grapefruit', title: 'fragrant grapefruit', titleCn: '香气葡萄柚', shortName: 'FR' },
|
|
||||||
{ name: 'freeze', title: 'freeze', titleCn: '冰冻', shortName: 'FR' },
|
|
||||||
{ name: 'freeze-mint', title: 'freeze mint', titleCn: '冰薄荷', shortName: 'FR' },
|
|
||||||
{ name: 'freeze-mint-salty', title: 'freeze mint salty', titleCn: '冰薄荷咸味', shortName: 'FR' },
|
|
||||||
{ name: 'freezing-peppermint', title: 'freezing peppermint', titleCn: '冰爽薄荷', shortName: 'FR' },
|
|
||||||
{ name: 'freezy-berry-peachy', title: 'freezy berry peachy', titleCn: '冰冻浆果桃', shortName: 'FR' },
|
|
||||||
{ name: 'fresh-fruit', title: 'fresh fruit', titleCn: '新鲜水果', shortName: 'FR' },
|
|
||||||
{ name: 'fresh-mint', title: 'fresh mint', titleCn: '新鲜薄荷', shortName: 'FR' },
|
|
||||||
{ name: 'fresh-mint-ice', title: 'fresh mint ice', titleCn: '新鲜薄荷冰', shortName: 'FR' },
|
|
||||||
{ name: 'froot-b', title: 'froot b', titleCn: '水果 B', shortName: 'FR' },
|
|
||||||
{ name: 'frost', title: 'frost', titleCn: '霜冻', shortName: 'FR' },
|
|
||||||
{ name: 'frost-mint', title: 'frost mint', titleCn: '霜薄荷', shortName: 'FR' },
|
|
||||||
{ name: 'frosted-strawberries', title: 'frosted strawberries', titleCn: '霜冻草莓', shortName: 'FR' },
|
|
||||||
{ name: 'frosty-grapefruit', title: 'frosty grapefruit', titleCn: '冰爽葡萄柚', shortName: 'FR' },
|
|
||||||
{ name: 'frozen-classical-ice', title: 'frozen classical ice', titleCn: '冷冻经典冰', shortName: 'FR' },
|
|
||||||
{ name: 'frozen-cloudberry', title: 'frozen cloudberry', titleCn: '冷冻云莓', shortName: 'FR' },
|
|
||||||
{ name: 'frozen-mint', title: 'frozen mint', titleCn: '冷冻薄荷', shortName: 'FR' },
|
|
||||||
{ name: 'frozen-pineapple', title: 'frozen pineapple', titleCn: '冷冻菠萝', shortName: 'FR' },
|
|
||||||
{ name: 'frozen-strawberry', title: 'frozen strawberry', titleCn: '冷冻草莓', shortName: 'FR' },
|
|
||||||
{ name: 'frozen-strawberrygb(gummy-bear)', title: 'frozen strawberrygb(gummy bear)', titleCn: '冷冻草莓软糖', shortName: 'FR' },
|
|
||||||
{ name: 'grapefruit-grape-gb(gummy-bear)', title: 'grapefruit grape gb(gummy bear)', titleCn: '葡萄柚葡萄软糖', shortName: 'GR' },
|
|
||||||
{ name: 'fruit-flash-ice', title: 'fruit flash ice', titleCn: '水果闪电冰', shortName: 'FR' },
|
|
||||||
{ name: 'fruity-explosion', title: 'fruity explosion', titleCn: '水果爆炸', shortName: 'FR' },
|
|
||||||
{ name: 'fuji-apple-ice', title: 'fuji apple ice', titleCn: '富士苹果冰', shortName: 'FU' },
|
|
||||||
{ name: 'fuji-ice', title: 'fuji ice', titleCn: '富士冰', shortName: 'FU' },
|
|
||||||
{ name: 'fuji-melon-ice', title: 'fuji melon ice', titleCn: '富士瓜冰', shortName: 'FU' },
|
|
||||||
{ name: 'full-charge', title: 'full charge', titleCn: '满电', shortName: 'FU' },
|
|
||||||
{ name: 'gb', title: 'gb', titleCn: '软糖', shortName: 'GB' },
|
|
||||||
{ name: 'gb(gummy-bear)', title: 'gb(gummy bear)', titleCn: '软糖(Gummy Bear)', shortName: 'GB' },
|
|
||||||
{ name: 'gentle-mint', title: 'gentle mint', titleCn: '温和薄荷', shortName: 'GE' },
|
|
||||||
{ name: 'ghost-cola-&-vanilla', title: 'ghost cola & vanilla', titleCn: '幽灵可乐香草', shortName: 'GH' },
|
|
||||||
{ name: 'ghost-cola-ice', title: 'ghost cola ice', titleCn: '幽灵可乐冰', shortName: 'GH' },
|
|
||||||
{ name: 'ghost-mango', title: 'ghost mango', titleCn: '幽灵芒果', shortName: 'GH' },
|
|
||||||
{ name: 'ghost-original', title: 'ghost original', titleCn: '幽灵原味', shortName: 'GH' },
|
|
||||||
{ name: 'ghost-watermelon-ice', title: 'ghost watermelon ice', titleCn: '幽灵西瓜冰', shortName: 'GH' },
|
|
||||||
{ name: 'gnarly-green-d-(green-dew)', title: 'gnarly green d (green dew)', titleCn: '狂野绿 D(绿色露水)', shortName: 'GN' },
|
|
||||||
{ name: 'gold-edition', title: 'gold edition', titleCn: '金版', shortName: 'GO' },
|
|
||||||
{ name: 'grape', title: 'grape', titleCn: '葡萄', shortName: 'GR' },
|
|
||||||
{ name: 'grape-cherry', title: 'grape cherry', titleCn: '葡萄樱桃', shortName: 'GR' },
|
|
||||||
{ name: 'grape-fury-ice', title: 'grape fury ice', titleCn: '葡萄狂怒冰', shortName: 'GR' },
|
|
||||||
{ name: 'grape-honeydew-ice', title: 'grape honeydew ice', titleCn: '葡萄蜜瓜冰', shortName: 'GR' },
|
|
||||||
{ name: 'grape-ice', title: 'grape ice', titleCn: '葡萄冰', shortName: 'GR' },
|
|
||||||
{ name: 'grape-pomegranate-ice', title: 'grape pomegranate ice', titleCn: '葡萄石榴冰', shortName: 'GR' },
|
|
||||||
{ name: 'grapefruit-grape', title: 'grapefruit grape', titleCn: '葡萄柚葡萄', shortName: 'GR' },
|
|
||||||
{ name: 'grapefruit-ice', title: 'grapefruit ice', titleCn: '葡萄柚冰', shortName: 'GR' },
|
|
||||||
{ name: 'grapes', title: 'grapes', titleCn: '葡萄', shortName: 'GR' },
|
|
||||||
{ name: 'grapplin-grape-sour-apple-iced', title: 'grapplin grape sour apple iced', titleCn: '葡萄酸苹果冰', shortName: 'GR' },
|
|
||||||
{ name: 'green-apple', title: 'green apple', titleCn: '青苹果', shortName: 'GR' },
|
|
||||||
{ name: 'green-apple-ice', title: 'green apple ice', titleCn: '青苹果冰', shortName: 'GR' },
|
|
||||||
{ name: 'green-grape-ice', title: 'green grape ice', titleCn: '青葡萄冰', shortName: 'GR' },
|
|
||||||
{ name: 'green-mango-ice', title: 'green mango ice', titleCn: '青芒果冰', shortName: 'GR' },
|
|
||||||
{ name: 'green-mint', title: 'green mint', titleCn: '青薄荷', shortName: 'GR' },
|
|
||||||
{ name: 'green-spearmint', title: 'green spearmint', titleCn: '青留兰香', shortName: 'GR' },
|
|
||||||
{ name: 'green-tea', title: 'green tea', titleCn: '绿茶', shortName: 'GR' },
|
|
||||||
{ name: 'groovy-grape', title: 'groovy grape', titleCn: '活力葡萄', shortName: 'GR' },
|
|
||||||
{ name: 'groovy-grape-passionfruit-iced', title: 'groovy grape passionfruit iced', titleCn: '活力葡萄激情果冰', shortName: 'GR' },
|
|
||||||
{ name: 'guava-ice', title: 'guava ice', titleCn: '番石榴冰', shortName: 'GU' },
|
|
||||||
{ name: 'guava-ice-t', title: 'guava ice t', titleCn: '番石榴冰 T', shortName: 'GU' },
|
|
||||||
{ name: 'guava-mango-peach', title: 'guava mango peach', titleCn: '番石榴芒果桃', shortName: 'GU' },
|
|
||||||
{ name: 'gusto-green-apple', title: 'gusto green apple', titleCn: '绿苹果狂热', shortName: 'GU' },
|
|
||||||
{ name: 'hakuna', title: 'hakuna', titleCn: '哈库纳', shortName: 'HA' },
|
|
||||||
{ name: 'harambae', title: 'harambae', titleCn: '哈兰贝', shortName: 'HA' },
|
|
||||||
{ name: 'harmony', title: 'harmony', titleCn: '和谐', shortName: 'HA' },
|
|
||||||
{ name: 'haven', title: 'haven', titleCn: '避风港', shortName: 'HA' },
|
|
||||||
{ name: 'haven-iced', title: 'haven iced', titleCn: '避风港冰', shortName: 'HA' },
|
|
||||||
{ name: 'hawaiian-blue', title: 'hawaiian blue', titleCn: '夏威夷蓝', shortName: 'HA' },
|
|
||||||
{ name: 'hawaiian-mist-ice', title: 'hawaiian mist ice', titleCn: '夏威夷薄雾冰', shortName: 'HA' },
|
|
||||||
{ name: 'hawaiian-storm', title: 'hawaiian storm', titleCn: '夏威夷风暴', shortName: 'HA' },
|
|
||||||
{ name: 'hip-honeydew-mango-iced', title: 'hip honeydew mango iced', titleCn: '蜜瓜芒果冰', shortName: 'HI' },
|
|
||||||
{ name: 'hokkaido-milk', title: 'hokkaido milk', titleCn: '北海道牛奶', shortName: 'HO' },
|
|
||||||
{ name: 'honeydew-blackcurrant', title: 'honeydew blackcurrant', titleCn: '蜜瓜黑加仑', shortName: 'HO' },
|
|
||||||
{ name: 'honeydew-mango-ice', title: 'honeydew mango ice', titleCn: '蜜瓜芒果冰', shortName: 'HO' },
|
|
||||||
{ name: 'hype', title: 'hype', titleCn: '狂热', shortName: 'HY' },
|
|
||||||
{ name: 'ice-blast', title: 'ice blast', titleCn: '冰爆', shortName: 'IC' },
|
|
||||||
{ name: 'ice-cool', title: 'ice cool', titleCn: '冰凉', shortName: 'IC' },
|
|
||||||
{ name: 'ice-cream', title: 'ice cream', titleCn: '冰淇淋', shortName: 'IC' },
|
|
||||||
{ name: 'ice-mint', title: 'ice mint', titleCn: '冰薄荷', shortName: 'IC' },
|
|
||||||
{ name: 'ice-wintergreen', title: 'ice wintergreen', titleCn: '冰冬青', shortName: 'IC' },
|
|
||||||
{ name: 'iced-americano', title: 'iced americano', titleCn: '冰美式', shortName: 'IC' },
|
|
||||||
{ name: 'icy-berries', title: 'icy berries', titleCn: '冰爽浆果', shortName: 'IC' },
|
|
||||||
{ name: 'icy-blackcurrant', title: 'icy blackcurrant', titleCn: '冰爽黑加仑', shortName: 'IC' },
|
|
||||||
{ name: 'icy-cherry', title: 'icy cherry', titleCn: '冰爽樱桃', shortName: 'IC' },
|
|
||||||
{ name: 'icy-mint', title: 'icy mint', titleCn: '冰爽薄荷', shortName: 'IC' },
|
|
||||||
{ name: 'icy-pink-clouds', title: 'icy pink clouds', titleCn: '冰粉云', shortName: 'IC' },
|
|
||||||
{ name: 'intense-blue-razz', title: 'intense blue razz', titleCn: '强烈蓝覆盆子', shortName: 'IN' },
|
|
||||||
{ name: 'intense-blueberry-lemon', title: 'intense blueberry lemon', titleCn: '强烈蓝莓柠檬', shortName: 'IN' },
|
|
||||||
{ name: 'intense-flavourless', title: 'intense flavourless', titleCn: '强烈无味', shortName: 'IN' },
|
|
||||||
{ name: 'intense-fruity-explosion', title: 'intense fruity explosion', titleCn: '强烈水果爆炸', shortName: 'IN' },
|
|
||||||
{ name: 'intense-juicy-peach', title: 'intense juicy peach', titleCn: '强烈多汁桃', shortName: 'IN' },
|
|
||||||
{ name: 'intense-red-apple', title: 'intense red apple', titleCn: '强烈红苹果', shortName: 'IN' },
|
|
||||||
{ name: 'intense-ripe-mango', title: 'intense ripe mango', titleCn: '强烈熟芒果', shortName: 'IN' },
|
|
||||||
{ name: 'intense-strawberry-watermelon', title: 'intense strawberry watermelon', titleCn: '强烈草莓西瓜', shortName: 'IN' },
|
|
||||||
{ name: 'intense-white-grape', title: 'intense white grape', titleCn: '强烈白葡萄', shortName: 'IN' },
|
|
||||||
{ name: 'intense-white-mint', title: 'intense white mint', titleCn: '强烈白薄荷', shortName: 'IN' },
|
|
||||||
{ name: 'jasmine-tea', title: 'jasmine tea', titleCn: '茉莉茶', shortName: 'JA' },
|
|
||||||
{ name: 'jiggly-b', title: 'jiggly b', titleCn: '果冻 B', shortName: 'JI' },
|
|
||||||
{ name: 'jiggly-sting', title: 'jiggly sting', titleCn: '果冻刺', shortName: 'JI' },
|
|
||||||
{ name: 'juicy-mango', title: 'juicy mango', titleCn: '多汁芒果', shortName: 'JU' },
|
|
||||||
{ name: 'juicy-peach', title: 'juicy peach', titleCn: '多汁桃', shortName: 'JU' },
|
|
||||||
{ name: 'juicy-peach-ice', title: 'juicy peach ice', titleCn: '多汁桃冰', shortName: 'JU' },
|
|
||||||
{ name: 'jungle-secrets', title: 'jungle secrets', titleCn: '丛林秘密', shortName: 'JU' },
|
|
||||||
{ name: 'kanzi', title: 'kanzi', titleCn: '甘之', shortName: 'KA' },
|
|
||||||
{ name: 'kewl-kiwi-passionfruit-iced', title: 'kewl kiwi passionfruit iced', titleCn: '酷奇奇', shortName: 'KE' },
|
|
||||||
{ name: 'kiwi-berry-ice', title: 'kiwi berry ice', titleCn: '奇异果浆果冰', shortName: 'KI' },
|
|
||||||
{ name: 'kiwi-dragon-berry', title: 'kiwi dragon berry', titleCn: '奇异果龙莓', shortName: 'KI' },
|
|
||||||
{ name: 'kiwi-green-t', title: 'kiwi green t', titleCn: '奇异果绿茶', shortName: 'KI' },
|
|
||||||
{ name: 'kiwi-guava-ice', title: 'kiwi guava ice', titleCn: '奇异果番石榴冰', shortName: 'KI' },
|
|
||||||
{ name: 'kiwi-guava-passionfruit-ice', title: 'kiwi guava passionfruit ice', titleCn: '奇异果番石榴激情果冰', shortName: 'KI' },
|
|
||||||
{ name: 'kiwi-passion-fruit-guava', title: 'kiwi passion fruit guava', titleCn: '奇异果激情果番石榴', shortName: 'KI' },
|
|
||||||
{ name: 'kyoho-grape', title: 'kyoho grape', titleCn: '巨峰葡萄', shortName: 'KY' },
|
|
||||||
{ name: 'kyoho-grape-ice', title: 'kyoho grape ice', titleCn: '巨峰葡萄冰', shortName: 'KY' },
|
|
||||||
{ name: 'lemon', title: 'lemon', titleCn: '柠檬', shortName: 'LE' },
|
|
||||||
{ name: 'lemon-berry', title: 'lemon berry', titleCn: '柠檬浆果', shortName: 'LE' },
|
|
||||||
{ name: 'lemon-blue-razz-ice', title: 'lemon blue razz ice', titleCn: '柠檬蓝覆盆子冰', shortName: 'LE' },
|
|
||||||
{ name: 'lemon-lime-cranberry', title: 'lemon lime cranberry', titleCn: '柠檬青柠蔓越莓', shortName: 'LE' },
|
|
||||||
{ name: 'lemon-lime-ice', title: 'lemon lime ice', titleCn: '柠檬青柠冰', shortName: 'LE' },
|
|
||||||
{ name: 'lemon-sprite', title: 'lemon sprite', titleCn: '柠檬汽水', shortName: 'LE' },
|
|
||||||
{ name: 'lemon-spritz', title: 'lemon spritz', titleCn: '柠檬气泡', shortName: 'LE' },
|
|
||||||
{ name: 'lemon-squeeze-ice', title: 'lemon squeeze ice', titleCn: '柠檬榨汁冰', shortName: 'LE' },
|
|
||||||
{ name: 'lemon-squeeze-iced', title: 'lemon squeeze iced', titleCn: '柠檬榨汁冷饮', shortName: 'LE' },
|
|
||||||
{ name: 'lemon-t', title: 'lemon t', titleCn: '柠檬 T', shortName: 'LE' },
|
|
||||||
{ name: 'lemon-tea-ice', title: 'lemon tea ice', titleCn: '柠檬茶冰', shortName: 'LE' },
|
|
||||||
{ name: 'lemon-twist-ice', title: 'lemon twist ice', titleCn: '柠檬扭转冰', shortName: 'LE' },
|
|
||||||
{ name: 'lemur', title: 'lemur', titleCn: '狐猴', shortName: 'LE' },
|
|
||||||
{ name: 'lime-berry-orange-ice', title: 'lime berry orange ice', titleCn: '青柠浆果橙冰', shortName: 'LI' },
|
|
||||||
{ name: 'lime-flame', title: 'lime flame', titleCn: '青柠火焰', shortName: 'LI' },
|
|
||||||
{ name: 'liquorice', title: 'liquorice', titleCn: '甘草', shortName: 'LI' },
|
|
||||||
{ name: 'lit-lychee-watermelon-iced', title: 'lit lychee watermelon iced', titleCn: '荔枝西瓜冰', shortName: 'LI' },
|
|
||||||
{ name: 'loco-cocoa-latte-iced', title: 'loco cocoa latte iced', titleCn: '可可拿铁冷饮', shortName: 'LO' },
|
|
||||||
{ name: 'lofty-liquorice', title: 'lofty liquorice', titleCn: '高挑甘草', shortName: 'LO' },
|
|
||||||
{ name: 'lush-ice', title: 'lush ice', titleCn: '冰爽浓郁', shortName: 'LU' },
|
|
||||||
{ name: 'lychee-ice', title: 'lychee ice', titleCn: '荔枝冰', shortName: 'LY' },
|
|
||||||
{ name: 'lychee-mango-ice', title: 'lychee mango ice', titleCn: '荔枝芒果冰', shortName: 'LY' },
|
|
||||||
{ name: 'lychee-mango-melon', title: 'lychee mango melon', titleCn: '荔枝芒果瓜', shortName: 'LY' },
|
|
||||||
{ name: 'lychee-melon-ice', title: 'lychee melon ice', titleCn: '荔枝瓜冰', shortName: 'LY' },
|
|
||||||
{ name: 'lychee-watermelon-strawberry', title: 'lychee watermelon strawberry', titleCn: '荔枝西瓜草莓', shortName: 'LY' },
|
|
||||||
{ name: 'mad-mango-peach', title: 'mad mango peach', titleCn: '疯狂芒果桃', shortName: 'MA' },
|
|
||||||
{ name: 'mangabeys', title: 'mangabeys', titleCn: '长臂猿', shortName: 'MA' },
|
|
||||||
{ name: 'mango', title: 'mango', titleCn: '芒果', shortName: 'MA' },
|
|
||||||
{ name: 'mango-berry', title: 'mango berry', titleCn: '芒果浆果', shortName: 'MA' },
|
|
||||||
{ name: 'mango-blueberry', title: 'mango blueberry', titleCn: '芒果蓝莓', shortName: 'MA' },
|
|
||||||
{ name: 'mango-dragon-fruit-lemon-ice', title: 'mango dragon fruit lemon ice', titleCn: '芒果龙果柠檬冰', shortName: 'MA' },
|
|
||||||
{ name: 'mango-flame', title: 'mango flame', titleCn: '芒果火焰', shortName: 'MA' },
|
|
||||||
{ name: 'mango-honeydew-ice', title: 'mango honeydew ice', titleCn: '芒果蜜瓜冰', shortName: 'MA' },
|
|
||||||
{ name: 'mango-ice', title: 'mango ice', titleCn: '芒果冰', shortName: 'MA' },
|
|
||||||
{ name: 'mango-madness', title: 'mango madness', titleCn: '芒果狂热', shortName: 'MA' },
|
|
||||||
{ name: 'mango-nectar-ice', title: 'mango nectar ice', titleCn: '芒果花蜜冰', shortName: 'MA' },
|
|
||||||
{ name: 'mango-on-ice', title: 'mango on ice', titleCn: '芒果冰镇', shortName: 'MA' },
|
|
||||||
{ name: 'mango-melon', title: 'mango melon', titleCn: '芒果瓜', shortName: 'MA' },
|
|
||||||
{ name: 'mango-peach', title: 'mango peach', titleCn: '芒果桃', shortName: 'MA' },
|
|
||||||
{ name: 'mango-peach-apricot-ice', title: 'mango peach apricot ice', titleCn: '芒果桃杏冰', shortName: 'MA' },
|
|
||||||
{ name: 'mango-peach-orange', title: 'mango peach orange', titleCn: '芒果桃橙', shortName: 'MA' },
|
|
||||||
{ name: 'mango-peach-tings', title: 'mango peach tings', titleCn: '芒果桃滋味', shortName: 'MA' },
|
|
||||||
{ name: 'mango-peach-watermelon', title: 'mango peach watermelon', titleCn: '芒果桃西瓜', shortName: 'MA' },
|
|
||||||
{ name: 'mango-pineapple', title: 'mango pineapple', titleCn: '芒果菠萝', shortName: 'MA' },
|
|
||||||
{ name: 'mango-pineapple-guava-ice', title: 'mango pineapple guava ice', titleCn: '芒果菠萝番石榴冰', shortName: 'MA' },
|
|
||||||
{ name: 'mango-pineapple-ice', title: 'mango pineapple ice', titleCn: '芒果菠萝冰', shortName: 'MA' },
|
|
||||||
{ name: 'mango-squared', title: 'mango squared', titleCn: '芒果平方', shortName: 'MA' },
|
|
||||||
{ name: 'matata', title: 'matata', titleCn: '马塔塔', shortName: 'MA' },
|
|
||||||
{ name: 'max-freeze', title: 'max freeze', titleCn: '极冻', shortName: 'MA' },
|
|
||||||
{ name: 'max-polar-mint', title: 'max polar mint', titleCn: '极地薄荷', shortName: 'MA' },
|
|
||||||
{ name: 'max-polarmint', title: 'max polarmint', titleCn: '极地薄荷', shortName: 'MA' },
|
|
||||||
{ name: 'mclaren-sweet-papaya', title: 'mclaren sweet papaya', titleCn: '迈凯轮甜木瓜', shortName: 'MC' },
|
|
||||||
{ name: 'mega-mixed-berries', title: 'mega mixed berries', titleCn: '超级混合浆果', shortName: 'ME' },
|
|
||||||
{ name: 'melon-&-mint', title: 'melon & mint', titleCn: '瓜与薄荷', shortName: 'ME' },
|
|
||||||
{ name: 'melon-ice', title: 'melon ice', titleCn: '瓜冰', shortName: 'ME' },
|
|
||||||
{ name: 'menthol', title: 'menthol', titleCn: '薄荷', shortName: 'ME' },
|
|
||||||
{ name: 'menthol-ice', title: 'menthol ice', titleCn: '薄荷冰', shortName: 'ME' },
|
|
||||||
{ name: 'mexican-mango-ice', title: 'mexican mango ice', titleCn: '墨西哥芒果冰', shortName: 'ME' },
|
|
||||||
{ name: 'miami-mint', title: 'miami mint', titleCn: '迈阿密薄荷', shortName: 'MI' },
|
|
||||||
{ name: 'mint', title: 'mint', titleCn: '薄荷', shortName: 'MI' },
|
|
||||||
{ name: 'mint-energy', title: 'mint energy', titleCn: '薄荷 能量', shortName: 'MI' },
|
|
||||||
{ name: 'mint-tobacco', title: 'mint tobacco', titleCn: '薄荷烟草', shortName: 'MI' },
|
|
||||||
{ name: 'mirage', title: 'mirage', titleCn: '海市蜃楼', shortName: 'MI' },
|
|
||||||
{ name: 'mix-berries', title: 'mix berries', titleCn: '混合浆果', shortName: 'MI' },
|
|
||||||
{ name: 'mixed-barries', title: 'mixed barries', titleCn: '混合浆果', shortName: 'MI' },
|
|
||||||
{ name: 'mixed-berry', title: 'mixed berry', titleCn: '混合浆果', shortName: 'MI' },
|
|
||||||
{ name: 'mixed-fruit', title: 'mixed fruit', titleCn: '混合水果', shortName: 'MI' },
|
|
||||||
{ name: 'mocha-ice', title: 'mocha ice', titleCn: '摩卡冰', shortName: 'MO' },
|
|
||||||
{ name: 'morocco-mint', title: 'morocco mint', titleCn: '摩洛哥薄荷', shortName: 'MO' },
|
|
||||||
{ name: 'morocco-mint-(thermal)', title: 'morocco mint (thermal)', titleCn: '摩洛哥薄荷(热感)', shortName: 'MO' },
|
|
||||||
{ name: 'mung-beans', title: 'mung beans', titleCn: '绿豆', shortName: 'MU' },
|
|
||||||
{ name: 'nasty-tropic', title: 'nasty tropic', titleCn: '恶搞热带', shortName: 'NA' },
|
|
||||||
{ name: 'nectarine-ice', title: 'nectarine ice', titleCn: '油桃冰', shortName: 'NE' },
|
|
||||||
{ name: 'night-rider', title: 'night rider', titleCn: '夜骑', shortName: 'NI' },
|
|
||||||
{ name: 'nirvana', title: 'nirvana', titleCn: '宁静蓝莓', shortName: 'NI' },
|
|
||||||
{ name: 'north-american-style(root-beer)', title: 'north american style(root beer)', titleCn: '北美风格(根啤)', shortName: 'NO' },
|
|
||||||
{ name: 'northern-blue-razz', title: 'northern blue razz', titleCn: '北方蓝覆盆子', shortName: 'NO' },
|
|
||||||
{ name: 'nutty-virginia', title: 'nutty virginia', titleCn: '坚果弗吉尼亚', shortName: 'NU' },
|
|
||||||
{ name: 'orange', title: 'orange', titleCn: '橙子', shortName: 'OR' },
|
|
||||||
{ name: 'orange-citrus', title: 'orange citrus', titleCn: '橙子柑橘', shortName: 'OR' },
|
|
||||||
{ name: 'orange-fizz-ice', title: 'orange fizz ice', titleCn: '橙子汽水冰', shortName: 'OR' },
|
|
||||||
{ name: 'orange-ft', title: 'orange ft', titleCn: '橙子 FT', shortName: 'OR' },
|
|
||||||
{ name: 'orange-mango-guava', title: 'orange mango guava', titleCn: '橙子芒果番石榴', shortName: 'OR' },
|
|
||||||
{ name: 'orange-mango-pineapple-ice', title: 'orange mango pineapple ice', titleCn: '橙子芒果菠萝冰', shortName: 'OR' },
|
|
||||||
{ name: 'orange-p', title: 'orange p', titleCn: '橙子 P', shortName: 'OR' },
|
|
||||||
{ name: 'orange-p(fanta)', title: 'orange p(fanta)', titleCn: '橙子 P(芬达)', shortName: 'OR' },
|
|
||||||
{ name: 'orange-spark', title: 'orange spark', titleCn: '橙色火花', shortName: 'OR' },
|
|
||||||
{ name: 'orange-tangerine', title: 'orange tangerine', titleCn: '橙子柑橘', shortName: 'OR' },
|
|
||||||
{ name: 'original', title: 'original', titleCn: '原味', shortName: 'OR' },
|
|
||||||
{ name: 'packin-peach-berry', title: 'packin peach berry', titleCn: '装满桃浆果', shortName: 'PA' },
|
|
||||||
{ name: 'packin-peach-berry-(popn-peach-berry)', title: 'packin peach berry (popn peach berry)', titleCn: '装满桃浆果(Pop’n 桃浆果)', shortName: 'PA' },
|
|
||||||
{ name: 'papio', title: 'papio', titleCn: 'Papio', shortName: 'PA' },
|
|
||||||
{ name: 'paradise', title: 'paradise', titleCn: '天堂', shortName: 'PA' },
|
|
||||||
{ name: 'paradise-iced', title: 'paradise iced', titleCn: '天堂冰', shortName: 'PA' },
|
|
||||||
{ name: 'passion', title: 'passion', titleCn: '百香果', shortName: 'PA' },
|
|
||||||
{ name: 'passion-fruit', title: 'passion fruit', titleCn: '百香果冰', shortName: 'PA' },
|
|
||||||
{ name: 'passion-fruit-mango', title: 'passion fruit mango', titleCn: '百香果芒果', shortName: 'PA' },
|
|
||||||
{ name: 'passion-fruit-mango-lime', title: 'passion fruit mango lime', titleCn: '百香果芒果青柠', shortName: 'PA' },
|
|
||||||
{ name: 'passion-guava-grapefruit', title: 'passion guava grapefruit', titleCn: '百香果番石榴葡萄柚', shortName: 'PA' },
|
|
||||||
{ name: 'patas-pipe', title: 'patas pipe', titleCn: '帕塔烟斗', shortName: 'PA' },
|
|
||||||
{ name: 'peach', title: 'peach', titleCn: '桃子', shortName: 'PE' },
|
|
||||||
{ name: 'peach-&-mint', title: 'peach & mint', titleCn: '桃子薄荷', shortName: 'PE' },
|
|
||||||
{ name: 'peach-bellini', title: 'peach bellini', titleCn: '桃子贝里尼', shortName: 'PE' },
|
|
||||||
{ name: 'peach-berry', title: 'peach berry', titleCn: '桃子浆果', shortName: 'PE' },
|
|
||||||
{ name: 'peach-berry-ice', title: 'peach berry ice', titleCn: '桃子浆果冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-berry-lime-ice', title: 'peach berry lime ice', titleCn: '桃子浆果青柠冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-blossom', title: 'peach blossom', titleCn: '桃花', shortName: 'PE' },
|
|
||||||
{ name: 'peach-blue-raspberry', title: 'peach blue raspberry', titleCn: '桃子蓝莓覆盆子', shortName: 'PE' },
|
|
||||||
{ name: 'peach-blue-razz-ice', title: 'peach blue razz ice', titleCn: '桃子蓝覆盆子冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-blue-razz-mango-ice', title: 'peach blue razz mango ice', titleCn: '桃子蓝覆盆子芒果冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-blue-s', title: 'peach blue s', titleCn: '桃子蓝覆盆子 S', shortName: 'PE' },
|
|
||||||
{ name: 'peach-ice', title: 'peach ice', titleCn: '桃子冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-lychee-ice', title: 'peach lychee ice', titleCn: '桃荔枝冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-mango', title: 'peach mango', titleCn: '桃芒果', shortName: 'PE' },
|
|
||||||
{ name: 'peach-mango-ice', title: 'peach mango ice', titleCn: '桃芒果冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-mango-watermelon', title: 'peach mango watermelon', titleCn: '桃芒果西瓜', shortName: 'PE' },
|
|
||||||
{ name: 'peach-mango-watermelon-ice', title: 'peach mango watermelon ice', titleCn: '桃芒果西瓜冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-nectarine-ice', title: 'peach nectarine ice', titleCn: '桃子花蜜冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-passion-ice', title: 'peach passion ice', titleCn: '桃子桃冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-raspberry', title: 'peach raspberry', titleCn: '桃覆盆子', shortName: 'PE' },
|
|
||||||
{ name: 'peach-strawberry-ice', title: 'peach strawberry ice', titleCn: '桃草莓冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-strawberry-watermelon', title: 'peach strawberry watermelon', titleCn: '桃草莓西瓜', shortName: 'PE' },
|
|
||||||
{ name: 'peach-watermelon-ice', title: 'peach watermelon ice', titleCn: '桃西瓜冰', shortName: 'PE' },
|
|
||||||
{ name: 'peach-zing', title: 'peach zing', titleCn: '桃子滋味', shortName: 'PE' },
|
|
||||||
{ name: 'peaches-cream', title: 'peaches cream', titleCn: '桃子奶油', shortName: 'PE' },
|
|
||||||
{ name: 'peppered-mint', title: 'peppered mint', titleCn: '胡椒薄荷', shortName: 'PE' },
|
|
||||||
{ name: 'peppermint', title: 'peppermint', titleCn: '薄荷', shortName: 'PE' },
|
|
||||||
{ name: 'peppermint-salty', title: 'peppermint salty', titleCn: '薄荷咸味', shortName: 'PE' },
|
|
||||||
{ name: 'peppermint-storm', title: 'peppermint storm', titleCn: '薄荷风暴', shortName: 'PE' },
|
|
||||||
{ name: 'pina-blend', title: 'pina blend', titleCn: '菠萝混合', shortName: 'PI' },
|
|
||||||
{ name: 'pina-colada-ice', title: 'pina colada ice', titleCn: '菠萝椰子冰', shortName: 'PI' },
|
|
||||||
{ name: 'pineapple', title: 'pineapple', titleCn: '菠萝', shortName: 'PI' },
|
|
||||||
{ name: 'pineapple-blueberry-kiwi-ice', title: 'pineapple blueberry kiwi ice', titleCn: '菠萝蓝莓奇异果冰', shortName: 'PI' },
|
|
||||||
{ name: 'pineapple-citrus', title: 'pineapple citrus', titleCn: '菠萝柑橘', shortName: 'PI' },
|
|
||||||
{ name: 'pineapple-coconut', title: 'pineapple coconut', titleCn: '菠萝椰子', shortName: 'PI' },
|
|
||||||
{ name: 'pineapple-coconut-ice', title: 'pineapple coconut ice', titleCn: '菠萝椰子冰', shortName: 'PI' },
|
|
||||||
{ name: 'pineapple-ice', title: 'pineapple ice', titleCn: '菠萝冰', shortName: 'PI' },
|
|
||||||
{ name: 'pineapple-lemonade', title: 'pineapple lemonade', titleCn: '菠萝柠檬水', shortName: 'PI' },
|
|
||||||
{ name: 'pineapple-orange-cherry', title: 'pineapple orange cherry', titleCn: '菠萝橙樱桃', shortName: 'PI' },
|
|
||||||
{ name: 'pink-lemon', title: 'pink lemon', titleCn: '粉柠檬', shortName: 'PI' },
|
|
||||||
{ name: 'pink-lemon-ice', title: 'pink lemon ice', titleCn: '粉柠檬冰', shortName: 'PI' },
|
|
||||||
{ name: 'pink-lemonade', title: 'pink lemonade', titleCn: '粉红柠檬水', shortName: 'PI' },
|
|
||||||
{ name: 'pink-punch', title: 'pink punch', titleCn: '粉红拳', shortName: 'PI' },
|
|
||||||
{ name: 'polar-chill', title: 'polar chill', titleCn: '极地清凉', shortName: 'PO' },
|
|
||||||
{ name: 'polar-mint-max', title: 'polar mint max', titleCn: '极地薄荷', shortName: 'PO' },
|
|
||||||
{ name: 'pomegranate-ice', title: 'pomegranate ice', titleCn: '石榴冰', shortName: 'PO' },
|
|
||||||
{ name: 'poppin-strawkiwi', title: 'poppin strawkiwi', titleCn: '草莓猕猴', shortName: 'PO' },
|
|
||||||
{ name: 'prism-ice', title: 'prism ice', titleCn: '棱镜冰', shortName: 'PR' },
|
|
||||||
{ name: 'punch', title: 'punch', titleCn: '果汁', shortName: 'PU' },
|
|
||||||
{ name: 'punch-ice', title: 'punch ice', titleCn: '果汁冰', shortName: 'PU' },
|
|
||||||
{ name: 'pure-tobacco', title: 'pure tobacco', titleCn: '纯烟草', shortName: 'PU' },
|
|
||||||
{ name: 'puris', title: 'puris', titleCn: '纯味', shortName: 'PU' },
|
|
||||||
{ name: 'purple-grape', title: 'purple grape', titleCn: '紫葡萄', shortName: 'PU' },
|
|
||||||
{ name: 'quad-berry', title: 'quad berry', titleCn: '四重浆果', shortName: 'QU' },
|
|
||||||
{ name: 'queen-soko', title: 'queen soko', titleCn: '女王索科', shortName: 'QU' },
|
|
||||||
{ name: 'rad-razz-melon-iced', title: 'rad razz melon iced', titleCn: '疯狂覆盆子瓜冰', shortName: 'RA' },
|
|
||||||
{ name: 'ragin-razz-mango-iced', title: 'ragin razz mango iced', titleCn: '狂暴覆盆子芒果冰', shortName: 'RA' },
|
|
||||||
{ name: 'rainbow-candy', title: 'rainbow candy', titleCn: '彩虹糖', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-blast', title: 'raspberry blast', titleCn: '覆盆子爆炸', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-buzz-ice', title: 'raspberry buzz ice', titleCn: '覆盆子嗡嗡冰', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-dragon-fruit-ice', title: 'raspberry dragon fruit ice', titleCn: '覆盆子龙果冰', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-ice', title: 'raspberry ice', titleCn: '覆盆子冰', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-lemon', title: 'raspberry lemon', titleCn: '覆盆子柠檬', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-mango-ice', title: 'raspberry mango ice', titleCn: '覆盆子芒果冰', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-peach-mango-ice', title: 'raspberry peach mango ice', titleCn: '覆盆子桃芒果冰', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-pomegranate', title: 'raspberry pomegranate', titleCn: '覆盆子石榴', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-vanilla', title: 'raspberry vanilla', titleCn: '覆盆子香草', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-watermelon', title: 'raspberry watermelon', titleCn: '覆盆子西瓜', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-watermelon-ice', title: 'raspberry watermelon ice', titleCn: '覆盆子西瓜冰', shortName: 'RA' },
|
|
||||||
{ name: 'raspberry-zing', title: 'raspberry zing', titleCn: '覆盆子滋味', shortName: 'RA' },
|
|
||||||
{ name: 'razz-apple-ice', title: 'razz apple ice', titleCn: '覆盆子苹果冰', shortName: 'RA' },
|
|
||||||
{ name: 'razz-currant-ice', title: 'razz currant ice', titleCn: '红苹果冰', shortName: 'RA' },
|
|
||||||
{ name: 'red-apple-ice', title: 'red apple ice', titleCn: '红豆', shortName: 'RE' },
|
|
||||||
{ name: 'red-bean', title: 'red bean', titleCn: '红枣 ', shortName: 'RE' },
|
|
||||||
{ name: 'red-berry-cherry', title: 'red berry cherry', titleCn: '红浆果樱桃', shortName: 'RE' },
|
|
||||||
{ name: 'red-date-yg', title: 'red date yg', titleCn: '红枣 Y', shortName: 'RE' },
|
|
||||||
{ name: 'red-eye-espresso', title: 'red eye espresso', titleCn: '红眼浓缩咖啡', shortName: 'RE' },
|
|
||||||
{ name: 'red-fruits', title: 'red fruits', titleCn: '红色水果', shortName: 'RE' },
|
|
||||||
{ name: 'red-lightning', title: 'red lightning', titleCn: '红色闪电', shortName: 'RE' },
|
|
||||||
{ name: 'red-line', title: 'red line', titleCn: '红线', shortName: 'RE' },
|
|
||||||
{ name: 'red-line-(energy-drink)', title: 'red line (energy drink)', titleCn: '红线(能量饮料)', shortName: 'RE' },
|
|
||||||
{ name: 'red-magic', title: 'red magic', titleCn: '红魔', shortName: 'RE' },
|
|
||||||
{ name: 'rich-tobacco', title: 'rich tobacco', titleCn: '浓烈烟草', shortName: 'RI' },
|
|
||||||
{ name: 'root-beer', title: 'root beer', titleCn: '根啤', shortName: 'RO' },
|
|
||||||
{ name: 'rose-grape', title: 'rose grape', titleCn: '玫瑰葡萄', shortName: 'RO' },
|
|
||||||
{ name: 'rosemary', title: 'rosemary', titleCn: '迷迭香', shortName: 'RO' },
|
|
||||||
{ name: 'royal-violet', title: 'royal violet', titleCn: '皇家紫罗兰', shortName: 'RO' },
|
|
||||||
{ name: 'ruby-berry', title: 'ruby berry', titleCn: '红宝石浆果', shortName: 'RU' },
|
|
||||||
{ name: 's-apple-ice', title: 's apple ice', titleCn: 'S 苹果冰', shortName: 'SA' },
|
|
||||||
{ name: 's-watermelon-peach', title: 's watermelon peach', titleCn: 'S 西瓜桃', shortName: 'SW' },
|
|
||||||
{ name: 'saimiri', title: 'saimiri', titleCn: '卷尾猴', shortName: 'SA' },
|
|
||||||
{ name: 'sakura-grap', title: 'sakura grap', titleCn: '樱花葡萄', shortName: 'SA' },
|
|
||||||
{ name: 'sakura-grape', title: 'sakura grape', titleCn: '樱花葡萄', shortName: 'SA' },
|
|
||||||
{ name: 'salt', title: 'salt', titleCn: '盐', shortName: 'SA' },
|
|
||||||
{ name: 'salted-caramel', title: 'salted caramel', titleCn: '咸焦糖', shortName: 'SA' },
|
|
||||||
{ name: 'salty-liquorice', title: 'salty liquorice', titleCn: '咸甘草', shortName: 'SA' },
|
|
||||||
{ name: 'sanctuary', title: 'sanctuary', titleCn: '避风港', shortName: 'SA' },
|
|
||||||
{ name: 'savage-strawberry-watermelon-iced', title: 'savage strawberry watermelon iced', titleCn: '狂野草莓西瓜冰', shortName: 'SA' },
|
|
||||||
{ name: 'shoku', title: 'shoku', titleCn: 'Shoku', shortName: 'SH' },
|
|
||||||
{ name: 'sic-strawberry-iced', title: 'sic strawberry iced', titleCn: '意大利草莓冰', shortName: 'SI' },
|
|
||||||
{ name: 'simply-spearmint', title: 'simply spearmint', titleCn: '清爽留兰香', shortName: 'SI' },
|
|
||||||
{ name: 'skc', title: 'skc', titleCn: 'SKC', shortName: 'SK' },
|
|
||||||
{ name: 'skc(skittles-candy)', title: 'skc(skittles candy)', titleCn: 'SKC(彩虹糖)', shortName: 'SK' },
|
|
||||||
{ name: 'slammin-sts-(sour-snap)', title: 'slammin sts (sour snap)', titleCn: '热烈 STS(酸糖)', shortName: 'SL' },
|
|
||||||
{ name: 'slammin-sts-iced', title: 'slammin sts iced', titleCn: '热烈 STS 冰', shortName: 'SL' },
|
|
||||||
{ name: 'smooth', title: 'smooth', titleCn: '顺滑', shortName: 'SM' },
|
|
||||||
{ name: 'smooth-mint', title: 'smooth mint', titleCn: '顺滑薄荷', shortName: 'SM' },
|
|
||||||
{ name: 'smooth-strawberry', title: 'smooth strawberry', titleCn: '顺滑草莓', shortName: 'SM' },
|
|
||||||
{ name: 'smooth-tobacco', title: 'smooth tobacco', titleCn: '顺滑烟草', shortName: 'SM' },
|
|
||||||
{ name: 'snazzy-razz', title: 'snazzy razz', titleCn: '炫酷覆盆子', shortName: 'SN' },
|
|
||||||
{ name: 'snazzy-s-storm', title: 'snazzy s storm', titleCn: '炫酷风暴', shortName: 'SN' },
|
|
||||||
{ name: 'snazzy-strawberrry-citrus', title: 'snazzy strawberrry citrus', titleCn: '炫酷草莓柑橘', shortName: 'SN' },
|
|
||||||
{ name: 'snow-pear', title: 'snow pear', titleCn: '酸梨', shortName: 'SN' },
|
|
||||||
{ name: 'sour', title: 'sour', titleCn: '酸', shortName: 'SO' },
|
|
||||||
{ name: 'sour-apple', title: 'sour apple', titleCn: '酸苹果', shortName: 'SO' },
|
|
||||||
{ name: 'sour-blue-razz', title: 'sour blue razz', titleCn: '酸蓝覆盆子', shortName: 'SO' },
|
|
||||||
{ name: 'sour-cherry', title: 'sour cherry', titleCn: '酸樱桃', shortName: 'SO' },
|
|
||||||
{ name: 'sour-lime', title: 'sour lime', titleCn: '酸青柠', shortName: 'SO' },
|
|
||||||
{ name: 'sour-ruby', title: 'sour ruby', titleCn: '酸红宝石', shortName: 'SO' },
|
|
||||||
{ name: 'spearmint', title: 'spearmint', titleCn: '留兰香', shortName: 'SP' },
|
|
||||||
{ name: 'spearmint-blast-ice', title: 'spearmint blast ice', titleCn: '留兰香爆发冰', shortName: 'SP' },
|
|
||||||
{ name: 'star-coffee', title: 'star coffee', titleCn: '星辰咖啡', shortName: 'ST' },
|
|
||||||
{ name: 'straw-kiwi-melon-ice', title: 'straw kiwi melon ice', titleCn: '草莓奇异果瓜冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawanna-ice', title: 'strawanna ice', titleCn: '草莓香蕉冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry', title: 'strawberry', titleCn: '草莓', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-&-watermelon', title: 'strawberry & watermelon', titleCn: '草莓西瓜', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-apple-grape', title: 'strawberry apple grape', titleCn: '草莓苹果葡萄', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-apricot-ice', title: 'strawberry apricot ice', titleCn: '草莓杏子冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-banana', title: 'strawberry banana', titleCn: '草莓香蕉', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-banana-ice', title: 'strawberry banana ice', titleCn: '草莓香蕉冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-banana-mango-ice', title: 'strawberry banana mango ice', titleCn: '草莓香蕉芒果冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-berry', title: 'strawberry berry', titleCn: '草莓浆果', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-burst-ice', title: 'strawberry burst ice', titleCn: '草莓爆发冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-cherry-lemon', title: 'strawberry cherry lemon', titleCn: '草莓樱桃柠檬', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-dragon-fruit', title: 'strawberry dragon fruit', titleCn: '草莓龙果', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-ft', title: 'strawberry ft', titleCn: '草莓 FT', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-grapefruit', title: 'strawberry grapefruit', titleCn: '草莓葡萄柚', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-ice', title: 'strawberry ice', titleCn: '草莓冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-jasmine-t', title: 'strawberry jasmine t', titleCn: '草莓茉莉茶', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-jasmine-tea', title: 'strawberry jasmine tea', titleCn: '草莓茉莉茶', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-kiwi', title: 'strawberry kiwi', titleCn: '草莓奇异果', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-kiwi-(solid)', title: 'strawberry kiwi (solid)', titleCn: '草莓奇异果(固体)', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-kiwi-banana-ice', title: 'strawberry kiwi banana ice', titleCn: '草莓奇异果香蕉冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-kiwi-guava-ice', title: 'strawberry kiwi guava ice', titleCn: '草莓奇异果番石榴冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-kiwi-ice', title: 'strawberry kiwi ice', titleCn: '草莓奇异果冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-lemon', title: 'strawberry lemon', titleCn: '草莓柠檬', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-lime-ice', title: 'strawberry lime ice', titleCn: '草莓青柠冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-lychee-ice', title: 'strawberry lychee ice', titleCn: '草莓荔枝冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-mango-ice', title: 'strawberry mango ice', titleCn: '草莓芒果冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-mint', title: 'strawberry mint', titleCn: '草莓薄荷', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-orange', title: 'strawberry orange', titleCn: '草莓橙', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-peach-mint', title: 'strawberry peach mint', titleCn: '草莓桃薄荷', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-raspberry', title: 'strawberry raspberry', titleCn: '草莓覆盆子', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-twist-ice', title: 'strawberry twist ice', titleCn: '草莓扭转冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-watermelon', title: 'strawberry watermelon', titleCn: '草莓西瓜', shortName: 'ST' },
|
|
||||||
{ name: 'strawberry-watermelon-ice', title: 'strawberry watermelon ice', titleCn: '草莓西瓜冰', shortName: 'ST' },
|
|
||||||
{ name: 'strawmelon-peach', title: 'strawmelon peach', titleCn: '草莓桃', shortName: 'ST' },
|
|
||||||
{ name: 'strawmelon-peach-(solid)', title: 'strawmelon peach (solid)', titleCn: '草莓桃(固体)', shortName: 'ST' },
|
|
||||||
{ name: 'strawnana-orange', title: 'strawnana orange', titleCn: '草莓香蕉橙', shortName: 'ST' },
|
|
||||||
{ name: 'summer-grape', title: 'summer grape', titleCn: '夏日葡萄', shortName: 'SU' },
|
|
||||||
{ name: 'summer-grape-(thermal)', title: 'summer grape (thermal)', titleCn: '夏日葡萄(热感)', shortName: 'SU' },
|
|
||||||
{ name: 'super-sour-blueberry-iced', title: 'super sour blueberry iced', titleCn: '超级酸蓝莓冰', shortName: 'SU' },
|
|
||||||
{ name: 'super-spearmint', title: 'super spearmint', titleCn: '超级留兰香', shortName: 'SU' },
|
|
||||||
{ name: 'super-spearmint-iced', title: 'super spearmint iced', titleCn: '超级留兰香冰', shortName: 'SU' },
|
|
||||||
{ name: 'sweet-blackcurrant', title: 'sweet blackcurrant', titleCn: '甜黑加仑', shortName: 'SW' },
|
|
||||||
{ name: 'sweet-mint', title: 'sweet mint', titleCn: '甜薄荷', shortName: 'SW' },
|
|
||||||
{ name: 't-berries', title: 't berries', titleCn: 'T 浆果', shortName: 'TB' },
|
|
||||||
{ name: 'taste-of-gods-x', title: 'taste of gods x', titleCn: '神之味 X', shortName: 'TA' },
|
|
||||||
{ name: 'the-prophet', title: 'the prophet', titleCn: '先知', shortName: 'TH' },
|
|
||||||
{ name: 'tiki-punch-ice', title: 'tiki punch ice', titleCn: 'Tiki 冲击冰', shortName: 'TI' },
|
|
||||||
{ name: 'triple-berry', title: 'triple berry', titleCn: '三重浆果', shortName: 'TR' },
|
|
||||||
{ name: 'triple-berry-ice', title: 'triple berry ice', titleCn: '三重浆果冰', shortName: 'TR' },
|
|
||||||
{ name: 'triple-mango', title: 'triple mango', titleCn: '三重芒果', shortName: 'TR' },
|
|
||||||
{ name: "trippin'-triple-berry", title: "trippin' triple berry", titleCn: '三重浆果旋风', shortName: 'TR' },
|
|
||||||
{ name: 'tropical', title: 'tropical', titleCn: '热带', shortName: 'TR' },
|
|
||||||
{ name: 'tropical-burst-ice', title: 'tropical burst ice', titleCn: '热带爆发冰', shortName: 'TR' },
|
|
||||||
{ name: 'tropical-mango', title: 'tropical mango', titleCn: '热带芒果', shortName: 'TR' },
|
|
||||||
{ name: 'tropical-mango-ice', title: 'tropical mango ice', titleCn: '热带芒果冰', shortName: 'TR' },
|
|
||||||
{ name: 'tropical-orang-ice', title: 'tropical orang ice', titleCn: '热带橙冰', shortName: 'TR' },
|
|
||||||
{ name: 'tropical-prism-blast', title: 'tropical prism blast', titleCn: '热带棱镜爆炸', shortName: 'TR' },
|
|
||||||
{ name: 'tropical-splash', title: 'tropical splash', titleCn: '热带飞溅', shortName: 'TR' },
|
|
||||||
{ name: 'tropical-splash-(solid)', title: 'tropical splash (solid)', titleCn: '热带飞溅(固体)', shortName: 'TR' },
|
|
||||||
{ name: 'tropical-storm-ice', title: 'tropical storm ice', titleCn: '热带风暴冰', shortName: 'TR' },
|
|
||||||
{ name: 'tropical-summer', title: 'tropical summer', titleCn: '热带夏日', shortName: 'TR' },
|
|
||||||
{ name: 'tropika', title: 'tropika', titleCn: '热带果', shortName: 'TR' },
|
|
||||||
{ name: 'twisted-apple', title: 'twisted apple', titleCn: '扭苹果', shortName: 'TW' },
|
|
||||||
{ name: 'twisted-pineapple', title: 'twisted pineapple', titleCn: '扭菠萝', shortName: 'TW' },
|
|
||||||
{ name: 'ultra-fresh-mint', title: 'ultra fresh mint', titleCn: '极新鲜薄荷', shortName: 'UL' },
|
|
||||||
{ name: 'vanilla', title: 'vanilla', titleCn: '香草', shortName: 'VA' },
|
|
||||||
{ name: 'vanilla-classic', title: 'vanilla classic', titleCn: '香草经典', shortName: 'VA' },
|
|
||||||
{ name: 'vanilla-classic-cola', title: 'vanilla classic cola', titleCn: '香草经典可乐', shortName: 'VA' },
|
|
||||||
{ name: 'vanilla-classic-red', title: 'vanilla classic red', titleCn: '香草经典红', shortName: 'VA' },
|
|
||||||
{ name: 'vanilla-tobacco', title: 'vanilla tobacco', titleCn: '香草烟草', shortName: 'VA' },
|
|
||||||
{ name: 'vb-arctic-berry', title: 'vb arctic berry', titleCn: 'VB 北极浆果', shortName: 'VB' },
|
|
||||||
{ name: 'vb-arctic-mint', title: 'vb arctic mint', titleCn: 'VB 北极薄荷', shortName: 'VB' },
|
|
||||||
{ name: 'vb-spearmint-salty', title: 'vb spearmint salty', titleCn: 'VB 留兰香咸味', shortName: 'VB' },
|
|
||||||
{ name: 'vc-delight', title: 'vc delight', titleCn: 'VC 美味', shortName: 'VC' },
|
|
||||||
{ name: 'vintage', title: 'vintage', titleCn: '复古', shortName: 'VI' },
|
|
||||||
{ name: 'violet-licorice', title: 'violet licorice', titleCn: '紫罗兰甘草', shortName: 'VI' },
|
|
||||||
{ name: 'watermelon', title: 'watermelon', titleCn: '西瓜', shortName: 'WA' },
|
|
||||||
{ name: 'watermelon-bbg', title: 'watermelon bbg', titleCn: '西瓜 BBG', shortName: 'WA' },
|
|
||||||
{ name: 'watermelon-bubble-gum', title: 'watermelon bubble gum', titleCn: '西瓜泡泡糖', shortName: 'WA' },
|
|
||||||
{ name: 'watermelon-cantaloupe-honeydew-ice', title: 'watermelon cantaloupe honeydew ice', titleCn: '西瓜香瓜蜜瓜冰', shortName: 'WA' },
|
|
||||||
{ name: 'watermelon-g', title: 'watermelon g', titleCn: '西瓜 G', shortName: 'WA' },
|
|
||||||
{ name: 'watermelon-ice', title: 'watermelon ice', titleCn: '西瓜冰', shortName: 'WA' },
|
|
||||||
{ name: 'watermelon-ice-(solid)', title: 'watermelon ice (solid)', titleCn: '西瓜冰(固体)', shortName: 'WA' },
|
|
||||||
{ name: 'watermelon-lime-ice', title: 'watermelon lime ice', titleCn: '西瓜青柠冰', shortName: 'WA' },
|
|
||||||
{ name: 'watermelon-mango-tango', title: 'watermelon mango tango', titleCn: '西瓜芒果探戈', shortName: 'WA' },
|
|
||||||
{ name: 'watermelona-cg', title: 'watermelona cg', titleCn: '西瓜 CG', shortName: 'WA' },
|
|
||||||
{ name: 'weekend-watermelon', title: 'weekend watermelon', titleCn: '周末西瓜', shortName: 'WE' },
|
|
||||||
{ name: 'weekend-watermelon-iced', title: 'weekend watermelon iced', titleCn: '周末西瓜冰', shortName: 'WE' },
|
|
||||||
{ name: 'white-grape', title: 'white grape', titleCn: '白葡萄', shortName: 'WH' },
|
|
||||||
{ name: 'white-grape-ice', title: 'white grape ice', titleCn: '白葡萄冰', shortName: 'WH' },
|
|
||||||
{ name: 'white-ice', title: 'white ice', titleCn: '白冰', shortName: 'WH' },
|
|
||||||
{ name: 'white-peach-ice', title: 'white peach ice', titleCn: '白桃冰', shortName: 'WH' },
|
|
||||||
{ name: 'white-peach-splash', title: 'white peach splash', titleCn: '白桃飞溅', shortName: 'WH' },
|
|
||||||
{ name: 'white-peach-yaklt', title: 'white peach yaklt', titleCn: '白桃益菌乳', shortName: 'WH' },
|
|
||||||
{ name: 'wicked-white-peach', title: 'wicked white peach', titleCn: '邪恶白桃', shortName: 'WI' },
|
|
||||||
{ name: 'wild-blue-raspberry', title: 'wild blue raspberry', titleCn: '野生蓝覆盆子', shortName: 'WI' },
|
|
||||||
{ name: 'wild-blueberry-ice', title: 'wild blueberry ice', titleCn: '野生蓝莓冰', shortName: 'WI' },
|
|
||||||
{ name: 'wild-cherry-cola', title: 'wild cherry cola', titleCn: '野樱桃可乐', shortName: 'WI' },
|
|
||||||
{ name: 'wild-dragonfruit-lychee', title: 'wild dragonfruit lychee', titleCn: '野生龙果荔枝', shortName: 'WI' },
|
|
||||||
{ name: 'wild-strawberry-banana', title: 'wild strawberry banana', titleCn: '野生草莓香蕉', shortName: 'WI' },
|
|
||||||
{ name: 'wild-strawberry-ice', title: 'wild strawberry ice', titleCn: '野生草莓冰', shortName: 'WI' },
|
|
||||||
{ name: 'wild-strawberry-watermelon', title: 'wild strawberry watermelon', titleCn: '野生草莓西瓜', shortName: 'WI' },
|
|
||||||
{ name: 'wild-white-grape', title: 'wild white grape', titleCn: '野生白葡萄', shortName: 'WI' },
|
|
||||||
{ name: 'wild-white-grape-ice', title: 'wild white grape ice', titleCn: '野生白葡萄冰', shortName: 'WI' },
|
|
||||||
{ name: 'wild-white-grape-iced', title: 'wild white grape iced', titleCn: '野生白葡萄冰(冷饮)', shortName: 'WI' },
|
|
||||||
{ name: 'winter-berry-ice', title: 'winter berry ice', titleCn: '冬季浆果冰', shortName: 'WI' },
|
|
||||||
{ name: 'winter-green', title: 'winter green', titleCn: '冬青', shortName: 'WI' },
|
|
||||||
{ name: 'wintergreen', title: 'wintergreen', titleCn: '冬青薄荷', shortName: 'WI' },
|
|
||||||
{ name: 'wintery-watermelon', title: 'wintery watermelon', titleCn: '冬季西瓜', shortName: 'WI' },
|
|
||||||
{ name: 'woke-watermelon-tropica-iced', title: 'woke watermelon tropica iced', titleCn: '觉醒西瓜热带冰', shortName: 'WO' },
|
|
||||||
{ name: 'wonder', title: 'wonder', titleCn: '奇迹', shortName: 'WO' },
|
|
||||||
{ name: 'x-freeze', title: 'x freeze', titleCn: 'X 冰冻', shortName: 'XF' },
|
|
||||||
{ name: 'zen', title: 'zen', titleCn: '禅', shortName: 'ZE' },
|
|
||||||
{ name: 'zest-flame', title: 'zest flame', titleCn: '清新火焰', shortName: 'ZE' },
|
|
||||||
{ name: 'zesty-elderflower', title: 'zesty elderflower', titleCn: '活力接骨木花', shortName: 'ZE' },
|
|
||||||
{ name: 'zingy-eucalyptus', title: 'zingy eucalyptus', titleCn: '清爽桉树', shortName: 'ZI' },
|
|
||||||
];
|
|
||||||
|
|
||||||
// Total flavors: 655
|
|
||||||
|
|
||||||
// 强度数据
|
|
||||||
const strengthsData = [
|
|
||||||
{ name: '1.5mg', title: '1.5mg', titleCn: '1.5毫克', shortName: '1.5' },
|
|
||||||
{ name: '2mg', title: '2mg', titleCn: '2毫克', shortName: '2MG' },
|
|
||||||
{ name: '3mg', title: '3mg', titleCn: '3毫克', shortName: '3MG' },
|
|
||||||
{ name: '3.5mg', title: '3.5mg', titleCn: '3.5毫克', shortName: '3.5' },
|
|
||||||
{ name: '4mg', title: '4mg', titleCn: '4毫克', shortName: '4MG' },
|
|
||||||
{ name: '5,2 mg', title: '5,2 mg', titleCn: '5,2毫克', shortName: '5,2' },
|
|
||||||
{ name: '5.6mg', title: '5.6mg', titleCn: '5.6毫克', shortName: '5.6' },
|
|
||||||
{ name: '6mg', title: '6mg', titleCn: '6毫克', shortName: '6MG' },
|
|
||||||
{ name: '6.5mg', title: '6.5mg', titleCn: '6.5毫克', shortName: '6.5' },
|
|
||||||
{ name: '8mg', title: '8mg', titleCn: '8毫克', shortName: '8MG' },
|
|
||||||
{ name: '9mg', title: '9mg', titleCn: '9毫克', shortName: '9MG' },
|
|
||||||
{ name: '10mg', title: '10mg', titleCn: '10毫克', shortName: '10M' },
|
|
||||||
{ name: '10,4 mg', title: '10,4 mg', titleCn: '10,4 毫克', shortName: '10,' },
|
|
||||||
{ name: '10,9mg', title: '10,9mg', titleCn: '10,9毫克', shortName: '10,' },
|
|
||||||
{ name: '11mg', title: '11mg', titleCn: '11毫克', shortName: '11M' },
|
|
||||||
{ name: '12mg', title: '12mg', titleCn: '12毫克', shortName: '12M' },
|
|
||||||
{ name: '12.5mg', title: '12.5mg', titleCn: '12.5毫克', shortName: '12.' },
|
|
||||||
{ name: '13.5mg', title: '13.5mg', titleCn: '13.5毫克', shortName: '13.' },
|
|
||||||
{ name: '14mg', title: '14mg', titleCn: '14毫克', shortName: '14M' },
|
|
||||||
{ name: '15mg', title: '15mg', titleCn: '15毫克', shortName: '15M' },
|
|
||||||
{ name: '16mg', title: '16mg', titleCn: '16毫克', shortName: '16M' },
|
|
||||||
{ name: '16.5mg', title: '16.5mg', titleCn: '16.5毫克', shortName: '16.' },
|
|
||||||
{ name: '16.6mg', title: '16.6mg', titleCn: '16.6毫克', shortName: '16.' },
|
|
||||||
{ name: '17mg', title: '17mg', titleCn: '17毫克', shortName: '17M' },
|
|
||||||
{ name: '18mg', title: '18mg', titleCn: '18毫克', shortName: '18M' },
|
|
||||||
{ name: '20mg', title: '20mg', titleCn: '20毫克', shortName: '20M' },
|
|
||||||
{ name: '30mg', title: '30mg', titleCn: '30毫克', shortName: '30M' },
|
|
||||||
{ name: 'extra-strong', title: 'extra strong', titleCn: '超强', shortName: 'EXT' },
|
|
||||||
{ name: 'low', title: 'low', titleCn: '低', shortName: 'LOW' },
|
|
||||||
{ name: 'max', title: 'max', titleCn: '最大', shortName: 'MAX' },
|
|
||||||
{ name: 'medium', title: 'medium', titleCn: '中等', shortName: 'MED' },
|
|
||||||
{ name: 'normal', title: 'normal', titleCn: '普通', shortName: 'NOR' },
|
|
||||||
{ name: 'strong', title: 'strong', titleCn: '强', shortName: 'STR' },
|
|
||||||
{ name: 'super-strong', title: 'super strong', titleCn: '特强', shortName: 'SUP' },
|
|
||||||
{ name: 'ultra-strong', title: 'ultra strong', titleCn: '极强', shortName: 'ULT' },
|
|
||||||
{ name: 'xx-strong', title: 'xx strong', titleCn: '超超强', shortName: 'XXS' },
|
|
||||||
{ name: 'x-intense', title: 'x intense', titleCn: '强', shortName: 'XIN' },
|
|
||||||
];
|
|
||||||
|
|
||||||
// Total strengths: 37
|
|
||||||
|
|
||||||
// 品牌数据
|
|
||||||
const brandsData = [
|
|
||||||
{ name: 'yoone', title: 'yoone', titleCn: '', shortName: 'YO' },
|
|
||||||
{ name: 'zyn', title: 'zyn', titleCn: '', shortName: 'ZY' },
|
|
||||||
{ name: 'on!', title: 'on!', titleCn: '', shortName: 'ON' },
|
|
||||||
{ name: 'alibarbar', title: 'alibarbar', titleCn: '', shortName: 'AL' },
|
|
||||||
{ name: 'iget-pro', title: 'iget pro', titleCn: '', shortName: 'IG' },
|
|
||||||
{ name: 'jux', title: 'jux', titleCn: '', shortName: 'JU' },
|
|
||||||
{ name: 'velo', title: 'velo', titleCn: '', shortName: 'VE' },
|
|
||||||
{ name: 'white-fox', title: 'white fox', titleCn: '', shortName: 'WH' },
|
|
||||||
{ name: 'zolt', title: 'zolt', titleCn: '', shortName: 'ZO' },
|
|
||||||
{ name: '77', title: '77', titleCn: '', shortName: '77' },
|
|
||||||
{ name: 'xqs', title: 'xqs', titleCn: '', shortName: 'XQ' },
|
|
||||||
{ name: 'zex', title: 'zex', titleCn: '', shortName: 'ZE' },
|
|
||||||
{ name: 'zonnic', title: 'zonnic', titleCn: '', shortName: 'ZO' },
|
|
||||||
{ name: 'lucy', title: 'Lucy', titleCn: '', shortName: 'LU' },
|
|
||||||
{ name: 'egp', title: 'EGP', titleCn: '', shortName: 'EG' },
|
|
||||||
{ name: 'bridge', title: 'Bridge', titleCn: '', shortName: 'BR' },
|
|
||||||
{ name: 'sesh', title: 'Sesh', titleCn: '', shortName: 'SE' },
|
|
||||||
{ name: 'pablo', title: 'Pablo', titleCn: '', shortName: 'PA' },
|
|
||||||
{ name: 'elfbar', title: 'elfbar', titleCn: '', shortName: 'EL' },
|
|
||||||
{ name: 'chacha', title: 'chacha', titleCn: '', shortName: 'CH' },
|
|
||||||
{ name: 'yoone-wave', title: 'yoone wave', titleCn: '', shortName: 'YO' },
|
|
||||||
{ name: 'yoone-e-liquid', title: 'yoone e-liquid', titleCn: '', shortName: 'YO' },
|
|
||||||
{ name: 'geek-bar', title: 'geek bar', titleCn: '', shortName: 'GE' },
|
|
||||||
{ name: 'iget-bar', title: 'iget bar', titleCn: '', shortName: 'IG' },
|
|
||||||
{ name: 'twelve-monkeys', title: 'twelve monkeys', titleCn: '', shortName: 'TW' },
|
|
||||||
{ name: 'z-pods', title: 'z pods', titleCn: '', shortName: 'ZP' },
|
|
||||||
{ name: 'yoone-y-pods', title: 'yoone y-pods', titleCn: '', shortName: 'YO' },
|
|
||||||
{ name: 'allo-e-liquid', title: 'allo e-liquid', titleCn: '', shortName: 'AL' },
|
|
||||||
{ name: 'allo-ultra', title: 'allo ultra', titleCn: '', shortName: 'AL' },
|
|
||||||
{ name: 'base-x', title: 'base x', titleCn: '', shortName: 'BA' },
|
|
||||||
{ name: 'breeze-pro', title: 'breeze pro', titleCn: '', shortName: 'BR' },
|
|
||||||
{ name: 'deu', title: 'deu', titleCn: '', shortName: 'DE' },
|
|
||||||
{ name: 'evo', title: 'evo', titleCn: '', shortName: 'EV' },
|
|
||||||
{ name: 'elf-bar', title: 'elf bar', titleCn: '', shortName: 'EL' },
|
|
||||||
{ name: 'feed', title: 'feed', titleCn: '', shortName: 'FE' },
|
|
||||||
{ name: 'flavour-beast', title: 'flavour beast', titleCn: '', shortName: 'FL' },
|
|
||||||
{ name: 'fog-formulas', title: 'fog formulas', titleCn: '', shortName: 'FO' },
|
|
||||||
{ name: 'fruitii', title: 'fruitii', titleCn: '', shortName: 'FR' },
|
|
||||||
{ name: 'gcore', title: 'gcore', titleCn: '', shortName: 'GC' },
|
|
||||||
{ name: 'gr1nds', title: 'gr1nds', titleCn: '', shortName: 'GR' },
|
|
||||||
{ name: 'hqd', title: 'hqd', titleCn: '', shortName: 'HQ' },
|
|
||||||
{ name: 'illusions', title: 'illusions', titleCn: '', shortName: 'IL' },
|
|
||||||
{ name: 'kraze', title: 'kraze', titleCn: '', shortName: 'KR' },
|
|
||||||
{ name: 'level-x', title: 'level x', titleCn: '', shortName: 'LE' },
|
|
||||||
{ name: 'lfgo-energy', title: 'lfgo energy', titleCn: '', shortName: 'LF' },
|
|
||||||
{ name: 'lost-mary', title: 'lost mary', titleCn: '', shortName: 'LO' },
|
|
||||||
{ name: 'mr-fog', title: 'mr fog', titleCn: '', shortName: 'MR' },
|
|
||||||
{ name: 'nicorette', title: 'nicorette', titleCn: '', shortName: 'NI' },
|
|
||||||
{ name: 'oxbar', title: 'oxbar', titleCn: '', shortName: 'OX' },
|
|
||||||
{ name: 'rabeats', title: 'rabeats', titleCn: '', shortName: 'RA' },
|
|
||||||
{ name: 'yoone-vapengin', title: 'yoone vapengin', titleCn: '', shortName: 'YO' },
|
|
||||||
{ name: 'sesh', title: 'sesh', titleCn: '', shortName: 'SE' },
|
|
||||||
{ name: 'spin', title: 'spin', titleCn: '', shortName: 'SP' },
|
|
||||||
{ name: 'stlth', title: 'stlth', titleCn: '', shortName: 'ST' },
|
|
||||||
{ name: 'tornado', title: 'tornado', titleCn: '', shortName: 'TO' },
|
|
||||||
{ name: 'uwell', title: 'uwell', titleCn: '', shortName: 'UW' },
|
|
||||||
{ name: 'vanza', title: 'vanza', titleCn: '', shortName: 'VA' },
|
|
||||||
{ name: 'vapgo', title: 'vapgo', titleCn: '', shortName: 'VA' },
|
|
||||||
{ name: 'vase', title: 'vase', titleCn: '', shortName: 'VA' },
|
|
||||||
{ name: 'vice-boost', title: 'vice boost', titleCn: '', shortName: 'VI' },
|
|
||||||
{ name: 'vozol-star', title: 'vozol star', titleCn: '', shortName: 'VO' },
|
|
||||||
{ name: 'zpods', title: 'zpods', titleCn: '', shortName: 'ZP' },
|
|
||||||
];
|
|
||||||
|
|
||||||
// Total brands: 62
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
import { Seeder, SeederFactoryManager } from 'typeorm-extension';
|
|
||||||
import { DataSource } from 'typeorm';
|
|
||||||
import { Template } from '../../entity/template.entity';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @class TemplateSeeder
|
|
||||||
* @description 模板数据填充器,用于在数据库初始化时插入默认的模板数据.
|
|
||||||
*/
|
|
||||||
export default class TemplateSeeder implements Seeder {
|
|
||||||
/**
|
|
||||||
* @method run
|
|
||||||
* @description 执行数据填充操作.如果模板不存在,则创建它;如果存在,则更新它.
|
|
||||||
* @param {DataSource} dataSource - 数据源实例,用于获取 repository.
|
|
||||||
* @param {SeederFactoryManager} factoryManager - Seeder 工厂管理器.
|
|
||||||
*/
|
|
||||||
public async run(
|
|
||||||
dataSource: DataSource,
|
|
||||||
factoryManager: SeederFactoryManager
|
|
||||||
): Promise<any> {
|
|
||||||
// 获取 Template 实体的 repository
|
|
||||||
const templateRepository = dataSource.getRepository(Template);
|
|
||||||
|
|
||||||
const templates = [
|
|
||||||
{
|
|
||||||
name: 'product.sku',
|
|
||||||
value: `<%
|
|
||||||
// 按分类判断属性排序逻辑
|
|
||||||
if (it.category.name === 'nicotine-pouches') {
|
|
||||||
// 1. 定义 nicotine-pouches 专属的属性固定顺序
|
|
||||||
const fixedOrder = ['brand','category', 'flavor', 'strength', 'humidity'];
|
|
||||||
sortedAttrShortNames = fixedOrder.map(attrKey => {
|
|
||||||
if(attrKey === 'category') return it.category.shortName
|
|
||||||
// 排序
|
|
||||||
const matchedAttr = it.attributes.find(a => a?.dict?.name === attrKey);
|
|
||||||
return matchedAttr ? matchedAttr.shortName : '';
|
|
||||||
}).filter(Boolean); // 移除空值,避免多余的 "-"
|
|
||||||
} else {
|
|
||||||
// 非目标分类,保留 attributes 原有顺序
|
|
||||||
sortedAttrShortNames = it.attributes.map(a => a.shortName);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. 拼接分类名 + 排序后的属性名
|
|
||||||
%><%= sortedAttrShortNames.join('-') %><%
|
|
||||||
%>`,
|
|
||||||
description: '产品SKU模板',
|
|
||||||
testData: JSON.stringify({
|
|
||||||
"category": {
|
|
||||||
"name": "nicotine-pouches",
|
|
||||||
"shortName": "NP"
|
|
||||||
},
|
|
||||||
"attributes": [
|
|
||||||
{ "dict": {"name": "brand"},"shortName": "YOONE" },
|
|
||||||
{ "dict": {"name": "flavor"},"shortName": "FL" },
|
|
||||||
{ "dict": {"name": "strength"},"shortName": "10MG" },
|
|
||||||
{ "dict": {"name": "humidity"},"shortName": "DRY" }
|
|
||||||
]
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'product.title',
|
|
||||||
value: "<%= it.attributes.map(a => a.title).join(' ') %>",
|
|
||||||
description: '产品标题模板',
|
|
||||||
testData: JSON.stringify({
|
|
||||||
attributes: [
|
|
||||||
{ title: 'Brand' },
|
|
||||||
{ title: 'Flavor' },
|
|
||||||
{ title: '10mg' },
|
|
||||||
{ title: 'Dry' },
|
|
||||||
],
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'site.product.sku',
|
|
||||||
value: '<%= it.site.skuPrefix %><%= it.product.sku %>',
|
|
||||||
description: '站点产品SKU模板',
|
|
||||||
testData: JSON.stringify({
|
|
||||||
site: {
|
|
||||||
skuPrefix: 'SITE-',
|
|
||||||
},
|
|
||||||
product: {
|
|
||||||
sku: 'PRODUCT-SKU-001',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const t of templates) {
|
|
||||||
// 检查模板是否已存在
|
|
||||||
const existingTemplate = await templateRepository.findOne({
|
|
||||||
where: { name: t.name },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (existingTemplate) {
|
|
||||||
// 如果存在,则更新
|
|
||||||
existingTemplate.value = t.value;
|
|
||||||
existingTemplate.description = t.description;
|
|
||||||
existingTemplate.testData = t.testData;
|
|
||||||
await templateRepository.save(existingTemplate);
|
|
||||||
} else {
|
|
||||||
// 如果不存在,则创建并保存
|
|
||||||
const template = new Template();
|
|
||||||
template.name = t.name;
|
|
||||||
template.value = t.value;
|
|
||||||
template.description = t.description;
|
|
||||||
template.testData = t.testData;
|
|
||||||
await templateRepository.save(template);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,223 +0,0 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { Rule, RuleType } from '@midwayjs/validate';
|
|
||||||
|
|
||||||
export class UnifiedPaginationDTO<T> {
|
|
||||||
// 分页DTO用于承载统一分页信息与列表数据
|
|
||||||
@ApiProperty({ description: '列表数据' })
|
|
||||||
items: T[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '总数', example: 100 })
|
|
||||||
total: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '当前页', example: 1 })
|
|
||||||
page: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '每页数量', example: 20 })
|
|
||||||
per_page: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '总页数', example: 5 })
|
|
||||||
totalPages: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class UnifiedSearchParamsDTO<Where=Record<string, any>> {
|
|
||||||
// 统一查询参数DTO用于承载分页与筛选与排序参数
|
|
||||||
@ApiProperty({ description: '页码', example: 1, required: false })
|
|
||||||
page?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '每页数量', example: 20, required: false })
|
|
||||||
per_page?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '查询时间范围开始', example: '2023-01-01T00:00:00Z', required: false })
|
|
||||||
after?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '查询时间范围结束', example: '2023-01-01T23:59:59Z', required: false })
|
|
||||||
before?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '搜索关键词', required: false })
|
|
||||||
search?: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
description: '过滤条件对象',
|
|
||||||
type: 'any', // FIXME 这里是因为 openapit2ts 会将 where 变成 undefined 所以有嵌套对象时先不指定类型
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
where?: Where;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
description: '排序对象,例如 { "sku": "desc" }',
|
|
||||||
type: 'any', // FIXME 这里是因为 openapit2ts 会将 where 变成 undefined 所以有嵌套对象时先不指定类型
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
orderBy?: Record<string, 'asc' | 'desc'> | string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
description: '分组字段,例如 "categoryId"',
|
|
||||||
type: 'string',
|
|
||||||
required: false,
|
|
||||||
})
|
|
||||||
groupBy?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shopyy获取所有订单参数DTO
|
|
||||||
*/
|
|
||||||
export class ShopyyGetAllOrdersParams {
|
|
||||||
@ApiProperty({ description: '每页数量', example: 100, required: false })
|
|
||||||
per_page?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '支付时间范围开始', example: '2023-01-01T00:00:00Z', required: false })
|
|
||||||
pay_at_min?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '支付时间范围结束', example: '2023-01-01T23:59:59Z', required: false })
|
|
||||||
pay_at_max?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '排序字段', example: 'id', required: false })
|
|
||||||
order_field?: string;//排序字段(默认id) id=订单ID updated_at=最后更新时间 pay_at=支付时间
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量操作错误项
|
|
||||||
*/
|
|
||||||
export interface BatchErrorItem {
|
|
||||||
// 错误项标识(可以是ID、邮箱等)
|
|
||||||
identifier: string;
|
|
||||||
// 错误信息
|
|
||||||
error: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量操作结果基础接口
|
|
||||||
*/
|
|
||||||
export interface BatchOperationResult {
|
|
||||||
// 总处理数量
|
|
||||||
total: number;
|
|
||||||
// 成功处理数量
|
|
||||||
processed: number;
|
|
||||||
// 创建数量
|
|
||||||
created?: number;
|
|
||||||
// 更新数量
|
|
||||||
updated?: number;
|
|
||||||
// 删除数量
|
|
||||||
deleted?: number;
|
|
||||||
// 跳过的数量(如数据已存在或无需处理)
|
|
||||||
skipped?: number;
|
|
||||||
// 错误列表
|
|
||||||
errors: BatchErrorItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步操作结果接口
|
|
||||||
*/
|
|
||||||
export class SyncOperationResult implements BatchOperationResult {
|
|
||||||
total: number;
|
|
||||||
processed: number;
|
|
||||||
created?: number;
|
|
||||||
updated?: number;
|
|
||||||
deleted?: number;
|
|
||||||
skipped?: number;
|
|
||||||
errors: BatchErrorItem[];
|
|
||||||
// 同步成功数量
|
|
||||||
synced: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量操作错误项DTO
|
|
||||||
*/
|
|
||||||
export class BatchErrorItemDTO {
|
|
||||||
@ApiProperty({ description: '错误项标识(如ID、邮箱等)', type: String })
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
identifier: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '错误信息', type: String })
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
error: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量操作结果基础DTO
|
|
||||||
*/
|
|
||||||
export class BatchOperationResultDTO {
|
|
||||||
@ApiProperty({ description: '总处理数量', type: Number })
|
|
||||||
total: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '成功处理数量', type: Number })
|
|
||||||
processed: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '创建数量', type: Number, required: false })
|
|
||||||
created?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '更新数量', type: Number, required: false })
|
|
||||||
updated?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '删除数量', type: Number, required: false })
|
|
||||||
deleted?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '跳过的数量', type: Number, required: false })
|
|
||||||
skipped?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '错误列表', type: [BatchErrorItemDTO] })
|
|
||||||
errors: BatchErrorItemDTO[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步操作结果DTO
|
|
||||||
*/
|
|
||||||
export class SyncOperationResultDTO extends BatchOperationResultDTO {
|
|
||||||
@ApiProperty({ description: '同步成功数量', type: Number })
|
|
||||||
synced: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步参数DTO
|
|
||||||
*/
|
|
||||||
export class SyncParamsDTO {
|
|
||||||
@ApiProperty({ description: '页码', type: Number, required: false, default: 1 })
|
|
||||||
@Rule(RuleType.number().integer().min(1).optional())
|
|
||||||
page?: number = 1;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '每页数量', type: Number, required: false, default: 100 })
|
|
||||||
@Rule(RuleType.number().integer().min(1).max(1000).optional())
|
|
||||||
pageSize?: number = 100;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '开始时间', type: String, required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
startDate?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '结束时间', type: String, required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
endDate?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '强制同步(忽略缓存)', type: Boolean, required: false, default: false })
|
|
||||||
@Rule(RuleType.boolean().optional())
|
|
||||||
force?: boolean = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量查询DTO
|
|
||||||
*/
|
|
||||||
export class BatchQueryDTO {
|
|
||||||
@ApiProperty({ description: 'ID列表', type: [String, Number] })
|
|
||||||
@Rule(RuleType.array().items(RuleType.alternatives().try(RuleType.string(), RuleType.number())).required())
|
|
||||||
ids: Array<string | number>;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '包含关联数据', type: Boolean, required: false, default: false })
|
|
||||||
@Rule(RuleType.boolean().optional())
|
|
||||||
includeRelations?: boolean = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量操作结果类(泛型支持)
|
|
||||||
*/
|
|
||||||
export class BatchOperationResultDTOGeneric<T> extends BatchOperationResultDTO {
|
|
||||||
@ApiProperty({ description: '操作成功的数据列表', type: Array })
|
|
||||||
data?: T[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步操作结果类(泛型支持)
|
|
||||||
*/
|
|
||||||
export class SyncOperationResultDTOGeneric<T> extends SyncOperationResultDTO {
|
|
||||||
@ApiProperty({ description: '同步成功的数据列表', type: Array })
|
|
||||||
data?: T[];
|
|
||||||
}
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
||||||
|
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { Rule, RuleType } from '@midwayjs/validate';
|
|
||||||
|
|
||||||
export class CreateAreaDTO {
|
|
||||||
@ApiProperty({ description: '编码' })
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
code: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UpdateAreaDTO {
|
|
||||||
@ApiProperty({ description: '编码', required: false })
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
code?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class QueryAreaDTO {
|
|
||||||
@ApiProperty({ description: '当前页', required: false, default: 1 })
|
|
||||||
@Rule(RuleType.number().integer().min(1).default(1))
|
|
||||||
currentPage?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '每页数量', required: false, default: 10 })
|
|
||||||
@Rule(RuleType.number().integer().min(1).default(10))
|
|
||||||
pageSize?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '关键词(名称或编码)', required: false })
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
keyword?: string;
|
|
||||||
}
|
|
||||||
|
|
@ -1,210 +0,0 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { Rule, RuleType } from '@midwayjs/validate';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量操作错误项
|
|
||||||
*/
|
|
||||||
export interface BatchErrorItem {
|
|
||||||
// 错误项标识(可以是ID、邮箱等)
|
|
||||||
identifier: string;
|
|
||||||
// 错误信息
|
|
||||||
error: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量操作结果基础接口
|
|
||||||
*/
|
|
||||||
export interface BatchOperationResult {
|
|
||||||
// 总处理数量
|
|
||||||
total: number;
|
|
||||||
// 成功处理数量
|
|
||||||
processed: number;
|
|
||||||
// 创建数量
|
|
||||||
created?: number;
|
|
||||||
// 更新数量
|
|
||||||
updated?: number;
|
|
||||||
// 删除数量
|
|
||||||
deleted?: number;
|
|
||||||
// 跳过的数量(如数据已存在或无需处理)
|
|
||||||
skipped?: number;
|
|
||||||
// 错误列表
|
|
||||||
errors: BatchErrorItem[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步操作结果接口
|
|
||||||
*/
|
|
||||||
export interface SyncOperationResult extends BatchOperationResult {
|
|
||||||
// 同步成功数量
|
|
||||||
synced: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量操作错误项DTO
|
|
||||||
*/
|
|
||||||
export class BatchErrorItemDTO {
|
|
||||||
@ApiProperty({ description: '错误项标识(如ID、邮箱等)', type: String })
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
identifier: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '错误信息', type: String })
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
error: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量操作结果基础DTO
|
|
||||||
*/
|
|
||||||
export class BatchOperationResultDTO {
|
|
||||||
@ApiProperty({ description: '总处理数量', type: Number })
|
|
||||||
total: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '成功处理数量', type: Number })
|
|
||||||
processed: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '创建数量', type: Number, required: false })
|
|
||||||
created?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '更新数量', type: Number, required: false })
|
|
||||||
updated?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '删除数量', type: Number, required: false })
|
|
||||||
deleted?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '跳过的数量', type: Number, required: false })
|
|
||||||
skipped?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '错误列表', type: [BatchErrorItemDTO] })
|
|
||||||
errors: BatchErrorItemDTO[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步操作结果DTO
|
|
||||||
*/
|
|
||||||
export class SyncOperationResultDTO extends BatchOperationResultDTO {
|
|
||||||
@ApiProperty({ description: '同步成功数量', type: Number })
|
|
||||||
synced: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量创建DTO
|
|
||||||
*/
|
|
||||||
export class BatchCreateDTO<T = any> {
|
|
||||||
@ApiProperty({ description: '要创建的数据列表', type: Array })
|
|
||||||
@Rule(RuleType.array().required())
|
|
||||||
items: T[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量更新DTO
|
|
||||||
*/
|
|
||||||
export class BatchUpdateDTO<T = any> {
|
|
||||||
@ApiProperty({ description: '要更新的数据列表', type: Array })
|
|
||||||
@Rule(RuleType.array().required())
|
|
||||||
items: T[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量删除DTO
|
|
||||||
*/
|
|
||||||
export class BatchDeleteDTO {
|
|
||||||
@ApiProperty({ description: '要删除的ID列表', type: [String, Number] })
|
|
||||||
@Rule(RuleType.array().items(RuleType.alternatives().try(RuleType.string(), RuleType.number())).required())
|
|
||||||
ids: Array<string | number>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量操作请求DTO(包含增删改)
|
|
||||||
*/
|
|
||||||
export class BatchOperationDTO<T = any> {
|
|
||||||
@ApiProperty({ description: '要创建的数据列表', type: Array, required: false })
|
|
||||||
@Rule(RuleType.array().optional())
|
|
||||||
create?: T[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '要更新的数据列表', type: Array, required: false })
|
|
||||||
@Rule(RuleType.array().optional())
|
|
||||||
update?: T[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '要删除的ID列表', type: [String, Number], required: false })
|
|
||||||
@Rule(RuleType.array().items(RuleType.alternatives().try(RuleType.string(), RuleType.number())).optional())
|
|
||||||
delete?: Array<string | number>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 分页批量操作DTO
|
|
||||||
*/
|
|
||||||
export class PaginatedBatchOperationDTO<T = any> {
|
|
||||||
@ApiProperty({ description: '页码', type: Number, required: false, default: 1 })
|
|
||||||
@Rule(RuleType.number().integer().min(1).optional())
|
|
||||||
page?: number = 1;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '每页数量', type: Number, required: false, default: 100 })
|
|
||||||
@Rule(RuleType.number().integer().min(1).max(1000).optional())
|
|
||||||
pageSize?: number = 100;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '要创建的数据列表', type: Array, required: false })
|
|
||||||
@Rule(RuleType.array().optional())
|
|
||||||
create?: T[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '要更新的数据列表', type: Array, required: false })
|
|
||||||
@Rule(RuleType.array().optional())
|
|
||||||
update?: T[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '要删除的ID列表', type: [String, Number], required: false })
|
|
||||||
@Rule(RuleType.array().items(RuleType.alternatives().try(RuleType.string(), RuleType.number())).optional())
|
|
||||||
delete?: Array<string | number>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步参数DTO
|
|
||||||
*/
|
|
||||||
export class SyncParamsDTO {
|
|
||||||
@ApiProperty({ description: '页码', type: Number, required: false, default: 1 })
|
|
||||||
@Rule(RuleType.number().integer().min(1).optional())
|
|
||||||
page?: number = 1;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '每页数量', type: Number, required: false, default: 100 })
|
|
||||||
@Rule(RuleType.number().integer().min(1).max(1000).optional())
|
|
||||||
pageSize?: number = 100;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '开始时间', type: String, required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
startDate?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '结束时间', type: String, required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
endDate?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '强制同步(忽略缓存)', type: Boolean, required: false, default: false })
|
|
||||||
@Rule(RuleType.boolean().optional())
|
|
||||||
force?: boolean = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量查询DTO
|
|
||||||
*/
|
|
||||||
export class BatchQueryDTO {
|
|
||||||
@ApiProperty({ description: 'ID列表', type: [String, Number] })
|
|
||||||
@Rule(RuleType.array().items(RuleType.alternatives().try(RuleType.string(), RuleType.number())).required())
|
|
||||||
ids: Array<string | number>;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '包含关联数据', type: Boolean, required: false, default: false })
|
|
||||||
@Rule(RuleType.boolean().optional())
|
|
||||||
includeRelations?: boolean = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量操作结果类(泛型支持)
|
|
||||||
*/
|
|
||||||
export class BatchOperationResultDTOGeneric<T> extends BatchOperationResultDTO {
|
|
||||||
@ApiProperty({ description: '操作成功的数据列表', type: Array })
|
|
||||||
data?: T[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步操作结果类(泛型支持)
|
|
||||||
*/
|
|
||||||
export class SyncOperationResultDTOGeneric<T> extends SyncOperationResultDTO {
|
|
||||||
@ApiProperty({ description: '同步成功的数据列表', type: Array })
|
|
||||||
data?: T[];
|
|
||||||
}
|
|
||||||
|
|
@ -1,364 +1,38 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
import { UnifiedSearchParamsDTO } from './api.dto';
|
|
||||||
import { Customer } from '../entity/customer.entity';
|
|
||||||
|
|
||||||
// 客户基本信息DTO(用于响应)
|
export class QueryCustomerListDTO {
|
||||||
export class CustomerDTO extends Customer{
|
@ApiProperty()
|
||||||
@ApiProperty({ description: '客户ID' })
|
current: string;
|
||||||
id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点ID', required: false })
|
@ApiProperty()
|
||||||
site_id: number;
|
pageSize: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '原始ID', required: false })
|
@ApiProperty()
|
||||||
origin_id: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点创建时间', required: false })
|
|
||||||
site_created_at: Date;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点更新时间', required: false })
|
|
||||||
site_updated_at: Date;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '邮箱' })
|
|
||||||
email: string;
|
email: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '名字', required: false })
|
@ApiProperty()
|
||||||
first_name: string;
|
tags: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '姓氏', required: false })
|
@ApiProperty()
|
||||||
last_name: string;
|
sorterKey: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '全名', required: false })
|
@ApiProperty()
|
||||||
fullname: string;
|
sorterValue: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '用户名', required: false })
|
@ApiProperty()
|
||||||
username: string;
|
state: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '电话', required: false })
|
@ApiProperty()
|
||||||
phone: string;
|
first_purchase_date: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '头像URL', required: false })
|
@ApiProperty()
|
||||||
avatar: string;
|
customerId: number;
|
||||||
|
|
||||||
@ApiProperty({ description: '账单信息', type: 'object', required: false })
|
|
||||||
billing: any;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '配送信息', type: 'object', required: false })
|
|
||||||
shipping: any;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '原始数据', type: 'object', required: false })
|
|
||||||
raw: any;
|
|
||||||
|
|
||||||
|
|
||||||
@ApiProperty({ description: '创建时间' })
|
|
||||||
created_at: Date;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '更新时间' })
|
|
||||||
updated_at: Date;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ApiProperty({ description: '评分' })
|
|
||||||
rate: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '标签列表', type: [String], required: false })
|
|
||||||
tags: string[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ====================== 单条操作 ======================
|
export class CustomerTagDTO {
|
||||||
|
@ApiProperty()
|
||||||
// 创建客户请求DTO
|
|
||||||
export class CreateCustomerDTO {
|
|
||||||
@ApiProperty({ description: '站点ID' })
|
|
||||||
site_id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '原始ID', required: false })
|
|
||||||
origin_id?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '邮箱' })
|
|
||||||
email: string;
|
email: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '名字', required: false })
|
@ApiProperty()
|
||||||
first_name?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '姓氏', required: false })
|
|
||||||
last_name?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '全名', required: false })
|
|
||||||
fullname?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '用户名', required: false })
|
|
||||||
username?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '电话', required: false })
|
|
||||||
phone?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '头像URL', required: false })
|
|
||||||
avatar?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '账单信息', type: 'object', required: false })
|
|
||||||
billing?: any;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '配送信息', type: 'object', required: false })
|
|
||||||
shipping?: any;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '原始数据', type: 'object', required: false })
|
|
||||||
raw?: any;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '评分', required: false })
|
|
||||||
rate?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '标签列表', type: [String], required: false })
|
|
||||||
tags?: string[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点创建时间', required: false })
|
|
||||||
site_created_at?: Date;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点更新时间', required: false })
|
|
||||||
site_updated_at?: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新客户请求DTO
|
|
||||||
export class UpdateCustomerDTO {
|
|
||||||
@ApiProperty({ description: '站点ID', required: false })
|
|
||||||
site_id?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '原始ID', required: false })
|
|
||||||
origin_id?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '邮箱', required: false })
|
|
||||||
email?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '名字', required: false })
|
|
||||||
first_name?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '姓氏', required: false })
|
|
||||||
last_name?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '全名', required: false })
|
|
||||||
fullname?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '用户名', required: false })
|
|
||||||
username?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '电话', required: false })
|
|
||||||
phone?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '头像URL', required: false })
|
|
||||||
avatar?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '账单信息', type: 'object', required: false })
|
|
||||||
billing?: any;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '配送信息', type: 'object', required: false })
|
|
||||||
shipping?: any;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '原始数据', type: 'object', required: false })
|
|
||||||
raw?: any;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '评分', required: false })
|
|
||||||
rate?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '标签列表', type: [String], required: false })
|
|
||||||
tags?: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 查询单个客户响应DTO(继承基本信息)
|
|
||||||
export class GetCustomerDTO extends CustomerDTO {
|
|
||||||
// 可以添加额外的详细信息字段
|
|
||||||
}
|
|
||||||
// 客户统计信息DTO(包含订单统计)
|
|
||||||
export class CustomerStatisticDTO extends CustomerDTO {
|
|
||||||
@ApiProperty({ description: '创建日期' })
|
|
||||||
date_created: Date;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '首次购买日期' })
|
|
||||||
first_purchase_date: Date;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '最后购买日期' })
|
|
||||||
last_purchase_date: Date;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '订单数量' })
|
|
||||||
orders: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '总消费金额' })
|
|
||||||
total: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: 'Yoone订单数量', required: false })
|
|
||||||
yoone_orders?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: 'Yoone总金额', required: false })
|
|
||||||
yoone_total?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 客户统计查询条件DTO
|
|
||||||
export class CustomerStatisticWhereDTO {
|
|
||||||
@ApiProperty({ description: '邮箱筛选', required: false })
|
|
||||||
email?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '标签筛选', required: false })
|
|
||||||
tags?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '首次购买日期筛选', required: false })
|
|
||||||
first_purchase_date?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '评分筛选', required: false })
|
|
||||||
rate?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '客户ID筛选', required: false })
|
|
||||||
customerId?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 客户统计查询参数DTO(继承通用查询参数)
|
|
||||||
export type CustomerStatisticQueryParamsDTO = UnifiedSearchParamsDTO<CustomerStatisticWhereDTO>;
|
|
||||||
|
|
||||||
// 客户统计列表响应DTO
|
|
||||||
export class CustomerStatisticListResponseDTO {
|
|
||||||
@ApiProperty({ description: '客户统计列表', type: [CustomerStatisticDTO] })
|
|
||||||
items: CustomerStatisticDTO[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '总数', example: 100 })
|
|
||||||
total: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '当前页', example: 1 })
|
|
||||||
current: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '每页数量', example: 20 })
|
|
||||||
pageSize: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ====================== 批量操作 ======================
|
|
||||||
|
|
||||||
// 批量创建客户请求DTO
|
|
||||||
export class BatchCreateCustomerDTO {
|
|
||||||
@ApiProperty({ description: '客户列表', type: [CreateCustomerDTO] })
|
|
||||||
customers: CreateCustomerDTO[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 单个客户更新项DTO
|
|
||||||
export class UpdateCustomerItemDTO {
|
|
||||||
@ApiProperty({ description: '客户ID' })
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '更新字段', type: UpdateCustomerDTO })
|
|
||||||
update_data: Partial<Customer>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量更新客户请求DTO - 每个对象包含id和要更新的字段
|
|
||||||
export class BatchUpdateCustomerDTO {
|
|
||||||
@ApiProperty({ description: '客户更新列表', type: [UpdateCustomerItemDTO] })
|
|
||||||
customers: UpdateCustomerItemDTO[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量删除客户请求DTO
|
|
||||||
export class BatchDeleteCustomerDTO {
|
|
||||||
@ApiProperty({ description: '客户ID列表', type: [Number] })
|
|
||||||
ids: number[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== 查询操作 ======================
|
|
||||||
|
|
||||||
// 客户查询条件DTO(用于UnifiedSearchParamsDTO的where参数)
|
|
||||||
export class CustomerWhereDTO {
|
|
||||||
@ApiProperty({ description: '邮箱筛选', required: false })
|
|
||||||
email?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '标签筛选', required: false })
|
|
||||||
tags?: string;
|
|
||||||
|
|
||||||
|
|
||||||
@ApiProperty({ description: '评分筛选', required: false })
|
|
||||||
rate?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点ID筛选', required: false })
|
|
||||||
site_id?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '客户ID筛选', required: false })
|
|
||||||
customerId?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '首次购买日期筛选', required: false })
|
|
||||||
first_purchase_date?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '角色筛选', required: false })
|
|
||||||
role?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 客户查询参数DTO(继承通用查询参数)
|
|
||||||
export type CustomerQueryParamsDTO = UnifiedSearchParamsDTO<CustomerWhereDTO>;
|
|
||||||
|
|
||||||
// 客户列表响应DTO(参考site-api.dto.ts中的分页格式)
|
|
||||||
export class CustomerListResponseDTO {
|
|
||||||
@ApiProperty({ description: '客户列表', type: [CustomerDTO] })
|
|
||||||
items: CustomerDTO[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '总数', example: 100 })
|
|
||||||
total: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '页码', example: 1 })
|
|
||||||
page: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '每页数量', example: 20 })
|
|
||||||
per_page: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '总页数', example: 5 })
|
|
||||||
total_pages: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== 客户标签相关 ======================
|
|
||||||
|
|
||||||
// 客户标签基本信息DTO
|
|
||||||
export class CustomerTagBasicDTO {
|
|
||||||
@ApiProperty({ description: '标签ID' })
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '客户ID' })
|
|
||||||
customer_id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '标签名称' })
|
|
||||||
tag: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '创建时间', required: false })
|
|
||||||
created_at?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加客户标签请求DTO
|
|
||||||
export class AddCustomerTagDTO {
|
|
||||||
@ApiProperty({ description: '客户ID' })
|
|
||||||
customer_id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '标签名称' })
|
|
||||||
tag: string;
|
tag: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量添加客户标签请求DTO
|
|
||||||
export class BatchAddCustomerTagDTO {
|
|
||||||
@ApiProperty({ description: '客户ID' })
|
|
||||||
customer_id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '标签列表', type: [String] })
|
|
||||||
tags: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除客户标签请求DTO
|
|
||||||
export class DeleteCustomerTagDTO {
|
|
||||||
@ApiProperty({ description: '标签ID' })
|
|
||||||
tag_id: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 批量删除客户标签请求DTO
|
|
||||||
export class BatchDeleteCustomerTagDTO {
|
|
||||||
@ApiProperty({ description: '标签ID列表', type: [Number] })
|
|
||||||
tag_ids: number[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// ====================== 同步操作 ======================
|
|
||||||
|
|
||||||
// 同步客户数据请求DTO
|
|
||||||
export class SyncCustomersDTO {
|
|
||||||
@ApiProperty({ description: '站点ID' })
|
|
||||||
siteId: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '查询参数(支持where和orderBy)', type: UnifiedSearchParamsDTO, required: false })
|
|
||||||
params?: UnifiedSearchParamsDTO<CustomerWhereDTO>;
|
|
||||||
}
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
import { Rule, RuleType } from '@midwayjs/validate';
|
|
||||||
|
|
||||||
// 创建字典的数据传输对象
|
|
||||||
export class CreateDictDTO {
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
name: string; // 字典名称
|
|
||||||
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
title: string; // 字典标题
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新字典的数据传输对象
|
|
||||||
export class UpdateDictDTO {
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
name?: string; // 字典名称 (可选)
|
|
||||||
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
title?: string; // 字典标题 (可选)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建字典项的数据传输对象
|
|
||||||
export class CreateDictItemDTO {
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
name: string; // 字典项名称
|
|
||||||
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
title: string; // 字典项标题
|
|
||||||
|
|
||||||
@Rule(RuleType.string().allow('').allow(null))
|
|
||||||
titleCN?: string; // 字典项中文标题 (可选)
|
|
||||||
|
|
||||||
@Rule(RuleType.string().allow('').allow(null))
|
|
||||||
image?: string; // 图片 (可选)
|
|
||||||
|
|
||||||
@Rule(RuleType.string().allow('').allow(null))
|
|
||||||
shortName?: string; // 简称 (可选)
|
|
||||||
|
|
||||||
@Rule(RuleType.number().required())
|
|
||||||
dictId: number; // 所属字典的ID
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新字典项的数据传输对象
|
|
||||||
export class UpdateDictItemDTO {
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
name?: string; // 字典项名称 (可选)
|
|
||||||
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
title?: string; // 字典项标题 (可选)
|
|
||||||
|
|
||||||
@Rule(RuleType.string().allow('').allow(null))
|
|
||||||
titleCN?: string; // 字典项中文标题 (可选)
|
|
||||||
|
|
||||||
@Rule(RuleType.string().allow(null))
|
|
||||||
value?: string; // 字典项值 (可选)
|
|
||||||
|
|
||||||
@Rule(RuleType.string().allow('').allow(null))
|
|
||||||
image?: string; // 图片 (可选)
|
|
||||||
|
|
||||||
@Rule(RuleType.string().allow('').allow(null))
|
|
||||||
shortName?: string; // 简称 (可选)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -8,7 +8,7 @@ export type PackagingType =
|
||||||
// | PackagingCourierPak
|
// | PackagingCourierPak
|
||||||
// | PackagingEnvelope;
|
// | PackagingEnvelope;
|
||||||
|
|
||||||
// 定义包装类型的枚举,用于 API 文档描述
|
// 定义包装类型的枚举,用于 API 文档描述
|
||||||
export enum PackagingTypeEnum {
|
export enum PackagingTypeEnum {
|
||||||
Pallet = 'pallet',
|
Pallet = 'pallet',
|
||||||
Package = 'package',
|
Package = 'package',
|
||||||
|
|
|
||||||
|
|
@ -19,22 +19,15 @@ export class ShipmentBookDTO {
|
||||||
@ApiProperty({ type: 'number', isArray: true })
|
@ApiProperty({ type: 'number', isArray: true })
|
||||||
@Rule(RuleType.array<number>().default([]))
|
@Rule(RuleType.array<number>().default([]))
|
||||||
orderIds?: number[];
|
orderIds?: number[];
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
shipmentPlatform: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ShipmentFeeBookDTO {
|
export class ShipmentFeeBookDTO {
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
shipmentPlatform: string;
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
stockPointId: number;
|
stockPointId: number;
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
sender: string;
|
sender: string;
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
startPhone: string|any;
|
startPhone: string;
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
startPostalCode: string;
|
startPostalCode: string;
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
|
|
@ -70,8 +63,6 @@ export class ShipmentFeeBookDTO {
|
||||||
weight: number;
|
weight: number;
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
weightUom: string;
|
weightUom: string;
|
||||||
@ApiProperty()
|
|
||||||
address_id: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class PaymentMethodDTO {
|
export class PaymentMethodDTO {
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,8 @@ export class QueryOrderDTO {
|
||||||
externalOrderId: string;
|
externalOrderId: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.number())
|
@Rule(RuleType.string())
|
||||||
siteId: number;
|
siteId: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.string().allow(''))
|
@Rule(RuleType.string().allow(''))
|
||||||
|
|
@ -92,15 +92,19 @@ export class QueryOrderDTO {
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
payment_method: string;
|
payment_method: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '仅订阅订单(父订阅订单或包含订阅商品)' })
|
@ApiProperty({ description: '仅订阅订单(父订阅订单或包含订阅商品)' })
|
||||||
@Rule(RuleType.bool().default(false))
|
@Rule(RuleType.bool().default(false))
|
||||||
isSubscriptionOnly?: boolean;
|
isSubscriptionOnly?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QueryOrderSalesDTO {
|
export class QueryOrderSalesDTO {
|
||||||
@ApiProperty({ description: '是否为原产品还是库存产品' })
|
@ApiProperty()
|
||||||
@Rule(RuleType.bool().default(false))
|
@Rule(RuleType.bool().default(false))
|
||||||
isSource: boolean;
|
isSource: boolean;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Rule(RuleType.bool().default(false))
|
||||||
|
exceptPackage: boolean;
|
||||||
|
|
||||||
@ApiProperty({ example: '1', description: '页码' })
|
@ApiProperty({ example: '1', description: '页码' })
|
||||||
@Rule(RuleType.number())
|
@Rule(RuleType.number())
|
||||||
|
|
@ -110,31 +114,19 @@ export class QueryOrderSalesDTO {
|
||||||
@Rule(RuleType.number())
|
@Rule(RuleType.number())
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
|
|
||||||
@ApiProperty({ description: '排序对象,格式如 { productName: "asc", sku: "desc" }',type: 'any', required: false })
|
@ApiProperty()
|
||||||
@Rule(RuleType.object().allow(null))
|
@Rule(RuleType.string())
|
||||||
orderBy?: Record<string, 'asc' | 'desc'>;
|
siteId: string;
|
||||||
// filter
|
|
||||||
@ApiProperty({ description: '是否排除套餐' })
|
|
||||||
@Rule(RuleType.bool().default(false))
|
|
||||||
exceptPackage: boolean;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点ID' })
|
@ApiProperty()
|
||||||
@Rule(RuleType.number())
|
|
||||||
siteId: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '名称' })
|
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@ApiProperty({ description: 'SKU' })
|
@ApiProperty()
|
||||||
@Rule(RuleType.string())
|
|
||||||
sku: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '开始日期' })
|
|
||||||
@Rule(RuleType.date())
|
@Rule(RuleType.date())
|
||||||
startDate: Date;
|
startDate: Date;
|
||||||
|
|
||||||
@ApiProperty({ description: '结束日期' })
|
@ApiProperty()
|
||||||
@Rule(RuleType.date())
|
@Rule(RuleType.date())
|
||||||
endDate: Date;
|
endDate: Date;
|
||||||
}
|
}
|
||||||
|
|
@ -164,8 +156,8 @@ export class QueryOrderItemDTO {
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.number().allow(''))
|
@Rule(RuleType.string().allow(''))
|
||||||
siteId: number;
|
siteId: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.string().allow(''))
|
@Rule(RuleType.string().allow(''))
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,5 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
import { Rule, RuleType } from '@midwayjs/validate';
|
import { Rule, RuleType } from '@midwayjs/validate';
|
||||||
import { UnifiedSearchParamsDTO } from './api.dto';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 属性输入DTO
|
|
||||||
*/
|
|
||||||
export class AttributeInputDTO {
|
|
||||||
@ApiProperty({ description: '属性字典标识', example: 'brand' })
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
dictName?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '属性值', example: 'ZYN' })
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
value?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '属性ID', example: 1 })
|
|
||||||
@Rule(RuleType.number())
|
|
||||||
id?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '属性名称', example: 'ZYN' })
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '属性显示名称', example: 'ZYN' })
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
title?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DTO 用于创建产品
|
* DTO 用于创建产品
|
||||||
|
|
@ -39,407 +13,162 @@ export class CreateProductDTO {
|
||||||
@Rule(RuleType.string().required().empty({ message: '产品名称不能为空' }))
|
@Rule(RuleType.string().required().empty({ message: '产品名称不能为空' }))
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '产品中文名称', required: false })
|
|
||||||
@Rule(RuleType.string().allow('').optional())
|
|
||||||
nameCn?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: '产品描述', description: '产品描述' })
|
@ApiProperty({ example: '产品描述', description: '产品描述' })
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
description: string;
|
description: string;
|
||||||
|
|
||||||
@ApiProperty({ example: '产品简短描述', description: '产品简短描述' })
|
@ApiProperty({ example: '1', description: '分类 ID' })
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.number())
|
||||||
shortDescription?: string;
|
categoryId: number;
|
||||||
|
|
||||||
@ApiProperty({ description: '产品 SKU', required: false })
|
@ApiProperty()
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
strengthId: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
flavorsId: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
sku?: string;
|
humidity: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '分类ID (DictItem ID)', required: false })
|
|
||||||
@Rule(RuleType.number())
|
|
||||||
categoryId?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类名称', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
categoryName?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点 SKU 列表', type: 'array', required: false })
|
|
||||||
@Rule(RuleType.array().items(RuleType.string()).optional())
|
|
||||||
siteSkus?: string[];
|
|
||||||
|
|
||||||
// 通用属性输入(通过 attributes 统一提交品牌/口味/强度/尺寸/干湿等)
|
|
||||||
// 当 type 为 'single' 时必填,当 type 为 'bundle' 时可选
|
|
||||||
@ApiProperty({ description: '属性列表', type: 'array', required: false })
|
|
||||||
@Rule(
|
|
||||||
RuleType.array()
|
|
||||||
.when('type', {
|
|
||||||
is: 'single',
|
|
||||||
then: RuleType.array().required(),
|
|
||||||
otherwise: RuleType.array().optional()
|
|
||||||
})
|
|
||||||
)
|
|
||||||
attributes?: AttributeInputDTO[];
|
|
||||||
|
|
||||||
// 商品价格
|
|
||||||
@ApiProperty({ description: '价格', example: 99.99, required: false })
|
|
||||||
@Rule(RuleType.number())
|
|
||||||
price?: number;
|
|
||||||
|
|
||||||
// 促销价格
|
|
||||||
@ApiProperty({ description: '促销价格', example: 99.99, required: false })
|
|
||||||
@Rule(RuleType.number())
|
|
||||||
promotionPrice?: number;
|
|
||||||
|
|
||||||
// 产品图片URL
|
|
||||||
@ApiProperty({ description: '产品图片URL', example: 'https://example.com/image.jpg', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
image?: string;
|
|
||||||
|
|
||||||
// 商品类型(默认 single; bundle 需手动设置组成)
|
|
||||||
@ApiProperty({ description: '商品类型', enum: ['single', 'bundle'], default: 'single', required: false })
|
|
||||||
@Rule(RuleType.string().valid('single', 'bundle').default('single'))
|
|
||||||
type?: string;
|
|
||||||
|
|
||||||
// 仅当 type 为 'bundle' 时,才需要提供 components
|
|
||||||
@ApiProperty({ description: '产品组成', type: 'array', required: false })
|
|
||||||
@Rule(
|
|
||||||
RuleType.array()
|
|
||||||
.items(
|
|
||||||
RuleType.object({
|
|
||||||
sku: RuleType.string().required(),
|
|
||||||
quantity: RuleType.number().required(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.when('type', {
|
|
||||||
is: 'bundle',
|
|
||||||
then: RuleType.array().required(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
components?: { sku: string; quantity: number }[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DTO 用于更新产品
|
* DTO 用于更新产品
|
||||||
*/
|
*/
|
||||||
export class UpdateProductDTO {
|
export class UpdateProductDTO extends CreateProductDTO {
|
||||||
@ApiProperty({ example: 'ZYN 6MG WINTERGREEN', description: '产品名称' })
|
@ApiProperty({ example: 'ZYN 6MG WINTERGREEN', description: '产品名称' })
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
name?: string;
|
name: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '产品中文名称', required: false })
|
|
||||||
@Rule(RuleType.string().allow('').optional())
|
|
||||||
nameCn?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: '产品描述', description: '产品描述' })
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: '产品简短描述', description: '产品简短描述' })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
shortDescription?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '产品 SKU', required: false })
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
sku?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类ID (DictItem ID)', required: false })
|
|
||||||
@Rule(RuleType.number())
|
|
||||||
categoryId?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类名称', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
categoryName?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点 SKU 列表', type: 'array', required: false })
|
|
||||||
@Rule(RuleType.array().items(RuleType.string()).optional())
|
|
||||||
siteSkus?: string[];
|
|
||||||
|
|
||||||
// 商品价格
|
|
||||||
@ApiProperty({ description: '价格', example: 99.99, required: false })
|
|
||||||
@Rule(RuleType.number())
|
|
||||||
price?: number;
|
|
||||||
|
|
||||||
// 促销价格
|
|
||||||
@ApiProperty({ description: '促销价格', example: 99.99, required: false })
|
|
||||||
@Rule(RuleType.number())
|
|
||||||
promotionPrice?: number;
|
|
||||||
|
|
||||||
// 产品图片URL
|
|
||||||
@ApiProperty({ description: '产品图片URL', example: 'https://example.com/image.jpg', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
image?: string;
|
|
||||||
|
|
||||||
// 属性更新(可选, 支持增量替换指定字典的属性项)
|
|
||||||
@ApiProperty({ description: '属性列表', type: 'array', required: false })
|
|
||||||
@Rule(RuleType.array())
|
|
||||||
attributes?: AttributeInputDTO[];
|
|
||||||
|
|
||||||
// 商品类型(single 或 bundle)
|
|
||||||
@ApiProperty({ description: '商品类型', enum: ['single', 'bundle'], required: false })
|
|
||||||
@Rule(RuleType.string().valid('single', 'bundle'))
|
|
||||||
type?: string;
|
|
||||||
|
|
||||||
// 仅当 type 为 'bundle' 时,才需要提供 components
|
|
||||||
@ApiProperty({ description: '产品组成', type: 'array', required: false })
|
|
||||||
@Rule(
|
|
||||||
RuleType.array()
|
|
||||||
.items(
|
|
||||||
RuleType.object({
|
|
||||||
sku: RuleType.string().required(),
|
|
||||||
quantity: RuleType.number().required(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
.when('type', {
|
|
||||||
is: 'bundle',
|
|
||||||
then: RuleType.array().optional(),
|
|
||||||
})
|
|
||||||
)
|
|
||||||
components?: { sku: string; quantity: number }[];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DTO 用于批量更新产品属性
|
|
||||||
*/
|
|
||||||
export class BatchUpdateProductDTO {
|
|
||||||
@ApiProperty({ description: '产品ID列表', type: 'array', required: true })
|
|
||||||
@Rule(RuleType.array().items(RuleType.number()).required().min(1))
|
|
||||||
ids: number[];
|
|
||||||
|
|
||||||
@ApiProperty({ example: 'ZYN 6MG WINTERGREEN', description: '产品名称', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '产品中文名称', required: false })
|
|
||||||
@Rule(RuleType.string().allow('').optional())
|
|
||||||
nameCn?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: '产品描述', description: '产品描述', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: '产品简短描述', description: '产品简短描述', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
shortDescription?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '产品 SKU', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
sku?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类ID (DictItem ID)', required: false })
|
|
||||||
@Rule(RuleType.number().optional())
|
|
||||||
categoryId?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点 SKU 列表', type: 'array', required: false })
|
|
||||||
@Rule(RuleType.array().items(RuleType.string()).optional())
|
|
||||||
siteSkus?: string[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '价格', example: 99.99, required: false })
|
|
||||||
@Rule(RuleType.number().optional())
|
|
||||||
price?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '促销价格', example: 99.99, required: false })
|
|
||||||
@Rule(RuleType.number().optional())
|
|
||||||
promotionPrice?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '产品图片URL', example: 'https://example.com/image.jpg', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
image?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '属性列表', type: 'array', required: false })
|
|
||||||
@Rule(RuleType.array().optional())
|
|
||||||
attributes?: AttributeInputDTO[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '商品类型', enum: ['single', 'bundle'], required: false })
|
|
||||||
@Rule(RuleType.string().valid('single', 'bundle').optional())
|
|
||||||
type?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DTO 用于批量删除产品
|
|
||||||
*/
|
|
||||||
export class BatchDeleteProductDTO {
|
|
||||||
@ApiProperty({ description: '产品ID列表', type: 'array', required: true })
|
|
||||||
@Rule(RuleType.array().items(RuleType.number()).required().min(1))
|
|
||||||
ids: number[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DTO 用于创建分类属性绑定
|
|
||||||
*/
|
|
||||||
export class CreateCategoryAttributeDTO {
|
|
||||||
@ApiProperty({ description: '分类字典项ID', example: 1 })
|
|
||||||
@Rule(RuleType.number().required())
|
|
||||||
categoryItemId: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '属性字典ID列表', example: [2, 3] })
|
|
||||||
@Rule(RuleType.array().items(RuleType.number()).required())
|
|
||||||
attributeDictIds: number[];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 产品查询过滤条件接口
|
|
||||||
*/
|
|
||||||
export interface ProductWhereFilter {
|
|
||||||
// 产品ID
|
|
||||||
id?: number;
|
|
||||||
// 产品ID列表
|
|
||||||
ids?: number[];
|
|
||||||
// SKU
|
|
||||||
sku?: string;
|
|
||||||
// SKU列表
|
|
||||||
skus?: string[];
|
|
||||||
// 产品名称
|
|
||||||
name?: string;
|
|
||||||
// 产品中文名称
|
|
||||||
nameCn?: string;
|
|
||||||
// 分类ID
|
|
||||||
categoryId?: number;
|
|
||||||
// 分类ID列表
|
|
||||||
categoryIds?: number[];
|
|
||||||
// 品牌ID
|
|
||||||
brandId?: number;
|
|
||||||
// 品牌ID列表
|
|
||||||
brandIds?: number[];
|
|
||||||
// 产品类型
|
|
||||||
type?: string;
|
|
||||||
// 价格最小值
|
|
||||||
minPrice?: number;
|
|
||||||
// 价格最大值
|
|
||||||
maxPrice?: number;
|
|
||||||
// 促销价格最小值
|
|
||||||
minPromotionPrice?: number;
|
|
||||||
// 促销价格最大值
|
|
||||||
maxPromotionPrice?: number;
|
|
||||||
// 创建时间范围开始
|
|
||||||
createdAtStart?: string;
|
|
||||||
// 创建时间范围结束
|
|
||||||
createdAtEnd?: string;
|
|
||||||
// 更新时间范围开始
|
|
||||||
updatedAtStart?: string;
|
|
||||||
// 更新时间范围结束
|
|
||||||
updatedAtEnd?: string;
|
|
||||||
// TODO 使用 attributes 过滤
|
|
||||||
attributes?: Record<string, string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 产品查询过滤条件DTO
|
|
||||||
*/
|
|
||||||
export class ProductWhereFilterDTO {
|
|
||||||
@ApiProperty({ description: '产品ID', example: 1 })
|
|
||||||
id?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '产品ID列表', example: [1, 2, 3] })
|
|
||||||
ids?: number[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: 'SKU', example: 'ZYN-6MG-WINTERGREEN' })
|
|
||||||
sku?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: 'SKU列表', example: ['ZYN-6MG-WINTERGREEN', 'ZYN-3MG-WINTERGREEN'] })
|
|
||||||
skus?: string[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '产品名称', example: 'ZYN 6MG WINTERGREEN' })
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '产品中文名称', example: 'ZYN 6毫克 冬清味' })
|
|
||||||
nameCn?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类ID', example: 1 })
|
|
||||||
categoryId?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类ID列表', example: [1, 2, 3] })
|
|
||||||
categoryIds?: number[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '品牌ID', example: 1 })
|
|
||||||
brandId?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '品牌ID列表', example: [1, 2, 3] })
|
|
||||||
brandIds?: number[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '产品类型', example: 'single', enum: ['single', 'bundle'] })
|
|
||||||
type?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '价格最小值', example: 99.99 })
|
|
||||||
minPrice?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '价格最大值', example: 199.99 })
|
|
||||||
maxPrice?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '促销价格最小值', example: 89.99 })
|
|
||||||
minPromotionPrice?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '促销价格最大值', example: 179.99 })
|
|
||||||
maxPromotionPrice?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '创建时间范围开始', example: '2023-01-01 00:00:00' })
|
|
||||||
createdAtStart?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '创建时间范围结束', example: '2023-12-31 23:59:59' })
|
|
||||||
createdAtEnd?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '更新时间范围开始', example: '2023-01-01 00:00:00' })
|
|
||||||
updatedAtStart?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '更新时间范围结束', example: '2023-12-31 23:59:59' })
|
|
||||||
updatedAtEnd?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DTO 用于分页查询产品
|
* DTO 用于分页查询产品
|
||||||
* 支持灵活的where条件、分页和排序
|
|
||||||
*/
|
*/
|
||||||
export class QueryProductDTO extends UnifiedSearchParamsDTO<ProductWhereFilter> {
|
export class QueryProductDTO {
|
||||||
|
@ApiProperty({ example: '1', description: '页码' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
current: number;
|
||||||
|
|
||||||
|
@ApiProperty({ example: '10', description: '每页大小' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
pageSize: number;
|
||||||
|
|
||||||
|
@ApiProperty({ example: 'ZYN', description: '关键字' })
|
||||||
|
@Rule(RuleType.string())
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ example: '1', description: '分类 ID' })
|
||||||
|
@Rule(RuleType.string())
|
||||||
|
categoryId: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DTO 用于创建分类
|
* DTO 用于创建分类
|
||||||
*/
|
*/
|
||||||
export class CreateCategoryDTO {
|
export class CreateCategoryDTO {
|
||||||
@ApiProperty({ description: '分类显示名称', required: true })
|
@ApiProperty({ example: 'ZYN', description: '分类名称', required: true })
|
||||||
@Rule(RuleType.string().required())
|
@Rule(RuleType.string().required().empty({ message: '分类名称不能为空' }))
|
||||||
title: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类中文名称', required: false })
|
|
||||||
@Rule(RuleType.string().allow('').optional())
|
|
||||||
titleCN?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类唯一标识', required: true })
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '分类短名称,用于生成SKU', required: false })
|
@Rule(RuleType.string().required().empty({ message: 'key不能为空' }))
|
||||||
@Rule(RuleType.string().allow('').optional())
|
unique_key: string;
|
||||||
shortName?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '排序', required: false })
|
|
||||||
@Rule(RuleType.number().optional())
|
|
||||||
sort?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DTO 用于更新分类
|
* DTO 用于更新分类
|
||||||
*/
|
*/
|
||||||
export class UpdateCategoryDTO {
|
export class UpdateCategoryDTO {
|
||||||
@ApiProperty({ description: '分类显示名称', required: false })
|
@ApiProperty({ example: 'ZYN', description: '分类名称' })
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string())
|
||||||
title?: string;
|
name: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '分类中文名称', required: false })
|
|
||||||
@Rule(RuleType.string().allow('').optional())
|
|
||||||
titleCN?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类唯一标识', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类短名称,用于生成SKU', required: false })
|
|
||||||
@Rule(RuleType.string().allow('').optional())
|
|
||||||
shortName?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '排序', required: false })
|
|
||||||
@Rule(RuleType.number().optional())
|
|
||||||
sort?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DTO 用于查询分类(支持分页)
|
||||||
|
*/
|
||||||
|
export class QueryCategoryDTO {
|
||||||
|
@ApiProperty({ example: '1', description: '页码' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
current: number; // 页码
|
||||||
|
|
||||||
|
@ApiProperty({ example: '10', description: '每页大小' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
pageSize: number; // 每页大小
|
||||||
|
|
||||||
|
@ApiProperty({ example: 'ZYN', description: '关键字' })
|
||||||
|
@Rule(RuleType.string())
|
||||||
|
name: string; // 搜索关键字(支持模糊查询)
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateFlavorsDTO {
|
||||||
|
@ApiProperty({ example: 'ZYN', description: '分类名称', required: true })
|
||||||
|
@Rule(RuleType.string().required().empty({ message: '分类名称不能为空' }))
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Rule(RuleType.string().required().empty({ message: 'key不能为空' }))
|
||||||
|
unique_key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateFlavorsDTO {
|
||||||
|
@ApiProperty({ example: 'ZYN', description: '分类名称' })
|
||||||
|
@Rule(RuleType.string())
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QueryFlavorsDTO {
|
||||||
|
@ApiProperty({ example: '1', description: '页码' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
current: number; // 页码
|
||||||
|
|
||||||
|
@ApiProperty({ example: '10', description: '每页大小' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
pageSize: number; // 每页大小
|
||||||
|
|
||||||
|
@ApiProperty({ example: 'ZYN', description: '关键字' })
|
||||||
|
@Rule(RuleType.string())
|
||||||
|
name: string; // 搜索关键字(支持模糊查询)
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CreateStrengthDTO {
|
||||||
|
@ApiProperty({ example: 'ZYN', description: '分类名称', required: true })
|
||||||
|
@Rule(RuleType.string().required().empty({ message: '分类名称不能为空' }))
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Rule(RuleType.string().required().empty({ message: 'key不能为空' }))
|
||||||
|
unique_key: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateStrengthDTO {
|
||||||
|
@ApiProperty({ example: 'ZYN', description: '分类名称' })
|
||||||
|
@Rule(RuleType.string())
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QueryStrengthDTO {
|
||||||
|
@ApiProperty({ example: '1', description: '页码' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
current: number; // 页码
|
||||||
|
|
||||||
|
@ApiProperty({ example: '10', description: '每页大小' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
pageSize: number; // 每页大小
|
||||||
|
|
||||||
|
@ApiProperty({ example: 'ZYN', description: '关键字' })
|
||||||
|
@Rule(RuleType.string())
|
||||||
|
name: string; // 搜索关键字(支持模糊查询)
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SkuItemDTO {
|
||||||
|
@ApiProperty({ description: '产品 ID' })
|
||||||
|
productId: number;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'sku 编码' })
|
||||||
|
sku: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BatchSetSkuDTO {
|
||||||
|
@ApiProperty({ description: 'sku 数据列表', type: [SkuItemDTO] })
|
||||||
|
skus: SkuItemDTO[];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
|
import { Category } from '../entity/category.entity';
|
||||||
import { Order } from '../entity/order.entity';
|
import { Order } from '../entity/order.entity';
|
||||||
import { Product } from '../entity/product.entity';
|
import { Product } from '../entity/product.entity';
|
||||||
import { StockPoint } from '../entity/stock_point.entity';
|
import { StockPoint } from '../entity/stock_point.entity';
|
||||||
|
|
@ -11,23 +12,22 @@ import { OrderStatusCountDTO } from './order.dto';
|
||||||
import { SiteConfig } from './site.dto';
|
import { SiteConfig } from './site.dto';
|
||||||
import { PurchaseOrderDTO, StockDTO, StockRecordDTO } from './stock.dto';
|
import { PurchaseOrderDTO, StockDTO, StockRecordDTO } from './stock.dto';
|
||||||
import { LoginResDTO } from './user.dto';
|
import { LoginResDTO } from './user.dto';
|
||||||
|
import { WpProductDTO } from './wp_product.dto';
|
||||||
import { OrderSale } from '../entity/order_sale.entity';
|
import { OrderSale } from '../entity/order_sale.entity';
|
||||||
import { Service } from '../entity/service.entity';
|
import { Service } from '../entity/service.entity';
|
||||||
import { RateDTO } from './freightcom.dto';
|
import { RateDTO } from './freightcom.dto';
|
||||||
import { ShippingAddress } from '../entity/shipping_address.entity';
|
import { ShippingAddress } from '../entity/shipping_address.entity';
|
||||||
import { OrderItem } from '../entity/order_item.entity';
|
import { OrderItem } from '../entity/order_item.entity';
|
||||||
import { OrderRefundItem } from '../entity/order_refund_item.entity';
|
import { OrderRefundItem } from '../entity/order_retund_item.entity';
|
||||||
import { OrderNote } from '../entity/order_note.entity';
|
import { OrderNote } from '../entity/order_note.entity';
|
||||||
import { PaymentMethodDTO } from './logistics.dto';
|
import { PaymentMethodDTO } from './logistics.dto';
|
||||||
|
import { Flavors } from '../entity/flavors.entity';
|
||||||
|
import { Strength } from '../entity/strength.entity';
|
||||||
import { Subscription } from '../entity/subscription.entity';
|
import { Subscription } from '../entity/subscription.entity';
|
||||||
import { Dict } from '../entity/dict.entity';
|
|
||||||
import { SyncOperationResultDTO } from './api.dto';
|
|
||||||
|
|
||||||
export class BooleanRes extends SuccessWrapper(Boolean) {}
|
export class BooleanRes extends SuccessWrapper(Boolean) {}
|
||||||
// 同步操作结果返回数据
|
|
||||||
export class SyncOperationResultRes extends SuccessWrapper(SyncOperationResultDTO) {}
|
|
||||||
//网站配置返回数据
|
//网站配置返回数据
|
||||||
export class SitesResponse extends SuccessArrayWrapper(SiteConfig) {}
|
export class WpSitesResponse extends SuccessArrayWrapper(SiteConfig) {}
|
||||||
//产品分页数据
|
//产品分页数据
|
||||||
export class ProductPaginatedResponse extends PaginatedWrapper(Product) {}
|
export class ProductPaginatedResponse extends PaginatedWrapper(Product) {}
|
||||||
//产品分页返回数据
|
//产品分页返回数据
|
||||||
|
|
@ -35,49 +35,27 @@ export class ProductListRes extends SuccessWrapper(ProductPaginatedResponse) {}
|
||||||
//产品返回数据
|
//产品返回数据
|
||||||
export class ProductRes extends SuccessWrapper(Product) {}
|
export class ProductRes extends SuccessWrapper(Product) {}
|
||||||
export class ProductsRes extends SuccessArrayWrapper(Product) {}
|
export class ProductsRes extends SuccessArrayWrapper(Product) {}
|
||||||
//产品品牌返分页数据
|
//产品分类返分页数据
|
||||||
export class BrandPaginatedResponse extends PaginatedWrapper(Dict) {}
|
export class CategoryPaginatedResponse extends PaginatedWrapper(Category) {}
|
||||||
//产品品牌返分页返回数据
|
export class FlavorsPaginatedResponse extends PaginatedWrapper(Flavors) {}
|
||||||
export class ProductBrandListRes extends SuccessWrapper(
|
export class StrengthPaginatedResponse extends PaginatedWrapper(Strength) {}
|
||||||
BrandPaginatedResponse
|
//产品分类返分页返回数据
|
||||||
|
export class ProductCatListRes extends SuccessWrapper(
|
||||||
|
CategoryPaginatedResponse
|
||||||
) {}
|
) {}
|
||||||
//产品品牌返所有数据
|
//产品分类返所有数据
|
||||||
export class ProductBrandAllRes extends SuccessArrayWrapper(Dict) {}
|
export class ProductCatAllRes extends SuccessArrayWrapper(Category) {}
|
||||||
//产品品牌返回数据
|
//产品分类返回数据
|
||||||
export class ProductBrandRes extends SuccessWrapper(Dict) {}
|
export class ProductCatRes extends SuccessWrapper(Category) {}
|
||||||
|
|
||||||
//产品口味返分页数据
|
//产品分页数据
|
||||||
export class FlavorsPaginatedResponse extends PaginatedWrapper(Dict) {}
|
export class WpProductPaginatedResponse extends PaginatedWrapper(
|
||||||
//产品口味返分页返回数据
|
WpProductDTO
|
||||||
export class ProductFlavorsListRes extends SuccessWrapper(
|
|
||||||
FlavorsPaginatedResponse
|
|
||||||
) {}
|
) {}
|
||||||
//产品口味返所有数据
|
//产品分页返回数据
|
||||||
export class ProductFlavorsAllRes extends SuccessArrayWrapper(Dict) {}
|
export class WpProductListRes extends SuccessWrapper(
|
||||||
//产品口味返回数据
|
WpProductPaginatedResponse
|
||||||
export class ProductFlavorsRes extends SuccessWrapper(Dict) {}
|
|
||||||
|
|
||||||
//产品规格返分页数据
|
|
||||||
export class StrengthPaginatedResponse extends PaginatedWrapper(Dict) {}
|
|
||||||
//产品规格返分页返回数据
|
|
||||||
export class ProductStrengthListRes extends SuccessWrapper(
|
|
||||||
StrengthPaginatedResponse
|
|
||||||
) {}
|
) {}
|
||||||
//产品规格返所有数据
|
|
||||||
export class ProductStrengthAllRes extends SuccessArrayWrapper(Dict) {}
|
|
||||||
//产品规格返返回数据
|
|
||||||
export class ProductStrengthRes extends SuccessWrapper(Dict) {}
|
|
||||||
|
|
||||||
// 产品尺寸返分页数据
|
|
||||||
export class SizePaginatedResponse extends PaginatedWrapper(Dict) {}
|
|
||||||
// 产品尺寸返分页返回数据
|
|
||||||
export class ProductSizeListRes extends SuccessWrapper(
|
|
||||||
SizePaginatedResponse
|
|
||||||
) {}
|
|
||||||
// 产品尺寸返所有数据
|
|
||||||
export class ProductSizeAllRes extends SuccessArrayWrapper(Dict) {}
|
|
||||||
// 产品尺寸返回数据
|
|
||||||
export class ProductSizeRes extends SuccessWrapper(Dict) {}
|
|
||||||
|
|
||||||
export class LoginRes extends SuccessWrapper(LoginResDTO) {}
|
export class LoginRes extends SuccessWrapper(LoginResDTO) {}
|
||||||
export class StockPaginatedRespone extends PaginatedWrapper(StockDTO) {}
|
export class StockPaginatedRespone extends PaginatedWrapper(StockDTO) {}
|
||||||
|
|
@ -141,7 +119,7 @@ export class PaymentMethodListRes extends SuccessArrayWrapper(
|
||||||
PaymentMethodDTO
|
PaymentMethodDTO
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
// 订阅分页数据(列表 + 总数等分页信息)
|
// 订阅分页数据(列表 + 总数等分页信息)
|
||||||
export class SubscriptionPaginatedResponse extends PaginatedWrapper(Subscription) {}
|
export class SubscriptionPaginatedResponse extends PaginatedWrapper(Subscription) {}
|
||||||
// 订阅分页返回数据(统一成功包装)
|
// 订阅分页返回数据(统一成功包装)
|
||||||
export class SubscriptionListRes extends SuccessWrapper(SubscriptionPaginatedResponse) {}
|
export class SubscriptionListRes extends SuccessWrapper(SubscriptionPaginatedResponse) {}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,51 +0,0 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { Rule, RuleType } from '@midwayjs/validate';
|
|
||||||
/**
|
|
||||||
* 产品站点SKU信息DTO
|
|
||||||
*/
|
|
||||||
export class ProductSiteSkuDTO {
|
|
||||||
@ApiProperty({ description: '产品ID', example: 1 })
|
|
||||||
@Rule(RuleType.number().required())
|
|
||||||
productId: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点SKU',nullable:true, example: 'SKU-001' })
|
|
||||||
siteSku?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步单个产品到站点的请求DTO
|
|
||||||
*/
|
|
||||||
export class SyncProductToSiteDTO extends ProductSiteSkuDTO {
|
|
||||||
@ApiProperty({ description: '站点ID', example: 1 })
|
|
||||||
@Rule(RuleType.number().required())
|
|
||||||
siteId: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 同步到站点的结果DTO
|
|
||||||
*/
|
|
||||||
export class SyncProductToSiteResultDTO {
|
|
||||||
@ApiProperty({ description: '同步状态', example: true })
|
|
||||||
success: boolean;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '远程产品ID', example: '123', required: false })
|
|
||||||
remoteId?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '错误信息', required: false })
|
|
||||||
error?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量同步产品到站点的请求DTO
|
|
||||||
*/
|
|
||||||
export class BatchSyncProductToSiteDTO {
|
|
||||||
@ApiProperty({ description: '站点ID', example: 1 })
|
|
||||||
@Rule(RuleType.number().required())
|
|
||||||
siteId: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '产品站点SKU列表', type: [ProductSiteSkuDTO] })
|
|
||||||
@Rule(RuleType.array().items(RuleType.object()).required().min(1))
|
|
||||||
data: ProductSiteSkuDTO[];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -20,11 +20,7 @@ export class SiteConfig {
|
||||||
|
|
||||||
@ApiProperty({ description: '站点名' })
|
@ApiProperty({ description: '站点名' })
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
name: string;
|
siteName: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '描述' })
|
|
||||||
@Rule(RuleType.string().allow('').optional())
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '平台类型', enum: ['woocommerce', 'shopyy'] })
|
@ApiProperty({ description: '平台类型', enum: ['woocommerce', 'shopyy'] })
|
||||||
@Rule(RuleType.string().valid('woocommerce', 'shopyy'))
|
@Rule(RuleType.string().valid('woocommerce', 'shopyy'))
|
||||||
|
|
@ -36,133 +32,51 @@ export class SiteConfig {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CreateSiteDTO {
|
export class CreateSiteDTO {
|
||||||
@ApiProperty({ description: '站点 API URL', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
apiUrl?: string;
|
apiUrl?: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '站点网站 URL', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
websiteUrl?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点 REST Key', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
consumerKey?: string;
|
consumerKey?: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '站点 REST 秘钥', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
consumerSecret?: string;
|
consumerSecret?: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '访问令牌', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
token?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点名称' })
|
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
name: string;
|
siteName: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '站点描述', required: false })
|
|
||||||
@Rule(RuleType.string().allow('').optional())
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '平台类型', enum: ['woocommerce', 'shopyy'], required: false })
|
|
||||||
@Rule(RuleType.string().valid('woocommerce', 'shopyy').optional())
|
@Rule(RuleType.string().valid('woocommerce', 'shopyy').optional())
|
||||||
type?: string;
|
type?: string;
|
||||||
|
|
||||||
@ApiProperty({ description: 'SKU 前缀', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
skuPrefix?: string;
|
skuPrefix?: string;
|
||||||
|
|
||||||
// 区域
|
|
||||||
@ApiProperty({ description: '区域' })
|
|
||||||
@Rule(RuleType.array().items(RuleType.string()).optional())
|
|
||||||
areas?: string[];
|
|
||||||
|
|
||||||
// 绑定仓库
|
|
||||||
@ApiProperty({ description: '绑定仓库ID列表' })
|
|
||||||
@Rule(RuleType.array().items(RuleType.number()).optional())
|
|
||||||
stockPointIds?: number[];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class UpdateSiteDTO {
|
export class UpdateSiteDTO {
|
||||||
@ApiProperty({ description: '站点 API URL', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
apiUrl?: string;
|
apiUrl?: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '站点 REST Key', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
consumerKey?: string;
|
consumerKey?: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '站点 REST 秘钥', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
consumerSecret?: string;
|
consumerSecret?: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '访问令牌', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
token?: string;
|
siteName?: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '站点名称', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
name?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点描述', required: false })
|
|
||||||
@Rule(RuleType.string().allow('').optional())
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '是否禁用', required: false })
|
|
||||||
@Rule(RuleType.boolean().optional())
|
@Rule(RuleType.boolean().optional())
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
|
|
||||||
@ApiProperty({ description: '平台类型', enum: ['woocommerce', 'shopyy'], required: false })
|
|
||||||
@Rule(RuleType.string().valid('woocommerce', 'shopyy').optional())
|
@Rule(RuleType.string().valid('woocommerce', 'shopyy').optional())
|
||||||
type?: string;
|
type?: string;
|
||||||
|
|
||||||
@ApiProperty({ description: 'SKU 前缀', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
skuPrefix?: string;
|
skuPrefix?: string;
|
||||||
|
|
||||||
// 区域
|
|
||||||
@ApiProperty({ description: '区域', required: false })
|
|
||||||
@Rule(RuleType.array().items(RuleType.string()).optional())
|
|
||||||
areas?: string[];
|
|
||||||
|
|
||||||
// 绑定仓库
|
|
||||||
@ApiProperty({ description: '绑定仓库ID列表' })
|
|
||||||
@Rule(RuleType.array().items(RuleType.number()).optional())
|
|
||||||
stockPointIds?: number[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点网站URL', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
websiteUrl?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: 'Webhook URL', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
webhookUrl?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class QuerySiteDTO {
|
export class QuerySiteDTO {
|
||||||
@ApiProperty({ description: '当前页码', required: false })
|
|
||||||
@Rule(RuleType.number().optional())
|
@Rule(RuleType.number().optional())
|
||||||
current?: number;
|
current?: number;
|
||||||
|
|
||||||
@ApiProperty({ description: '每页数量', required: false })
|
|
||||||
@Rule(RuleType.number().optional())
|
@Rule(RuleType.number().optional())
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
|
|
||||||
@ApiProperty({ description: '搜索关键词', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
keyword?: string;
|
keyword?: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '是否禁用', required: false })
|
|
||||||
@Rule(RuleType.boolean().optional())
|
@Rule(RuleType.boolean().optional())
|
||||||
isDisabled?: boolean;
|
isDisabled?: boolean;
|
||||||
|
|
||||||
@ApiProperty({ description: '站点ID列表(逗号分隔)', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
@Rule(RuleType.string().optional())
|
||||||
ids?: string;
|
ids?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DisableSiteDTO {
|
export class DisableSiteDTO {
|
||||||
@ApiProperty({ description: '是否禁用' })
|
|
||||||
@Rule(RuleType.boolean())
|
@Rule(RuleType.boolean())
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,10 +19,6 @@ export class OrderStatisticsParams {
|
||||||
@Rule(RuleType.number().allow(null))
|
@Rule(RuleType.number().allow(null))
|
||||||
siteId?: number;
|
siteId?: number;
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Rule(RuleType.array().allow(null))
|
|
||||||
country?: any[];
|
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
enum: ['all', 'first_purchase', 'repeat_purchase'],
|
enum: ['all', 'first_purchase', 'repeat_purchase'],
|
||||||
default: 'all',
|
default: 'all',
|
||||||
|
|
@ -37,9 +33,4 @@ export class OrderStatisticsParams {
|
||||||
@ApiProperty({ enum: ['all', 'zyn', 'yoone', 'zolt'], default: 'all' })
|
@ApiProperty({ enum: ['all', 'zyn', 'yoone', 'zolt'], default: 'all' })
|
||||||
@Rule(RuleType.string().valid('all', 'zyn', 'yoone', 'zolt'))
|
@Rule(RuleType.string().valid('all', 'zyn', 'yoone', 'zolt'))
|
||||||
brand: string;
|
brand: string;
|
||||||
|
|
||||||
@ApiProperty({ enum: ['day', 'week', 'month'], default: 'day' })
|
|
||||||
@Rule(RuleType.string().valid('day', 'week', 'month'))
|
|
||||||
grouping: string;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,19 +20,7 @@ export class QueryStockDTO {
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
name: string;
|
productName: string;
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Rule(RuleType.string())
|
|
||||||
sku: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '按库存点ID排序', required: false })
|
|
||||||
@Rule(RuleType.number().allow(null))
|
|
||||||
sortPointId?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '排序对象,格式如 { productName: "asc", sku: "desc" }', required: false })
|
|
||||||
@Rule(RuleType.object().allow(null))
|
|
||||||
order?: Record<string, 'asc' | 'desc'>;
|
|
||||||
}
|
}
|
||||||
export class QueryPointDTO {
|
export class QueryPointDTO {
|
||||||
@ApiProperty({ example: '1', description: '页码' })
|
@ApiProperty({ example: '1', description: '页码' })
|
||||||
|
|
@ -58,11 +46,11 @@ export class QueryStockRecordDTO {
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
sku: string;
|
productSku: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
name: string;
|
productName: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
|
|
@ -96,7 +84,7 @@ export class QueryPurchaseOrderDTO {
|
||||||
export class StockDTO extends Stock {
|
export class StockDTO extends Stock {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
name: string;
|
productName: string;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
type: 'object',
|
type: 'object',
|
||||||
|
|
@ -132,7 +120,7 @@ export class UpdateStockDTO {
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
sku: string;
|
productSku: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.number())
|
@Rule(RuleType.number())
|
||||||
|
|
@ -167,19 +155,6 @@ export class CreateStockPointDTO {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Rule(RuleType.string())
|
@Rule(RuleType.string())
|
||||||
contactPhone: string;
|
contactPhone: string;
|
||||||
|
|
||||||
// 区域
|
|
||||||
@ApiProperty({ description: '区域' })
|
|
||||||
@Rule(RuleType.array().items(RuleType.string()).optional())
|
|
||||||
areas?: string[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '上游仓库点ID' })
|
|
||||||
@Rule(RuleType.number().optional())
|
|
||||||
upStreamStockPointId?: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '上游名称' })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
upStreamName?: string;
|
|
||||||
}
|
}
|
||||||
export class UpdateStockPointDTO extends CreateStockPointDTO {}
|
export class UpdateStockPointDTO extends CreateStockPointDTO {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@ import { ApiProperty } from '@midwayjs/swagger';
|
||||||
import { Rule, RuleType } from '@midwayjs/validate';
|
import { Rule, RuleType } from '@midwayjs/validate';
|
||||||
import { SubscriptionStatus } from '../enums/base.enum';
|
import { SubscriptionStatus } from '../enums/base.enum';
|
||||||
|
|
||||||
// 订阅列表查询参数(分页与筛选)
|
// 订阅列表查询参数(分页与筛选)
|
||||||
export class QuerySubscriptionDTO {
|
export class QuerySubscriptionDTO {
|
||||||
// 当前页码(从 1 开始)
|
// 当前页码(从 1 开始)
|
||||||
@ApiProperty({ example: 1, description: '页码' })
|
@ApiProperty({ example: 1, description: '页码' })
|
||||||
@Rule(RuleType.number().default(1))
|
@Rule(RuleType.number().default(1))
|
||||||
current: number;
|
current: number;
|
||||||
|
|
@ -14,23 +14,23 @@ export class QuerySubscriptionDTO {
|
||||||
@Rule(RuleType.number().default(10))
|
@Rule(RuleType.number().default(10))
|
||||||
pageSize: number;
|
pageSize: number;
|
||||||
|
|
||||||
// 站点 ID(可选)
|
// 站点 ID(可选)
|
||||||
@ApiProperty({ description: '站点ID' })
|
@ApiProperty({ description: '站点ID' })
|
||||||
@Rule(RuleType.string().allow(''))
|
@Rule(RuleType.string().allow(''))
|
||||||
siteId: string;
|
siteId: string;
|
||||||
|
|
||||||
// 订阅状态筛选(可选),支持枚举值
|
// 订阅状态筛选(可选),支持枚举值
|
||||||
@ApiProperty({ description: '订阅状态', enum: SubscriptionStatus })
|
@ApiProperty({ description: '订阅状态', enum: SubscriptionStatus })
|
||||||
@Rule(RuleType.string().valid(...Object.values(SubscriptionStatus)).allow(''))
|
@Rule(RuleType.string().valid(...Object.values(SubscriptionStatus)).allow(''))
|
||||||
status: SubscriptionStatus | '';
|
status: SubscriptionStatus | '';
|
||||||
|
|
||||||
// 客户邮箱(模糊匹配,可选)
|
// 客户邮箱(模糊匹配,可选)
|
||||||
@ApiProperty({ description: '客户邮箱' })
|
@ApiProperty({ description: '客户邮箱' })
|
||||||
@Rule(RuleType.string().allow(''))
|
@Rule(RuleType.string().allow(''))
|
||||||
customer_email: string;
|
customer_email: string;
|
||||||
|
|
||||||
// 关键字(订阅ID,邮箱等,模糊匹配,可选)
|
// 关键字(订阅ID、邮箱等,模糊匹配,可选)
|
||||||
@ApiProperty({ description: '关键字(订阅ID,邮箱等)' })
|
@ApiProperty({ description: '关键字(订阅ID、邮箱等)' })
|
||||||
@Rule(RuleType.string().allow(''))
|
@Rule(RuleType.string().allow(''))
|
||||||
keyword: string;
|
keyword: string;
|
||||||
}
|
}
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { Rule, RuleType } from '@midwayjs/validate';
|
|
||||||
|
|
||||||
export class CreateTemplateDTO {
|
|
||||||
@ApiProperty({ description: '模板名称', required: true })
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '模板内容', required: true })
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
value: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '测试数据JSON', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
testData?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UpdateTemplateDTO {
|
|
||||||
@ApiProperty({ description: '模板名称', required: true })
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '模板内容', required: true })
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
value: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '测试数据JSON', required: false })
|
|
||||||
@Rule(RuleType.string().optional())
|
|
||||||
testData?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RenderTemplateDTO {
|
|
||||||
@ApiProperty({ description: '模板内容', required: true })
|
|
||||||
@Rule(RuleType.string().required())
|
|
||||||
template: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '渲染数据', required: true })
|
|
||||||
@Rule(RuleType.object().required())
|
|
||||||
data: Record<string, any>;
|
|
||||||
}
|
|
||||||
|
|
@ -1,744 +0,0 @@
|
||||||
// WooCommerce 平台原始数据类型定义
|
|
||||||
// 仅包含当前映射逻辑所需字段以保持简洁与类型安全
|
|
||||||
|
|
||||||
|
|
||||||
// 产品类型
|
|
||||||
export interface WooProduct {
|
|
||||||
// 产品主键
|
|
||||||
id: number;
|
|
||||||
// 创建时间
|
|
||||||
date_created: string;
|
|
||||||
// 创建时间(GMT)
|
|
||||||
date_created_gmt: string;
|
|
||||||
// 更新时间
|
|
||||||
date_modified: string;
|
|
||||||
// 更新时间(GMT)
|
|
||||||
date_modified_gmt: string;
|
|
||||||
// 产品类型 simple grouped external variable
|
|
||||||
type: string;
|
|
||||||
// 产品状态 draft pending private publish
|
|
||||||
status: string;
|
|
||||||
// 是否为特色产品
|
|
||||||
featured: boolean;
|
|
||||||
// 目录可见性选项:visible, catalog, search and hidden. Default is visible.
|
|
||||||
catalog_visibility: string;
|
|
||||||
|
|
||||||
// 常规价格
|
|
||||||
regular_price?: string;
|
|
||||||
// 促销价格
|
|
||||||
sale_price?: string;
|
|
||||||
// 当前价格
|
|
||||||
price?: string;
|
|
||||||
price_html?: string;
|
|
||||||
date_on_sale_from?: string; // Date the product is on sale from.
|
|
||||||
date_on_sale_from_gmt?: string; // Date the product is on sale from (GMT).
|
|
||||||
date_on_sale_to?: string; // Date the product is on sale to.
|
|
||||||
date_on_sale_to_gmt?: string; // Date the product is on sale to (GMT).
|
|
||||||
on_sale: boolean; // Whether the product is on sale.
|
|
||||||
purchasable: boolean; // Whether the product is purchasable.
|
|
||||||
total_sales: number; // Total sales for this product.
|
|
||||||
virtual: boolean; // Whether the product is virtual.
|
|
||||||
downloadable: boolean; // Whether the product is downloadable.
|
|
||||||
downloads: Array<{ id?: number; name?: string; file?: string }>; // Downloadable files for the product.
|
|
||||||
download_limit: number; // Download limit.
|
|
||||||
download_expiry: number; // Download expiry days.
|
|
||||||
external_url: string; // URL of the external product.
|
|
||||||
|
|
||||||
global_unique_id: string; // GTIN, UPC, EAN or ISBN - a unique identifier for each distinct product and service that can be purchased.
|
|
||||||
// 产品SKU
|
|
||||||
sku: string;
|
|
||||||
// 产品名称
|
|
||||||
name: string;
|
|
||||||
// 产品描述
|
|
||||||
description: string;
|
|
||||||
// 产品短描述
|
|
||||||
short_description: string;
|
|
||||||
|
|
||||||
// 产品永久链接
|
|
||||||
permalink: string;
|
|
||||||
// 产品URL路径
|
|
||||||
slug: string;
|
|
||||||
|
|
||||||
// 库存状态
|
|
||||||
stock_status?: 'instock' | 'outofstock' | 'onbackorder';
|
|
||||||
// 库存数量
|
|
||||||
stock_quantity?: number;
|
|
||||||
// 是否管理库存
|
|
||||||
manage_stock?: boolean;
|
|
||||||
// 缺货预定设置 no notify yes
|
|
||||||
backorders?: 'no' | 'notify' | 'yes';
|
|
||||||
// 是否允许缺货预定 只读
|
|
||||||
backorders_allowed?: boolean;
|
|
||||||
// 是否处于缺货预定状态 只读
|
|
||||||
backordered?: boolean;
|
|
||||||
// 是否单独出售
|
|
||||||
sold_individually?: boolean;
|
|
||||||
// 重量
|
|
||||||
weight?: string;
|
|
||||||
// 尺寸
|
|
||||||
dimensions?: { length?: string; width?: string; height?: string };
|
|
||||||
// 是否需要运输 只读
|
|
||||||
shipping_required?: boolean;
|
|
||||||
// 运输是否计税 只读
|
|
||||||
shipping_taxable?: boolean;
|
|
||||||
// 运输类别 slug
|
|
||||||
shipping_class?: string;
|
|
||||||
// 运输类别ID 只读
|
|
||||||
shipping_class_id?: number;
|
|
||||||
// 图片列表
|
|
||||||
images?: Array<{ id: number; src: string; name?: string; alt?: string }>;
|
|
||||||
// 属性列表
|
|
||||||
attributes?: Array<{
|
|
||||||
id?: number;
|
|
||||||
name?: string;
|
|
||||||
position?: number;
|
|
||||||
visible?: boolean;
|
|
||||||
variation?: boolean;
|
|
||||||
options?: string[];
|
|
||||||
}>;
|
|
||||||
// 变体列表
|
|
||||||
variations?: number[];
|
|
||||||
// 默认变体属性
|
|
||||||
default_attributes?: Array<{ id?: number; name?: string; option?: string }>;
|
|
||||||
// 允许评论
|
|
||||||
reviews_allowed?: boolean;
|
|
||||||
// 平均评分 只读
|
|
||||||
average_rating?: string;
|
|
||||||
// 评分数量 只读
|
|
||||||
rating_count?: number;
|
|
||||||
// 相关产品ID列表 只读
|
|
||||||
related_ids?: number[];
|
|
||||||
// 追加销售产品ID列表
|
|
||||||
upsell_ids?: number[];
|
|
||||||
// 交叉销售产品ID列表
|
|
||||||
cross_sell_ids?: number[];
|
|
||||||
// 父产品ID
|
|
||||||
parent_id?: number;
|
|
||||||
// 购买备注
|
|
||||||
purchase_note?: string;
|
|
||||||
// 分类列表
|
|
||||||
categories?: Array<{ id?: number; name?: string; slug?: string }>;
|
|
||||||
// 标签列表
|
|
||||||
tags?: Array<{ id?: number; name?: string; slug?: string }>;
|
|
||||||
// 菜单排序
|
|
||||||
menu_order?: number;
|
|
||||||
// 元数据
|
|
||||||
meta_data?: Array<{ id?: number; key: string; value: any }>;
|
|
||||||
}
|
|
||||||
export interface WooVariation {
|
|
||||||
// 变体主键
|
|
||||||
id: number;
|
|
||||||
// 创建时间
|
|
||||||
date_created: string;
|
|
||||||
// 创建时间(GMT)
|
|
||||||
date_created_gmt: string;
|
|
||||||
// 更新时间
|
|
||||||
date_modified: string;
|
|
||||||
// 更新时间(GMT)
|
|
||||||
date_modified_gmt: string;
|
|
||||||
// 变体描述
|
|
||||||
description: string;
|
|
||||||
// 变体SKU
|
|
||||||
sku: string;
|
|
||||||
// 常规价格
|
|
||||||
regular_price?: string;
|
|
||||||
// 促销价格
|
|
||||||
sale_price?: string;
|
|
||||||
// 当前价格
|
|
||||||
price?: string;
|
|
||||||
// 价格HTML
|
|
||||||
price_html?: string;
|
|
||||||
// 促销开始日期
|
|
||||||
date_on_sale_from?: string;
|
|
||||||
// 促销开始日期(GMT)
|
|
||||||
date_on_sale_from_gmt?: string;
|
|
||||||
// 促销结束日期
|
|
||||||
date_on_sale_to?: string;
|
|
||||||
// 促销结束日期(GMT)
|
|
||||||
date_on_sale_to_gmt?: string;
|
|
||||||
// 是否在促销中
|
|
||||||
on_sale: boolean;
|
|
||||||
// 是否可购买
|
|
||||||
purchasable: boolean;
|
|
||||||
// 总销量
|
|
||||||
total_sales: number;
|
|
||||||
// 是否为虚拟商品
|
|
||||||
virtual: boolean;
|
|
||||||
// 是否可下载
|
|
||||||
downloadable: boolean;
|
|
||||||
// 下载文件
|
|
||||||
downloads: Array<{ id?: number; name?: string; file?: string }>;
|
|
||||||
// 下载限制
|
|
||||||
download_limit: number;
|
|
||||||
// 下载过期天数
|
|
||||||
download_expiry: number;
|
|
||||||
// 库存状态
|
|
||||||
stock_status?: 'instock' | 'outofstock' | 'onbackorder';
|
|
||||||
// 库存数量
|
|
||||||
stock_quantity?: number;
|
|
||||||
// 是否管理库存
|
|
||||||
manage_stock?: boolean;
|
|
||||||
// 缺货预定设置
|
|
||||||
backorders?: 'no' | 'notify' | 'yes';
|
|
||||||
// 是否允许缺货预定
|
|
||||||
backorders_allowed?: boolean;
|
|
||||||
// 是否处于缺货预定状态
|
|
||||||
backordered?: boolean;
|
|
||||||
// 是否单独出售
|
|
||||||
sold_individually?: boolean;
|
|
||||||
// 重量
|
|
||||||
weight?: string;
|
|
||||||
// 尺寸
|
|
||||||
dimensions?: { length?: string; width?: string; height?: string };
|
|
||||||
// 是否需要运输
|
|
||||||
shipping_required?: boolean;
|
|
||||||
// 运输是否计税
|
|
||||||
shipping_taxable?: boolean;
|
|
||||||
// 运输类别
|
|
||||||
shipping_class?: string;
|
|
||||||
// 运输类别ID
|
|
||||||
shipping_class_id?: number;
|
|
||||||
// 变体图片
|
|
||||||
image?: { id: number; src: string; name?: string; alt?: string };
|
|
||||||
// 变体属性列表
|
|
||||||
attributes?: Array<{
|
|
||||||
id?: number;
|
|
||||||
name?: string;
|
|
||||||
option?: string;
|
|
||||||
}>;
|
|
||||||
// 菜单排序
|
|
||||||
menu_order?: number;
|
|
||||||
// 元数据
|
|
||||||
meta_data?: Array<{ id?: number; key: string; value: any }>;
|
|
||||||
// 父产品ID
|
|
||||||
parent_id?: number;
|
|
||||||
// 变体名称
|
|
||||||
name?: string;
|
|
||||||
// 是否启用
|
|
||||||
status?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 订单类型
|
|
||||||
export interface WooOrder {
|
|
||||||
// 订单主键
|
|
||||||
id: number;
|
|
||||||
// 父订单ID
|
|
||||||
parent_id?: number;
|
|
||||||
// 订单号
|
|
||||||
number: string;
|
|
||||||
// 订单键 只读
|
|
||||||
order_key?: string;
|
|
||||||
// 创建来源
|
|
||||||
created_via?: string;
|
|
||||||
// WooCommerce版本 只读
|
|
||||||
version?: string;
|
|
||||||
// 状态
|
|
||||||
status: string;
|
|
||||||
// 币种
|
|
||||||
currency: string;
|
|
||||||
// 价格是否含税 只读
|
|
||||||
prices_include_tax?: boolean;
|
|
||||||
// 总金额
|
|
||||||
total: string;
|
|
||||||
// 总税额 只读
|
|
||||||
total_tax?: string;
|
|
||||||
// 折扣总额 只读
|
|
||||||
discount_total?: string;
|
|
||||||
// 折扣税额 只读
|
|
||||||
discount_tax?: string;
|
|
||||||
// 运费总额 只读
|
|
||||||
shipping_total?: string;
|
|
||||||
// 运费税额 只读
|
|
||||||
shipping_tax?: string;
|
|
||||||
// 购物车税额 只读
|
|
||||||
cart_tax?: string;
|
|
||||||
// 客户ID
|
|
||||||
customer_id: number;
|
|
||||||
// 客户IP 只读
|
|
||||||
customer_ip_address?: string;
|
|
||||||
// 客户UA 只读
|
|
||||||
customer_user_agent?: string;
|
|
||||||
// 客户备注
|
|
||||||
customer_note?: string;
|
|
||||||
// 账单信息
|
|
||||||
billing?: {
|
|
||||||
first_name?: string;
|
|
||||||
last_name?: string;
|
|
||||||
email?: string;
|
|
||||||
company?: string;
|
|
||||||
address_1?: string;
|
|
||||||
address_2?: string;
|
|
||||||
city?: string;
|
|
||||||
state?: string;
|
|
||||||
postcode?: string;
|
|
||||||
country?: string;
|
|
||||||
phone?: string;
|
|
||||||
fullname?: string;
|
|
||||||
};
|
|
||||||
// 收货信息
|
|
||||||
shipping?: {
|
|
||||||
first_name?: string;
|
|
||||||
last_name?: string;
|
|
||||||
company?: string;
|
|
||||||
address_1?: string;
|
|
||||||
address_2?: string;
|
|
||||||
city?: string;
|
|
||||||
state?: string;
|
|
||||||
postcode?: string;
|
|
||||||
country?: string;
|
|
||||||
phone?: string;
|
|
||||||
fullname?: string;
|
|
||||||
};
|
|
||||||
// 订单项
|
|
||||||
line_items?: Array<{
|
|
||||||
product_id?: number;
|
|
||||||
variation_id?: number;
|
|
||||||
quantity?: number;
|
|
||||||
subtotal?: string;
|
|
||||||
subtotal_tax?: string;
|
|
||||||
total?: string;
|
|
||||||
total_tax?: string;
|
|
||||||
name?: string;
|
|
||||||
sku?: string;
|
|
||||||
price?: number;
|
|
||||||
meta_data?: Array<{ key: string; value: any }>;
|
|
||||||
[key: string]: any;
|
|
||||||
}>;
|
|
||||||
// 税费行 只读
|
|
||||||
tax_lines?: Array<{
|
|
||||||
id?: number;
|
|
||||||
rate_code?: string;
|
|
||||||
rate_id?: number;
|
|
||||||
label?: string;
|
|
||||||
tax_total?: string;
|
|
||||||
shipping_tax_total?: string;
|
|
||||||
compound?: boolean;
|
|
||||||
meta_data?: any[];
|
|
||||||
}>;
|
|
||||||
// 物流费用行
|
|
||||||
shipping_lines?: Array<{
|
|
||||||
id?: number;
|
|
||||||
method_title?: string;
|
|
||||||
method_id?: string;
|
|
||||||
total?: string;
|
|
||||||
total_tax?: string;
|
|
||||||
taxes?: any[];
|
|
||||||
meta_data?: any[];
|
|
||||||
}>;
|
|
||||||
// 手续费行
|
|
||||||
fee_lines?: Array<{
|
|
||||||
id?: number;
|
|
||||||
name?: string;
|
|
||||||
tax_class?: string;
|
|
||||||
tax_status?: string;
|
|
||||||
total?: string;
|
|
||||||
total_tax?: string;
|
|
||||||
taxes?: any[];
|
|
||||||
meta_data?: any[];
|
|
||||||
}>;
|
|
||||||
// 优惠券行
|
|
||||||
coupon_lines?: Array<{
|
|
||||||
id?: number;
|
|
||||||
code?: string;
|
|
||||||
discount?: string;
|
|
||||||
discount_tax?: string;
|
|
||||||
meta_data?: any[];
|
|
||||||
}>;
|
|
||||||
// 退款列表 只读
|
|
||||||
refunds?: Array<WooOrderRefund>;
|
|
||||||
// 支付方式标题
|
|
||||||
payment_method_title?: string;
|
|
||||||
// 支付方式ID
|
|
||||||
payment_method?: string;
|
|
||||||
// 交易ID
|
|
||||||
transaction_id?: string;
|
|
||||||
// 已支付时间
|
|
||||||
date_paid?: string;
|
|
||||||
date_paid_gmt?: string;
|
|
||||||
// 完成时间
|
|
||||||
date_completed?: string;
|
|
||||||
date_completed_gmt?: string;
|
|
||||||
// 购物车hash 只读
|
|
||||||
cart_hash?: string;
|
|
||||||
// 设置为已支付 写入专用
|
|
||||||
set_paid?: boolean;
|
|
||||||
// 元数据
|
|
||||||
meta_data?: Array<{ id?: number; key: string; value: any }>;
|
|
||||||
// 创建与更新时间
|
|
||||||
date_created: string;
|
|
||||||
date_created_gmt?: string;
|
|
||||||
date_modified?: string;
|
|
||||||
date_modified_gmt?: string;
|
|
||||||
// 物流追踪信息
|
|
||||||
fulfillments?: WooFulfillment[];
|
|
||||||
}
|
|
||||||
// 这个是一个插件的物流追踪信息
|
|
||||||
// 接口:
|
|
||||||
export interface WooFulfillment {
|
|
||||||
data_sipped: string;
|
|
||||||
tracking_id: string;
|
|
||||||
tracking_link: string;
|
|
||||||
tracking_number: string;
|
|
||||||
tracking_provider: string;
|
|
||||||
}
|
|
||||||
// https://docs.zorem.com/docs/ast-free/developers/adding-tracking-info-to-orders/
|
|
||||||
export interface WooFulfillmentCreateParams {
|
|
||||||
order_id: string;
|
|
||||||
tracking_provider: string;
|
|
||||||
tracking_number: string;
|
|
||||||
date_shipped?: string;
|
|
||||||
status_shipped?: string;
|
|
||||||
}
|
|
||||||
export interface WooOrderRefund {
|
|
||||||
id?: number;
|
|
||||||
reason?: string;
|
|
||||||
total?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 订阅类型
|
|
||||||
export interface WooSubscription {
|
|
||||||
// 订阅主键
|
|
||||||
id: number;
|
|
||||||
// 订阅状态
|
|
||||||
status: string;
|
|
||||||
// 客户ID
|
|
||||||
customer_id: number;
|
|
||||||
// 计费周期
|
|
||||||
billing_period?: string;
|
|
||||||
// 计费间隔
|
|
||||||
billing_interval?: number;
|
|
||||||
// 开始时间
|
|
||||||
start_date?: string;
|
|
||||||
// 下次支付时间
|
|
||||||
next_payment_date?: string;
|
|
||||||
// 订阅项
|
|
||||||
line_items?: any[];
|
|
||||||
// 创建时间
|
|
||||||
date_created?: string;
|
|
||||||
// 更新时间
|
|
||||||
date_modified?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// WordPress 媒体类型
|
|
||||||
export interface WpMedia {
|
|
||||||
// 媒体主键
|
|
||||||
id: number;
|
|
||||||
// 标题可能为字符串或包含rendered的对象
|
|
||||||
title?: { rendered?: string } | string;
|
|
||||||
// 媒体类型
|
|
||||||
media_type?: string;
|
|
||||||
// MIME类型
|
|
||||||
mime_type?: string;
|
|
||||||
// 源地址
|
|
||||||
source_url?: string;
|
|
||||||
// 创建时间兼容date字段
|
|
||||||
date_created?: string;
|
|
||||||
date?: string;
|
|
||||||
// 更新时间兼容modified字段
|
|
||||||
date_modified?: string;
|
|
||||||
modified?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 评论类型
|
|
||||||
export interface WooReview {
|
|
||||||
// 评论ID
|
|
||||||
id: number;
|
|
||||||
// 评论内容
|
|
||||||
review: string;
|
|
||||||
// 评分
|
|
||||||
rating: number;
|
|
||||||
// 评论者
|
|
||||||
reviewer: string;
|
|
||||||
// 评论者邮箱
|
|
||||||
reviewer_email: string;
|
|
||||||
// 状态
|
|
||||||
status: string;
|
|
||||||
// 产品ID
|
|
||||||
product_id: number;
|
|
||||||
// 创建日期
|
|
||||||
date_created: string;
|
|
||||||
// 更新日期
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 客户类型
|
|
||||||
export interface WooCustomer {
|
|
||||||
// 客户主键
|
|
||||||
id: number;
|
|
||||||
// 头像URL
|
|
||||||
avatar_url?: string;
|
|
||||||
// 邮箱
|
|
||||||
email: string;
|
|
||||||
// 订单总数
|
|
||||||
orders?: number;
|
|
||||||
// 总花费
|
|
||||||
total_spent?: number | string;
|
|
||||||
// 名
|
|
||||||
first_name?: string;
|
|
||||||
// 姓
|
|
||||||
last_name?: string;
|
|
||||||
// 用户名
|
|
||||||
username?: string;
|
|
||||||
// 角色 只读
|
|
||||||
role?: string;
|
|
||||||
// 密码 写入专用
|
|
||||||
password?: string;
|
|
||||||
// 账单信息
|
|
||||||
billing?: {
|
|
||||||
first_name?: string;
|
|
||||||
last_name?: string;
|
|
||||||
email?: string;
|
|
||||||
company?: string;
|
|
||||||
phone?: string;
|
|
||||||
address_1?: string;
|
|
||||||
address_2?: string;
|
|
||||||
city?: string;
|
|
||||||
state?: string;
|
|
||||||
postcode?: string;
|
|
||||||
country?: string;
|
|
||||||
};
|
|
||||||
// 收货信息
|
|
||||||
shipping?: {
|
|
||||||
first_name?: string;
|
|
||||||
last_name?: string;
|
|
||||||
company?: string;
|
|
||||||
phone?: string;
|
|
||||||
address_1?: string;
|
|
||||||
address_2?: string;
|
|
||||||
city?: string;
|
|
||||||
state?: string;
|
|
||||||
postcode?: string;
|
|
||||||
country?: string;
|
|
||||||
};
|
|
||||||
// 是否为付费客户 只读
|
|
||||||
is_paying_customer?: boolean;
|
|
||||||
// 元数据
|
|
||||||
meta_data?: Array<{ id?: number; key: string; value: any }>;
|
|
||||||
// 创建时间
|
|
||||||
date_created?: string;
|
|
||||||
date_created_gmt?: string;
|
|
||||||
// 更新时间
|
|
||||||
date_modified?: string;
|
|
||||||
date_modified_gmt?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Webhook类型
|
|
||||||
export interface WooWebhook {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
status: string;
|
|
||||||
topic: string;
|
|
||||||
resource: string;
|
|
||||||
event: string;
|
|
||||||
hooks: string;
|
|
||||||
delivery_url: string;
|
|
||||||
secret: string;
|
|
||||||
date_created: string;
|
|
||||||
date_created_gmt: string;
|
|
||||||
date_modified: string;
|
|
||||||
date_modified_gmt: string;
|
|
||||||
api_version: string;
|
|
||||||
meta_data?: Array<{ id?: number; key: string; value: any }>;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export interface WooOrderSearchParams {
|
|
||||||
context: WooContext;
|
|
||||||
page: number;
|
|
||||||
per_page: number;
|
|
||||||
search: string;
|
|
||||||
after: string;
|
|
||||||
before: string;
|
|
||||||
modified_after: string;
|
|
||||||
modified_before: string;
|
|
||||||
date_are_gmt: boolean;
|
|
||||||
exclude: string[];
|
|
||||||
include: string[];
|
|
||||||
offset: number;
|
|
||||||
order: string;
|
|
||||||
orderby: string;
|
|
||||||
parant: string[];
|
|
||||||
parent_exclude: string[];
|
|
||||||
status: WooOrderStatusSearchParams[];
|
|
||||||
customer: number;
|
|
||||||
product: number;
|
|
||||||
dp: number;
|
|
||||||
created_via: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export enum WooOrderStatusSearchParams {
|
|
||||||
pending,
|
|
||||||
processing,
|
|
||||||
"on-hold",
|
|
||||||
completed,
|
|
||||||
cancelled,
|
|
||||||
refunded,
|
|
||||||
failed,
|
|
||||||
trash,
|
|
||||||
any
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export interface WooProductSearchParams extends ListParams {
|
|
||||||
slug: string;
|
|
||||||
status: string[];
|
|
||||||
include_status: string;
|
|
||||||
exclude_status: string;
|
|
||||||
type: string;
|
|
||||||
include_types: string;
|
|
||||||
exclude_types: string;
|
|
||||||
sku: string;
|
|
||||||
featured: boolean;
|
|
||||||
category: string;
|
|
||||||
tag: string;
|
|
||||||
shipping_class: string;
|
|
||||||
attribute: string;
|
|
||||||
attribute_term: string;
|
|
||||||
tax_class: string;
|
|
||||||
on_sale: boolean;
|
|
||||||
min_price: string;
|
|
||||||
max_price: string;
|
|
||||||
stock_status: string;
|
|
||||||
virtual: boolean;
|
|
||||||
downloadable: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ListParams {
|
|
||||||
context: WooContext;
|
|
||||||
page: number;
|
|
||||||
per_page: number;
|
|
||||||
search: string;
|
|
||||||
search_fields: any[];
|
|
||||||
after: string;
|
|
||||||
before: string;
|
|
||||||
modified_after: string;
|
|
||||||
modified_before: string;
|
|
||||||
date_are_gmt: boolean;
|
|
||||||
exclude: string[];
|
|
||||||
include: string[];
|
|
||||||
offset: number;
|
|
||||||
order: string;
|
|
||||||
orderby: string;
|
|
||||||
parant: string[];
|
|
||||||
parent_exclude: string[];
|
|
||||||
}
|
|
||||||
export interface WpMediaGetListParams {
|
|
||||||
// 请求范围,决定响应中包含的字段
|
|
||||||
// 默认: view
|
|
||||||
// 可选值: view, embed, edit
|
|
||||||
context?: 'view' | 'embed' | 'edit';
|
|
||||||
|
|
||||||
// 当前页码
|
|
||||||
// 默认: 1
|
|
||||||
page?: number;
|
|
||||||
|
|
||||||
// 每页最大返回数量
|
|
||||||
// 默认: 10
|
|
||||||
per_page?: number;
|
|
||||||
|
|
||||||
// 搜索字符串,限制结果匹配
|
|
||||||
search?: string;
|
|
||||||
|
|
||||||
// ISO8601格式日期,限制发布时间之后的结果
|
|
||||||
after?: string;
|
|
||||||
|
|
||||||
// ISO8601格式日期,限制修改时间之后的结果
|
|
||||||
modified_after?: string;
|
|
||||||
|
|
||||||
// 作者ID数组,限制结果集为特定作者
|
|
||||||
author?: number[];
|
|
||||||
|
|
||||||
// 作者ID数组,排除特定作者的结果
|
|
||||||
author_exclude?: number[];
|
|
||||||
|
|
||||||
// ISO8601格式日期,限制发布时间之前的结果
|
|
||||||
before?: string;
|
|
||||||
|
|
||||||
// ISO8601格式日期,限制修改时间之前的结果
|
|
||||||
modified_before?: string;
|
|
||||||
|
|
||||||
// ID数组,排除特定ID的结果
|
|
||||||
exclude?: number[];
|
|
||||||
|
|
||||||
// ID数组,限制结果集为特定ID
|
|
||||||
include?: number[];
|
|
||||||
|
|
||||||
// 结果集偏移量
|
|
||||||
offset?: number;
|
|
||||||
|
|
||||||
// 排序方向
|
|
||||||
// 默认: desc
|
|
||||||
// 可选值: asc, desc
|
|
||||||
order?: 'asc' | 'desc';
|
|
||||||
|
|
||||||
// 排序字段
|
|
||||||
// 默认: date
|
|
||||||
// 可选值: author, date, id, include, modified, parent, relevance, slug, include_slugs, title
|
|
||||||
orderby?: 'author' | 'date' | 'id' | 'include' | 'modified' | 'parent' | 'relevance' | 'slug' | 'include_slugs' | 'title';
|
|
||||||
|
|
||||||
// 父ID数组,限制结果集为特定父ID
|
|
||||||
parent?: number[];
|
|
||||||
|
|
||||||
// 父ID数组,排除特定父ID的结果
|
|
||||||
parent_exclude?: number[];
|
|
||||||
|
|
||||||
// 搜索的列名数组
|
|
||||||
search_columns?: string[];
|
|
||||||
|
|
||||||
// slug数组,限制结果集为特定slug
|
|
||||||
slug?: string[];
|
|
||||||
|
|
||||||
// 状态数组,限制结果集为特定状态
|
|
||||||
// 默认: inherit
|
|
||||||
status?: string[];
|
|
||||||
|
|
||||||
// 媒体类型,限制结果集为特定媒体类型
|
|
||||||
// 可选值: image, video, text, application, audio
|
|
||||||
media_type?: 'image' | 'video' | 'text' | 'application' | 'audio';
|
|
||||||
|
|
||||||
// MIME类型,限制结果集为特定MIME类型
|
|
||||||
mime_type?: string;
|
|
||||||
}
|
|
||||||
export enum WooContext {
|
|
||||||
view,
|
|
||||||
edit
|
|
||||||
}
|
|
||||||
export enum WooProductStatusSearchParams {
|
|
||||||
any,
|
|
||||||
draft,
|
|
||||||
pending,
|
|
||||||
private,
|
|
||||||
publish
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发货相关DTO
|
|
||||||
export class WooShipOrderItemDTO {
|
|
||||||
order_item_id: number;
|
|
||||||
quantity: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WooShipOrderDTO {
|
|
||||||
tracking_number?: string;
|
|
||||||
shipping_provider?: string;
|
|
||||||
shipping_method?: string;
|
|
||||||
items?: WooShipOrderItemDTO[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WooCancelShipOrderDTO {
|
|
||||||
reason?: string;
|
|
||||||
shipment_id?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WooBatchShipOrderItemDTO {
|
|
||||||
order_id: string;
|
|
||||||
tracking_number?: string;
|
|
||||||
shipping_provider?: string;
|
|
||||||
shipping_method?: string;
|
|
||||||
items?: WooShipOrderItemDTO[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export class WooBatchShipOrdersDTO {
|
|
||||||
orders: WooBatchShipOrderItemDTO[];
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,98 @@
|
||||||
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
|
import { Variation } from '../entity/variation.entity';
|
||||||
|
import { WpProduct } from '../entity/wp_product.entity';
|
||||||
|
import { Rule, RuleType } from '@midwayjs/validate';
|
||||||
|
import { ProductStatus } from '../enums/base.enum';
|
||||||
|
|
||||||
|
export class VariationDTO extends Variation {}
|
||||||
|
|
||||||
|
export class WpProductDTO extends WpProduct {
|
||||||
|
@ApiProperty({ description: '变体列表', type: VariationDTO, isArray: true })
|
||||||
|
variations?: VariationDTO[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateVariationDTO {
|
||||||
|
@ApiProperty({ description: '产品名称' })
|
||||||
|
@Rule(RuleType.string())
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'SKU' })
|
||||||
|
@Rule(RuleType.string().allow(''))
|
||||||
|
sku: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '常规价格', type: Number })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
regular_price: number; // 常规价格
|
||||||
|
|
||||||
|
@ApiProperty({ description: '销售价格', type: Number })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
sale_price: number; // 销售价格
|
||||||
|
|
||||||
|
@ApiProperty({ description: '是否促销中', type: Boolean })
|
||||||
|
@Rule(RuleType.boolean())
|
||||||
|
on_sale: boolean; // 是否促销中
|
||||||
|
}
|
||||||
|
|
||||||
|
export class UpdateWpProductDTO {
|
||||||
|
@ApiProperty({ description: '变体名称' })
|
||||||
|
@Rule(RuleType.string())
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'SKU' })
|
||||||
|
@Rule(RuleType.string().allow(''))
|
||||||
|
sku: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '常规价格', type: Number })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
regular_price: number; // 常规价格
|
||||||
|
|
||||||
|
@ApiProperty({ description: '销售价格', type: Number })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
sale_price: number; // 销售价格
|
||||||
|
|
||||||
|
@ApiProperty({ description: '是否促销中', type: Boolean })
|
||||||
|
@Rule(RuleType.boolean())
|
||||||
|
on_sale: boolean; // 是否促销中
|
||||||
|
}
|
||||||
|
|
||||||
|
export class QueryWpProductDTO {
|
||||||
|
@ApiProperty({ example: '1', description: '页码' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
current: number;
|
||||||
|
|
||||||
|
@ApiProperty({ example: '10', description: '每页大小' })
|
||||||
|
@Rule(RuleType.number())
|
||||||
|
pageSize: number;
|
||||||
|
|
||||||
|
@ApiProperty({ example: 'ZYN', description: '产品名' })
|
||||||
|
@Rule(RuleType.string())
|
||||||
|
name?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ example: '1', description: '站点ID' })
|
||||||
|
@Rule(RuleType.string())
|
||||||
|
siteId?: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '产品状态', enum: ProductStatus })
|
||||||
|
@Rule(RuleType.string().valid(...Object.values(ProductStatus)))
|
||||||
|
status?: ProductStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class SetConstitutionDTO {
|
||||||
|
@ApiProperty({ type: Boolean })
|
||||||
|
@Rule(RuleType.boolean())
|
||||||
|
isProduct: boolean;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: '构成成分',
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
sku: { type: 'string' },
|
||||||
|
quantity: { type: 'number' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@Rule(RuleType.array())
|
||||||
|
constitution: { sku: string; quantity: number }[] | null;
|
||||||
|
}
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
|
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { Entity, PrimaryGeneratedColumn, Column, ManyToMany } from 'typeorm';
|
|
||||||
import { Site } from './site.entity';
|
|
||||||
import { StockPoint } from './stock_point.entity';
|
|
||||||
|
|
||||||
@Entity('area')
|
|
||||||
export class Area {
|
|
||||||
@PrimaryGeneratedColumn()
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '名称' })
|
|
||||||
@Column()
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '编码' })
|
|
||||||
@Column({ unique: true })
|
|
||||||
code: string;
|
|
||||||
|
|
||||||
@ManyToMany(() => Site, site => site.areas)
|
|
||||||
sites: Site[];
|
|
||||||
|
|
||||||
@ManyToMany(() => StockPoint, stockPoint => stockPoint.areas)
|
|
||||||
stockPoints: StockPoint[];
|
|
||||||
}
|
|
||||||
|
|
@ -1,43 +1,53 @@
|
||||||
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, OneToMany } from 'typeorm';
|
import {
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
Entity,
|
||||||
|
} from 'typeorm';
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
import { Product } from './product.entity';
|
|
||||||
import { CategoryAttribute } from './category_attribute.entity';
|
|
||||||
|
|
||||||
@Entity('category')
|
@Entity()
|
||||||
export class Category {
|
export class Category {
|
||||||
@ApiProperty({ description: 'ID' })
|
@ApiProperty({
|
||||||
|
example: '1',
|
||||||
|
description: '分类 ID',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@ApiProperty({ description: '分类显示名称' })
|
@ApiProperty({
|
||||||
|
example: '分类名称',
|
||||||
|
description: '分类名称',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
@Column()
|
@Column()
|
||||||
title: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类中文名称' })
|
|
||||||
@Column({ nullable: true })
|
|
||||||
titleCN: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类唯一标识' })
|
|
||||||
@Column({ unique: true })
|
|
||||||
name: string;
|
name: string;
|
||||||
// 分类短名称, 用于生成SKU
|
|
||||||
@ApiProperty({ description: '分类短名称' })
|
|
||||||
@Column({ nullable: true })
|
|
||||||
shortName: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '排序' })
|
@ApiProperty({
|
||||||
@Column({ default: 0 })
|
description: '唯一识别key',
|
||||||
sort: number;
|
type: 'string',
|
||||||
|
required: true,
|
||||||
@OneToMany(() => Product, product => product.category)
|
})
|
||||||
products: Product[];
|
@Column()
|
||||||
|
unique_key: string;
|
||||||
@OneToMany(() => CategoryAttribute, categoryAttribute => categoryAttribute.category)
|
|
||||||
attributes: CategoryAttribute[];
|
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2022-12-12 11:11:11',
|
||||||
|
description: '创建时间',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2022-12-12 11:11:11',
|
||||||
|
description: '更新时间',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
@UpdateDateColumn()
|
@UpdateDateColumn()
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
||||||
import { Entity, PrimaryGeneratedColumn, ManyToOne, JoinColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
|
||||||
import { Category } from './category.entity';
|
|
||||||
import { Dict } from './dict.entity';
|
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
export class CategoryAttribute {
|
|
||||||
@PrimaryGeneratedColumn()
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类' })
|
|
||||||
@ManyToOne(() => Category, { onDelete: 'CASCADE' })
|
|
||||||
@JoinColumn({ name: 'category_id' })
|
|
||||||
category: Category;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '关联的属性字典' })
|
|
||||||
@ManyToOne(() => Dict, { onDelete: 'CASCADE' })
|
|
||||||
@JoinColumn({ name: 'attribute_dict_id' })
|
|
||||||
attributeDict: Dict;
|
|
||||||
|
|
||||||
@CreateDateColumn()
|
|
||||||
createdAt: Date;
|
|
||||||
|
|
||||||
@UpdateDateColumn()
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
||||||
|
|
@ -1,58 +1,13 @@
|
||||||
import { Column, Entity, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn } from 'typeorm';
|
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
|
||||||
@Entity('customer')
|
@Entity('customer')
|
||||||
export class Customer {
|
export class Customer {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
site_id: number;
|
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
origin_id: string;
|
|
||||||
|
|
||||||
@Column({ unique: true })
|
@Column({ unique: true })
|
||||||
email: string;
|
email: string;
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
first_name: string;
|
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
last_name: string;
|
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
fullname: string;
|
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
username: string;
|
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
phone: string;
|
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
avatar: string;
|
|
||||||
|
|
||||||
@Column({ type: 'json', nullable: true })
|
|
||||||
billing: any;
|
|
||||||
|
|
||||||
@Column({ type: 'json', nullable: true })
|
|
||||||
shipping: any;
|
|
||||||
|
|
||||||
@Column({ type: 'json', nullable: true })
|
|
||||||
raw: any;
|
|
||||||
|
|
||||||
@Column({ default: 0})
|
@Column({ default: 0})
|
||||||
rate: number;
|
rate: number;
|
||||||
|
|
||||||
@CreateDateColumn()
|
|
||||||
created_at: Date;
|
|
||||||
|
|
||||||
@UpdateDateColumn()
|
|
||||||
updated_at: Date;
|
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
site_created_at: Date;
|
|
||||||
|
|
||||||
@Column({ nullable: true })
|
|
||||||
site_updated_at: Date;
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
/**
|
|
||||||
* @description 字典
|
|
||||||
* @author ZKS
|
|
||||||
* @date 2025-11-27
|
|
||||||
*/
|
|
||||||
import { DictItem } from './dict_item.entity';
|
|
||||||
import {
|
|
||||||
Column,
|
|
||||||
CreateDateColumn,
|
|
||||||
Entity,
|
|
||||||
OneToMany,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
UpdateDateColumn,
|
|
||||||
} from 'typeorm';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
export class Dict {
|
|
||||||
// 主键
|
|
||||||
@PrimaryGeneratedColumn()
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@Column({comment: '字典显示名称'})
|
|
||||||
title: string;
|
|
||||||
// 字典名称
|
|
||||||
@Column({ unique: true, comment: '字典名称' })
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
// 字典项
|
|
||||||
@OneToMany(() => DictItem, item => item.dict)
|
|
||||||
items: DictItem[];
|
|
||||||
|
|
||||||
// 排序
|
|
||||||
@Column({ default: 0, comment: '排序' })
|
|
||||||
sort: number;
|
|
||||||
|
|
||||||
// 是否可删除
|
|
||||||
@Column({ default: true, comment: '是否可删除' })
|
|
||||||
deletable: boolean;
|
|
||||||
// 创建时间
|
|
||||||
@CreateDateColumn()
|
|
||||||
createdAt: Date;
|
|
||||||
|
|
||||||
// 更新时间
|
|
||||||
@UpdateDateColumn()
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +0,0 @@
|
||||||
/**
|
|
||||||
* @description 字典项
|
|
||||||
* @author ZKS
|
|
||||||
* @date 2025-11-27
|
|
||||||
*/
|
|
||||||
import { Dict } from './dict.entity';
|
|
||||||
import { Product } from './product.entity';
|
|
||||||
import {
|
|
||||||
Column,
|
|
||||||
CreateDateColumn,
|
|
||||||
Entity,
|
|
||||||
Index,
|
|
||||||
JoinColumn,
|
|
||||||
ManyToMany,
|
|
||||||
ManyToOne,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
UpdateDateColumn,
|
|
||||||
} from 'typeorm';
|
|
||||||
|
|
||||||
@Entity()
|
|
||||||
@Index(['name', 'dict'], { unique: true })
|
|
||||||
export class DictItem {
|
|
||||||
// 主键
|
|
||||||
@PrimaryGeneratedColumn()
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
// 字典项名称
|
|
||||||
@Column({ comment: '字典项显示名称' })
|
|
||||||
title: string;
|
|
||||||
// 目前没有单独做国际化, 所以这里先添加 titleCN 用来标注
|
|
||||||
@Column({ comment: '字典项中文名称', nullable: true })
|
|
||||||
titleCN: string;
|
|
||||||
// 唯一标识
|
|
||||||
@Column({ comment: '字典唯一标识名称' })
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
@Column({ nullable: true, comment: '简称' })
|
|
||||||
shortName: string;
|
|
||||||
|
|
||||||
@Column({ nullable: true, comment: '字典项描述' })
|
|
||||||
description?: string
|
|
||||||
|
|
||||||
// 字典项值
|
|
||||||
@Column({ nullable: true, comment: '字典项值' })
|
|
||||||
value?: string;
|
|
||||||
|
|
||||||
@Column({ nullable: true, comment: '图片' })
|
|
||||||
image: string;
|
|
||||||
|
|
||||||
// 排序
|
|
||||||
@Column({ default: 0, comment: '排序' })
|
|
||||||
sort: number;
|
|
||||||
|
|
||||||
// 属于哪个字典
|
|
||||||
@ManyToOne(() => Dict, dict => dict.items)
|
|
||||||
@JoinColumn({ name: 'dict_id' })
|
|
||||||
dict: Dict;
|
|
||||||
|
|
||||||
// 关联的产品
|
|
||||||
@ManyToMany(() => Product, product => product.attributes)
|
|
||||||
products: Product[];
|
|
||||||
|
|
||||||
// 创建时间
|
|
||||||
@CreateDateColumn()
|
|
||||||
createdAt: Date;
|
|
||||||
|
|
||||||
// 更新时间
|
|
||||||
@UpdateDateColumn()
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
import {
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
Entity,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Flavors {
|
||||||
|
@ApiProperty()
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: '唯一识别key',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@Column()
|
||||||
|
unique_key: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2022-12-12 11:11:11',
|
||||||
|
description: '创建时间',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2022-12-12 11:11:11',
|
||||||
|
description: '更新时间',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
@ -24,9 +24,9 @@ export class Order {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
siteId: number; // 来源站点唯一标识
|
siteId: string; // 来源站点唯一标识
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column()
|
@Column()
|
||||||
|
|
@ -62,14 +62,14 @@ export class Order {
|
||||||
currency: string;
|
currency: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
currency_symbol?: string;
|
currency_symbol: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
@Expose()
|
@Expose()
|
||||||
prices_include_tax?: boolean;
|
prices_include_tax: boolean;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'timestamp', nullable: true })
|
@Column({ type: 'timestamp', nullable: true })
|
||||||
|
|
@ -178,7 +178,7 @@ export class Order {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({
|
@Column({
|
||||||
type: 'mediumtext', // 设置字段类型为 MEDIUMTEXT
|
type: 'mediumtext', // 设置字段类型为 MEDIUMTEXT
|
||||||
nullable: true, // 可选:是否允许为 NULL
|
nullable: true, // 可选:是否允许为 NULL
|
||||||
})
|
})
|
||||||
@Expose()
|
@Expose()
|
||||||
customer_note: string;
|
customer_note: string;
|
||||||
|
|
@ -272,14 +272,6 @@ export class Order {
|
||||||
@Expose()
|
@Expose()
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
|
|
||||||
@ApiProperty({ type: 'json', nullable: true, description: '订单项列表' })
|
|
||||||
@Expose()
|
|
||||||
orderItems?: any[];
|
|
||||||
|
|
||||||
@ApiProperty({ type: 'json', nullable: true, description: '销售项列表' })
|
|
||||||
@Expose()
|
|
||||||
orderSales?: any[];
|
|
||||||
|
|
||||||
// 在插入或更新前处理用户代理字符串
|
// 在插入或更新前处理用户代理字符串
|
||||||
@BeforeInsert()
|
@BeforeInsert()
|
||||||
@BeforeUpdate()
|
@BeforeUpdate()
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ export class OrderCoupon {
|
||||||
orderId: number; // 订单 ID
|
orderId: number; // 订单 ID
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
siteId: number; // 站点ID
|
siteId: string; // 来源站点唯一标识
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column()
|
@Column()
|
||||||
|
|
@ -22,9 +22,9 @@ export class OrderFee {
|
||||||
orderId: number; // 订单 ID
|
orderId: number; // 订单 ID
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
siteId: number; // 站点ID
|
siteId: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column()
|
@Column()
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { Exclude, Expose } from 'class-transformer';
|
|
||||||
import {
|
|
||||||
Column,
|
|
||||||
CreateDateColumn,
|
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
UpdateDateColumn,
|
|
||||||
} from 'typeorm';
|
|
||||||
|
|
||||||
@Entity('order_fulfillment')
|
|
||||||
@Exclude()
|
|
||||||
export class OrderFulfillment {
|
|
||||||
@ApiProperty()
|
|
||||||
@PrimaryGeneratedColumn()
|
|
||||||
@Expose()
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ name: 'order_id', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
order_id: number; // 订单 ID
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ name: 'site_id', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
site_id: number; // 站点ID
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ name: 'external_order_id', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
external_order_id: string; // 外部订单 ID
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ name: 'external_fulfillment_id', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
external_fulfillment_id: string; // 外部履约 ID
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ name: 'tracking_number' })
|
|
||||||
@Expose()
|
|
||||||
tracking_number: string; // 物流单号
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ name: 'shipping_provider', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
shipping_provider: string; // 物流公司
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ name: 'shipping_method', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
shipping_method: string; // 发货方式
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ nullable: true })
|
|
||||||
@Expose()
|
|
||||||
status: string; // 状态
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ name: 'date_created', type: 'timestamp', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
date_created: Date; // 创建时间
|
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column({ type: 'json', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
items: any[]; // 发货商品项
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: '2022-12-12 11:11:11',
|
|
||||||
description: '创建时间',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@CreateDateColumn({ name: 'created_at' })
|
|
||||||
@Expose()
|
|
||||||
created_at: Date;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: '2022-12-12 11:11:11',
|
|
||||||
description: '更新时间',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@UpdateDateColumn({ name: 'updated_at' })
|
|
||||||
@Expose()
|
|
||||||
updated_at: Date;
|
|
||||||
}
|
|
||||||
|
|
@ -22,9 +22,9 @@ export class OrderItem {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
siteId: number; // 来源站点唯一标识
|
siteId: string; // 来源站点唯一标识
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column()
|
@Column()
|
||||||
|
|
@ -79,17 +79,17 @@ export class OrderItem {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
tax_class?: string; // 税类(来自 line_items.tax_class)
|
tax_class?: string; // 税类(来自 line_items.tax_class)
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'json', nullable: true })
|
@Column({ type: 'json', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
taxes?: any[]; // 税明细(来自 line_items.taxes,数组)
|
taxes?: any[]; // 税明细(来自 line_items.taxes,数组)
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'json', nullable: true })
|
@Column({ type: 'json', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
meta_data?: any[]; // 行项目元数据(包含订阅相关键值)
|
meta_data?: any[]; // 行项目元数据(包含订阅相关键值)
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
|
|
@ -99,7 +99,7 @@ export class OrderItem {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
global_unique_id?: string; // 全局唯一ID(部分主题/插件会提供)
|
global_unique_id?: string; // 全局唯一ID(部分主题/插件会提供)
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column('decimal', { precision: 10, scale: 2 })
|
@Column('decimal', { precision: 10, scale: 2 })
|
||||||
|
|
@ -109,17 +109,17 @@ export class OrderItem {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'json', nullable: true })
|
@Column({ type: 'json', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
image?: { id?: string | number; src?: string }; // 商品图片(对象,包含 id/src)
|
image?: { id?: string | number; src?: string }; // 商品图片(对象,包含 id/src)
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
parent_name?: string; // 父商品名称(组合/捆绑时可能使用)
|
parent_name?: string; // 父商品名称(组合/捆绑时可能使用)
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
bundled_by?: string; // 捆绑来源标识(bundled_by)
|
bundled_by?: string; // 捆绑来源标识(bundled_by)
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
|
|
@ -129,7 +129,7 @@ export class OrderItem {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'json', nullable: true })
|
@Column({ type: 'json', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
bundled_items?: any[]; // 捆绑项列表(数组)
|
bundled_items?: any[]; // 捆绑项列表(数组)
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: '2022-12-12 11:11:11',
|
example: '2022-12-12 11:11:11',
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,9 @@ import {
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { Order } from './order.entity';
|
import { Order } from './order.entity';
|
||||||
|
|
||||||
@Entity('order_item_original')
|
@Entity('order_sale_original')
|
||||||
@Exclude()
|
@Exclude()
|
||||||
export class OrderItemOriginal {
|
export class OrderSaleOriginal {
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
@Expose()
|
@Expose()
|
||||||
|
|
@ -27,9 +27,9 @@ export class OrderItemOriginal {
|
||||||
orderId: number; // 订单 ID
|
orderId: number; // 订单 ID
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
siteId: number; // 站点ID
|
siteId: string; // 来源站点唯一标识
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ export class OrderRefund {
|
||||||
orderId: number; // 订单 ID
|
orderId: number; // 订单 ID
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
siteId: number; // 站点ID
|
siteId: string; // 来源站点唯一标识
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column()
|
@Column()
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ export class OrderRefundItem {
|
||||||
refundId: number; // 订单 refund ID
|
refundId: number; // 订单 refund ID
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
siteId: number; // 站点ID
|
siteId: string; // 来源站点唯一标识
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column()
|
@Column()
|
||||||
|
|
@ -1,52 +1,43 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
import { Exclude, Expose } from 'class-transformer';
|
import { Exclude, Expose } from 'class-transformer';
|
||||||
import {
|
import {
|
||||||
// BeforeInsert,
|
BeforeInsert,
|
||||||
// BeforeUpdate,
|
BeforeUpdate,
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
Entity,
|
Entity,
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
// import { Product } from './product.entity';
|
|
||||||
|
|
||||||
@Entity('order_sale')
|
@Entity('order_sale')
|
||||||
@Exclude()
|
@Exclude()
|
||||||
export class OrderSale {
|
export class OrderSale {
|
||||||
// @ManyToOne(() => Product, { onDelete: 'CASCADE' })
|
|
||||||
// @JoinColumn({ name: 'productId' })
|
|
||||||
// product: Product;
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
@Expose()
|
@Expose()
|
||||||
id?: number;
|
id?: number;
|
||||||
|
|
||||||
@ApiProperty({ name:'原始订单ID' })
|
@ApiProperty()
|
||||||
@Column()
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
orderId: number; // 订单 ID
|
orderId: number; // 订单 ID
|
||||||
|
|
||||||
@ApiProperty({ name:'站点' })
|
@ApiProperty()
|
||||||
@Column()
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
siteId: number; // 来源站点唯一标识
|
siteId: string; // 来源站点唯一标识
|
||||||
|
|
||||||
@ApiProperty({name: "原始订单 itemId"})
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
externalOrderItemId: string; // WooCommerce 订单item ID
|
externalOrderItemId: string; // WooCommerce 订单item ID
|
||||||
|
|
||||||
@ApiProperty({name: "父产品 ID"})
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
|
||||||
@Expose()
|
|
||||||
parentProductId?: number; // 父产品 ID 用于统计套餐 如果是单品则不记录
|
|
||||||
|
|
||||||
@ApiProperty({name: "产品 ID"})
|
|
||||||
@Column()
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
productId: number;
|
productId: number;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column()
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
|
|
@ -55,7 +46,7 @@ export class OrderSale {
|
||||||
@ApiProperty({ description: 'sku', type: 'string' })
|
@ApiProperty({ description: 'sku', type: 'string' })
|
||||||
@Expose()
|
@Expose()
|
||||||
@Column()
|
@Column()
|
||||||
sku: string;// 库存产品sku
|
sku: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column()
|
@Column()
|
||||||
|
|
@ -67,40 +58,25 @@ export class OrderSale {
|
||||||
@Expose()
|
@Expose()
|
||||||
isPackage: boolean;
|
isPackage: boolean;
|
||||||
|
|
||||||
@ApiProperty({ description: '商品品类', type: 'string',nullable: true})
|
@ApiProperty()
|
||||||
|
@Column({ default: false })
|
||||||
@Expose()
|
@Expose()
|
||||||
@Column({ nullable: true })
|
isYoone: boolean;
|
||||||
category?: string;
|
|
||||||
// TODO 这个其实还是直接保存 product 比较好
|
|
||||||
@ApiProperty({ description: '品牌', type: 'string',nullable: true})
|
|
||||||
@Expose()
|
|
||||||
@Column({ nullable: true })
|
|
||||||
brand?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '口味', type: 'string', nullable: true })
|
@ApiProperty()
|
||||||
|
@Column({ default: false })
|
||||||
@Expose()
|
@Expose()
|
||||||
@Column({ nullable: true })
|
isZex: boolean;
|
||||||
flavor?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '湿度', type: 'string', nullable: true })
|
@ApiProperty({ nullable: true })
|
||||||
|
@Column({ type: 'int', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
@Column({ nullable: true })
|
size: number | null;
|
||||||
humidity?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '尺寸', type: 'string', nullable: true })
|
@ApiProperty()
|
||||||
|
@Column({ default: false })
|
||||||
@Expose()
|
@Expose()
|
||||||
@Column({ nullable: true })
|
isYooneNew: boolean;
|
||||||
size?: string;
|
|
||||||
|
|
||||||
@ApiProperty({name: '强度', nullable: true })
|
|
||||||
@Column({ nullable: true })
|
|
||||||
@Expose()
|
|
||||||
strength: string | null;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '版本', type: 'string', nullable: true })
|
|
||||||
@Expose()
|
|
||||||
@Column({ nullable: true })
|
|
||||||
version?: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: '2022-12-12 11:11:11',
|
example: '2022-12-12 11:11:11',
|
||||||
|
|
@ -117,4 +93,25 @@ export class OrderSale {
|
||||||
@UpdateDateColumn()
|
@UpdateDateColumn()
|
||||||
@Expose()
|
@Expose()
|
||||||
updatedAt?: Date;
|
updatedAt?: Date;
|
||||||
|
|
||||||
|
// === 自动计算逻辑 ===
|
||||||
|
@BeforeInsert()
|
||||||
|
@BeforeUpdate()
|
||||||
|
setFlags() {
|
||||||
|
if (!this.name) return;
|
||||||
|
const lower = this.name.toLowerCase();
|
||||||
|
this.isYoone = lower.includes('yoone');
|
||||||
|
this.isZex = lower.includes('zex');
|
||||||
|
this.isYooneNew = this.isYoone && lower.includes('new');
|
||||||
|
let size: number | null = null;
|
||||||
|
const sizes = [3, 6, 9, 12, 15, 18];
|
||||||
|
for (const s of sizes) {
|
||||||
|
if (lower.includes(s.toString())) {
|
||||||
|
size = s;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.size = size;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,9 @@ export class OrderShipping {
|
||||||
orderId: number; // 订单 ID
|
orderId: number; // 订单 ID
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
siteId: number; // 站点ID
|
siteId: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column()
|
@Column()
|
||||||
|
|
@ -47,9 +47,9 @@ export class OrderShipping {
|
||||||
method_id: string;
|
method_id: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
instance_id?: string;
|
instance_id: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column('decimal', { precision: 10, scale: 2 })
|
@Column('decimal', { precision: 10, scale: 2 })
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,10 @@ import {
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
Entity,
|
Entity,
|
||||||
ManyToMany,
|
|
||||||
JoinTable,
|
|
||||||
OneToMany,
|
|
||||||
ManyToOne,
|
|
||||||
JoinColumn,
|
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
import { DictItem } from './dict_item.entity';
|
|
||||||
import { ProductStockComponent } from './product_stock_component.entity';
|
|
||||||
import { Category } from './category.entity';
|
|
||||||
|
|
||||||
@Entity('product')
|
@Entity()
|
||||||
export class Product {
|
export class Product {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: '1',
|
example: '1',
|
||||||
|
|
@ -25,87 +17,43 @@ export class Product {
|
||||||
})
|
})
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@ApiProperty({ description: 'sku'})
|
|
||||||
@Column({ unique: true })
|
|
||||||
sku: string;
|
|
||||||
// 类型 主要用来区分混装和单品 单品死
|
|
||||||
@ApiProperty({ description: '类型' })
|
|
||||||
@Column({ length: 16, default: 'single' })
|
|
||||||
type: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: 'ZYN 6MG WINTERGREEN',
|
example: 'ZYN 6MG WINTERGREEN',
|
||||||
description: '产品名称',
|
description: '产品名称',
|
||||||
type: 'string',
|
type: 'string',
|
||||||
required: true,
|
required: true,
|
||||||
})
|
})
|
||||||
@Column()
|
@Column()
|
||||||
name: string;
|
name: string;
|
||||||
|
|
||||||
@ApiProperty({ description: '产品中文名称' })
|
@ApiProperty()
|
||||||
@Column({ default: '' })
|
@Column({ default: ''})
|
||||||
nameCn: string;
|
nameCn: string;
|
||||||
|
|
||||||
@ApiProperty({ example: '产品简短描述', description: '产品简短描述' })
|
@ApiProperty({ example: '产品描述', description: '产品描述', type: 'string' })
|
||||||
@Column({ nullable: true })
|
|
||||||
shortDescription?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ example: '产品描述', description: '产品描述' })
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
description?: string;
|
description?: string;
|
||||||
|
|
||||||
@ApiProperty({ example: '图片URL', description: '产品图片URL' })
|
@ApiProperty({ example: '1', description: '分类 ID', type: 'number' })
|
||||||
|
@Column()
|
||||||
|
categoryId: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column()
|
||||||
|
flavorsId: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column()
|
||||||
|
strengthId: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column()
|
||||||
|
humidity: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'sku', type: 'string' })
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
image?: string;
|
sku?: string;
|
||||||
// 商品价格
|
|
||||||
@ApiProperty({ description: '价格', example: 99.99 })
|
|
||||||
@Column({ type: 'decimal', precision: 10, scale: 2, default: 0 })
|
|
||||||
price: number;
|
|
||||||
|
|
||||||
// 促销价格
|
|
||||||
@ApiProperty({ description: '促销价格', example: 99.99 })
|
|
||||||
@Column({ type: 'decimal', precision: 10, scale: 2, default: 0 })
|
|
||||||
promotionPrice: number;
|
|
||||||
|
|
||||||
// 分类关联
|
|
||||||
@ManyToOne(() => Category, category => category.products)
|
|
||||||
@JoinColumn({ name: 'categoryId' })
|
|
||||||
category: Category;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '分类 ID', nullable: true, example: 1 })
|
|
||||||
@Column({ nullable: true })
|
|
||||||
categoryId?: number;
|
|
||||||
|
|
||||||
@ManyToMany(() => DictItem, dictItem => dictItem.products, {
|
|
||||||
cascade: true,
|
|
||||||
})
|
|
||||||
@JoinTable({
|
|
||||||
name: 'product_attributes_dict_item',
|
|
||||||
joinColumn: {
|
|
||||||
name: 'productId',
|
|
||||||
referencedColumnName: 'id'
|
|
||||||
},
|
|
||||||
inverseJoinColumn: {
|
|
||||||
name: 'dictItemId',
|
|
||||||
referencedColumnName: 'id'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
attributes: DictItem[];
|
|
||||||
|
|
||||||
// 产品的库存组成,一对多关系(使用独立表)
|
|
||||||
@ApiProperty({ description: '库存组成', type: ProductStockComponent, isArray: true })
|
|
||||||
@OneToMany(() => ProductStockComponent, (component) => component.product, { cascade: true })
|
|
||||||
components: ProductStockComponent[];
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点 SKU 列表', type: 'string', isArray: true })
|
|
||||||
@Column({ type: 'simple-array' ,nullable:true})
|
|
||||||
siteSkus: string[];
|
|
||||||
|
|
||||||
// 来源
|
|
||||||
@ApiProperty({ description: '来源', example: '1' })
|
|
||||||
@Column({ default: 0 })
|
|
||||||
source: number;
|
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: '2022-12-12 11:11:11',
|
example: '2022-12-12 11:11:11',
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
|
|
||||||
import { Product } from './product.entity';
|
|
||||||
|
|
||||||
@Entity('product_stock_component')
|
|
||||||
export class ProductStockComponent {
|
|
||||||
@ApiProperty({ type: Number })
|
|
||||||
@PrimaryGeneratedColumn()
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ type: Number })
|
|
||||||
@Column()
|
|
||||||
productId: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '组件所关联的 SKU', type: 'string' })
|
|
||||||
@Column({ type: 'varchar', length: 64 })
|
|
||||||
sku: string;
|
|
||||||
|
|
||||||
@ApiProperty({ type: Number, description: '组成数量' })
|
|
||||||
@Column({ type: 'int', default: 1 })
|
|
||||||
quantity: number;
|
|
||||||
|
|
||||||
// 多对一,组件隶属于一个产品
|
|
||||||
@ManyToOne(() => Product, (product) => product.components, { onDelete: 'CASCADE' })
|
|
||||||
product: Product;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '创建时间' })
|
|
||||||
@CreateDateColumn()
|
|
||||||
createdAt: Date;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '更新时间' })
|
|
||||||
@UpdateDateColumn()
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
||||||
|
|
@ -10,11 +10,11 @@ export class PurchaseOrderItem {
|
||||||
|
|
||||||
@ApiProperty({ type: String })
|
@ApiProperty({ type: String })
|
||||||
@Column()
|
@Column()
|
||||||
sku: string;
|
productSku: string;
|
||||||
|
|
||||||
@ApiProperty({ type: String })
|
@ApiProperty({ type: String })
|
||||||
@Column()
|
@Column()
|
||||||
name: string;
|
productName: string;
|
||||||
|
|
||||||
@ApiProperty({ type: Number })
|
@ApiProperty({ type: Number })
|
||||||
@Column()
|
@Column()
|
||||||
|
|
|
||||||
|
|
@ -54,9 +54,9 @@ export class Shipment {
|
||||||
tracking_provider?: string;
|
tracking_provider?: string;
|
||||||
|
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
unique_id?: string;
|
unique_id: string;
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
|
|
|
||||||
|
|
@ -47,11 +47,6 @@ export class ShippingAddress {
|
||||||
@Expose()
|
@Expose()
|
||||||
phone_number_country: string;
|
phone_number_country: string;
|
||||||
|
|
||||||
@ApiProperty()
|
|
||||||
@Column()
|
|
||||||
@Expose()
|
|
||||||
email: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: '2022-12-12 11:11:11',
|
example: '2022-12-12 11:11:11',
|
||||||
description: '创建时间',
|
description: '创建时间',
|
||||||
|
|
|
||||||
|
|
@ -1,86 +0,0 @@
|
||||||
import {
|
|
||||||
Column,
|
|
||||||
CreateDateColumn,
|
|
||||||
UpdateDateColumn,
|
|
||||||
Entity,
|
|
||||||
ManyToOne,
|
|
||||||
JoinColumn,
|
|
||||||
} from 'typeorm';
|
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import { Site } from './site.entity';
|
|
||||||
import { Product } from './product.entity';
|
|
||||||
|
|
||||||
@Entity('site_product')
|
|
||||||
export class SiteProduct {
|
|
||||||
@ApiProperty({
|
|
||||||
example: '12345',
|
|
||||||
description: '站点商品ID',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@Column({ primary: true })
|
|
||||||
id: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '站点ID' })
|
|
||||||
@Column()
|
|
||||||
siteId: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '商品ID' })
|
|
||||||
@Column({ nullable: true })
|
|
||||||
productId: number;
|
|
||||||
|
|
||||||
@ApiProperty({ description: 'sku'})
|
|
||||||
@Column()
|
|
||||||
sku: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '类型' })
|
|
||||||
@Column({ length: 16, default: 'single' })
|
|
||||||
type: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
description: '产品名称',
|
|
||||||
type: 'string',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@Column()
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '产品图片' })
|
|
||||||
@Column({ default: '' })
|
|
||||||
image: string;
|
|
||||||
|
|
||||||
@ApiProperty({ description: '父商品ID', example: '12345' })
|
|
||||||
@Column({ nullable: true })
|
|
||||||
parentId: string;
|
|
||||||
|
|
||||||
// 站点关联
|
|
||||||
@ManyToOne(() => Site, site => site.id)
|
|
||||||
@JoinColumn({ name: 'siteId' })
|
|
||||||
site: Site;
|
|
||||||
|
|
||||||
// 商品关联
|
|
||||||
@ManyToOne(() => Product, product => product.id)
|
|
||||||
@JoinColumn({ name: 'productId' })
|
|
||||||
product: Product;
|
|
||||||
|
|
||||||
// 父商品关联
|
|
||||||
@ManyToOne(() => SiteProduct, siteProduct => siteProduct.id)
|
|
||||||
@JoinColumn({ name: 'parentId' })
|
|
||||||
parent: SiteProduct;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: '2022-12-12 11:11:11',
|
|
||||||
description: '创建时间',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@CreateDateColumn()
|
|
||||||
createdAt: Date;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: '2022-12-12 11:11:11',
|
|
||||||
description: '更新时间',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@UpdateDateColumn()
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
||||||
|
|
@ -1,70 +1,28 @@
|
||||||
import { Column, Entity, JoinTable, ManyToMany, PrimaryGeneratedColumn } from 'typeorm';
|
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { Area } from './area.entity';
|
|
||||||
import { StockPoint } from './stock_point.entity';
|
|
||||||
|
|
||||||
@Entity('site')
|
@Entity('site')
|
||||||
export class Site {
|
export class Site {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn({ type: 'int' })
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
@Column({ length: 255, nullable: true })
|
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||||
apiUrl: string;
|
apiUrl: string;
|
||||||
|
|
||||||
@Column({ name: 'website_url', length: 255, nullable: true })
|
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||||
websiteUrl?: string;
|
consumerKey: string;
|
||||||
|
|
||||||
@Column({ name: 'webhook_url', length: 255, nullable: true })
|
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||||
webhookUrl?: string;
|
consumerSecret: string;
|
||||||
|
|
||||||
@Column({ length: 255, nullable: true })
|
@Column({ type: 'varchar', length: 255, unique: true })
|
||||||
consumerKey?: string;
|
siteName: string;
|
||||||
|
|
||||||
@Column({ length: 255, nullable: true })
|
@Column({ type: 'varchar', length: 32, default: 'woocommerce' })
|
||||||
consumerSecret?: string;
|
type: string; // 平台类型:woocommerce | shopyy
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ type: 'varchar', length: 64, nullable: true })
|
||||||
token?: string;
|
|
||||||
|
|
||||||
@Column({ length: 255, unique: true })
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
@Column({ length: 255, nullable: true })
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
@Column({ length: 32, default: 'woocommerce' })
|
|
||||||
type: string; // 平台类型:woocommerce | shopyy
|
|
||||||
|
|
||||||
@Column({ length: 64, nullable: true })
|
|
||||||
skuPrefix: string;
|
skuPrefix: string;
|
||||||
|
|
||||||
@Column({ default: false })
|
@Column({ type: 'tinyint', default: 0 })
|
||||||
isDisabled: boolean;
|
isDisabled: number;
|
||||||
|
|
||||||
@ManyToMany(() => Area, { cascade: true })
|
|
||||||
@JoinTable({
|
|
||||||
name: 'site_areas_area',
|
|
||||||
joinColumn: {
|
|
||||||
name: 'siteId',
|
|
||||||
referencedColumnName: 'id'
|
|
||||||
},
|
|
||||||
inverseJoinColumn: {
|
|
||||||
name: 'areaId',
|
|
||||||
referencedColumnName: 'id'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
areas: Area[];
|
|
||||||
|
|
||||||
@ManyToMany(() => StockPoint, stockPoint => stockPoint.sites)
|
|
||||||
@JoinTable({
|
|
||||||
name: 'site_stock_points_stock_point',
|
|
||||||
joinColumn: {
|
|
||||||
name: 'siteId',
|
|
||||||
referencedColumnName: 'id'
|
|
||||||
},
|
|
||||||
inverseJoinColumn: {
|
|
||||||
name: 'stockPointId',
|
|
||||||
referencedColumnName: 'id'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
stockPoints: StockPoint[];
|
|
||||||
}
|
}
|
||||||
|
|
@ -20,7 +20,7 @@ export class Stock {
|
||||||
|
|
||||||
@ApiProperty({ type: String })
|
@ApiProperty({ type: String })
|
||||||
@Column()
|
@Column()
|
||||||
sku: string;
|
productSku: string;
|
||||||
|
|
||||||
@ApiProperty({ type: Number })
|
@ApiProperty({ type: Number })
|
||||||
@Column()
|
@Column()
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,8 @@ import {
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
ManyToMany,
|
|
||||||
JoinTable,
|
|
||||||
} from 'typeorm';
|
} from 'typeorm';
|
||||||
import { Shipment } from './shipment.entity';
|
import { Shipment } from './shipment.entity';
|
||||||
import { Area } from './area.entity';
|
|
||||||
import { Site } from './site.entity';
|
|
||||||
|
|
||||||
@Entity('stock_point')
|
@Entity('stock_point')
|
||||||
export class StockPoint extends BaseEntity {
|
export class StockPoint extends BaseEntity {
|
||||||
|
|
@ -55,7 +51,7 @@ export class StockPoint extends BaseEntity {
|
||||||
@Column({ default: 'uniuni' })
|
@Column({ default: 'uniuni' })
|
||||||
upStreamName: string;
|
upStreamName: string;
|
||||||
|
|
||||||
@Column({ default: 0 })
|
@Column()
|
||||||
upStreamStockPointId: number;
|
upStreamStockPointId: number;
|
||||||
|
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
|
|
@ -76,21 +72,4 @@ export class StockPoint extends BaseEntity {
|
||||||
|
|
||||||
@DeleteDateColumn()
|
@DeleteDateColumn()
|
||||||
deletedAt: Date; // 软删除时间
|
deletedAt: Date; // 软删除时间
|
||||||
|
|
||||||
@ManyToMany(() => Area)
|
|
||||||
@JoinTable({
|
|
||||||
name: 'stock_point_areas_area',
|
|
||||||
joinColumn: {
|
|
||||||
name: 'stockPointId',
|
|
||||||
referencedColumnName: 'id'
|
|
||||||
},
|
|
||||||
inverseJoinColumn: {
|
|
||||||
name: 'areaId',
|
|
||||||
referencedColumnName: 'id'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
areas: Area[];
|
|
||||||
|
|
||||||
@ManyToMany(() => Site, site => site.stockPoints)
|
|
||||||
sites: Site[];
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ export class StockRecord {
|
||||||
|
|
||||||
@ApiProperty({ type: String })
|
@ApiProperty({ type: String })
|
||||||
@Column()
|
@Column()
|
||||||
sku: string;
|
productSku: string;
|
||||||
|
|
||||||
@ApiProperty({ type: StockRecordOperationType })
|
@ApiProperty({ type: StockRecordOperationType })
|
||||||
@Column({ type: 'enum', enum: StockRecordOperationType })
|
@Column({ type: 'enum', enum: StockRecordOperationType })
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
import {
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
Entity,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Strength {
|
||||||
|
@ApiProperty()
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@ApiProperty()
|
||||||
|
@Column()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: '唯一识别key',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@Column()
|
||||||
|
unique_key: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2022-12-12 11:11:11',
|
||||||
|
description: '创建时间',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2022-12-12 11:11:11',
|
||||||
|
description: '更新时间',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
@ -12,68 +12,68 @@ import { SubscriptionStatus } from '../enums/base.enum';
|
||||||
@Entity('subscription')
|
@Entity('subscription')
|
||||||
@Exclude()
|
@Exclude()
|
||||||
export class Subscription {
|
export class Subscription {
|
||||||
// 本地主键,自增 ID
|
// 本地主键,自增 ID
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
@Expose()
|
@Expose()
|
||||||
id: number;
|
id: number;
|
||||||
|
|
||||||
// 站点唯一标识,用于区分不同来源站点
|
// 站点唯一标识,用于区分不同来源站点
|
||||||
@ApiProperty({ description: '来源站点唯一标识' })
|
@ApiProperty({ description: '来源站点唯一标识' })
|
||||||
@Column({ nullable: true })
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
siteId: number;
|
siteId: string;
|
||||||
|
|
||||||
// WooCommerce 订阅的原始 ID(字符串化),用于幂等更新
|
// WooCommerce 订阅的原始 ID(字符串化),用于幂等更新
|
||||||
@ApiProperty({ description: 'WooCommerce 订阅 ID' })
|
@ApiProperty({ description: 'WooCommerce 订阅 ID' })
|
||||||
@Column()
|
@Column()
|
||||||
@Expose()
|
@Expose()
|
||||||
externalSubscriptionId: string;
|
externalSubscriptionId: string;
|
||||||
|
|
||||||
// 订阅状态(active/cancelled/on-hold 等)
|
// 订阅状态(active/cancelled/on-hold 等)
|
||||||
@ApiProperty({ type: SubscriptionStatus })
|
@ApiProperty({ type: SubscriptionStatus })
|
||||||
@Column({ type: 'enum', enum: SubscriptionStatus })
|
@Column({ type: 'enum', enum: SubscriptionStatus })
|
||||||
@Expose()
|
@Expose()
|
||||||
status: SubscriptionStatus;
|
status: SubscriptionStatus;
|
||||||
|
|
||||||
// 货币代码,例如 USD/CAD
|
// 货币代码,例如 USD/CAD
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ default: '' })
|
@Column({ default: '' })
|
||||||
@Expose()
|
@Expose()
|
||||||
currency: string;
|
currency: string;
|
||||||
|
|
||||||
// 总金额,保留两位小数
|
// 总金额,保留两位小数
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column('decimal', { precision: 10, scale: 2, default: 0 })
|
@Column('decimal', { precision: 10, scale: 2, default: 0 })
|
||||||
@Expose()
|
@Expose()
|
||||||
total: number;
|
total: number;
|
||||||
|
|
||||||
// 计费周期(day/week/month/year)
|
// 计费周期(day/week/month/year)
|
||||||
@ApiProperty({ description: '计费周期 e.g. day/week/month/year' })
|
@ApiProperty({ description: '计费周期 e.g. day/week/month/year' })
|
||||||
@Column({ default: '' })
|
@Column({ default: '' })
|
||||||
@Expose()
|
@Expose()
|
||||||
billing_period: string;
|
billing_period: string;
|
||||||
|
|
||||||
// 计费周期间隔(例如 1/3/12)
|
// 计费周期间隔(例如 1/3/12)
|
||||||
@ApiProperty({ description: '计费周期间隔 e.g. 1/3/12' })
|
@ApiProperty({ description: '计费周期间隔 e.g. 1/3/12' })
|
||||||
@Column({ type: 'int', default: 0 })
|
@Column({ type: 'int', default: 0 })
|
||||||
@Expose()
|
@Expose()
|
||||||
billing_interval: number;
|
billing_interval: number;
|
||||||
|
|
||||||
// 客户 ID(WooCommerce 用户 ID)
|
// 客户 ID(WooCommerce 用户 ID)
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'int', default: 0 })
|
@Column({ type: 'int', default: 0 })
|
||||||
@Expose()
|
@Expose()
|
||||||
customer_id: number;
|
customer_id: number;
|
||||||
|
|
||||||
// 客户邮箱(从 billing.email 或 customer_email 提取)
|
// 客户邮箱(从 billing.email 或 customer_email 提取)
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ default: '' })
|
@Column({ default: '' })
|
||||||
@Expose()
|
@Expose()
|
||||||
customer_email: string;
|
customer_email: string;
|
||||||
|
|
||||||
// 父订单/订阅 ID(如有)
|
// 父订单/订阅 ID(如有)
|
||||||
@ApiProperty({ description: '父订单/父订阅ID(如有)' })
|
@ApiProperty({ description: '父订单/父订阅ID(如有)' })
|
||||||
@Column({ type: 'int', default: 0 })
|
@Column({ type: 'int', default: 0 })
|
||||||
@Expose()
|
@Expose()
|
||||||
parent_id: number;
|
parent_id: number;
|
||||||
|
|
@ -102,25 +102,25 @@ export class Subscription {
|
||||||
@Expose()
|
@Expose()
|
||||||
end_date: Date;
|
end_date: Date;
|
||||||
|
|
||||||
// 商品项(订阅行项目)
|
// 商品项(订阅行项目)
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'json', nullable: true })
|
@Column({ type: 'json', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
line_items: any[];
|
line_items: any[];
|
||||||
|
|
||||||
// 额外元数据(键值对)
|
// 额外元数据(键值对)
|
||||||
@ApiProperty()
|
@ApiProperty()
|
||||||
@Column({ type: 'json', nullable: true })
|
@Column({ type: 'json', nullable: true })
|
||||||
@Expose()
|
@Expose()
|
||||||
meta_data: any[];
|
meta_data: any[];
|
||||||
|
|
||||||
// 创建时间(数据库自动生成)
|
// 创建时间(数据库自动生成)
|
||||||
@ApiProperty({ example: '2022-12-12 11:11:11', description: '创建时间', required: true })
|
@ApiProperty({ example: '2022-12-12 11:11:11', description: '创建时间', required: true })
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
@Expose()
|
@Expose()
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|
||||||
// 更新时间(数据库自动生成)
|
// 更新时间(数据库自动生成)
|
||||||
@ApiProperty({ example: '2022-12-12 11:11:11', description: '更新时间', required: true })
|
@ApiProperty({ example: '2022-12-12 11:11:11', description: '更新时间', required: true })
|
||||||
@UpdateDateColumn()
|
@UpdateDateColumn()
|
||||||
@Expose()
|
@Expose()
|
||||||
|
|
|
||||||
|
|
@ -1,54 +0,0 @@
|
||||||
import { ApiProperty } from '@midwayjs/swagger';
|
|
||||||
import {
|
|
||||||
Column,
|
|
||||||
CreateDateColumn,
|
|
||||||
Entity,
|
|
||||||
PrimaryGeneratedColumn,
|
|
||||||
UpdateDateColumn,
|
|
||||||
} from 'typeorm';
|
|
||||||
|
|
||||||
@Entity('template')
|
|
||||||
export class Template {
|
|
||||||
@ApiProperty({ type: 'number' })
|
|
||||||
@PrimaryGeneratedColumn()
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
@ApiProperty({ type: 'string' })
|
|
||||||
@Column({ unique: true })
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
@ApiProperty({ type: 'string' })
|
|
||||||
@Column('text')
|
|
||||||
value: string;
|
|
||||||
|
|
||||||
@ApiProperty({ nullable: true ,name:"描述"})
|
|
||||||
@Column('text',{nullable: true,comment: "描述"})
|
|
||||||
description?: string;
|
|
||||||
|
|
||||||
@ApiProperty({ type: 'string', nullable: true, description: '测试数据JSON' })
|
|
||||||
@Column('text', { nullable: true, comment: '测试数据JSON' })
|
|
||||||
testData?: string;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: true,
|
|
||||||
description: '是否可删除',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@Column({ default: true })
|
|
||||||
deletable: boolean;
|
|
||||||
@ApiProperty({
|
|
||||||
example: '2022-12-12 11:11:11',
|
|
||||||
description: '创建时间',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@CreateDateColumn()
|
|
||||||
createdAt: Date;
|
|
||||||
|
|
||||||
@ApiProperty({
|
|
||||||
example: '2022-12-12 11:11:11',
|
|
||||||
description: '更新时间',
|
|
||||||
required: true,
|
|
||||||
})
|
|
||||||
@UpdateDateColumn()
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
||||||
|
|
@ -9,11 +9,11 @@ export class TransferItem {
|
||||||
|
|
||||||
@ApiProperty({ type: String })
|
@ApiProperty({ type: String })
|
||||||
@Column()
|
@Column()
|
||||||
sku: string;
|
productSku: string;
|
||||||
|
|
||||||
@ApiProperty({ type: String })
|
@ApiProperty({ type: String })
|
||||||
@Column()
|
@Column()
|
||||||
name: string;
|
productName: string;
|
||||||
|
|
||||||
@ApiProperty({ type: Number })
|
@ApiProperty({ type: Number })
|
||||||
@Column()
|
@Column()
|
||||||
|
|
|
||||||
|
|
@ -15,14 +15,10 @@ export class User {
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
// @Column() // 默认角色为管理员
|
// @Column() // 默认角色为管理员
|
||||||
// roleId: number; // 角色 (如:admin, editor, viewer)
|
// roleId: number; // 角色 (如:admin, editor, viewer)
|
||||||
|
|
||||||
@Column({ type: 'simple-array', nullable: true })
|
@Column({ type: 'simple-array', nullable: true })
|
||||||
permissions: string[]; // 自定义权限 (如:['user:add', 'user:edit'])
|
permissions: string[]; // 自定义权限 (如:['user:add', 'user:edit'])
|
||||||
|
|
||||||
// 邮箱字段,可选且唯一
|
|
||||||
@Column({ unique: true, nullable: true })
|
|
||||||
email?: string;
|
|
||||||
|
|
||||||
@Column({ default: false })
|
@Column({ default: false })
|
||||||
isSuper: boolean; // 超级管理员
|
isSuper: boolean; // 超级管理员
|
||||||
|
|
@ -32,8 +28,4 @@ export class User {
|
||||||
|
|
||||||
@Column({ default: true })
|
@Column({ default: true })
|
||||||
isActive: boolean; // 用户是否启用
|
isActive: boolean; // 用户是否启用
|
||||||
|
|
||||||
// 备注字段(可选)
|
|
||||||
@Column({ nullable: true })
|
|
||||||
remark?: string;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,119 @@
|
||||||
|
import {
|
||||||
|
Entity,
|
||||||
|
Column,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Unique,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
|
|
||||||
|
@Entity('variation')
|
||||||
|
@Unique(['siteId', 'externalProductId', 'externalVariationId']) // 确保变体的唯一性
|
||||||
|
export class Variation {
|
||||||
|
@ApiProperty({
|
||||||
|
example: '1',
|
||||||
|
description: 'ID',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '1',
|
||||||
|
description: 'wp网站ID',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@Column()
|
||||||
|
siteId: string; // 来源站点唯一标识
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '1',
|
||||||
|
description: 'wp产品ID',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@Column()
|
||||||
|
externalProductId: string; // WooCommerce 产品 ID
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '1',
|
||||||
|
description: 'wp变体ID',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@Column()
|
||||||
|
externalVariationId: string; // WooCommerce 变体 ID
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '1',
|
||||||
|
description: '对应WP产品表的ID',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@Column()
|
||||||
|
productId: number; // 对应WP产品表的 ID
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'sku', type: 'string' })
|
||||||
|
@Column({ nullable: true })
|
||||||
|
sku?: string; // sku 编码
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: '变体名称',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@Column()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '常规价格', type: Number })
|
||||||
|
@Column('decimal', { precision: 10, scale: 2, nullable: true })
|
||||||
|
regular_price: number; // 常规价格
|
||||||
|
|
||||||
|
@ApiProperty({ description: '销售价格', type: Number })
|
||||||
|
@Column('decimal', { precision: 10, scale: 2, nullable: true })
|
||||||
|
sale_price: number; // 销售价格
|
||||||
|
|
||||||
|
@ApiProperty({ description: '是否促销中', type: Boolean })
|
||||||
|
@Column({ nullable: true, type: Boolean })
|
||||||
|
on_sale: boolean; // 是否促销中
|
||||||
|
|
||||||
|
@ApiProperty({ description: '是否删除', type: Boolean })
|
||||||
|
@Column({ nullable: true, type: Boolean , default: false })
|
||||||
|
on_delete: boolean; // 是否删除
|
||||||
|
|
||||||
|
@Column({ type: 'json', nullable: true })
|
||||||
|
attributes: Record<string, any>; // 变体的属性
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2022-12-12 11:11:11',
|
||||||
|
description: '创建时间',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2022-12-12 11:11:11',
|
||||||
|
description: '更新时间',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: '变体构成成分',
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
sku: { type: 'string' },
|
||||||
|
quantity: { type: 'number' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@Column('json', { nullable: true, comment: '变体构成成分' })
|
||||||
|
constitution: { sku: string; quantity: number }[] | null;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
import {
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
Unique,
|
||||||
|
Entity,
|
||||||
|
} from 'typeorm';
|
||||||
|
import { ApiProperty } from '@midwayjs/swagger';
|
||||||
|
import { ProductStatus, ProductStockStatus, ProductType } from '../enums/base.enum';
|
||||||
|
|
||||||
|
@Entity('wp_product')
|
||||||
|
@Unique(['siteId', 'externalProductId']) // 确保产品的唯一性
|
||||||
|
export class WpProduct {
|
||||||
|
@ApiProperty({
|
||||||
|
example: '1',
|
||||||
|
description: 'ID',
|
||||||
|
type: 'number',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '1',
|
||||||
|
description: 'wp网站ID',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@Column()
|
||||||
|
siteId: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '1',
|
||||||
|
description: 'wp产品ID',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@Column()
|
||||||
|
externalProductId: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: 'sku', type: 'string' })
|
||||||
|
@Column({ nullable: true })
|
||||||
|
sku?: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: 'ZYN 6MG WINTERGREEN',
|
||||||
|
description: '产品名称',
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@Column()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '产品状态', enum: ProductStatus })
|
||||||
|
@Column({ type: 'enum', enum: ProductStatus })
|
||||||
|
status: ProductStatus;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '上下架状态', enum: ProductStockStatus })
|
||||||
|
@Column({
|
||||||
|
name: 'stock_status',
|
||||||
|
type: 'enum',
|
||||||
|
enum: ProductStockStatus,
|
||||||
|
default: ProductStockStatus.INSTOCK
|
||||||
|
})
|
||||||
|
stockStatus: ProductStockStatus;
|
||||||
|
|
||||||
|
@ApiProperty({ description: '常规价格', type: Number })
|
||||||
|
@Column('decimal', { precision: 10, scale: 2, nullable: true })
|
||||||
|
regular_price: number; // 常规价格
|
||||||
|
|
||||||
|
@ApiProperty({ description: '销售价格', type: Number })
|
||||||
|
@Column('decimal', { precision: 10, scale: 2, nullable: true })
|
||||||
|
sale_price: number; // 销售价格
|
||||||
|
|
||||||
|
@ApiProperty({ description: '是否促销中', type: Boolean })
|
||||||
|
@Column({ nullable: true, type: Boolean })
|
||||||
|
on_sale: boolean; // 是否促销中
|
||||||
|
|
||||||
|
@ApiProperty({ description: '是否删除', type: Boolean })
|
||||||
|
@Column({ nullable: true, type: Boolean , default: false })
|
||||||
|
on_delete: boolean; // 是否删除
|
||||||
|
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: '产品类型',
|
||||||
|
enum: ProductType,
|
||||||
|
})
|
||||||
|
@Column({ type: 'enum', enum: ProductType })
|
||||||
|
type: ProductType;
|
||||||
|
|
||||||
|
|
||||||
|
@Column({ type: 'json', nullable: true })
|
||||||
|
metadata: Record<string, any>; // 产品的其他扩展字段
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2022-12-12 11:11:11',
|
||||||
|
description: '创建时间',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
example: '2022-12-12 11:11:11',
|
||||||
|
description: '更新时间',
|
||||||
|
required: true,
|
||||||
|
})
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
description: '产品构成成分',
|
||||||
|
type: 'array',
|
||||||
|
items: {
|
||||||
|
type: 'object',
|
||||||
|
properties: {
|
||||||
|
sku: { type: 'string' },
|
||||||
|
quantity: { type: 'number' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
@Column('json', { nullable: true, comment: '产品构成成分' })
|
||||||
|
constitution: { sku: string; quantity: number }[] | null;
|
||||||
|
}
|
||||||
|
|
@ -42,8 +42,7 @@ export enum OrderStatus {
|
||||||
REFUNDED = 'refunded', // 已退款
|
REFUNDED = 'refunded', // 已退款
|
||||||
FAILED = 'failed', // 失败订单
|
FAILED = 'failed', // 失败订单
|
||||||
DRAFT = 'draft', // 草稿
|
DRAFT = 'draft', // 草稿
|
||||||
AUTO_DRAFT = 'auto-draft', // 自动草稿(TODO:不知道为什么出现)
|
AUTO_DRAFT = 'auto-draft', // 自动草稿
|
||||||
|
|
||||||
// TRASH = 'trash',
|
// TRASH = 'trash',
|
||||||
// refund 也就是退款相关的状态
|
// refund 也就是退款相关的状态
|
||||||
RETURN_REQUESTED = 'return-requested', // 已申请退款
|
RETURN_REQUESTED = 'return-requested', // 已申请退款
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,15 @@ export interface IUserOptions {
|
||||||
uid: number;
|
uid: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface WpSite {
|
||||||
|
id: string;
|
||||||
|
wpApiUrl: string;
|
||||||
|
consumerKey: string;
|
||||||
|
consumerSecret: string;
|
||||||
|
siteName: string;
|
||||||
|
email: string;
|
||||||
|
emailPswd: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface PaginationParams {
|
export interface PaginationParams {
|
||||||
current?: number; // 当前页码
|
current?: number; // 当前页码
|
||||||
|
|
|
||||||
|
|
@ -1,287 +0,0 @@
|
||||||
// src/interface/platform.interface.ts
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 电商平台抽象接口
|
|
||||||
* 定义所有平台必须实现的通用方法
|
|
||||||
*/
|
|
||||||
export interface IPlatformService {
|
|
||||||
/**
|
|
||||||
* 获取产品列表
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @returns 产品列表数据
|
|
||||||
*/
|
|
||||||
getProducts(site: any): Promise<any[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取单个产品
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param id 产品ID
|
|
||||||
* @returns 产品数据
|
|
||||||
*/
|
|
||||||
getProduct(site: any, id: number): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取产品变体列表
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param productId 产品ID
|
|
||||||
* @returns 变体列表数据
|
|
||||||
*/
|
|
||||||
getVariations(site: any, productId: number): Promise<any[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取产品变体详情
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param productId 产品ID
|
|
||||||
* @param variationId 变体ID
|
|
||||||
* @returns 变体详情数据
|
|
||||||
*/
|
|
||||||
getVariation(site: any, productId: number, variationId: number): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取订单列表
|
|
||||||
* @param siteId 站点ID
|
|
||||||
* @returns 订单列表数据
|
|
||||||
*/
|
|
||||||
getOrders(siteId: number, params: Record<string, any>): Promise<any[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取订单详情
|
|
||||||
* @param siteId 站点ID
|
|
||||||
* @param orderId 订单ID
|
|
||||||
* @returns 订单详情数据
|
|
||||||
*/
|
|
||||||
getOrder(siteId: number, orderId: string): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取订阅列表(如果平台支持)
|
|
||||||
* @param siteId 站点ID
|
|
||||||
* @returns 订阅列表数据
|
|
||||||
*/
|
|
||||||
getSubscriptions?(siteId: number): Promise<any[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取客户列表
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @returns 客户列表数据
|
|
||||||
*/
|
|
||||||
getCustomers(site: any): Promise<any[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取单个客户
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param id 客户ID
|
|
||||||
* @returns 客户数据
|
|
||||||
*/
|
|
||||||
getCustomer(site: any, id: number): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建产品
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param data 产品数据
|
|
||||||
* @returns 创建结果
|
|
||||||
*/
|
|
||||||
createProduct(site: any, data: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新产品
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param productId 产品ID
|
|
||||||
* @param data 更新数据
|
|
||||||
* @returns 更新结果
|
|
||||||
*/
|
|
||||||
updateProduct(site: any, productId: string, data: any): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新产品状态
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param productId 产品ID
|
|
||||||
* @param status 产品状态
|
|
||||||
* @param stockStatus 库存状态
|
|
||||||
* @returns 更新结果
|
|
||||||
*/
|
|
||||||
updateProductStatus(site: any, productId: string, status: string, stockStatus: string): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新产品变体
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param productId 产品ID
|
|
||||||
* @param variationId 变体ID
|
|
||||||
* @param data 更新数据
|
|
||||||
* @returns 更新后的变体数据
|
|
||||||
*/
|
|
||||||
updateVariation(site: any, productId: string, variationId: string, data: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新订单
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param orderId 订单ID
|
|
||||||
* @param data 更新数据
|
|
||||||
* @returns 更新结果
|
|
||||||
*/
|
|
||||||
updateOrder(site: any, orderId: string, data: Record<string, any>): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建履约信息
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param orderId 订单ID
|
|
||||||
* @param data 履约数据
|
|
||||||
* @returns 创建结果
|
|
||||||
*/
|
|
||||||
createFulfillment(site: any, orderId: string, data: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除履约信息
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param orderId 订单ID
|
|
||||||
* @param fulfillmentId 履约跟踪ID
|
|
||||||
* @returns 删除结果
|
|
||||||
*/
|
|
||||||
deleteFulfillment(site: any, orderId: string, fulfillmentId: string): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量处理产品
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param data 批量操作数据
|
|
||||||
* @returns 处理结果
|
|
||||||
*/
|
|
||||||
batchProcessProducts(site: any, data: { create?: any[]; update?: any[]; delete?: any[] }): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取 api 客户端
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @returns api 客户端
|
|
||||||
*/
|
|
||||||
getApiClient(site: any): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取客户列表
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @returns 客户列表数据
|
|
||||||
*/
|
|
||||||
getCustomers(site: any): Promise<any[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取单个客户
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param id 客户ID
|
|
||||||
* @returns 客户数据
|
|
||||||
*/
|
|
||||||
getCustomer(site: any, id: number): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取评论列表
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @returns 评论列表数据
|
|
||||||
*/
|
|
||||||
getReviews(site: any): Promise<any[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建评论
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param data 评论数据
|
|
||||||
* @returns 创建结果
|
|
||||||
*/
|
|
||||||
createReview(site: any, data: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新评论
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param reviewId 评论ID
|
|
||||||
* @param data 更新数据
|
|
||||||
* @returns 更新结果
|
|
||||||
*/
|
|
||||||
updateReview(site: any, reviewId: number, data: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除评论
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param reviewId 评论ID
|
|
||||||
* @returns 删除结果
|
|
||||||
*/
|
|
||||||
deleteReview(site: any, reviewId: number): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取分页资源
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param resource 资源类型
|
|
||||||
* @param params 查询参数
|
|
||||||
* @param namespace API命名空间
|
|
||||||
* @returns 分页数据
|
|
||||||
*/
|
|
||||||
fetchResourcePaged<T>(site: any, resource: string, params: Record<string, any>, namespace?: any): Promise<{ items: T[]; total: number; totalPages: number; page: number; per_page: number }>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取分页媒体
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param params 查询参数
|
|
||||||
* @returns 分页媒体数据
|
|
||||||
*/
|
|
||||||
fetchMediaPaged(site: any, params: Record<string, any>): Promise<{ items: any[]; total: number; totalPages: number; page: number; per_page: number }>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除媒体
|
|
||||||
* @param siteId 站点ID
|
|
||||||
* @param mediaId 媒体ID
|
|
||||||
* @param force 是否强制删除
|
|
||||||
* @returns 删除结果
|
|
||||||
*/
|
|
||||||
deleteMedia(siteId: number, mediaId: number, force?: boolean): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新媒体
|
|
||||||
* @param siteId 站点ID
|
|
||||||
* @param mediaId 媒体ID
|
|
||||||
* @param data 更新数据
|
|
||||||
* @returns 更新结果
|
|
||||||
*/
|
|
||||||
updateMedia(siteId: number, mediaId: number, data: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 转换媒体为WebP格式
|
|
||||||
* @param siteId 站点ID
|
|
||||||
* @param mediaIds 媒体ID列表
|
|
||||||
* @returns 转换结果
|
|
||||||
*/
|
|
||||||
convertMediaToWebp(siteId: number, mediaIds: Array<number | string>): Promise<{ converted: any[]; failed: Array<{ id: number | string; error: string }> }>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取webhook列表
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param params 查询参数
|
|
||||||
* @returns 分页webhook列表
|
|
||||||
*/
|
|
||||||
getWebhooks(site: any, params: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取单个webhook
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param webhookId webhook ID
|
|
||||||
* @returns webhook详情
|
|
||||||
*/
|
|
||||||
getWebhook(site: any, webhookId: string | number): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建webhook
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param data webhook数据
|
|
||||||
* @returns 创建结果
|
|
||||||
*/
|
|
||||||
createWebhook(site: any, data: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新webhook
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param webhookId webhook ID
|
|
||||||
* @param data 更新数据
|
|
||||||
* @returns 更新结果
|
|
||||||
*/
|
|
||||||
updateWebhook(site: any, webhookId: string | number, data: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除webhook
|
|
||||||
* @param site 站点配置信息
|
|
||||||
* @param webhookId webhook ID
|
|
||||||
* @returns 删除结果
|
|
||||||
*/
|
|
||||||
deleteWebhook(site: any, webhookId: string | number): Promise<boolean>;
|
|
||||||
}
|
|
||||||
|
|
@ -1,496 +0,0 @@
|
||||||
import {
|
|
||||||
CreateReviewDTO,
|
|
||||||
UpdateReviewDTO,
|
|
||||||
UnifiedMediaDTO,
|
|
||||||
UnifiedOrderDTO,
|
|
||||||
UnifiedProductDTO,
|
|
||||||
UnifiedReviewDTO,
|
|
||||||
UnifiedSubscriptionDTO,
|
|
||||||
UnifiedCustomerDTO,
|
|
||||||
UnifiedWebhookDTO,
|
|
||||||
UnifiedWebhookPaginationDTO,
|
|
||||||
CreateWebhookDTO,
|
|
||||||
UpdateWebhookDTO,
|
|
||||||
CreateVariationDTO,
|
|
||||||
UpdateVariationDTO,
|
|
||||||
UnifiedProductVariationDTO,
|
|
||||||
UnifiedVariationPaginationDTO,
|
|
||||||
} from '../dto/site-api.dto';
|
|
||||||
import { UnifiedPaginationDTO, UnifiedSearchParamsDTO } from '../dto/api.dto';
|
|
||||||
import { BatchOperationDTO, BatchOperationResultDTO } from '../dto/batch.dto';
|
|
||||||
|
|
||||||
export interface ISiteAdapter {
|
|
||||||
// ========== 客户映射方法 ==========
|
|
||||||
/**
|
|
||||||
* 将平台客户数据转换为统一客户数据格式
|
|
||||||
* @param data 平台特定客户数据
|
|
||||||
* @returns 统一客户数据格式
|
|
||||||
*/
|
|
||||||
mapPlatformToUnifiedCustomer(data: any): UnifiedCustomerDTO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一客户数据格式转换为平台客户数据
|
|
||||||
* @param data 统一客户数据格式
|
|
||||||
* @returns 平台特定客户数据
|
|
||||||
*/
|
|
||||||
mapUnifiedToPlatformCustomer(data: Partial<UnifiedCustomerDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取单个客户
|
|
||||||
*/
|
|
||||||
getCustomer(where: Partial<Pick<UnifiedCustomerDTO, 'id' | 'email' | 'phone'>>): Promise<UnifiedCustomerDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取客户列表
|
|
||||||
*/
|
|
||||||
getCustomers(params: UnifiedSearchParamsDTO): Promise<UnifiedPaginationDTO<UnifiedCustomerDTO>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有客户
|
|
||||||
*/
|
|
||||||
getAllCustomers(params?: UnifiedSearchParamsDTO): Promise<UnifiedCustomerDTO[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建客户
|
|
||||||
*/
|
|
||||||
createCustomer(data: Partial<UnifiedCustomerDTO>): Promise<UnifiedCustomerDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新客户
|
|
||||||
*/
|
|
||||||
updateCustomer(where: Partial<Pick<UnifiedCustomerDTO, 'id' | 'email' | 'phone'>>, data: Partial<UnifiedCustomerDTO>): Promise<UnifiedCustomerDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除客户
|
|
||||||
*/
|
|
||||||
deleteCustomer(where: Partial<Pick<UnifiedCustomerDTO, 'id' | 'email' | 'phone'>>): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量处理客户
|
|
||||||
*/
|
|
||||||
batchProcessCustomers?(data: BatchOperationDTO): Promise<BatchOperationResultDTO>;
|
|
||||||
|
|
||||||
// ========== 媒体映射方法 ==========
|
|
||||||
/**
|
|
||||||
* 将平台媒体数据转换为统一媒体数据格式
|
|
||||||
* @param data 平台特定媒体数据
|
|
||||||
* @returns 统一媒体数据格式
|
|
||||||
*/
|
|
||||||
mapPlatformToUnifiedMedia(data: any): UnifiedMediaDTO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一媒体数据格式转换为平台媒体数据
|
|
||||||
* @param data 统一媒体数据格式
|
|
||||||
* @returns 平台特定媒体数据
|
|
||||||
*/
|
|
||||||
mapUnifiedToPlatformMedia(data: Partial<UnifiedMediaDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取媒体列表
|
|
||||||
*/
|
|
||||||
getMedia(params: UnifiedSearchParamsDTO): Promise<UnifiedPaginationDTO<UnifiedMediaDTO>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有媒体
|
|
||||||
*/
|
|
||||||
getAllMedia(params?: UnifiedSearchParamsDTO): Promise<UnifiedMediaDTO[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建媒体
|
|
||||||
*/
|
|
||||||
createMedia(file: any): Promise<UnifiedMediaDTO>;
|
|
||||||
|
|
||||||
// ========== 订单映射方法 ==========
|
|
||||||
/**
|
|
||||||
* 将平台订单数据转换为统一订单数据格式
|
|
||||||
* @param data 平台特定订单数据
|
|
||||||
* @returns 统一订单数据格式
|
|
||||||
*/
|
|
||||||
mapPlatformToUnifiedOrder(data: any): UnifiedOrderDTO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一订单数据格式转换为平台订单数据
|
|
||||||
* @param data 统一订单数据格式
|
|
||||||
* @returns 平台特定订单数据
|
|
||||||
*/
|
|
||||||
mapUnifiedToPlatformOrder(data: Partial<UnifiedOrderDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一订单创建参数转换为平台订单创建参数
|
|
||||||
* @param data 统一订单创建参数
|
|
||||||
* @returns 平台订单创建参数
|
|
||||||
*/
|
|
||||||
mapCreateOrderParams(data: Partial<UnifiedOrderDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一订单更新参数转换为平台订单更新参数
|
|
||||||
* @param data 统一订单更新参数
|
|
||||||
* @returns 平台订单更新参数
|
|
||||||
*/
|
|
||||||
mapUpdateOrderParams(data: Partial<UnifiedOrderDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取单个订单
|
|
||||||
*/
|
|
||||||
getOrder(where: Partial<Pick<UnifiedOrderDTO, 'id'>>): Promise<UnifiedOrderDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取订单列表
|
|
||||||
*/
|
|
||||||
getOrders(params: UnifiedSearchParamsDTO): Promise<UnifiedPaginationDTO<UnifiedOrderDTO>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有订单
|
|
||||||
*/
|
|
||||||
getAllOrders(params?: UnifiedSearchParamsDTO): Promise<UnifiedOrderDTO[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取订单总数
|
|
||||||
*/
|
|
||||||
countOrders(params: Record<string, any>): Promise<number>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建订单
|
|
||||||
*/
|
|
||||||
createOrder(data: Partial<UnifiedOrderDTO>): Promise<UnifiedOrderDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新订单
|
|
||||||
*/
|
|
||||||
updateOrder(where: Partial<Pick<UnifiedOrderDTO, 'id'>>, data: Partial<UnifiedOrderDTO>): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除订单
|
|
||||||
*/
|
|
||||||
deleteOrder(where: Partial<Pick<UnifiedOrderDTO, 'id'>>): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取订单备注
|
|
||||||
*/
|
|
||||||
getOrderNotes(orderId: string | number): Promise<any[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建订单备注
|
|
||||||
*/
|
|
||||||
createOrderNote(orderId: string | number, data: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 取消订单履行
|
|
||||||
*/
|
|
||||||
cancelFulfillment(orderId: string | number, data: {
|
|
||||||
reason?: string;
|
|
||||||
shipment_id?: string;
|
|
||||||
}): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取订单履行信息
|
|
||||||
*/
|
|
||||||
getOrderFulfillments(orderId: string | number): Promise<any[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建订单履行信息
|
|
||||||
*/
|
|
||||||
createOrderFulfillment(orderId: string | number, data: {
|
|
||||||
tracking_number: string;
|
|
||||||
shipping_provider: string;
|
|
||||||
shipping_method?: string;
|
|
||||||
status?: string;
|
|
||||||
date_created?: string;
|
|
||||||
items?: Array<{
|
|
||||||
order_item_id: number;
|
|
||||||
quantity: number;
|
|
||||||
}>;
|
|
||||||
}): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新订单履行信息
|
|
||||||
*/
|
|
||||||
updateOrderFulfillment(orderId: string | number, fulfillmentId: string, data: {
|
|
||||||
tracking_number?: string;
|
|
||||||
shipping_provider?: string;
|
|
||||||
shipping_method?: string;
|
|
||||||
status?: string;
|
|
||||||
date_created?: string;
|
|
||||||
items?: Array<{
|
|
||||||
order_item_id: number;
|
|
||||||
quantity: number;
|
|
||||||
}>;
|
|
||||||
}): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除订单履行信息
|
|
||||||
*/
|
|
||||||
deleteOrderFulfillment(orderId: string | number, fulfillmentId: string): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量处理订单
|
|
||||||
*/
|
|
||||||
batchProcessOrders?(data: BatchOperationDTO): Promise<BatchOperationResultDTO>;
|
|
||||||
|
|
||||||
// ========== 产品映射方法 ==========
|
|
||||||
/**
|
|
||||||
* 将平台产品数据转换为统一产品数据格式
|
|
||||||
* @param data 平台特定产品数据
|
|
||||||
* @returns 统一产品数据格式
|
|
||||||
*/
|
|
||||||
mapPlatformToUnifiedProduct(data: any): UnifiedProductDTO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一产品数据格式转换为平台产品数据
|
|
||||||
* @param data 统一产品数据格式
|
|
||||||
* @returns 平台特定产品数据
|
|
||||||
*/
|
|
||||||
mapUnifiedToPlatformProduct(data: Partial<UnifiedProductDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一产品创建参数转换为平台产品创建参数
|
|
||||||
* @param data 统一产品创建参数
|
|
||||||
* @returns 平台产品创建参数
|
|
||||||
*/
|
|
||||||
mapCreateProductParams(data: Partial<UnifiedProductDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一产品更新参数转换为平台产品更新参数
|
|
||||||
* @param data 统一产品更新参数
|
|
||||||
* @returns 平台产品更新参数
|
|
||||||
*/
|
|
||||||
mapUpdateProductParams(data: Partial<UnifiedProductDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取单个产品
|
|
||||||
*/
|
|
||||||
getProduct(where: Partial<Pick<UnifiedProductDTO, 'id' | 'sku'>>): Promise<UnifiedProductDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取产品列表
|
|
||||||
*/
|
|
||||||
getProducts(params: UnifiedSearchParamsDTO): Promise<UnifiedPaginationDTO<UnifiedProductDTO>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有产品
|
|
||||||
*/
|
|
||||||
getAllProducts(params?: UnifiedSearchParamsDTO): Promise<UnifiedProductDTO[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建产品
|
|
||||||
*/
|
|
||||||
createProduct(data: Partial<UnifiedProductDTO>): Promise<UnifiedProductDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新产品
|
|
||||||
*/
|
|
||||||
updateProduct(where: Partial<Pick<UnifiedProductDTO, 'id' | 'sku'>>, data: Partial<UnifiedProductDTO>): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除产品
|
|
||||||
*/
|
|
||||||
deleteProduct(where: Partial<Pick<UnifiedProductDTO, 'id' | 'sku'>>): Promise<boolean>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 批量处理产品
|
|
||||||
*/
|
|
||||||
batchProcessProducts?(data: BatchOperationDTO): Promise<BatchOperationResultDTO>;
|
|
||||||
|
|
||||||
// ========== 评论映射方法 ==========
|
|
||||||
/**
|
|
||||||
* 将平台评论数据转换为统一评论数据格式
|
|
||||||
* @param data 平台特定评论数据
|
|
||||||
* @returns 统一评论数据格式
|
|
||||||
*/
|
|
||||||
mapPlatformToUnifiedReview(data: any): UnifiedReviewDTO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一评论数据格式转换为平台评论数据
|
|
||||||
* @param data 统一评论数据格式
|
|
||||||
* @returns 平台特定评论数据
|
|
||||||
*/
|
|
||||||
mapUnifiedToPlatformReview(data: Partial<UnifiedReviewDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一评论创建参数转换为平台评论创建参数
|
|
||||||
* @param data 统一评论创建参数
|
|
||||||
* @returns 平台评论创建参数
|
|
||||||
*/
|
|
||||||
mapCreateReviewParams(data: CreateReviewDTO): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一评论更新参数转换为平台评论更新参数
|
|
||||||
* @param data 统一评论更新参数
|
|
||||||
* @returns 平台评论更新参数
|
|
||||||
*/
|
|
||||||
mapUpdateReviewParams(data: UpdateReviewDTO): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取评论列表
|
|
||||||
*/
|
|
||||||
getReviews(params: UnifiedSearchParamsDTO): Promise<UnifiedPaginationDTO<UnifiedReviewDTO>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有评论
|
|
||||||
*/
|
|
||||||
getAllReviews(params?: UnifiedSearchParamsDTO): Promise<UnifiedReviewDTO[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建评论
|
|
||||||
*/
|
|
||||||
createReview(data: CreateReviewDTO): Promise<UnifiedReviewDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新评论
|
|
||||||
*/
|
|
||||||
updateReview(where: Partial<Pick<UnifiedReviewDTO, 'id'>>, data: UpdateReviewDTO): Promise<UnifiedReviewDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除评论
|
|
||||||
*/
|
|
||||||
deleteReview(where: Partial<Pick<UnifiedReviewDTO, 'id'>>): Promise<boolean>;
|
|
||||||
|
|
||||||
// ========== 订阅映射方法 ==========
|
|
||||||
/**
|
|
||||||
* 将平台订阅数据转换为统一订阅数据格式
|
|
||||||
* @param data 平台特定订阅数据
|
|
||||||
* @returns 统一订阅数据格式
|
|
||||||
*/
|
|
||||||
mapPlatformToUnifiedSubscription(data: any): UnifiedSubscriptionDTO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一订阅数据格式转换为平台订阅数据
|
|
||||||
* @param data 统一订阅数据格式
|
|
||||||
* @returns 平台特定订阅数据
|
|
||||||
*/
|
|
||||||
mapUnifiedToPlatformSubscription(data: Partial<UnifiedSubscriptionDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取订阅列表
|
|
||||||
*/
|
|
||||||
getSubscriptions(params: UnifiedSearchParamsDTO): Promise<UnifiedPaginationDTO<UnifiedSubscriptionDTO>>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有订阅
|
|
||||||
*/
|
|
||||||
getAllSubscriptions(params?: UnifiedSearchParamsDTO): Promise<UnifiedSubscriptionDTO[]>;
|
|
||||||
|
|
||||||
// ========== 产品变体映射方法 ==========
|
|
||||||
/**
|
|
||||||
* 将平台产品变体数据转换为统一产品变体数据格式
|
|
||||||
* @param data 平台特定产品变体数据
|
|
||||||
* @returns 统一产品变体数据格式
|
|
||||||
*/
|
|
||||||
mapPlatformToUnifiedVariation(data: any): UnifiedProductVariationDTO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一产品变体数据格式转换为平台产品变体数据
|
|
||||||
* @param data 统一产品变体数据格式
|
|
||||||
* @returns 平台特定产品变体数据
|
|
||||||
*/
|
|
||||||
mapUnifiedToPlatformVariation(data: Partial<UnifiedProductVariationDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一产品变体创建参数转换为平台产品变体创建参数
|
|
||||||
* @param data 统一产品变体创建参数
|
|
||||||
* @returns 平台产品变体创建参数
|
|
||||||
*/
|
|
||||||
mapCreateVariationParams(data: CreateVariationDTO): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一产品变体更新参数转换为平台产品变体更新参数
|
|
||||||
* @param data 统一产品变体更新参数
|
|
||||||
* @returns 平台产品变体更新参数
|
|
||||||
*/
|
|
||||||
mapUpdateVariationParams(data: UpdateVariationDTO): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取单个产品变体
|
|
||||||
*/
|
|
||||||
getVariation(productId: string | number, variationId: string | number): Promise<UnifiedProductVariationDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取产品变体列表
|
|
||||||
*/
|
|
||||||
getVariations(productId: string | number, params: UnifiedSearchParamsDTO): Promise<UnifiedVariationPaginationDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有产品变体
|
|
||||||
*/
|
|
||||||
getAllVariations(productId: string | number, params?: UnifiedSearchParamsDTO): Promise<UnifiedProductVariationDTO[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建产品变体
|
|
||||||
*/
|
|
||||||
createVariation(productId: string | number, data: CreateVariationDTO): Promise<UnifiedProductVariationDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新产品变体
|
|
||||||
*/
|
|
||||||
updateVariation(productId: string | number, variationId: string | number, data: UpdateVariationDTO): Promise<UnifiedProductVariationDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除产品变体
|
|
||||||
*/
|
|
||||||
deleteVariation(productId: string | number, variationId: string | number): Promise<boolean>;
|
|
||||||
|
|
||||||
// ========== Webhook映射方法 ==========
|
|
||||||
/**
|
|
||||||
* 将平台Webhook数据转换为统一Webhook数据格式
|
|
||||||
* @param data 平台特定Webhook数据
|
|
||||||
* @returns 统一Webhook数据格式
|
|
||||||
*/
|
|
||||||
mapPlatformToUnifiedWebhook(data: any): UnifiedWebhookDTO;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一Webhook数据格式转换为平台Webhook数据
|
|
||||||
* @param data 统一Webhook数据格式
|
|
||||||
* @returns 平台特定Webhook数据
|
|
||||||
*/
|
|
||||||
mapUnifiedToPlatformWebhook(data: Partial<UnifiedWebhookDTO>): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一Webhook创建参数转换为平台Webhook创建参数
|
|
||||||
* @param data 统一Webhook创建参数
|
|
||||||
* @returns 平台Webhook创建参数
|
|
||||||
*/
|
|
||||||
mapCreateWebhookParams(data: CreateWebhookDTO): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 将统一Webhook更新参数转换为平台Webhook更新参数
|
|
||||||
* @param data 统一Webhook更新参数
|
|
||||||
* @returns 平台Webhook更新参数
|
|
||||||
*/
|
|
||||||
mapUpdateWebhookParams(data: UpdateWebhookDTO): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取单个webhook
|
|
||||||
*/
|
|
||||||
getWebhook(where: Partial<Pick<UnifiedWebhookDTO, 'id'>>): Promise<UnifiedWebhookDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取webhooks列表
|
|
||||||
*/
|
|
||||||
getWebhooks(params: UnifiedSearchParamsDTO): Promise<UnifiedWebhookPaginationDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 获取所有webhooks
|
|
||||||
*/
|
|
||||||
getAllWebhooks(params?: UnifiedSearchParamsDTO): Promise<UnifiedWebhookDTO[]>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建webhook
|
|
||||||
*/
|
|
||||||
createWebhook(data: CreateWebhookDTO): Promise<UnifiedWebhookDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 更新webhook
|
|
||||||
*/
|
|
||||||
updateWebhook(where: Partial<Pick<UnifiedWebhookDTO, 'id'>>, data: UpdateWebhookDTO): Promise<UnifiedWebhookDTO>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除webhook
|
|
||||||
*/
|
|
||||||
deleteWebhook(where: Partial<Pick<UnifiedWebhookDTO, 'id'>>): Promise<boolean>;
|
|
||||||
|
|
||||||
// ========== 站点/其他方法 ==========
|
|
||||||
/**
|
|
||||||
* 获取站点链接列表
|
|
||||||
*/
|
|
||||||
getLinks(): Promise<Array<{ title: string, url: string }>>;
|
|
||||||
}
|
|
||||||
|
|
@ -1 +1,15 @@
|
||||||
export {}
|
import { FORMAT, ILogger, Logger } from '@midwayjs/core';
|
||||||
|
import { IJob, Job } from '@midwayjs/cron';
|
||||||
|
|
||||||
|
@Job({
|
||||||
|
cronTime: FORMAT.CRONTAB.EVERY_DAY,
|
||||||
|
runOnInit: true,
|
||||||
|
})
|
||||||
|
export class SyncProductJob implements IJob {
|
||||||
|
@Logger()
|
||||||
|
logger: ILogger;
|
||||||
|
|
||||||
|
onTick() {
|
||||||
|
}
|
||||||
|
onComplete?(result: any) {}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,20 +75,13 @@ export class SyncUniuniShipmentJob implements IJob{
|
||||||
'255': 'Gateway_To_Gateway_Transit'
|
'255': 'Gateway_To_Gateway_Transit'
|
||||||
};
|
};
|
||||||
async onTick() {
|
async onTick() {
|
||||||
const shipments:Shipment[] = await this.shipmentModel.findBy({ finished: false });
|
const shipments:Shipment[] = await this.shipmentModel.findBy({ finished: false });
|
||||||
const results = await Promise.all(
|
shipments.forEach(shipment => {
|
||||||
shipments.map(async shipment => {
|
this.logisticsService.updateShipmentState(shipment);
|
||||||
return await this.logisticsService.updateShipmentState(shipment);
|
});
|
||||||
})
|
|
||||||
)
|
|
||||||
this.logger.info(`更新运单状态完毕 ${JSON.stringify(results)}`);
|
|
||||||
return results
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onComplete(result: any) {
|
onComplete(result: any) {
|
||||||
this.logger.info(`更新运单状态完成 ${result}`);
|
|
||||||
}
|
|
||||||
onError(error: any) {
|
|
||||||
this.logger.error(`更新运单状态失败 ${error.message}`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import { ILogger, Inject, Logger } from '@midwayjs/core';
|
|
||||||
import { IJob, Job } from '@midwayjs/cron';
|
|
||||||
import { LogisticsService } from '../service/logistics.service';
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import { Shipment } from '../entity/shipment.entity';
|
|
||||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
|
||||||
|
|
||||||
|
|
||||||
@Job({
|
|
||||||
cronTime: '0 0 12 * * *', // 每天12点执行
|
|
||||||
start: true
|
|
||||||
})
|
|
||||||
export class SyncTmsJob implements IJob {
|
|
||||||
@Logger()
|
|
||||||
logger: ILogger;
|
|
||||||
|
|
||||||
@Inject()
|
|
||||||
logisticsService: LogisticsService;
|
|
||||||
|
|
||||||
@InjectEntityModel(Shipment)
|
|
||||||
shipmentModel: Repository<Shipment>
|
|
||||||
|
|
||||||
async onTick() {
|
|
||||||
const shipments:Shipment[] = await this.shipmentModel.findBy({ tracking_provider: 'freightwaves',finished: false });
|
|
||||||
const results = await Promise.all(
|
|
||||||
shipments.map(async shipment => {
|
|
||||||
return await this.logisticsService.updateFreightwavesShipmentState(shipment);
|
|
||||||
})
|
|
||||||
)
|
|
||||||
this.logger.info(`更新运单状态完毕 ${JSON.stringify(results)}`);
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
onComplete(result: any) {
|
|
||||||
this.logger.info(`更新运单状态完成 ${result}`);
|
|
||||||
}
|
|
||||||
onError(error: any) {
|
|
||||||
this.logger.error(`更新运单状态失败 ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -21,16 +21,8 @@ export class AuthMiddleware implements IMiddleware<Context, NextFunction> {
|
||||||
whiteList = [
|
whiteList = [
|
||||||
'/user/login',
|
'/user/login',
|
||||||
'/webhook/woocommerce',
|
'/webhook/woocommerce',
|
||||||
'/webhook/shoppy',
|
|
||||||
'/logistics/getTrackingNumber',
|
'/logistics/getTrackingNumber',
|
||||||
'/logistics/getListByTrackingId',
|
'/logistics/getListByTrackingId',
|
||||||
'/product/categories/all',
|
|
||||||
'/product/category/1/attributes',
|
|
||||||
'/product/category/2/attributes',
|
|
||||||
'/product/category/3/attributes',
|
|
||||||
'/product/category/4/attributes',
|
|
||||||
'/product/list',
|
|
||||||
'/dict/items',
|
|
||||||
];
|
];
|
||||||
|
|
||||||
match(ctx: Context) {
|
match(ctx: Context) {
|
||||||
|
|
|
||||||
|
|
@ -1,140 +0,0 @@
|
||||||
import { Middleware, IMiddleware } from '@midwayjs/core';
|
|
||||||
import { NextFunction, Context } from '@midwayjs/koa';
|
|
||||||
import * as qs from 'qs';
|
|
||||||
|
|
||||||
@Middleware()
|
|
||||||
export class QueryNormalizeMiddleware implements IMiddleware<Context, NextFunction> {
|
|
||||||
// 数值与布尔转换函数,用于将字符串转换为合适的类型
|
|
||||||
private toPrimitive(value: any): any {
|
|
||||||
const s = String(value);
|
|
||||||
if (s === 'true') return true;
|
|
||||||
if (s === 'false') return false;
|
|
||||||
const n = Number(s);
|
|
||||||
return Number.isFinite(n) && s !== '' ? n : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 深度遍历对象并对字符串进行trim
|
|
||||||
private trimDeep(input: any): any {
|
|
||||||
if (input === null || input === undefined) return input;
|
|
||||||
if (typeof input === 'string') return input.trim();
|
|
||||||
if (Array.isArray(input)) return input.map(v => this.trimDeep(v));
|
|
||||||
if (typeof input === 'object') {
|
|
||||||
const out: Record<string, any> = {};
|
|
||||||
for (const key of Object.keys(input)) {
|
|
||||||
out[key] = this.trimDeep((input as any)[key]);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
return input;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 将路径数组对应的值赋到对象中,支持构建嵌套结构与数组
|
|
||||||
private assignByPath(target: Record<string, any>, path: string[], value: any): void {
|
|
||||||
let cur: any = target;
|
|
||||||
for (let i = 0; i < path.length; i++) {
|
|
||||||
const key = path[i];
|
|
||||||
const isLast = i === path.length - 1;
|
|
||||||
if (isLast) {
|
|
||||||
if (key === '') {
|
|
||||||
if (!Array.isArray(cur)) return;
|
|
||||||
cur.push(value);
|
|
||||||
} else {
|
|
||||||
if (cur[key] === undefined) cur[key] = value;
|
|
||||||
else if (Array.isArray(cur[key])) cur[key].push(value);
|
|
||||||
else cur[key] = [cur[key], value];
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!cur[key] || typeof cur[key] !== 'object') cur[key] = {};
|
|
||||||
cur = cur[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析可能为 JSON 字符串或鍵值串的输入为对象
|
|
||||||
private parseLooseObject(input: any): Record<string, any> {
|
|
||||||
if (!input) return {};
|
|
||||||
if (typeof input === 'object') return input as Record<string, any>;
|
|
||||||
const str = String(input).trim();
|
|
||||||
try {
|
|
||||||
if (str.startsWith('{') || str.startsWith('[')) {
|
|
||||||
const json = JSON.parse(str);
|
|
||||||
if (json && typeof json === 'object') return json as Record<string, any>;
|
|
||||||
}
|
|
||||||
} catch {}
|
|
||||||
const obj: Record<string, any> = {};
|
|
||||||
const pairs = str.split(/[&;,]/).map(s => s.trim()).filter(Boolean);
|
|
||||||
for (const pair of pairs) {
|
|
||||||
const idxEq = pair.indexOf('=');
|
|
||||||
const idxColon = pair.indexOf(':');
|
|
||||||
const idx = idxEq >= 0 ? idxEq : idxColon;
|
|
||||||
if (idx < 0) continue;
|
|
||||||
const key = decodeURIComponent(pair.slice(0, idx)).trim();
|
|
||||||
const valueRaw = decodeURIComponent(pair.slice(idx + 1)).trim();
|
|
||||||
obj[key] = this.toPrimitive(valueRaw);
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve() {
|
|
||||||
return async (ctx: Context, next: NextFunction) => {
|
|
||||||
const raw = String((ctx.request as any).querystring || '');
|
|
||||||
const parsed = qs.parse(raw, { allowDots: true, depth: 10, ignoreQueryPrefix: false, comma: true });
|
|
||||||
const query = { ...(ctx.request.query || {}), ...(parsed as any) } as Record<string, any>;
|
|
||||||
const trimmedTop: Record<string, any> = {};
|
|
||||||
for (const k of Object.keys(query)) {
|
|
||||||
const v = (query as any)[k];
|
|
||||||
trimmedTop[k] = typeof v === 'string' ? String(v).trim() : v;
|
|
||||||
}
|
|
||||||
Object.assign(query, trimmedTop);
|
|
||||||
|
|
||||||
// 解析 where 对象,支持 JSON 字符串与括号或点号语法
|
|
||||||
const hasWhereInput = (query as any).where !== undefined;
|
|
||||||
let whereObj: Record<string, any> = this.parseLooseObject((query as any).where);
|
|
||||||
for (const k of Object.keys(query)) {
|
|
||||||
if (k === 'where') continue;
|
|
||||||
if (k.startsWith('where[') || k.startsWith('where.')) {
|
|
||||||
const pathStr = k.replace(/^where\.?/, '').replace(/\]/g, '').replace(/\[/g, '.');
|
|
||||||
const path = pathStr.split('.');
|
|
||||||
const val = this.toPrimitive((query as any)[k]);
|
|
||||||
this.assignByPath(whereObj, path, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const hasWhereBracketKeys = Object.keys(query).some(k => k.startsWith('where[') || k.startsWith('where.'));
|
|
||||||
if (hasWhereInput || hasWhereBracketKeys) (query as any).where = this.trimDeep(whereObj);
|
|
||||||
|
|
||||||
// 解析 order 对象,支持 JSON 字符串与括号或点号语法
|
|
||||||
const hasOrderInput = (query as any).order !== undefined;
|
|
||||||
let orderObj: Record<string, any> = this.parseLooseObject((query as any).order);
|
|
||||||
for (const k of Object.keys(query)) {
|
|
||||||
if (k === 'order') continue;
|
|
||||||
if (k.startsWith('order[') || k.startsWith('order.')) {
|
|
||||||
const pathStr = k.replace(/^order\.?/, '').replace(/\]/g, '').replace(/\[/g, '.');
|
|
||||||
const path = pathStr.split('.');
|
|
||||||
const val = this.toPrimitive((query as any)[k]);
|
|
||||||
this.assignByPath(orderObj, path, val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const hasOrderBracketKeys = Object.keys(query).some(k => k.startsWith('order[') || k.startsWith('order.'));
|
|
||||||
if (hasOrderInput || hasOrderBracketKeys) (query as any).order = this.trimDeep(orderObj);
|
|
||||||
|
|
||||||
// 将常见分页参数转换为数字类型
|
|
||||||
if (query.page !== undefined) (query as any).page = Number(query.page);
|
|
||||||
if ((query as any).page_size !== undefined) (query as any).page_size = Number((query as any).page_size);
|
|
||||||
if ((query as any).per_page !== undefined) (query as any).per_page = Number((query as any).per_page);
|
|
||||||
if ((query as any).customer_id !== undefined) (query as any).customer_id = Number((query as any).customer_id);
|
|
||||||
|
|
||||||
ctx.request.query = query as any;
|
|
||||||
(ctx as any).query = query as any;
|
|
||||||
return await next();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static getName(): string {
|
|
||||||
return 'queryNormalize';
|
|
||||||
}
|
|
||||||
|
|
||||||
static getPriority(): number {
|
|
||||||
// 优先级靠前,优先处理查询参数
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue