refactor(实体): 重构产品相关实体及字典系统
- 将分类、口味、规格实体重构为字典系统 - 新增dict和dict_item实体实现通用字典管理 - 修改product实体字段从categoryId改为brandId - 修复order_coupon和order_refund_item实体文件名拼写错误 - 更新typeorm配置和种子数据初始化逻辑 - 调整相关DTO和控制器接口适配新字典系统 - 更新package.json依赖版本和脚本
This commit is contained in:
parent
3c1da145d3
commit
d9800f341f
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
|
|
@ -17,14 +17,17 @@
|
|||
"@midwayjs/typeorm": "^3.20.0",
|
||||
"@midwayjs/validate": "^3.20.2",
|
||||
"@woocommerce/woocommerce-rest-api": "^1.0.2",
|
||||
"axios": "^1.7.9",
|
||||
"axios": "^1.13.2",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"class-transformer": "^0.5.1",
|
||||
"dayjs": "^1.11.13",
|
||||
"mysql2": "^3.11.5",
|
||||
"nodemailer": "^7.0.5",
|
||||
"npm-check-updates": "^19.1.2",
|
||||
"swagger-ui-dist": "^5.18.2",
|
||||
"typeorm": "^0.3.20",
|
||||
"typeorm": "^0.3.27",
|
||||
"typeorm-extension": "^3.7.2",
|
||||
"xlsx": "^0.18.5",
|
||||
"xml2js": "^0.6.2"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -36,10 +39,13 @@
|
|||
"dev": "cross-env NODE_ENV=local mwtsc --watch --run @midwayjs/mock/app.js",
|
||||
"test": "cross-env NODE_ENV=unittest jest",
|
||||
"cov": "jest --coverage",
|
||||
"lint": "mwts check",
|
||||
"lint:fix": "mwts fix",
|
||||
"lint": "mwtsc check",
|
||||
"lint:fix": "mwtsc fix",
|
||||
"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"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -51,6 +57,7 @@
|
|||
"@midwayjs/mock": "^3.20.11",
|
||||
"cross-env": "^10.1.0",
|
||||
"mwtsc": "^1.15.2",
|
||||
"tsx": "^4.20.6",
|
||||
"typescript": "^5.9.3"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
365
pnpm-lock.yaml
365
pnpm-lock.yaml
|
|
@ -65,12 +65,18 @@ importers:
|
|||
nodemailer:
|
||||
specifier: ^7.0.5
|
||||
version: 7.0.10
|
||||
npm-check-updates:
|
||||
specifier: ^19.1.2
|
||||
version: 19.1.2
|
||||
swagger-ui-dist:
|
||||
specifier: ^5.18.2
|
||||
version: 5.30.2
|
||||
typeorm:
|
||||
specifier: ^0.3.20
|
||||
version: 0.3.27(mysql2@3.15.3)(reflect-metadata@0.2.2)
|
||||
xlsx:
|
||||
specifier: ^0.18.5
|
||||
version: 0.18.5
|
||||
xml2js:
|
||||
specifier: ^0.6.2
|
||||
version: 0.6.2
|
||||
|
|
@ -84,6 +90,9 @@ importers:
|
|||
mwtsc:
|
||||
specifier: ^1.15.2
|
||||
version: 1.15.2
|
||||
tsx:
|
||||
specifier: ^4.20.6
|
||||
version: 4.20.6
|
||||
typescript:
|
||||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
|
|
@ -97,6 +106,162 @@ packages:
|
|||
'@epic-web/invariant@1.0.0':
|
||||
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':
|
||||
resolution: {integrity: sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==}
|
||||
|
||||
|
|
@ -314,6 +479,10 @@ packages:
|
|||
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
adler-32@1.3.1:
|
||||
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
ansi-regex@5.0.1:
|
||||
resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -415,6 +584,10 @@ packages:
|
|||
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
cfb@1.2.2:
|
||||
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
chokidar@3.6.0:
|
||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||
engines: {node: '>= 8.10.0'}
|
||||
|
|
@ -446,6 +619,10 @@ packages:
|
|||
resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
|
||||
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:
|
||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||
engines: {node: '>=7.0.0'}
|
||||
|
|
@ -488,6 +665,11 @@ packages:
|
|||
core-util-is@1.0.3:
|
||||
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:
|
||||
resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==}
|
||||
|
||||
|
|
@ -606,6 +788,11 @@ packages:
|
|||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
esbuild@0.25.12:
|
||||
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
escalade@3.2.0:
|
||||
resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
|
||||
engines: {node: '>=6'}
|
||||
|
|
@ -651,6 +838,10 @@ packages:
|
|||
formidable@2.1.5:
|
||||
resolution: {integrity: sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==}
|
||||
|
||||
frac@1.1.2:
|
||||
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
fresh@0.5.2:
|
||||
resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
|
@ -961,6 +1152,11 @@ packages:
|
|||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||
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:
|
||||
resolution: {integrity: sha512-6bkxv3N4Gu5lty4viIcIAnq5GbxECviMBeKR3WX/q87SPQ8E8aursPZUtsXDnxCs787af09WPRBLqYrf/lwoYQ==}
|
||||
|
||||
|
|
@ -1160,6 +1356,10 @@ packages:
|
|||
resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
ssf@0.11.2:
|
||||
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
statuses@1.5.0:
|
||||
resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
|
@ -1228,6 +1428,11 @@ packages:
|
|||
resolution: {integrity: sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==}
|
||||
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:
|
||||
resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
|
@ -1327,6 +1532,14 @@ packages:
|
|||
engines: {node: '>= 8'}
|
||||
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:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
|
@ -1338,6 +1551,11 @@ packages:
|
|||
wrappy@1.0.2:
|
||||
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:
|
||||
resolution: {integrity: sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==}
|
||||
engines: {node: '>=4.0.0'}
|
||||
|
|
@ -1373,6 +1591,84 @@ snapshots:
|
|||
|
||||
'@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/hoek@9.3.0': {}
|
||||
|
|
@ -1645,6 +1941,8 @@ snapshots:
|
|||
mime-types: 2.1.35
|
||||
negotiator: 0.6.3
|
||||
|
||||
adler-32@1.3.1: {}
|
||||
|
||||
ansi-regex@5.0.1: {}
|
||||
|
||||
ansi-regex@6.2.2: {}
|
||||
|
|
@ -1735,6 +2033,11 @@ snapshots:
|
|||
call-bind-apply-helpers: 1.0.2
|
||||
get-intrinsic: 1.3.0
|
||||
|
||||
cfb@1.2.2:
|
||||
dependencies:
|
||||
adler-32: 1.3.1
|
||||
crc-32: 1.2.2
|
||||
|
||||
chokidar@3.6.0:
|
||||
dependencies:
|
||||
anymatch: 3.1.3
|
||||
|
|
@ -1779,6 +2082,8 @@ snapshots:
|
|||
|
||||
co@4.6.0: {}
|
||||
|
||||
codepage@1.15.0: {}
|
||||
|
||||
color-convert@2.0.1:
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
|
|
@ -1812,6 +2117,8 @@ snapshots:
|
|||
|
||||
core-util-is@1.0.3: {}
|
||||
|
||||
crc-32@1.2.2: {}
|
||||
|
||||
create-hash@1.2.0:
|
||||
dependencies:
|
||||
cipher-base: 1.0.7
|
||||
|
|
@ -1919,6 +2226,35 @@ snapshots:
|
|||
has-tostringtag: 1.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: {}
|
||||
|
||||
escape-html@1.0.3: {}
|
||||
|
|
@ -1967,6 +2303,8 @@ snapshots:
|
|||
once: 1.4.0
|
||||
qs: 6.14.0
|
||||
|
||||
frac@1.1.2: {}
|
||||
|
||||
fresh@0.5.2: {}
|
||||
|
||||
fsevents@2.3.3:
|
||||
|
|
@ -2313,6 +2651,8 @@ snapshots:
|
|||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
npm-check-updates@19.1.2: {}
|
||||
|
||||
oauth-1.0a@2.2.6: {}
|
||||
|
||||
object-inspect@1.13.4: {}
|
||||
|
|
@ -2494,6 +2834,10 @@ snapshots:
|
|||
|
||||
sqlstring@2.3.3: {}
|
||||
|
||||
ssf@0.11.2:
|
||||
dependencies:
|
||||
frac: 1.1.2
|
||||
|
||||
statuses@1.5.0: {}
|
||||
|
||||
statuses@2.0.1: {}
|
||||
|
|
@ -2582,6 +2926,13 @@ snapshots:
|
|||
|
||||
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:
|
||||
dependencies:
|
||||
media-typer: 0.3.0
|
||||
|
|
@ -2647,6 +2998,10 @@ snapshots:
|
|||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
wmf@1.0.2: {}
|
||||
|
||||
word@0.3.0: {}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
|
|
@ -2661,6 +3016,16 @@ snapshots:
|
|||
|
||||
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:
|
||||
dependencies:
|
||||
sax: 1.4.3
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { MidwayConfig } from '@midwayjs/core';
|
||||
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';
|
||||
|
|
@ -11,10 +10,10 @@ import { StockPoint } from '../entity/stock_point.entity';
|
|||
import { StockRecord } from '../entity/stock_record.entity';
|
||||
import { Order } from '../entity/order.entity';
|
||||
import { OrderItem } from '../entity/order_item.entity';
|
||||
import { OrderCoupon } from '../entity/order_copon.entity';
|
||||
import { OrderCoupon } from '../entity/order_coupon.entity';
|
||||
import { OrderFee } from '../entity/order_fee.entity';
|
||||
import { OrderRefund } from '../entity/order_refund.entity';
|
||||
import { OrderRefundItem } from '../entity/order_retund_item.entity';
|
||||
import { OrderRefundItem } from '../entity/order_refund_item.entity';
|
||||
import { OrderSale } from '../entity/order_sale.entity';
|
||||
import { OrderSaleOriginal } from '../entity/order_item_original.entity';
|
||||
import { OrderShipping } from '../entity/order_shipping.entity';
|
||||
|
|
@ -26,14 +25,14 @@ import { Shipment } from '../entity/shipment.entity';
|
|||
import { ShipmentItem } from '../entity/shipment_item.entity';
|
||||
import { Transfer } from '../entity/transfer.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 { Customer } from '../entity/customer.entity';
|
||||
import { DeviceWhitelist } from '../entity/device_whitelist';
|
||||
import { AuthCode } from '../entity/auth_code';
|
||||
import { Subscription } from '../entity/subscription.entity';
|
||||
import { Site } from '../entity/site.entity';
|
||||
import { Dict } from '../entity/dict.entity';
|
||||
import { DictItem } from '../entity/dict_item.entity';
|
||||
|
||||
export default {
|
||||
// use for cookie sign key, should change to your own and keep security
|
||||
|
|
@ -42,9 +41,6 @@ export default {
|
|||
default: {
|
||||
entities: [
|
||||
Product,
|
||||
Category,
|
||||
Strength,
|
||||
Flavors,
|
||||
WpProduct,
|
||||
Variation,
|
||||
User,
|
||||
|
|
@ -76,6 +72,8 @@ export default {
|
|||
AuthCode,
|
||||
Subscription,
|
||||
Site,
|
||||
Dict,
|
||||
DictItem,
|
||||
],
|
||||
synchronize: true,
|
||||
logging: false,
|
||||
|
|
|
|||
|
|
@ -13,15 +13,15 @@ import { ProductService } from '../service/product.service';
|
|||
import { errorResponse, successResponse } from '../utils/response.util';
|
||||
import {
|
||||
BatchSetSkuDTO,
|
||||
CreateCategoryDTO,
|
||||
CreateBrandDTO,
|
||||
CreateFlavorsDTO,
|
||||
CreateProductDTO,
|
||||
CreateStrengthDTO,
|
||||
QueryCategoryDTO,
|
||||
QueryBrandDTO,
|
||||
QueryFlavorsDTO,
|
||||
QueryProductDTO,
|
||||
QueryStrengthDTO,
|
||||
UpdateCategoryDTO,
|
||||
UpdateBrandDTO,
|
||||
UpdateFlavorsDTO,
|
||||
UpdateProductDTO,
|
||||
UpdateStrengthDTO,
|
||||
|
|
@ -29,8 +29,8 @@ import {
|
|||
import { ApiOkResponse } from '@midwayjs/swagger';
|
||||
import {
|
||||
BooleanRes,
|
||||
ProductCatListRes,
|
||||
ProductCatRes,
|
||||
ProductBrandListRes,
|
||||
ProductBrandRes,
|
||||
ProductListRes,
|
||||
ProductRes,
|
||||
ProductsRes,
|
||||
|
|
@ -79,12 +79,12 @@ export class ProductController {
|
|||
async getProductList(
|
||||
@Query() query: QueryProductDTO
|
||||
): Promise<ProductListRes> {
|
||||
const { current = 1, pageSize = 10, name, categoryId } = query;
|
||||
const { current = 1, pageSize = 10, name, brandId } = query;
|
||||
try {
|
||||
const data = await this.productService.getProductList(
|
||||
{ current, pageSize },
|
||||
name,
|
||||
categoryId
|
||||
brandId
|
||||
);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
|
|
@ -138,8 +138,6 @@ export class ProductController {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ApiOkResponse({
|
||||
type: BooleanRes,
|
||||
})
|
||||
|
|
@ -154,13 +152,13 @@ export class ProductController {
|
|||
}
|
||||
|
||||
@ApiOkResponse({
|
||||
type: ProductCatListRes,
|
||||
type: ProductBrandListRes,
|
||||
})
|
||||
@Get('/categories')
|
||||
async getCategories(@Query() query: QueryCategoryDTO) {
|
||||
@Get('/brands')
|
||||
async getBrands(@Query() query: QueryBrandDTO) {
|
||||
const { current = 1, pageSize = 10, name } = query;
|
||||
try {
|
||||
let data = await this.productService.getCategoryList(
|
||||
let data = await this.productService.getBrandList(
|
||||
{ current, pageSize },
|
||||
name
|
||||
);
|
||||
|
|
@ -171,10 +169,10 @@ export class ProductController {
|
|||
}
|
||||
|
||||
@ApiOkResponse()
|
||||
@Get('/categorieAll')
|
||||
async getCategorieAll() {
|
||||
@Get('/brandAll')
|
||||
async getBrandAll() {
|
||||
try {
|
||||
let data = await this.productService.getCategoryAll();
|
||||
let data = await this.productService.getBrandAll();
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
return errorResponse(error?.message || error);
|
||||
|
|
@ -182,18 +180,18 @@ export class ProductController {
|
|||
}
|
||||
|
||||
@ApiOkResponse({
|
||||
type: ProductCatRes,
|
||||
type: ProductBrandRes,
|
||||
})
|
||||
@Post('/category')
|
||||
async createCategory(@Body() categoryData: CreateCategoryDTO) {
|
||||
@Post('/brand')
|
||||
async createBrand(@Body() brandData: CreateBrandDTO) {
|
||||
try {
|
||||
const hasCategory = await this.productService.hasCategory(
|
||||
categoryData.name
|
||||
const hasBrand = await this.productService.hasBrand(
|
||||
brandData.name
|
||||
);
|
||||
if (hasCategory) {
|
||||
return errorResponse('分类已存在');
|
||||
if (hasBrand) {
|
||||
return errorResponse('品牌已存在');
|
||||
}
|
||||
let data = await this.productService.createCategory(categoryData);
|
||||
let data = await this.productService.createBrand(brandData);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
return errorResponse(error?.message || error);
|
||||
|
|
@ -201,21 +199,21 @@ export class ProductController {
|
|||
}
|
||||
|
||||
@ApiOkResponse({
|
||||
type: ProductCatRes,
|
||||
type: ProductBrandRes,
|
||||
})
|
||||
@Put('/category/:id')
|
||||
async updateCategory(
|
||||
@Put('/brand/:id')
|
||||
async updateBrand(
|
||||
@Param('id') id: number,
|
||||
@Body() categoryData: UpdateCategoryDTO
|
||||
@Body() brandData: UpdateBrandDTO
|
||||
) {
|
||||
try {
|
||||
const hasCategory = await this.productService.hasCategory(
|
||||
categoryData.name
|
||||
const hasBrand = await this.productService.hasBrand(
|
||||
brandData.name
|
||||
);
|
||||
if (hasCategory) {
|
||||
return errorResponse('分类已存在');
|
||||
if (hasBrand) {
|
||||
return errorResponse('品牌已存在');
|
||||
}
|
||||
const data = this.productService.updateCategory(id, categoryData);
|
||||
const data = this.productService.updateBrand(id, brandData);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
return errorResponse(error?.message || error);
|
||||
|
|
@ -225,12 +223,12 @@ export class ProductController {
|
|||
@ApiOkResponse({
|
||||
type: BooleanRes,
|
||||
})
|
||||
@Del('/category/:id')
|
||||
async deleteCategory(@Param('id') id: number) {
|
||||
@Del('/brand/:id')
|
||||
async deleteBrand(@Param('id') id: number) {
|
||||
try {
|
||||
const hasProducts = await this.productService.hasProductsInCategory(id);
|
||||
if (hasProducts) throw new Error('该分类下有商品,无法删除');
|
||||
const data = await this.productService.deleteCategory(id);
|
||||
const hasProducts = await this.productService.hasProductsInBrand(id);
|
||||
if (hasProducts) throw new Error('该品牌下有商品,无法删除');
|
||||
const data = await this.productService.deleteBrand(id);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
return errorResponse(error?.message || error);
|
||||
|
|
@ -283,7 +281,7 @@ export class ProductController {
|
|||
try {
|
||||
const hasFlavors = await this.productService.hasFlavors(flavorsData.name);
|
||||
if (hasFlavors) {
|
||||
return errorResponse('分类已存在');
|
||||
return errorResponse('口味已存在');
|
||||
}
|
||||
let data = await this.productService.createFlavors(flavorsData);
|
||||
return successResponse(data);
|
||||
|
|
@ -301,7 +299,7 @@ export class ProductController {
|
|||
try {
|
||||
const hasFlavors = await this.productService.hasFlavors(flavorsData.name);
|
||||
if (hasFlavors) {
|
||||
return errorResponse('分类已存在');
|
||||
return errorResponse('口味已存在');
|
||||
}
|
||||
const data = this.productService.updateFlavors(id, flavorsData);
|
||||
return successResponse(data);
|
||||
|
|
@ -317,7 +315,7 @@ export class ProductController {
|
|||
async deleteFlavors(@Param('id') id: number) {
|
||||
try {
|
||||
const hasProducts = await this.productService.hasProductsInFlavors(id);
|
||||
if (hasProducts) throw new Error('该分类下有商品,无法删除');
|
||||
if (hasProducts) throw new Error('该口味下有商品,无法删除');
|
||||
const data = await this.productService.deleteFlavors(id);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
|
|
@ -359,7 +357,7 @@ export class ProductController {
|
|||
strengthData.name
|
||||
);
|
||||
if (hasStrength) {
|
||||
return errorResponse('分类已存在');
|
||||
return errorResponse('规格已存在');
|
||||
}
|
||||
let data = await this.productService.createStrength(strengthData);
|
||||
return successResponse(data);
|
||||
|
|
@ -379,7 +377,7 @@ export class ProductController {
|
|||
strengthData.name
|
||||
);
|
||||
if (hasStrength) {
|
||||
return errorResponse('分类已存在');
|
||||
return errorResponse('规格已存在');
|
||||
}
|
||||
const data = this.productService.updateStrength(id, strengthData);
|
||||
return successResponse(data);
|
||||
|
|
@ -395,7 +393,7 @@ export class ProductController {
|
|||
async deleteStrength(@Param('id') id: number) {
|
||||
try {
|
||||
const hasProducts = await this.productService.hasProductsInStrength(id);
|
||||
if (hasProducts) throw new Error('该分类下有商品,无法删除');
|
||||
if (hasProducts) throw new Error('该规格下有商品,无法删除');
|
||||
const data = await this.productService.deleteStrength(id);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
import { DataSource, DataSourceOptions } from 'typeorm';
|
||||
import { SeederOptions } from 'typeorm-extension';
|
||||
import { Product } from '../entity/product.entity';
|
||||
import { WpProduct } from '../entity/wp_product.entity';
|
||||
import { Variation } from '../entity/variation.entity';
|
||||
import { User } from '../entity/user.entity';
|
||||
import { PurchaseOrder } from '../entity/purchase_order.entity';
|
||||
import { PurchaseOrderItem } from '../entity/purchase_order_item.entity';
|
||||
import { Stock } from '../entity/stock.entity';
|
||||
import { StockPoint } from '../entity/stock_point.entity';
|
||||
import { StockRecord } from '../entity/stock_record.entity';
|
||||
import { Order } from '../entity/order.entity';
|
||||
import { OrderItem } from '../entity/order_item.entity';
|
||||
import { OrderCoupon } from '../entity/order_coupon.entity';
|
||||
import { OrderFee } from '../entity/order_fee.entity';
|
||||
import { OrderRefund } from '../entity/order_refund.entity';
|
||||
import { OrderRefundItem } from '../entity/order_refund_item.entity';
|
||||
import { OrderSale } from '../entity/order_sale.entity';
|
||||
import { OrderSaleOriginal } from '../entity/order_item_original.entity';
|
||||
import { OrderShipping } from '../entity/order_shipping.entity';
|
||||
import { Service } from '../entity/service.entity';
|
||||
import { ShippingAddress } from '../entity/shipping_address.entity';
|
||||
import { OrderNote } from '../entity/order_note.entity';
|
||||
import { OrderShipment } from '../entity/order_shipment.entity';
|
||||
import { Shipment } from '../entity/shipment.entity';
|
||||
import { ShipmentItem } from '../entity/shipment_item.entity';
|
||||
import { Transfer } from '../entity/transfer.entity';
|
||||
import { TransferItem } from '../entity/transfer_item.entity';
|
||||
import { CustomerTag } from '../entity/customer_tag.entity';
|
||||
import { Customer } from '../entity/customer.entity';
|
||||
import { DeviceWhitelist } from '../entity/device_whitelist';
|
||||
import { AuthCode } from '../entity/auth_code';
|
||||
import { Subscription } from '../entity/subscription.entity';
|
||||
import { Site } from '../entity/site.entity';
|
||||
import { Dict } from '../entity/dict.entity';
|
||||
import { DictItem } from '../entity/dict_item.entity';
|
||||
|
||||
const options: DataSourceOptions & SeederOptions = {
|
||||
type: 'mysql',
|
||||
host: 'localhost',
|
||||
port: 23306,
|
||||
username: 'root',
|
||||
password: '12345678',
|
||||
database: 'inventory',
|
||||
synchronize: false,
|
||||
logging: true,
|
||||
entities: [
|
||||
Product,
|
||||
WpProduct,
|
||||
Variation,
|
||||
User,
|
||||
PurchaseOrder,
|
||||
PurchaseOrderItem,
|
||||
Stock,
|
||||
StockPoint,
|
||||
StockRecord,
|
||||
Order,
|
||||
OrderItem,
|
||||
OrderCoupon,
|
||||
OrderFee,
|
||||
OrderRefund,
|
||||
OrderRefundItem,
|
||||
OrderSale,
|
||||
OrderSaleOriginal,
|
||||
OrderShipment,
|
||||
ShipmentItem,
|
||||
Shipment,
|
||||
OrderShipping,
|
||||
Service,
|
||||
ShippingAddress,
|
||||
OrderNote,
|
||||
Transfer,
|
||||
TransferItem,
|
||||
CustomerTag,
|
||||
Customer,
|
||||
DeviceWhitelist,
|
||||
AuthCode,
|
||||
Subscription,
|
||||
Site,
|
||||
Dict,
|
||||
DictItem,
|
||||
],
|
||||
migrations: ['src/migration/*.ts'],
|
||||
seeds: ['src/db/seeds/**/*.ts'],
|
||||
};
|
||||
|
||||
export default new DataSource(options);
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
/**
|
||||
* @description 字典数据备份
|
||||
* @author ZKS
|
||||
* @date 2025-11-27
|
||||
*/
|
||||
|
||||
// 真实数据
|
||||
export const flavorsData: { id: number; title: string; name: string }[] = [
|
||||
{ id: 1, title: 'Bellini Mini', name: 'Bellini Mini' },
|
||||
{ id: 2, title: 'Max Polarmint', name: 'Max Polarmint' },
|
||||
{ id: 3, title: 'Blueberry', name: 'Blueberry' },
|
||||
{ id: 4, title: 'Citrus', name: 'Citrus' },
|
||||
{ id: 5, title: 'Wintergreen', name: 'Wintergreen' },
|
||||
{ id: 6, title: 'COOL MINT', name: 'COOL MINT' },
|
||||
{ id: 7, title: 'JUICY PEACH', name: 'JUICY PEACH' },
|
||||
{ id: 8, title: 'ORANGE', name: 'ORANGE' },
|
||||
{ id: 9, title: 'PEPPERMINT', name: 'PEPPERMINT' },
|
||||
{ id: 10, title: 'SPEARMINT', name: 'SPEARMINT' },
|
||||
{ id: 11, title: 'STRAWBERRY', name: 'STRAWBERRY' },
|
||||
{ id: 12, title: 'WATERMELON', name: 'WATERMELON' },
|
||||
{ id: 13, title: 'COFFEE', name: 'COFFEE' },
|
||||
{ id: 14, title: 'LEMONADE', name: 'LEMONADE' },
|
||||
{ id: 15, title: 'apple mint', name: 'apple mint' },
|
||||
{ id: 16, title: 'PEACH', name: 'PEACH' },
|
||||
{ id: 17, title: 'Mango', name: 'Mango' },
|
||||
{ id: 18, title: 'ICE WINTERGREEN', name: 'ICE WINTERGREEN' },
|
||||
{ id: 19, title: 'Pink Lemonade', name: 'Pink Lemonade' },
|
||||
{ id: 20, title: 'Blackcherry', name: 'Blackcherry' },
|
||||
{ id: 21, title: 'fresh mint', name: 'fresh mint' },
|
||||
{ id: 22, title: 'Strawberry Lychee', name: 'Strawberry Lychee' },
|
||||
{ id: 23, title: 'Passion Fruit', name: 'Passion Fruit' },
|
||||
{ id: 24, title: 'Banana lce', name: 'Banana lce' },
|
||||
{ id: 25, title: 'Bubblegum', name: 'Bubblegum' },
|
||||
{ id: 26, title: 'Mango lce', name: 'Mango lce' },
|
||||
{ id: 27, title: 'Grape lce', name: 'Grape lce' },
|
||||
{ id: 28, title: 'Peach', name: 'Peach' },
|
||||
];
|
||||
|
||||
export const brandsData: { id: number; title: string; name: string }[] = [
|
||||
{ id: 1, title: 'Yoone', name: 'YOONE' },
|
||||
{ id: 2, title: 'White Fox', name: 'WHITE_FOX' },
|
||||
{ id: 3, title: 'ZYN', name: 'ZYN' },
|
||||
{ id: 4, title: 'Zonnic', name: 'ZONNIC' },
|
||||
{ id: 5, title: 'Zolt', name: 'ZOLT' },
|
||||
{ id: 6, title: 'Velo', name: 'VELO' },
|
||||
{ id: 7, title: 'Lucy', name: 'LUCY' },
|
||||
{ id: 8, title: 'EGP', name: 'EGP' },
|
||||
{ id: 9, title: 'Bridge', name: 'BRIDGE' },
|
||||
{ id: 10, title: 'ZEX', name: 'ZEX' },
|
||||
{ id: 11, title: 'Sesh', name: 'Sesh' },
|
||||
{ id: 12, title: 'Pablo', name: 'Pablo' },
|
||||
];
|
||||
|
||||
export const strengthsData: { id: number; title: string; name: string }[] = [
|
||||
{ id: 1, title: '3MG', name: '3MG' },
|
||||
{ id: 2, title: '9MG', name: '9MG' },
|
||||
{ id: 3, title: '2MG', name: '2MG' },
|
||||
{ id: 4, title: '4MG', name: '4MG' },
|
||||
{ id: 5, title: '12MG', name: '12MG' },
|
||||
{ id: 6, title: '18MG', name: '18MG' },
|
||||
{ id: 7, title: '6MG', name: '6MG' },
|
||||
{ id: 8, title: '16.5MG', name: '16.5MG' },
|
||||
{ id: 9, title: '6.5MG', name: '6.5MG' },
|
||||
{ id: 10, title: '30MG', name: '30MG' },
|
||||
];
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
import { createConnection } from 'typeorm';
|
||||
import { Dict } from '../../entity/dict.entity';
|
||||
import { DictItem } from '../../entity/dict_item.entity';
|
||||
import { brandsData, flavorsData, strengthsData } from './dict';
|
||||
|
||||
async function seed() {
|
||||
const connection = await createConnection();
|
||||
|
||||
// 创建字典
|
||||
const brandDict = await connection.manager.save(Dict, { name: '品牌', unique_key: 'brand' });
|
||||
const flavorDict = await connection.manager.save(Dict, { name: '口味', unique_key: 'flavor' });
|
||||
const strengthDict = await connection.manager.save(Dict, { name: '规格', unique_key: 'strength' });
|
||||
|
||||
// 植入品牌数据
|
||||
for (const brand of brandsData) {
|
||||
await connection.manager.save(DictItem, {
|
||||
name: brand.name,
|
||||
unique_key: brand.unique_key,
|
||||
dict_id: brandDict.id,
|
||||
});
|
||||
}
|
||||
|
||||
// 植入口味数据
|
||||
for (const flavor of flavorsData) {
|
||||
await connection.manager.save(DictItem, {
|
||||
name: flavor.name,
|
||||
unique_key: flavor.unique_key,
|
||||
dict_id: flavorDict.id,
|
||||
});
|
||||
}
|
||||
|
||||
// 植入规格数据
|
||||
for (const strength of strengthsData) {
|
||||
await connection.manager.save(DictItem, {
|
||||
name: strength.name,
|
||||
unique_key: strength.unique_key,
|
||||
dict_id: strengthDict.id,
|
||||
});
|
||||
}
|
||||
|
||||
await connection.close();
|
||||
}
|
||||
|
||||
seed().catch(error => console.error(error));
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
import { Seeder, SeederFactoryManager } 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 {
|
||||
public async run(
|
||||
dataSource: DataSource,
|
||||
factoryManager: SeederFactoryManager
|
||||
): Promise<any> {
|
||||
const dictRepository = dataSource.getRepository(Dict);
|
||||
const dictItemRepository = dataSource.getRepository(DictItem);
|
||||
|
||||
const flavorsData = [
|
||||
{ id: 1, title: 'Bellini Mini', name: 'Bellini Mini' },
|
||||
{ id: 2, title: 'Max Polarmint', name: 'Max Polarmint' },
|
||||
{ id: 3, title: 'Blueberry', name: 'Blueberry' },
|
||||
{ id: 4, title: 'Citrus', name: 'Citrus' },
|
||||
{ id: 5, title: 'Wintergreen', name: 'Wintergreen' },
|
||||
{ id: 6, title: 'COOL MINT', name: 'COOL MINT' },
|
||||
{ id: 7, title: 'JUICY PEACH', name: 'JUICY PEACH' },
|
||||
{ id: 8, title: 'ORANGE', name: 'ORANGE' },
|
||||
{ id: 9, title: 'PEPPERMINT', name: 'PEPPERMINT' },
|
||||
{ id: 10, title: 'SPEARMINT', name: 'SPEARMINT' },
|
||||
{ id: 11, title: 'STRAWBERRY', name: 'STRAWBERRY' },
|
||||
{ id: 12, title: 'WATERMELON', name: 'WATERMELON' },
|
||||
{ id: 13, title: 'COFFEE', name: 'COFFEE' },
|
||||
{ id: 14, title: 'LEMONADE', name: 'LEMONADE' },
|
||||
{ id: 15, title: 'apple mint', name: 'apple mint' },
|
||||
{ id: 16, title: 'PEACH', name: 'PEACH' },
|
||||
{ id: 17, title: 'Mango', name: 'Mango' },
|
||||
{ id: 18, title: 'ICE WINTERGREEN', name: 'ICE WINTERGREEN' },
|
||||
{ id: 19, title: 'Pink Lemonade', name: 'Pink Lemonade' },
|
||||
{ id: 20, title: 'Blackcherry', name: 'Blackcherry' },
|
||||
{ id: 21, title: 'fresh mint', name: 'fresh mint' },
|
||||
{ id: 22, title: 'Strawberry Lychee', name: 'Strawberry Lychee' },
|
||||
{ id: 23, title: 'Passion Fruit', name: 'Passion Fruit' },
|
||||
{ id: 24, title: 'Banana lce', name: 'Banana lce' },
|
||||
{ id: 25, title: 'Bubblegum', name: 'Bubblegum' },
|
||||
{ id: 26, title: 'Mango lce', name: 'Mango lce' },
|
||||
{ id: 27, title: 'Grape lce', name: 'Grape lce' },
|
||||
];
|
||||
|
||||
const brandsData = [
|
||||
{ id: 1, title: 'Yoone', name: 'YOONE' },
|
||||
{ id: 2, title: 'White Fox', name: 'WHITE_FOX' },
|
||||
{ id: 3, title: 'ZYN', name: 'ZYN' },
|
||||
{ id: 4, title: 'Zonnic', name: 'ZONNIC' },
|
||||
{ id: 5, title: 'Zolt', name: 'ZOLT' },
|
||||
{ id: 6, title: 'Velo', name: 'VELO' },
|
||||
{ id: 7, title: 'Lucy', name: 'LUCY' },
|
||||
{ id: 8, title: 'EGP', name: 'EGP' },
|
||||
{ id: 9, title: 'Bridge', name: 'BRIDGE' },
|
||||
{ id: 10, title: 'ZEX', name: 'ZEX' },
|
||||
{ id: 11, title: 'Sesh', name: 'Sesh' },
|
||||
{ id: 12, title: 'Pablo', name: 'Pablo' },
|
||||
];
|
||||
|
||||
const strengthsData = [
|
||||
{ id: 1, title: '3MG', name: '3MG' },
|
||||
{ id: 2, title: '9MG', name: '9MG' },
|
||||
{ id: 3, title: '2MG', name: '2MG' },
|
||||
{ id: 4, title: '4MG', name: '4MG' },
|
||||
{ id: 5, title: '12MG', name: '12MG' },
|
||||
{ id: 6, title: '18MG', name: '18MG' },
|
||||
{ id: 7, title: '6MG', name: '6MG' },
|
||||
{ id: 8, title: '16.5MG', name: '16.5MG' },
|
||||
{ id: 9, title: '6.5MG', name: '6.5MG' },
|
||||
{ id: 10, title: '30MG', name: '30MG' },
|
||||
];
|
||||
|
||||
// 在插入新数据前,清空旧数据
|
||||
await dictItemRepository.query('DELETE FROM `dict_item`');
|
||||
await dictRepository.query('DELETE FROM `dict`');
|
||||
// 重置自增 ID
|
||||
await dictItemRepository.query('ALTER TABLE `dict_item` AUTO_INCREMENT = 1');
|
||||
await dictRepository.query('ALTER TABLE `dict` AUTO_INCREMENT = 1');
|
||||
|
||||
const brandDict = await dictRepository.save({ title: '品牌', name: 'brand' });
|
||||
const flavorDict = await dictRepository.save({ title: '口味', name: 'flavor' });
|
||||
const strengthDict = await dictRepository.save({ title: '强度', name: 'strength' });
|
||||
|
||||
// 遍历品牌数据
|
||||
for (const brand of brandsData) {
|
||||
// 保存字典项,并关联到品牌字典
|
||||
await dictItemRepository.save({
|
||||
title: brand.title,
|
||||
name: brand.name,
|
||||
dict: brandDict,
|
||||
});
|
||||
}
|
||||
|
||||
// 遍历口味数据
|
||||
for (const flavor of flavorsData) {
|
||||
// 保存字典项,并关联到口味字典
|
||||
await dictItemRepository.save({
|
||||
title: flavor.title,
|
||||
name: flavor.name,
|
||||
dict: flavorDict,
|
||||
});
|
||||
}
|
||||
|
||||
// 遍历强度数据
|
||||
for (const strength of strengthsData) {
|
||||
// 保存字典项,并关联到强度字典
|
||||
await dictItemRepository.save({
|
||||
title: strength.title,
|
||||
name: strength.name,
|
||||
dict: strengthDict,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,16 @@
|
|||
import { ApiProperty } from '@midwayjs/swagger';
|
||||
import { Rule, RuleType } from '@midwayjs/validate';
|
||||
|
||||
class DictItemDTO {
|
||||
@ApiProperty({ description: '显示名称', required: false })
|
||||
@Rule(RuleType.string())
|
||||
title?: string;
|
||||
|
||||
@ApiProperty({ description: '唯一标识', required: true })
|
||||
@Rule(RuleType.string().required())
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO 用于创建产品
|
||||
*/
|
||||
|
|
@ -17,17 +27,17 @@ export class CreateProductDTO {
|
|||
@Rule(RuleType.string())
|
||||
description: string;
|
||||
|
||||
@ApiProperty({ example: '1', description: '分类 ID' })
|
||||
@Rule(RuleType.number())
|
||||
categoryId: number;
|
||||
@ApiProperty({ description: '品牌', type: DictItemDTO })
|
||||
@Rule(RuleType.object().keys({ title: RuleType.string().required(), name: RuleType.string() }))
|
||||
brand: DictItemDTO;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.number())
|
||||
strengthId: number;
|
||||
@ApiProperty({ description: '规格', type: DictItemDTO })
|
||||
@Rule(RuleType.object().keys({ title: RuleType.string().required(), name: RuleType.string() }))
|
||||
strength: DictItemDTO;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.number())
|
||||
flavorsId: number;
|
||||
@ApiProperty({ description: '口味', type: DictItemDTO })
|
||||
@Rule(RuleType.object().keys({ title: RuleType.string().required(), name: RuleType.string() }))
|
||||
flavor: DictItemDTO;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string())
|
||||
|
|
@ -59,36 +69,41 @@ export class QueryProductDTO {
|
|||
@Rule(RuleType.string())
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ example: '1', description: '分类 ID' })
|
||||
@ApiProperty({ example: '1', description: '品牌 ID' })
|
||||
@Rule(RuleType.string())
|
||||
categoryId: number;
|
||||
brandId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO 用于创建分类
|
||||
* DTO 用于创建品牌
|
||||
*/
|
||||
export class CreateCategoryDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '分类名称', required: true })
|
||||
@Rule(RuleType.string().required().empty({ message: '分类名称不能为空' }))
|
||||
name: string;
|
||||
export class CreateBrandDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '品牌名称', required: true })
|
||||
@Rule(RuleType.string().required().empty({ message: '品牌名称不能为空' }))
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ example: 'ZYN', description: '品牌唯一标识', required: true })
|
||||
@Rule(RuleType.string().required().empty({ message: 'key不能为空' }))
|
||||
unique_key: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO 用于更新分类
|
||||
* DTO 用于更新品牌
|
||||
*/
|
||||
export class UpdateCategoryDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '分类名称' })
|
||||
export class UpdateBrandDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '品牌名称' })
|
||||
@Rule(RuleType.string())
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ example: 'ZYN', description: '品牌唯一标识' })
|
||||
@Rule(RuleType.string())
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO 用于查询分类(支持分页)
|
||||
* DTO 用于查询品牌(支持分页)
|
||||
*/
|
||||
export class QueryCategoryDTO {
|
||||
export class QueryBrandDTO {
|
||||
@ApiProperty({ example: '1', description: '页码' })
|
||||
@Rule(RuleType.number())
|
||||
current: number; // 页码
|
||||
|
|
@ -98,21 +113,30 @@ export class QueryCategoryDTO {
|
|||
pageSize: number; // 每页大小
|
||||
|
||||
@ApiProperty({ example: 'ZYN', description: '关键字' })
|
||||
@Rule(RuleType.string())
|
||||
@Rule(RuleType.string().required())
|
||||
name: string; // 搜索关键字(支持模糊查询)
|
||||
}
|
||||
|
||||
export class CreateFlavorsDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '分类名称', required: true })
|
||||
@Rule(RuleType.string().required().empty({ message: '分类名称不能为空' }))
|
||||
name: string;
|
||||
@ApiProperty({ example: 'WINTERGREEN', description: '口味名称', required: true })
|
||||
@Rule(RuleType.string().required().empty({ message: '口味名称不能为空' }))
|
||||
title: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: 'WINTERGREEN',
|
||||
description: '口味唯一标识',
|
||||
required: true,
|
||||
})
|
||||
@Rule(RuleType.string().required().empty({ message: 'key不能为空' }))
|
||||
unique_key: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class UpdateFlavorsDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '分类名称' })
|
||||
@ApiProperty({ example: 'WINTERGREEN', description: '口味名称' })
|
||||
@Rule(RuleType.string())
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ example: 'WINTERGREEN', description: '口味唯一标识' })
|
||||
@Rule(RuleType.string())
|
||||
name: string;
|
||||
}
|
||||
|
|
@ -132,16 +156,21 @@ export class QueryFlavorsDTO {
|
|||
}
|
||||
|
||||
export class CreateStrengthDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '分类名称', required: true })
|
||||
@Rule(RuleType.string().required().empty({ message: '分类名称不能为空' }))
|
||||
name: string;
|
||||
@ApiProperty({ example: '6MG', description: '规格名称', required: false })
|
||||
@Rule(RuleType.string())
|
||||
title?: string;
|
||||
|
||||
@ApiProperty({ example: '6MG', description: '规格唯一标识', required: true })
|
||||
@Rule(RuleType.string().required().empty({ message: 'key不能为空' }))
|
||||
unique_key: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export class UpdateStrengthDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '分类名称' })
|
||||
@ApiProperty({ example: '6MG', description: '规格名称' })
|
||||
@Rule(RuleType.string())
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ example: '6MG', description: '规格唯一标识' })
|
||||
@Rule(RuleType.string())
|
||||
name: string;
|
||||
}
|
||||
|
|
@ -155,7 +184,7 @@ export class QueryStrengthDTO {
|
|||
@Rule(RuleType.number())
|
||||
pageSize: number; // 每页大小
|
||||
|
||||
@ApiProperty({ example: 'ZYN', description: '关键字' })
|
||||
@ApiProperty({ example: 'YOONE', description: '关键字' })
|
||||
@Rule(RuleType.string())
|
||||
name: string; // 搜索关键字(支持模糊查询)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { ApiProperty } from '@midwayjs/swagger';
|
||||
import { Category } from '../entity/category.entity';
|
||||
import { Order } from '../entity/order.entity';
|
||||
import { Product } from '../entity/product.entity';
|
||||
import { StockPoint } from '../entity/stock_point.entity';
|
||||
|
|
@ -18,12 +17,11 @@ import { Service } from '../entity/service.entity';
|
|||
import { RateDTO } from './freightcom.dto';
|
||||
import { ShippingAddress } from '../entity/shipping_address.entity';
|
||||
import { OrderItem } from '../entity/order_item.entity';
|
||||
import { OrderRefundItem } from '../entity/order_retund_item.entity';
|
||||
import { OrderRefundItem } from '../entity/order_refund_item.entity';
|
||||
import { OrderNote } from '../entity/order_note.entity';
|
||||
import { PaymentMethodDTO } from './logistics.dto';
|
||||
import { Flavors } from '../entity/flavors.entity';
|
||||
import { Strength } from '../entity/strength.entity';
|
||||
import { Subscription } from '../entity/subscription.entity';
|
||||
import { Dict } from '../entity/dict.entity';
|
||||
|
||||
export class BooleanRes extends SuccessWrapper(Boolean) {}
|
||||
//网站配置返回数据
|
||||
|
|
@ -35,18 +33,38 @@ export class ProductListRes extends SuccessWrapper(ProductPaginatedResponse) {}
|
|||
//产品返回数据
|
||||
export class ProductRes extends SuccessWrapper(Product) {}
|
||||
export class ProductsRes extends SuccessArrayWrapper(Product) {}
|
||||
//产品分类返分页数据
|
||||
export class CategoryPaginatedResponse extends PaginatedWrapper(Category) {}
|
||||
export class FlavorsPaginatedResponse extends PaginatedWrapper(Flavors) {}
|
||||
export class StrengthPaginatedResponse extends PaginatedWrapper(Strength) {}
|
||||
//产品分类返分页返回数据
|
||||
export class ProductCatListRes extends SuccessWrapper(
|
||||
CategoryPaginatedResponse
|
||||
//产品品牌返分页数据
|
||||
export class BrandPaginatedResponse extends PaginatedWrapper(Dict) {}
|
||||
//产品品牌返分页返回数据
|
||||
export class ProductBrandListRes extends SuccessWrapper(
|
||||
BrandPaginatedResponse
|
||||
) {}
|
||||
//产品分类返所有数据
|
||||
export class ProductCatAllRes extends SuccessArrayWrapper(Category) {}
|
||||
//产品分类返回数据
|
||||
export class ProductCatRes extends SuccessWrapper(Category) {}
|
||||
//产品品牌返所有数据
|
||||
export class ProductBrandAllRes extends SuccessArrayWrapper(Dict) {}
|
||||
//产品品牌返回数据
|
||||
export class ProductBrandRes extends SuccessWrapper(Dict) {}
|
||||
|
||||
//产品口味返分页数据
|
||||
export class FlavorsPaginatedResponse extends PaginatedWrapper(Dict) {}
|
||||
//产品口味返分页返回数据
|
||||
export class ProductFlavorsListRes extends SuccessWrapper(
|
||||
FlavorsPaginatedResponse
|
||||
) {}
|
||||
//产品口味返所有数据
|
||||
export class ProductFlavorsAllRes extends SuccessArrayWrapper(Dict) {}
|
||||
//产品口味返回数据
|
||||
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 WpProductPaginatedResponse extends PaginatedWrapper(
|
||||
|
|
|
|||
|
|
@ -1,53 +0,0 @@
|
|||
import {
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Entity,
|
||||
} from 'typeorm';
|
||||
import { ApiProperty } from '@midwayjs/swagger';
|
||||
|
||||
@Entity()
|
||||
export class Category {
|
||||
@ApiProperty({
|
||||
example: '1',
|
||||
description: '分类 ID',
|
||||
type: 'number',
|
||||
required: true,
|
||||
})
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@ApiProperty({
|
||||
example: '分类名称',
|
||||
description: '分类名称',
|
||||
type: 'string',
|
||||
required: true,
|
||||
})
|
||||
@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;
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
* @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[];
|
||||
|
||||
// 创建时间
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
// 更新时间
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
* @description 字典项
|
||||
* @author ZKS
|
||||
* @date 2025-11-27
|
||||
*/
|
||||
import { Dict } from './dict.entity';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
export class DictItem {
|
||||
// 主键
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
// 字典项名称
|
||||
@Column({ comment: '字典项显示名称' })
|
||||
title: string;
|
||||
|
||||
// 唯一标识
|
||||
@Column({ unique: true, comment: '字典唯一标识名称' })
|
||||
name: string;
|
||||
|
||||
// 属于哪个字典
|
||||
@ManyToOne(() => Dict, dict => dict.items)
|
||||
@JoinColumn({ name: 'dict_id' })
|
||||
dict: Dict;
|
||||
|
||||
// 创建时间
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
// 更新时间
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ export class OrderCoupon {
|
|||
orderId: number; // 订单 ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
@Column( )
|
||||
@Expose()
|
||||
siteId: string; // 来源站点唯一标识
|
||||
|
||||
|
|
@ -35,9 +35,9 @@ export class Product {
|
|||
@Column({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@ApiProperty({ example: '1', description: '分类 ID', type: 'number' })
|
||||
@ApiProperty({ example: '1', description: '品牌 ID', type: 'number' })
|
||||
@Column()
|
||||
categoryId: number;
|
||||
brandId: number;
|
||||
|
||||
@ApiProperty({ description: '口味ID' })
|
||||
@Column()
|
||||
|
|
|
|||
|
|
@ -2,27 +2,27 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
|||
|
||||
@Entity('site')
|
||||
export class Site {
|
||||
@PrimaryGeneratedColumn({ type: 'int' })
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
@Column({ length: 255, nullable: true })
|
||||
apiUrl: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
@Column({ length: 255, nullable: true })
|
||||
consumerKey: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
@Column({ length: 255, nullable: true })
|
||||
consumerSecret: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 255, unique: true })
|
||||
@Column({ length: 255, unique: true })
|
||||
siteName: string;
|
||||
|
||||
@Column({ type: 'varchar', length: 32, default: 'woocommerce' })
|
||||
@Column({ length: 32, default: 'woocommerce' })
|
||||
type: string; // 平台类型:woocommerce | shopyy
|
||||
|
||||
@Column({ type: 'varchar', length: 64, nullable: true })
|
||||
@Column({ length: 64, nullable: true })
|
||||
skuPrefix: string;
|
||||
|
||||
@Column({ type: 'tinyint', default: 0 })
|
||||
isDisabled: number;
|
||||
@Column({ default: false })
|
||||
isDisabled: boolean;
|
||||
}
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
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,8 +12,8 @@ import { WpProduct } from '../entity/wp_product.entity';
|
|||
import { Product } from '../entity/product.entity';
|
||||
import { OrderFee } from '../entity/order_fee.entity';
|
||||
import { OrderRefund } from '../entity/order_refund.entity';
|
||||
import { OrderRefundItem } from '../entity/order_retund_item.entity';
|
||||
import { OrderCoupon } from '../entity/order_copon.entity';
|
||||
import { OrderRefundItem } from '../entity/order_refund_item.entity';
|
||||
import { OrderCoupon } from '../entity/order_coupon.entity';
|
||||
import { OrderShipping } from '../entity/order_shipping.entity';
|
||||
import { Shipment } from '../entity/shipment.entity';
|
||||
import { Customer } from '../entity/customer.entity';
|
||||
|
|
|
|||
|
|
@ -1,21 +1,20 @@
|
|||
import { Provide } from '@midwayjs/core';
|
||||
import { Inject, Provide } from '@midwayjs/core';
|
||||
import { In, Like, Not, Repository } from 'typeorm';
|
||||
import { Product } from '../entity/product.entity';
|
||||
import { Category } from '../entity/category.entity';
|
||||
import { paginate } from '../utils/paginate.util';
|
||||
import { PaginationParams } from '../interface';
|
||||
import {
|
||||
CreateCategoryDTO,
|
||||
CreateBrandDTO,
|
||||
CreateFlavorsDTO,
|
||||
CreateProductDTO,
|
||||
CreateStrengthDTO,
|
||||
UpdateCategoryDTO,
|
||||
UpdateBrandDTO,
|
||||
UpdateFlavorsDTO,
|
||||
UpdateProductDTO,
|
||||
UpdateStrengthDTO,
|
||||
} from '../dto/product.dto';
|
||||
import {
|
||||
CategoryPaginatedResponse,
|
||||
BrandPaginatedResponse,
|
||||
FlavorsPaginatedResponse,
|
||||
ProductPaginatedResponse,
|
||||
StrengthPaginatedResponse,
|
||||
|
|
@ -23,22 +22,23 @@ import {
|
|||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { WpProduct } from '../entity/wp_product.entity';
|
||||
import { Variation } from '../entity/variation.entity';
|
||||
import { Strength } from '../entity/strength.entity';
|
||||
import { Flavors } from '../entity/flavors.entity';
|
||||
import { Dict } from '../entity/dict.entity';
|
||||
import { DictItem } from '../entity/dict_item.entity';
|
||||
import { Context } from '@midwayjs/koa';
|
||||
|
||||
@Provide()
|
||||
export class ProductService {
|
||||
@Inject()
|
||||
ctx: Context;
|
||||
|
||||
@InjectEntityModel(Product)
|
||||
productModel: Repository<Product>;
|
||||
|
||||
@InjectEntityModel(Category)
|
||||
categoryModel: Repository<Category>;
|
||||
@InjectEntityModel(Dict)
|
||||
dictModel: Repository<Dict>;
|
||||
|
||||
@InjectEntityModel(Strength)
|
||||
strengthModel: Repository<Strength>;
|
||||
|
||||
@InjectEntityModel(Flavors)
|
||||
flavorsModel: Repository<Flavors>;
|
||||
@InjectEntityModel(DictItem)
|
||||
dictItemModel: Repository<DictItem>;
|
||||
|
||||
@InjectEntityModel(WpProduct)
|
||||
wpProductModel: Repository<WpProduct>;
|
||||
|
|
@ -111,15 +111,42 @@ export class ProductService {
|
|||
async getProductList(
|
||||
pagination: PaginationParams,
|
||||
name?: string,
|
||||
categoryId?: number
|
||||
brandId?: number
|
||||
): Promise<ProductPaginatedResponse> {
|
||||
const nameFilter = name ? name.split(' ').filter(Boolean) : [];
|
||||
|
||||
// 查询品牌、口味、规格字典
|
||||
const brandDict = await this.dictModel.findOne({
|
||||
where: { name: 'brand' },
|
||||
});
|
||||
const flavorDict = await this.dictModel.findOne({
|
||||
where: { name: 'flavor' },
|
||||
});
|
||||
const strengthDict = await this.dictModel.findOne({
|
||||
where: { name: 'strength' },
|
||||
});
|
||||
|
||||
// 构建查询
|
||||
const qb = this.productModel
|
||||
.createQueryBuilder('product')
|
||||
.leftJoin(Category, 'category', 'category.id = product.categoryId')
|
||||
.leftJoin(Strength, 'strength', 'strength.id = product.strengthId')
|
||||
.leftJoin(Flavors, 'flavors', 'flavors.id = product.flavorsId')
|
||||
.leftJoin(
|
||||
DictItem,
|
||||
'brand',
|
||||
'brand.id = product.brandId AND brand.dict_id = :brandDictId',
|
||||
{ brandDictId: brandDict?.id }
|
||||
)
|
||||
.leftJoin(
|
||||
DictItem,
|
||||
'flavor',
|
||||
'flavor.id = product.flavorsId AND flavor.dict_id = :flavorDictId',
|
||||
{ flavorDictId: flavorDict?.id }
|
||||
)
|
||||
.leftJoin(
|
||||
DictItem,
|
||||
'strength',
|
||||
'strength.id = product.strengthId AND strength.dict_id = :strengthDictId',
|
||||
{ strengthDictId: strengthDict?.id }
|
||||
)
|
||||
.select([
|
||||
'product.id as id',
|
||||
'product.name as name',
|
||||
|
|
@ -129,9 +156,9 @@ export class ProductService {
|
|||
'product.sku as sku',
|
||||
'product.createdAt as createdAt',
|
||||
'product.updatedAt as updatedAt',
|
||||
'category.name AS categoryName',
|
||||
'strength.name AS strengthName',
|
||||
'flavors.name AS flavorsName',
|
||||
'brand.title AS brandName',
|
||||
'flavor.title AS flavorsName',
|
||||
'strength.title AS strengthName',
|
||||
]);
|
||||
|
||||
// 模糊搜索 name,支持多个关键词
|
||||
|
|
@ -141,9 +168,9 @@ export class ProductService {
|
|||
});
|
||||
});
|
||||
|
||||
// 分类过滤
|
||||
if (categoryId) {
|
||||
qb.andWhere('product.categoryId = :categoryId', { categoryId });
|
||||
// 品牌过滤
|
||||
if (brandId) {
|
||||
qb.andWhere('product.brandId = :brandId', { brandId });
|
||||
}
|
||||
|
||||
// 分页
|
||||
|
|
@ -162,35 +189,79 @@ export class ProductService {
|
|||
};
|
||||
}
|
||||
|
||||
async getOrCreateDictItem(
|
||||
dictName: string,
|
||||
itemTitle: string,
|
||||
itemName?: string
|
||||
): Promise<DictItem> {
|
||||
// 查找字典
|
||||
const dict = await this.dictModel.findOne({ where: { name: dictName } });
|
||||
if (!dict) {
|
||||
throw new Error(`字典 '${dictName}' 不存在`);
|
||||
}
|
||||
|
||||
// 查找字典项
|
||||
let item = await this.dictItemModel.findOne({
|
||||
where: { title: itemTitle, dict_id: dict.id },
|
||||
});
|
||||
|
||||
// 如果字典项不存在,则创建
|
||||
if (!item) {
|
||||
item = new DictItem();
|
||||
item.title = itemTitle;
|
||||
item.name = itemName || itemTitle; // 如果没有提供 name,则使用 title
|
||||
item.dict_id = dict.id;
|
||||
await this.dictItemModel.save(item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
async createProduct(createProductDTO: CreateProductDTO): Promise<Product> {
|
||||
const { name, description, categoryId, strengthId, flavorsId, humidity } =
|
||||
const { name, description, brand, flavor, strength, humidity } =
|
||||
createProductDTO;
|
||||
|
||||
// 获取或创建品牌、口味、规格
|
||||
const brandItem = await this.getOrCreateDictItem(
|
||||
'brand',
|
||||
brand.title,
|
||||
brand.name
|
||||
);
|
||||
const flavorItem = await this.getOrCreateDictItem(
|
||||
'flavor',
|
||||
flavor.title,
|
||||
flavor.name
|
||||
);
|
||||
const strengthItem = await this.getOrCreateDictItem(
|
||||
'strength',
|
||||
strength.title,
|
||||
strength.name
|
||||
);
|
||||
|
||||
// 检查产品是否已存在
|
||||
const isExit = await this.productModel.findOne({
|
||||
where: {
|
||||
categoryId,
|
||||
strengthId,
|
||||
flavorsId,
|
||||
brandId: brandItem.id,
|
||||
flavorsId: flavorItem.id,
|
||||
strengthId: strengthItem.id,
|
||||
humidity,
|
||||
},
|
||||
});
|
||||
if (isExit) throw new Error('产品已存在');
|
||||
|
||||
// 创建新产品实例
|
||||
const product = new Product();
|
||||
product.name = name;
|
||||
product.description = description;
|
||||
product.categoryId = categoryId;
|
||||
product.strengthId = strengthId;
|
||||
product.flavorsId = flavorsId;
|
||||
product.brandId = brandItem.id;
|
||||
product.flavorsId = flavorItem.id;
|
||||
product.strengthId = strengthItem.id;
|
||||
product.humidity = humidity;
|
||||
const categoryKey = (
|
||||
await this.categoryModel.findOne({ where: { id: categoryId } })
|
||||
).unique_key;
|
||||
const strengthKey = (
|
||||
await this.strengthModel.findOne({ where: { id: strengthId } })
|
||||
).unique_key;
|
||||
const flavorsKey = (
|
||||
await this.flavorsModel.findOne({ where: { id: flavorsId } })
|
||||
).unique_key;
|
||||
product.sku = `${categoryKey}-${flavorsKey}-${strengthKey}-${humidity}`;
|
||||
|
||||
// 生成 SKU
|
||||
product.sku = `${brandItem.name}-${flavorItem.name}-${strengthItem.name}-${humidity}`;
|
||||
|
||||
// 保存产品
|
||||
return await this.productModel.save(product);
|
||||
}
|
||||
|
||||
|
|
@ -257,67 +328,126 @@ export class ProductService {
|
|||
return result.affected > 0; // `affected` 表示删除的行数
|
||||
}
|
||||
|
||||
async hasProductsInCategory(categoryId: number): Promise<boolean> {
|
||||
async hasProductsInBrand(brandId: number): Promise<boolean> {
|
||||
// 检查是否有产品属于该品牌
|
||||
const count = await this.productModel.count({
|
||||
where: { categoryId },
|
||||
where: { brandId },
|
||||
});
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
async hasCategory(name: string, id?: string): Promise<boolean> {
|
||||
const where: any = { name };
|
||||
async hasBrand(title: string, id?: number): Promise<boolean> {
|
||||
// 查找 'brand' 字典
|
||||
const brandDict = await this.dictModel.findOne({
|
||||
where: { name: 'brand' },
|
||||
});
|
||||
|
||||
// 如果字典不存在,则品牌不存在
|
||||
if (!brandDict) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 设置查询条件
|
||||
const where: any = { title, dict_id: brandDict.id };
|
||||
if (id) where.id = Not(id);
|
||||
const count = await this.categoryModel.count({
|
||||
|
||||
// 统计数量
|
||||
const count = await this.dictItemModel.count({
|
||||
where,
|
||||
});
|
||||
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
async getCategoryList(
|
||||
async getBrandList(
|
||||
pagination: PaginationParams,
|
||||
name?: string
|
||||
): Promise<CategoryPaginatedResponse> {
|
||||
const where: any = {};
|
||||
if (name) {
|
||||
where.name = Like(`%${name}%`);
|
||||
title?: string
|
||||
): Promise<BrandPaginatedResponse> {
|
||||
// 查找 'brand' 字典
|
||||
const brandDict = await this.dictModel.findOne({
|
||||
where: { name: 'brand' },
|
||||
});
|
||||
|
||||
// 如果字典不存在,则返回空
|
||||
if (!brandDict) {
|
||||
return {
|
||||
items: [],
|
||||
total: 0,
|
||||
...pagination,
|
||||
};
|
||||
}
|
||||
return await paginate(this.categoryModel, { pagination, where });
|
||||
}
|
||||
|
||||
async getCategoryAll(): Promise<CategoryPaginatedResponse> {
|
||||
return await this.categoryModel.find();
|
||||
}
|
||||
|
||||
async createCategory(
|
||||
createCategoryDTO: CreateCategoryDTO
|
||||
): Promise<Category> {
|
||||
const { name, unique_key } = createCategoryDTO;
|
||||
const category = new Category();
|
||||
category.name = name;
|
||||
category.unique_key = unique_key;
|
||||
return await this.categoryModel.save(category);
|
||||
}
|
||||
|
||||
async updateCategory(id: number, updateCategory: UpdateCategoryDTO) {
|
||||
// 确认产品是否存在
|
||||
const category = await this.categoryModel.findOneBy({ id });
|
||||
if (!category) {
|
||||
throw new Error(`产品分类 ID ${id} 不存在`);
|
||||
// 设置查询条件
|
||||
const where: any = { dict_id: brandDict.id };
|
||||
if (title) {
|
||||
where.title = Like(`%${title}%`);
|
||||
}
|
||||
// 更新产品
|
||||
await this.categoryModel.update(id, updateCategory);
|
||||
// 返回更新后的产品
|
||||
return await this.categoryModel.findOneBy({ id });
|
||||
|
||||
// 分页查询
|
||||
return await paginate(this.dictItemModel, { pagination, where });
|
||||
}
|
||||
|
||||
async deleteCategory(id: number): Promise<boolean> {
|
||||
// 检查产品是否存在
|
||||
const category = await this.categoryModel.findOneBy({ id });
|
||||
if (!category) {
|
||||
throw new Error(`产品分类 ID ${id} 不存在`);
|
||||
async getBrandAll(): Promise<BrandPaginatedResponse> {
|
||||
// 查找 'brand' 字典
|
||||
const brandDict = await this.dictModel.findOne({
|
||||
where: { name: 'brand' },
|
||||
});
|
||||
|
||||
// 如果字典不存在,则返回空数组
|
||||
if (!brandDict) {
|
||||
return [];
|
||||
}
|
||||
// 删除产品
|
||||
const result = await this.categoryModel.delete(id);
|
||||
|
||||
// 返回所有品牌
|
||||
return this.dictItemModel.find({ where: { dict_id: brandDict.id } });
|
||||
}
|
||||
|
||||
async createBrand(createBrandDTO: CreateBrandDTO): Promise<DictItem> {
|
||||
const { title, name } = createBrandDTO;
|
||||
|
||||
// 查找 'brand' 字典
|
||||
const brandDict = await this.dictModel.findOne({
|
||||
where: { name: 'brand' },
|
||||
});
|
||||
|
||||
// 如果字典不存在,则抛出错误
|
||||
if (!brandDict) {
|
||||
throw new Error('品牌字典不存在');
|
||||
}
|
||||
|
||||
// 创建新的品牌实例
|
||||
const brand = new DictItem();
|
||||
brand.title = title;
|
||||
brand.name = name;
|
||||
brand.dict_id = brandDict.id;
|
||||
|
||||
// 保存到数据库
|
||||
return await this.dictItemModel.save(brand);
|
||||
}
|
||||
|
||||
async updateBrand(id: number, updateBrand: UpdateBrandDTO) {
|
||||
// 确认品牌是否存在
|
||||
const brand = await this.dictItemModel.findOneBy({ id });
|
||||
if (!brand) {
|
||||
throw new Error(`品牌 ID ${id} 不存在`);
|
||||
}
|
||||
|
||||
// 更新品牌
|
||||
await this.dictItemModel.update(id, updateBrand);
|
||||
|
||||
// 返回更新后的品牌
|
||||
return await this.dictItemModel.findOneBy({ id });
|
||||
}
|
||||
|
||||
async deleteBrand(id: number): Promise<boolean> {
|
||||
// 检查品牌是否存在
|
||||
const brand = await this.dictItemModel.findOneBy({ id });
|
||||
if (!brand) {
|
||||
throw new Error(`品牌 ID ${id} 不存在`);
|
||||
}
|
||||
|
||||
// 删除品牌
|
||||
const result = await this.dictItemModel.delete(id);
|
||||
return result.affected > 0; // `affected` 表示删除的行数
|
||||
}
|
||||
|
||||
|
|
@ -328,58 +458,82 @@ export class ProductService {
|
|||
return count > 0;
|
||||
}
|
||||
|
||||
async hasFlavors(name: string, id?: string): Promise<boolean> {
|
||||
const where: any = { name };
|
||||
async hasFlavors(title: string, id?: string): Promise<boolean> {
|
||||
const flavorsDict = await this.dictModel.findOne({
|
||||
where: { name: 'flavor' },
|
||||
});
|
||||
if (!flavorsDict) {
|
||||
return false;
|
||||
}
|
||||
const where: any = { title, dict_id: flavorsDict.id };
|
||||
if (id) where.id = Not(id);
|
||||
const count = await this.flavorsModel.count({
|
||||
const count = await this.dictItemModel.count({
|
||||
where,
|
||||
});
|
||||
return count > 0;
|
||||
}
|
||||
async getFlavorsList(
|
||||
pagination: PaginationParams,
|
||||
name?: string
|
||||
title?: string
|
||||
): Promise<FlavorsPaginatedResponse> {
|
||||
const where: any = {};
|
||||
if (name) {
|
||||
where.name = Like(`%${name}%`);
|
||||
const flavorsDict = await this.dictModel.findOne({
|
||||
where: { name: 'flavor' },
|
||||
});
|
||||
if (!flavorsDict) {
|
||||
return {
|
||||
items: [],
|
||||
total: 0,
|
||||
...pagination,
|
||||
};
|
||||
}
|
||||
return await paginate(this.flavorsModel, { pagination, where });
|
||||
const where: any = { dict_id: flavorsDict.id };
|
||||
if (title) {
|
||||
where.title = Like(`%${title}%`);
|
||||
}
|
||||
return await paginate(this.dictItemModel, { pagination, where });
|
||||
}
|
||||
|
||||
async getFlavorsAll(): Promise<FlavorsPaginatedResponse> {
|
||||
return await this.flavorsModel.find();
|
||||
const flavorsDict = await this.dictModel.findOne({
|
||||
where: { name: 'flavor' },
|
||||
});
|
||||
if (!flavorsDict) {
|
||||
return [];
|
||||
}
|
||||
return this.dictItemModel.find({ where: { dict_id: flavorsDict.id } });
|
||||
}
|
||||
|
||||
async createFlavors(createFlavorsDTO: CreateFlavorsDTO): Promise<Flavors> {
|
||||
const { name, unique_key } = createFlavorsDTO;
|
||||
const flavors = new Flavors();
|
||||
async createFlavors(createFlavorsDTO: CreateFlavorsDTO): Promise<DictItem> {
|
||||
const { title, name } = createFlavorsDTO;
|
||||
const flavorsDict = await this.dictModel.findOne({
|
||||
where: { name: 'flavor' },
|
||||
});
|
||||
if (!flavorsDict) {
|
||||
throw new Error('口味字典不存在');
|
||||
}
|
||||
const flavors = new DictItem();
|
||||
flavors.title = title;
|
||||
flavors.name = name;
|
||||
flavors.unique_key = unique_key;
|
||||
return await this.flavorsModel.save(flavors);
|
||||
flavors.dict_id = flavorsDict.id;
|
||||
return await this.dictItemModel.save(flavors);
|
||||
}
|
||||
|
||||
async updateFlavors(id: number, updateFlavors: UpdateFlavorsDTO) {
|
||||
// 确认产品是否存在
|
||||
const flavors = await this.flavorsModel.findOneBy({ id });
|
||||
const flavors = await this.dictItemModel.findOneBy({ id });
|
||||
if (!flavors) {
|
||||
throw new Error(`口味 ID ${id} 不存在`);
|
||||
}
|
||||
// 更新产品
|
||||
await this.flavorsModel.update(id, updateFlavors);
|
||||
// 返回更新后的产品
|
||||
return await this.flavorsModel.findOneBy({ id });
|
||||
await this.dictItemModel.update(id, updateFlavors);
|
||||
return await this.dictItemModel.findOneBy({ id });
|
||||
}
|
||||
|
||||
async deleteFlavors(id: number): Promise<boolean> {
|
||||
// 检查产品是否存在
|
||||
const flavors = await this.flavorsModel.findOneBy({ id });
|
||||
const flavors = await this.dictItemModel.findOneBy({ id });
|
||||
if (!flavors) {
|
||||
throw new Error(`口味 ID ${id} 不存在`);
|
||||
}
|
||||
// 删除产品
|
||||
const result = await this.flavorsModel.delete(id);
|
||||
return result.affected > 0; // `affected` 表示删除的行数
|
||||
const result = await this.dictItemModel.delete(id);
|
||||
return result.affected > 0;
|
||||
}
|
||||
async hasProductsInStrength(strengthId: number): Promise<boolean> {
|
||||
const count = await this.productModel.count({
|
||||
|
|
@ -388,60 +542,82 @@ export class ProductService {
|
|||
return count > 0;
|
||||
}
|
||||
|
||||
async hasStrength(name: string, id?: string): Promise<boolean> {
|
||||
const where: any = { name };
|
||||
async hasStrength(title: string, id?: string): Promise<boolean> {
|
||||
const strengthDict = await this.dictModel.findOne({
|
||||
where: { name: 'strength' },
|
||||
});
|
||||
if (!strengthDict) {
|
||||
return false;
|
||||
}
|
||||
const where: any = { title, dict_id: strengthDict.id };
|
||||
if (id) where.id = Not(id);
|
||||
const count = await this.strengthModel.count({
|
||||
const count = await this.dictItemModel.count({
|
||||
where,
|
||||
});
|
||||
return count > 0;
|
||||
}
|
||||
async getStrengthList(
|
||||
pagination: PaginationParams,
|
||||
name?: string
|
||||
title?: string
|
||||
): Promise<StrengthPaginatedResponse> {
|
||||
const where: any = {};
|
||||
if (name) {
|
||||
where.name = Like(`%${name}%`);
|
||||
const strengthDict = await this.dictModel.findOne({
|
||||
where: { name: 'strength' },
|
||||
});
|
||||
if (!strengthDict) {
|
||||
return {
|
||||
items: [],
|
||||
total: 0,
|
||||
...pagination,
|
||||
};
|
||||
}
|
||||
return await paginate(this.strengthModel, { pagination, where });
|
||||
const where: any = { dict_id: strengthDict.id };
|
||||
if (title) {
|
||||
where.title = Like(`%${title}%`);
|
||||
}
|
||||
return await paginate(this.dictItemModel, { pagination, where });
|
||||
}
|
||||
|
||||
async getStrengthAll(): Promise<StrengthPaginatedResponse> {
|
||||
return await this.strengthModel.find();
|
||||
const strengthDict = await this.dictModel.findOne({
|
||||
where: { name: 'strength' },
|
||||
});
|
||||
if (!strengthDict) {
|
||||
return [];
|
||||
}
|
||||
return this.dictItemModel.find({ where: { dict_id: strengthDict.id } });
|
||||
}
|
||||
|
||||
async createStrength(
|
||||
createStrengthDTO: CreateStrengthDTO
|
||||
): Promise<Strength> {
|
||||
const { name, unique_key } = createStrengthDTO;
|
||||
const strength = new Strength();
|
||||
async createStrength(createStrengthDTO: CreateStrengthDTO): Promise<DictItem> {
|
||||
const { title, name } = createStrengthDTO;
|
||||
const strengthDict = await this.dictModel.findOne({
|
||||
where: { name: 'strength' },
|
||||
});
|
||||
if (!strengthDict) {
|
||||
throw new Error('规格字典不存在');
|
||||
}
|
||||
const strength = new DictItem();
|
||||
strength.title = title;
|
||||
strength.name = name;
|
||||
strength.unique_key = unique_key;
|
||||
return await this.strengthModel.save(strength);
|
||||
strength.dict_id = strengthDict.id;
|
||||
return await this.dictItemModel.save(strength);
|
||||
}
|
||||
|
||||
async updateStrength(id: number, updateStrength: UpdateStrengthDTO) {
|
||||
// 确认产品是否存在
|
||||
const strength = await this.strengthModel.findOneBy({ id });
|
||||
const strength = await this.dictItemModel.findOneBy({ id });
|
||||
if (!strength) {
|
||||
throw new Error(`口味 ID ${id} 不存在`);
|
||||
throw new Error(`规格 ID ${id} 不存在`);
|
||||
}
|
||||
// 更新产品
|
||||
await this.strengthModel.update(id, updateStrength);
|
||||
// 返回更新后的产品
|
||||
return await this.strengthModel.findOneBy({ id });
|
||||
await this.dictItemModel.update(id, updateStrength);
|
||||
return await this.dictItemModel.findOneBy({ id });
|
||||
}
|
||||
|
||||
async deleteStrength(id: number): Promise<boolean> {
|
||||
// 检查产品是否存在
|
||||
const strength = await this.strengthModel.findOneBy({ id });
|
||||
const strength = await this.dictItemModel.findOneBy({ id });
|
||||
if (!strength) {
|
||||
throw new Error(`口味 ID ${id} 不存在`);
|
||||
throw new Error(`规格 ID ${id} 不存在`);
|
||||
}
|
||||
// 删除产品
|
||||
const result = await this.flavorsModel.delete(id);
|
||||
return result.affected > 0; // `affected` 表示删除的行数
|
||||
const result = await this.dictItemModel.delete(id);
|
||||
return result.affected > 0;
|
||||
}
|
||||
|
||||
async batchSetSku(skus: { productId: number; sku: string }[]) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue