Compare commits
No commits in common. "3545633f9e6b3c4cc2e5592d30a349855a99e903" and "b0903aca60430fdc2a35e7a5c5f0a885abda9b66" have entirely different histories.
3545633f9e
...
b0903aca60
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
|
|
@ -17,18 +17,14 @@
|
|||
"@midwayjs/typeorm": "^3.20.0",
|
||||
"@midwayjs/validate": "^3.20.2",
|
||||
"@woocommerce/woocommerce-rest-api": "^1.0.2",
|
||||
"axios": "^1.13.2",
|
||||
"axios": "^1.7.9",
|
||||
"bcryptjs": "^2.4.3",
|
||||
"class-transformer": "^0.5.1",
|
||||
"csv-parse": "^6.1.0",
|
||||
"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.27",
|
||||
"typeorm-extension": "^3.7.2",
|
||||
"xlsx": "^0.18.5",
|
||||
"typeorm": "^0.3.20",
|
||||
"xml2js": "^0.6.2"
|
||||
},
|
||||
"engines": {
|
||||
|
|
@ -40,13 +36,10 @@
|
|||
"dev": "cross-env NODE_ENV=local mwtsc --watch --run @midwayjs/mock/app.js",
|
||||
"test": "cross-env NODE_ENV=unittest jest",
|
||||
"cov": "jest --coverage",
|
||||
"lint": "mwtsc check",
|
||||
"lint:fix": "mwtsc fix",
|
||||
"lint": "mwts check",
|
||||
"lint:fix": "mwts fix",
|
||||
"ci": "npm run cov",
|
||||
"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"
|
||||
"build": "mwtsc --cleanOutDir"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
|
@ -58,7 +51,6 @@
|
|||
"@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,18 +65,12 @@ 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
|
||||
|
|
@ -90,9 +84,6 @@ 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
|
||||
|
|
@ -106,162 +97,6 @@ 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==}
|
||||
|
||||
|
|
@ -479,10 +314,6 @@ 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'}
|
||||
|
|
@ -584,10 +415,6 @@ 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'}
|
||||
|
|
@ -619,10 +446,6 @@ 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'}
|
||||
|
|
@ -665,11 +488,6 @@ 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==}
|
||||
|
||||
|
|
@ -788,11 +606,6 @@ 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'}
|
||||
|
|
@ -838,10 +651,6 @@ 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'}
|
||||
|
|
@ -1152,11 +961,6 @@ 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==}
|
||||
|
||||
|
|
@ -1356,10 +1160,6 @@ 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'}
|
||||
|
|
@ -1428,11 +1228,6 @@ 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'}
|
||||
|
|
@ -1532,14 +1327,6 @@ 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'}
|
||||
|
|
@ -1551,11 +1338,6 @@ 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'}
|
||||
|
|
@ -1591,84 +1373,6 @@ 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': {}
|
||||
|
|
@ -1941,8 +1645,6 @@ 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: {}
|
||||
|
|
@ -2033,11 +1735,6 @@ 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
|
||||
|
|
@ -2082,8 +1779,6 @@ snapshots:
|
|||
|
||||
co@4.6.0: {}
|
||||
|
||||
codepage@1.15.0: {}
|
||||
|
||||
color-convert@2.0.1:
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
|
|
@ -2117,8 +1812,6 @@ snapshots:
|
|||
|
||||
core-util-is@1.0.3: {}
|
||||
|
||||
crc-32@1.2.2: {}
|
||||
|
||||
create-hash@1.2.0:
|
||||
dependencies:
|
||||
cipher-base: 1.0.7
|
||||
|
|
@ -2226,35 +1919,6 @@ 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: {}
|
||||
|
|
@ -2303,8 +1967,6 @@ snapshots:
|
|||
once: 1.4.0
|
||||
qs: 6.14.0
|
||||
|
||||
frac@1.1.2: {}
|
||||
|
||||
fresh@0.5.2: {}
|
||||
|
||||
fsevents@2.3.3:
|
||||
|
|
@ -2651,8 +2313,6 @@ snapshots:
|
|||
|
||||
normalize-path@3.0.0: {}
|
||||
|
||||
npm-check-updates@19.1.2: {}
|
||||
|
||||
oauth-1.0a@2.2.6: {}
|
||||
|
||||
object-inspect@1.13.4: {}
|
||||
|
|
@ -2834,10 +2494,6 @@ snapshots:
|
|||
|
||||
sqlstring@2.3.3: {}
|
||||
|
||||
ssf@0.11.2:
|
||||
dependencies:
|
||||
frac: 1.1.2
|
||||
|
||||
statuses@1.5.0: {}
|
||||
|
||||
statuses@2.0.1: {}
|
||||
|
|
@ -2926,13 +2582,6 @@ 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
|
||||
|
|
@ -2998,10 +2647,6 @@ 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
|
||||
|
|
@ -3016,16 +2661,6 @@ 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,5 +1,6 @@
|
|||
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';
|
||||
|
|
@ -10,10 +11,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_coupon.entity';
|
||||
import { OrderCoupon } from '../entity/order_copon.entity';
|
||||
import { OrderFee } from '../entity/order_fee.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 { OrderSaleOriginal } from '../entity/order_item_original.entity';
|
||||
import { OrderShipping } from '../entity/order_shipping.entity';
|
||||
|
|
@ -25,16 +26,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';
|
||||
import { Template } from '../entity/template.entity';
|
||||
import DictSeeder from '../db/seeds/dict.seeder';
|
||||
|
||||
export default {
|
||||
// use for cookie sign key, should change to your own and keep security
|
||||
|
|
@ -43,6 +42,9 @@ export default {
|
|||
default: {
|
||||
entities: [
|
||||
Product,
|
||||
Category,
|
||||
Strength,
|
||||
Flavors,
|
||||
WpProduct,
|
||||
Variation,
|
||||
User,
|
||||
|
|
@ -74,13 +76,9 @@ export default {
|
|||
AuthCode,
|
||||
Subscription,
|
||||
Site,
|
||||
Dict,
|
||||
DictItem,
|
||||
Template
|
||||
],
|
||||
synchronize: true,
|
||||
logging: false,
|
||||
seeders: [DictSeeder],
|
||||
},
|
||||
dataSource: {
|
||||
default: {
|
||||
|
|
|
|||
|
|
@ -1,171 +0,0 @@
|
|||
|
||||
import { Inject, Controller, Get, Post, Put, Del, Query, Body, Param, Files, 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';
|
||||
|
||||
/**
|
||||
* 字典管理
|
||||
* @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.importDictsFromXLSX(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) {
|
||||
// 调用服务层方法
|
||||
return this.dictService.createDict(createDictDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新字典
|
||||
* @param id 字典ID
|
||||
* @param updateDictDTO 待更新的字典数据
|
||||
*/
|
||||
@Put('/:id')
|
||||
@Validate()
|
||||
async updateDict(@Param('id') id: number, @Body() updateDictDTO: UpdateDictDTO) {
|
||||
// 调用服务层方法
|
||||
return this.dictService.updateDict(id, updateDictDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除字典
|
||||
* @param id 字典ID
|
||||
*/
|
||||
@Del('/:id')
|
||||
async deleteDict(@Param('id') id: number) {
|
||||
// 调用服务层方法
|
||||
return this.dictService.deleteDict(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量导入字典项
|
||||
* @param files 上传的文件
|
||||
* @param body 请求体,包含字典ID
|
||||
*/
|
||||
@Post('/item/import')
|
||||
@Validate()
|
||||
async importDictItems(@Files() files: any, @Body() body: { dictId: number }) {
|
||||
// 获取第一个文件
|
||||
const file = files[0];
|
||||
// 调用服务层方法
|
||||
const result = await this.dictService.importDictItemsFromXLSX(file.data, body.dictId);
|
||||
// 返回结果
|
||||
return 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字典项列表
|
||||
* @param dictId 字典ID (可选)
|
||||
*/
|
||||
@Get('/items')
|
||||
async getDictItems(@Query('dictId') dictId?: number) {
|
||||
// 调用服务层方法
|
||||
return this.dictService.getDictItems(dictId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建新字典项
|
||||
* @param createDictItemDTO 字典项数据
|
||||
*/
|
||||
@Post('/item')
|
||||
@Validate()
|
||||
async createDictItem(@Body() createDictItemDTO: CreateDictItemDTO) {
|
||||
// 调用服务层方法
|
||||
return this.dictService.createDictItem(createDictItemDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新字典项
|
||||
* @param id 字典项ID
|
||||
* @param updateDictItemDTO 待更新的字典项数据
|
||||
*/
|
||||
@Put('/item/:id')
|
||||
@Validate()
|
||||
async updateDictItem(@Param('id') id: number, @Body() updateDictItemDTO: UpdateDictItemDTO) {
|
||||
// 调用服务层方法
|
||||
return this.dictService.updateDictItem(id, updateDictItemDTO);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除字典项
|
||||
* @param id 字典项ID
|
||||
*/
|
||||
@Del('/item/:id')
|
||||
async deleteDictItem(@Param('id') id: number) {
|
||||
// 调用服务层方法
|
||||
return this.dictService.deleteDictItem(id);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -13,15 +13,15 @@ import { ProductService } from '../service/product.service';
|
|||
import { errorResponse, successResponse } from '../utils/response.util';
|
||||
import {
|
||||
BatchSetSkuDTO,
|
||||
CreateBrandDTO,
|
||||
CreateCategoryDTO,
|
||||
CreateFlavorsDTO,
|
||||
CreateProductDTO,
|
||||
CreateStrengthDTO,
|
||||
QueryBrandDTO,
|
||||
QueryCategoryDTO,
|
||||
QueryFlavorsDTO,
|
||||
QueryProductDTO,
|
||||
QueryStrengthDTO,
|
||||
UpdateBrandDTO,
|
||||
UpdateCategoryDTO,
|
||||
UpdateFlavorsDTO,
|
||||
UpdateProductDTO,
|
||||
UpdateStrengthDTO,
|
||||
|
|
@ -29,8 +29,8 @@ import {
|
|||
import { ApiOkResponse } from '@midwayjs/swagger';
|
||||
import {
|
||||
BooleanRes,
|
||||
ProductBrandListRes,
|
||||
ProductBrandRes,
|
||||
ProductCatListRes,
|
||||
ProductCatRes,
|
||||
ProductListRes,
|
||||
ProductRes,
|
||||
ProductsRes,
|
||||
|
|
@ -79,12 +79,12 @@ export class ProductController {
|
|||
async getProductList(
|
||||
@Query() query: QueryProductDTO
|
||||
): Promise<ProductListRes> {
|
||||
const { current = 1, pageSize = 10, name, brandId } = query;
|
||||
const { current = 1, pageSize = 10, name, categoryId } = query;
|
||||
try {
|
||||
const data = await this.productService.getProductList(
|
||||
{ current, pageSize },
|
||||
name,
|
||||
brandId
|
||||
categoryId
|
||||
);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
|
|
@ -138,6 +138,8 @@ export class ProductController {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ApiOkResponse({
|
||||
type: BooleanRes,
|
||||
})
|
||||
|
|
@ -152,13 +154,13 @@ export class ProductController {
|
|||
}
|
||||
|
||||
@ApiOkResponse({
|
||||
type: ProductBrandListRes,
|
||||
type: ProductCatListRes,
|
||||
})
|
||||
@Get('/brands')
|
||||
async getBrands(@Query() query: QueryBrandDTO) {
|
||||
@Get('/categories')
|
||||
async getCategories(@Query() query: QueryCategoryDTO) {
|
||||
const { current = 1, pageSize = 10, name } = query;
|
||||
try {
|
||||
let data = await this.productService.getBrandList(
|
||||
let data = await this.productService.getCategoryList(
|
||||
{ current, pageSize },
|
||||
name
|
||||
);
|
||||
|
|
@ -169,10 +171,10 @@ export class ProductController {
|
|||
}
|
||||
|
||||
@ApiOkResponse()
|
||||
@Get('/brandAll')
|
||||
async getBrandAll() {
|
||||
@Get('/categorieAll')
|
||||
async getCategorieAll() {
|
||||
try {
|
||||
let data = await this.productService.getBrandAll();
|
||||
let data = await this.productService.getCategoryAll();
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
return errorResponse(error?.message || error);
|
||||
|
|
@ -180,19 +182,18 @@ export class ProductController {
|
|||
}
|
||||
|
||||
@ApiOkResponse({
|
||||
type: ProductBrandRes,
|
||||
type: ProductCatRes,
|
||||
})
|
||||
@Post('/brand')
|
||||
async createBrand(@Body() brandData: CreateBrandDTO) {
|
||||
@Post('/category')
|
||||
async createCategory(@Body() categoryData: CreateCategoryDTO) {
|
||||
try {
|
||||
const hasBrand = await this.productService.hasAttribute(
|
||||
'brand',
|
||||
brandData.name
|
||||
const hasCategory = await this.productService.hasCategory(
|
||||
categoryData.name
|
||||
);
|
||||
if (hasBrand) {
|
||||
return errorResponse('品牌已存在');
|
||||
if (hasCategory) {
|
||||
return errorResponse('分类已存在');
|
||||
}
|
||||
let data = await this.productService.createBrand(brandData);
|
||||
let data = await this.productService.createCategory(categoryData);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
return errorResponse(error?.message || error);
|
||||
|
|
@ -200,23 +201,21 @@ export class ProductController {
|
|||
}
|
||||
|
||||
@ApiOkResponse({
|
||||
type: ProductBrandRes,
|
||||
type: ProductCatRes,
|
||||
})
|
||||
@Put('/brand/:id')
|
||||
async updateBrand(
|
||||
@Put('/category/:id')
|
||||
async updateCategory(
|
||||
@Param('id') id: number,
|
||||
@Body() brandData: UpdateBrandDTO
|
||||
@Body() categoryData: UpdateCategoryDTO
|
||||
) {
|
||||
try {
|
||||
const hasBrand = await this.productService.hasAttribute(
|
||||
'brand',
|
||||
brandData.name,
|
||||
id
|
||||
const hasCategory = await this.productService.hasCategory(
|
||||
categoryData.name
|
||||
);
|
||||
if (hasBrand) {
|
||||
return errorResponse('品牌已存在');
|
||||
if (hasCategory) {
|
||||
return errorResponse('分类已存在');
|
||||
}
|
||||
const data = this.productService.updateBrand(id, brandData);
|
||||
const data = this.productService.updateCategory(id, categoryData);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
return errorResponse(error?.message || error);
|
||||
|
|
@ -226,12 +225,12 @@ export class ProductController {
|
|||
@ApiOkResponse({
|
||||
type: BooleanRes,
|
||||
})
|
||||
@Del('/brand/:id')
|
||||
async deleteBrand(@Param('id') id: number) {
|
||||
@Del('/category/:id')
|
||||
async deleteCategory(@Param('id') id: number) {
|
||||
try {
|
||||
const hasProducts = await this.productService.hasProductsInAttribute(id);
|
||||
if (hasProducts) throw new Error('该品牌下有商品,无法删除');
|
||||
const data = await this.productService.deleteBrand(id);
|
||||
const hasProducts = await this.productService.hasProductsInCategory(id);
|
||||
if (hasProducts) throw new Error('该分类下有商品,无法删除');
|
||||
const data = await this.productService.deleteCategory(id);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
return errorResponse(error?.message || error);
|
||||
|
|
@ -282,9 +281,9 @@ export class ProductController {
|
|||
@Post('/flavors')
|
||||
async createFlavors(@Body() flavorsData: CreateFlavorsDTO) {
|
||||
try {
|
||||
const hasFlavors = await this.productService.hasAttribute('flavor', flavorsData.name);
|
||||
const hasFlavors = await this.productService.hasFlavors(flavorsData.name);
|
||||
if (hasFlavors) {
|
||||
return errorResponse('口味已存在');
|
||||
return errorResponse('分类已存在');
|
||||
}
|
||||
let data = await this.productService.createFlavors(flavorsData);
|
||||
return successResponse(data);
|
||||
|
|
@ -300,9 +299,9 @@ export class ProductController {
|
|||
@Body() flavorsData: UpdateFlavorsDTO
|
||||
) {
|
||||
try {
|
||||
const hasFlavors = await this.productService.hasAttribute('flavor', flavorsData.name, id);
|
||||
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,8 +316,8 @@ export class ProductController {
|
|||
@Del('/flavors/:id')
|
||||
async deleteFlavors(@Param('id') id: number) {
|
||||
try {
|
||||
const hasProducts = await this.productService.hasProductsInAttribute(id);
|
||||
if (hasProducts) throw new Error('该口味下有商品,无法删除');
|
||||
const hasProducts = await this.productService.hasProductsInFlavors(id);
|
||||
if (hasProducts) throw new Error('该分类下有商品,无法删除');
|
||||
const data = await this.productService.deleteFlavors(id);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
|
|
@ -356,12 +355,11 @@ export class ProductController {
|
|||
@Post('/strength')
|
||||
async createStrength(@Body() strengthData: CreateStrengthDTO) {
|
||||
try {
|
||||
const hasStrength = await this.productService.hasAttribute(
|
||||
'strength',
|
||||
const hasStrength = await this.productService.hasStrength(
|
||||
strengthData.name
|
||||
);
|
||||
if (hasStrength) {
|
||||
return errorResponse('规格已存在');
|
||||
return errorResponse('分类已存在');
|
||||
}
|
||||
let data = await this.productService.createStrength(strengthData);
|
||||
return successResponse(data);
|
||||
|
|
@ -377,13 +375,11 @@ export class ProductController {
|
|||
@Body() strengthData: UpdateStrengthDTO
|
||||
) {
|
||||
try {
|
||||
const hasStrength = await this.productService.hasAttribute(
|
||||
'strength',
|
||||
strengthData.name,
|
||||
id
|
||||
const hasStrength = await this.productService.hasStrength(
|
||||
strengthData.name
|
||||
);
|
||||
if (hasStrength) {
|
||||
return errorResponse('规格已存在');
|
||||
return errorResponse('分类已存在');
|
||||
}
|
||||
const data = this.productService.updateStrength(id, strengthData);
|
||||
return successResponse(data);
|
||||
|
|
@ -398,8 +394,8 @@ export class ProductController {
|
|||
@Del('/strength/:id')
|
||||
async deleteStrength(@Param('id') id: number) {
|
||||
try {
|
||||
const hasProducts = await this.productService.hasProductsInAttribute(id);
|
||||
if (hasProducts) throw new Error('该规格下有商品,无法删除');
|
||||
const hasProducts = await this.productService.hasProductsInStrength(id);
|
||||
if (hasProducts) throw new Error('该分类下有商品,无法删除');
|
||||
const data = await this.productService.deleteStrength(id);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -1,115 +0,0 @@
|
|||
import { Inject, Controller, Get, Post, Put, Del, Body, Param } from '@midwayjs/core';
|
||||
import { TemplateService } from '../service/template.service';
|
||||
import { successResponse, errorResponse } from '../utils/response.util';
|
||||
import { CreateTemplateDTO, UpdateTemplateDTO } 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('/')
|
||||
async getTemplateList() {
|
||||
try {
|
||||
// 调用服务层获取列表
|
||||
const data = await this.templateService.getTemplateList();
|
||||
// 返回成功响应
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
// 返回错误响应
|
||||
return errorResponse(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,88 +0,0 @@
|
|||
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 const AppDataSource = new DataSource(options);
|
||||
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class ProductDictItemManyToMany1764238434984 implements MigrationInterface {
|
||||
name = 'ProductDictItemManyToMany1764238434984'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`CREATE TABLE \`product_attributes_dict_item\` (\`productId\` int NOT NULL, \`dictItemId\` int NOT NULL, INDEX \`IDX_592cdbdaebfec346c202ffb82c\` (\`productId\`), INDEX \`IDX_406c1da5b6de45fecb7967c3ec\` (\`dictItemId\`), PRIMARY KEY (\`productId\`, \`dictItemId\`)) ENGINE=InnoDB`);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` DROP COLUMN \`brandId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` DROP COLUMN \`flavorsId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` DROP COLUMN \`strengthId\``);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` DROP COLUMN \`humidity\``);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` ADD \`sku\` varchar(255) NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` ADD UNIQUE INDEX \`IDX_34f6ca1cd897cc926bdcca1ca3\` (\`sku\`)`);
|
||||
await queryRunner.query(`ALTER TABLE \`product_attributes_dict_item\` ADD CONSTRAINT \`FK_592cdbdaebfec346c202ffb82ca\` FOREIGN KEY (\`productId\`) REFERENCES \`product\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE`);
|
||||
await queryRunner.query(`ALTER TABLE \`product_attributes_dict_item\` ADD CONSTRAINT \`FK_406c1da5b6de45fecb7967c3ec0\` FOREIGN KEY (\`dictItemId\`) REFERENCES \`dict_item\`(\`id\`) ON DELETE CASCADE ON UPDATE CASCADE`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE \`product_attributes_dict_item\` DROP FOREIGN KEY \`FK_406c1da5b6de45fecb7967c3ec0\``);
|
||||
await queryRunner.query(`ALTER TABLE \`product_attributes_dict_item\` DROP FOREIGN KEY \`FK_592cdbdaebfec346c202ffb82ca\``);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` DROP INDEX \`IDX_34f6ca1cd897cc926bdcca1ca3\``);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` DROP COLUMN \`sku\``);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` ADD \`humidity\` varchar(255) NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` ADD \`strengthId\` int NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` ADD \`flavorsId\` int NOT NULL`);
|
||||
await queryRunner.query(`ALTER TABLE \`product\` ADD \`brandId\` int NOT NULL`);
|
||||
await queryRunner.query(`DROP INDEX \`IDX_406c1da5b6de45fecb7967c3ec\` ON \`product_attributes_dict_item\``);
|
||||
await queryRunner.query(`DROP INDEX \`IDX_592cdbdaebfec346c202ffb82c\` ON \`product_attributes_dict_item\``);
|
||||
await queryRunner.query(`DROP TABLE \`product_attributes_dict_item\``);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,153 +0,0 @@
|
|||
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 locales = [
|
||||
{ name: 'zh-CN', title: '简体中文' },
|
||||
{ name: 'en-US', title: 'English' },
|
||||
];
|
||||
|
||||
for (const locale of locales) {
|
||||
let dict = await dictRepository.findOne({ where: { name: locale.name } });
|
||||
if (!dict) {
|
||||
dict = await dictRepository.save(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, 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, dict: enDict });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
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,36 +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 执行数据填充操作。如果 product_sku 模板不存在,则创建它。
|
||||
* @param {DataSource} dataSource - 数据源实例,用于获取 repository。
|
||||
* @param {SeederFactoryManager} factoryManager - Seeder 工厂管理器。
|
||||
*/
|
||||
public async run(
|
||||
dataSource: DataSource,
|
||||
factoryManager: SeederFactoryManager
|
||||
): Promise<any> {
|
||||
// 获取 Template 实体的 repository
|
||||
const templateRepository = dataSource.getRepository(Template);
|
||||
|
||||
// 检查名为 'product_sku' 的模板是否已存在
|
||||
const existingTemplate = await templateRepository.findOne({
|
||||
where: { name: 'product_sku' },
|
||||
});
|
||||
|
||||
// 如果模板不存在,则创建并保存
|
||||
if (!existingTemplate) {
|
||||
const template = new Template();
|
||||
template.name = 'product_sku';
|
||||
template.value = '{{brand}}-{{flavor}}-{{strength}}-{{humidity}}';
|
||||
await templateRepository.save(template);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +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.number().required())
|
||||
dictId: number; // 所属字典的ID
|
||||
}
|
||||
|
||||
// 更新字典项的数据传输对象
|
||||
export class UpdateDictItemDTO {
|
||||
@Rule(RuleType.string())
|
||||
name?: string; // 字典项名称 (可选)
|
||||
|
||||
@Rule(RuleType.string())
|
||||
title?: string; // 字典项标题 (可选)
|
||||
}
|
||||
|
|
@ -1,16 +1,6 @@
|
|||
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 用于创建产品
|
||||
*/
|
||||
|
|
@ -27,36 +17,17 @@ export class CreateProductDTO {
|
|||
@Rule(RuleType.string())
|
||||
description: string;
|
||||
|
||||
@ApiProperty({ description: '产品 SKU', required: false })
|
||||
@Rule(RuleType.string())
|
||||
sku?: 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({ description: '口味', type: DictItemDTO })
|
||||
@Rule(
|
||||
RuleType.object().keys({
|
||||
title: RuleType.string().required(),
|
||||
name: RuleType.string(),
|
||||
})
|
||||
)
|
||||
flavor: DictItemDTO;
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.number())
|
||||
flavorsId: number;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string())
|
||||
|
|
@ -88,41 +59,36 @@ export class QueryProductDTO {
|
|||
@Rule(RuleType.string())
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ example: '1', description: '品牌 ID' })
|
||||
@ApiProperty({ example: '1', description: '分类 ID' })
|
||||
@Rule(RuleType.string())
|
||||
brandId: number;
|
||||
categoryId: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO 用于创建品牌
|
||||
* DTO 用于创建分类
|
||||
*/
|
||||
export class CreateBrandDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '品牌名称', required: true })
|
||||
@Rule(RuleType.string().required().empty({ message: '品牌名称不能为空' }))
|
||||
title: string;
|
||||
export class CreateCategoryDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '分类名称', required: true })
|
||||
@Rule(RuleType.string().required().empty({ message: '分类名称不能为空' }))
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ example: 'ZYN', description: '品牌唯一标识', required: true })
|
||||
@Rule(RuleType.string().required().empty({ message: 'key不能为空' }))
|
||||
name: string;
|
||||
unique_key: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO 用于更新品牌
|
||||
* DTO 用于更新分类
|
||||
*/
|
||||
export class UpdateBrandDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '品牌名称' })
|
||||
@Rule(RuleType.string())
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ example: 'ZYN', description: '品牌唯一标识' })
|
||||
export class UpdateCategoryDTO {
|
||||
@ApiProperty({ example: 'ZYN', description: '分类名称' })
|
||||
@Rule(RuleType.string())
|
||||
name: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO 用于查询品牌(支持分页)
|
||||
* DTO 用于查询分类(支持分页)
|
||||
*/
|
||||
export class QueryBrandDTO {
|
||||
export class QueryCategoryDTO {
|
||||
@ApiProperty({ example: '1', description: '页码' })
|
||||
@Rule(RuleType.number())
|
||||
current: number; // 页码
|
||||
|
|
@ -132,30 +98,21 @@ export class QueryBrandDTO {
|
|||
pageSize: number; // 每页大小
|
||||
|
||||
@ApiProperty({ example: 'ZYN', description: '关键字' })
|
||||
@Rule(RuleType.string().required())
|
||||
@Rule(RuleType.string())
|
||||
name: string; // 搜索关键字(支持模糊查询)
|
||||
}
|
||||
|
||||
export class CreateFlavorsDTO {
|
||||
@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不能为空' }))
|
||||
@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: 'WINTERGREEN', description: '口味名称' })
|
||||
@Rule(RuleType.string())
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ example: 'WINTERGREEN', description: '口味唯一标识' })
|
||||
@ApiProperty({ example: 'ZYN', description: '分类名称' })
|
||||
@Rule(RuleType.string())
|
||||
name: string;
|
||||
}
|
||||
|
|
@ -175,21 +132,16 @@ export class QueryFlavorsDTO {
|
|||
}
|
||||
|
||||
export class CreateStrengthDTO {
|
||||
@ApiProperty({ example: '6MG', description: '规格名称', required: false })
|
||||
@Rule(RuleType.string())
|
||||
title?: string;
|
||||
|
||||
@ApiProperty({ example: '6MG', description: '规格唯一标识', required: true })
|
||||
@Rule(RuleType.string().required().empty({ message: 'key不能为空' }))
|
||||
@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: '6MG', description: '规格名称' })
|
||||
@Rule(RuleType.string())
|
||||
title: string;
|
||||
|
||||
@ApiProperty({ example: '6MG', description: '规格唯一标识' })
|
||||
@ApiProperty({ example: 'ZYN', description: '分类名称' })
|
||||
@Rule(RuleType.string())
|
||||
name: string;
|
||||
}
|
||||
|
|
@ -203,7 +155,7 @@ export class QueryStrengthDTO {
|
|||
@Rule(RuleType.number())
|
||||
pageSize: number; // 每页大小
|
||||
|
||||
@ApiProperty({ example: 'YOONE', description: '关键字' })
|
||||
@ApiProperty({ example: 'ZYN', description: '关键字' })
|
||||
@Rule(RuleType.string())
|
||||
name: string; // 搜索关键字(支持模糊查询)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
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';
|
||||
|
|
@ -17,11 +18,12 @@ 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_refund_item.entity';
|
||||
import { OrderRefundItem } from '../entity/order_retund_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) {}
|
||||
//网站配置返回数据
|
||||
|
|
@ -33,38 +35,18 @@ export class ProductListRes extends SuccessWrapper(ProductPaginatedResponse) {}
|
|||
//产品返回数据
|
||||
export class ProductRes extends SuccessWrapper(Product) {}
|
||||
export class ProductsRes extends SuccessArrayWrapper(Product) {}
|
||||
//产品品牌返分页数据
|
||||
export class BrandPaginatedResponse extends PaginatedWrapper(Dict) {}
|
||||
//产品品牌返分页返回数据
|
||||
export class ProductBrandListRes extends SuccessWrapper(
|
||||
BrandPaginatedResponse
|
||||
//产品分类返分页数据
|
||||
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 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 ProductCatAllRes extends SuccessArrayWrapper(Category) {}
|
||||
//产品分类返回数据
|
||||
export class ProductCatRes extends SuccessWrapper(Category) {}
|
||||
|
||||
//产品分页数据
|
||||
export class WpProductPaginatedResponse extends PaginatedWrapper(
|
||||
|
|
|
|||
|
|
@ -1,22 +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;
|
||||
}
|
||||
|
||||
export class UpdateTemplateDTO {
|
||||
@ApiProperty({ description: '模板名称', required: true })
|
||||
@Rule(RuleType.string().required())
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ description: '模板内容', required: true })
|
||||
@Rule(RuleType.string().required())
|
||||
value: string;
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
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;
|
||||
}
|
||||
|
|
@ -1,42 +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: true, comment: '是否可删除' })
|
||||
deletable: boolean;
|
||||
// 创建时间
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
||||
// 更新时间
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
/**
|
||||
* @description 字典项
|
||||
* @author ZKS
|
||||
* @date 2025-11-27
|
||||
*/
|
||||
import { Dict } from './dict.entity';
|
||||
import { Product } from './product.entity';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
JoinColumn,
|
||||
ManyToMany,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
@Entity()
|
||||
export class DictItem {
|
||||
// 主键
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
// 字典项名称
|
||||
@Column({ comment: '字典项显示名称' })
|
||||
title: string;
|
||||
|
||||
// 唯一标识
|
||||
@Column({ unique: true, comment: '字典唯一标识名称' })
|
||||
name: string;
|
||||
|
||||
// 字典项值
|
||||
@Column({ nullable: true, comment: '字典项值' })
|
||||
value?: 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;
|
||||
}
|
||||
|
|
@ -1,29 +1,29 @@
|
|||
import { ApiProperty } from '@midwayjs/swagger';
|
||||
import {
|
||||
PrimaryGeneratedColumn,
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
PrimaryGeneratedColumn,
|
||||
UpdateDateColumn,
|
||||
Entity,
|
||||
} from 'typeorm';
|
||||
import { ApiProperty } from '@midwayjs/swagger';
|
||||
|
||||
@Entity('template')
|
||||
export class Template {
|
||||
@ApiProperty({ type: 'number' })
|
||||
@Entity()
|
||||
export class Flavors {
|
||||
@ApiProperty()
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@ApiProperty({ type: 'string' })
|
||||
@Column({ unique: true })
|
||||
@ApiProperty()
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@ApiProperty({ type: 'string' })
|
||||
@Column('text')
|
||||
value: string;
|
||||
|
||||
@ApiProperty({ nullable: true ,name:"描述"})
|
||||
@Column('text',{nullable: true,comment: "描述"})
|
||||
description?: string;
|
||||
@ApiProperty({
|
||||
description: '唯一识别key',
|
||||
type: 'string',
|
||||
required: true,
|
||||
})
|
||||
@Column()
|
||||
unique_key: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: '2022-12-12 11:11:11',
|
||||
|
|
@ -22,7 +22,7 @@ export class OrderCoupon {
|
|||
orderId: number; // 订单 ID
|
||||
|
||||
@ApiProperty()
|
||||
@Column( )
|
||||
@Column()
|
||||
@Expose()
|
||||
siteId: string; // 来源站点唯一标识
|
||||
|
||||
|
|
@ -4,11 +4,8 @@ import {
|
|||
CreateDateColumn,
|
||||
UpdateDateColumn,
|
||||
Entity,
|
||||
ManyToMany,
|
||||
JoinTable,
|
||||
} from 'typeorm';
|
||||
import { ApiProperty } from '@midwayjs/swagger';
|
||||
import { DictItem } from './dict_item.entity';
|
||||
|
||||
@Entity()
|
||||
export class Product {
|
||||
|
|
@ -34,19 +31,30 @@ export class Product {
|
|||
@Column({ default: '' })
|
||||
nameCn: string;
|
||||
|
||||
@ApiProperty({ example: '产品描述', description: '产品描述' })
|
||||
@ApiProperty({ example: '产品描述', description: '产品描述', type: 'string' })
|
||||
@Column({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@ApiProperty({ description: 'sku'})
|
||||
@Column({ unique: true })
|
||||
sku: string;
|
||||
@ApiProperty({ example: '1', description: '分类 ID', type: 'number' })
|
||||
@Column()
|
||||
categoryId: number;
|
||||
|
||||
@ManyToMany(() => DictItem, {
|
||||
cascade: true,
|
||||
})
|
||||
@JoinTable()
|
||||
attributes: DictItem[];
|
||||
@ApiProperty({ description: '口味ID' })
|
||||
@Column()
|
||||
flavorsId: number;
|
||||
|
||||
@ApiProperty({ description: '尼古丁强度ID' })
|
||||
@Column()
|
||||
strengthId: number;
|
||||
|
||||
@ApiProperty({ description: '湿度' })
|
||||
@Column()
|
||||
humidity: string;
|
||||
|
||||
|
||||
@ApiProperty({ description: 'sku', type: 'string' })
|
||||
@Column({ nullable: true })
|
||||
sku?: string;
|
||||
|
||||
@ApiProperty({
|
||||
example: '2022-12-12 11:11:11',
|
||||
|
|
|
|||
|
|
@ -2,27 +2,27 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
|||
|
||||
@Entity('site')
|
||||
export class Site {
|
||||
@PrimaryGeneratedColumn()
|
||||
@PrimaryGeneratedColumn({ type: 'int' })
|
||||
id: number;
|
||||
|
||||
@Column({ length: 255, nullable: true })
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
apiUrl: string;
|
||||
|
||||
@Column({ length: 255, nullable: true })
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
consumerKey: string;
|
||||
|
||||
@Column({ length: 255, nullable: true })
|
||||
@Column({ type: 'varchar', length: 255, nullable: true })
|
||||
consumerSecret: string;
|
||||
|
||||
@Column({ length: 255, unique: true })
|
||||
@Column({ type: 'varchar', length: 255, unique: true })
|
||||
siteName: string;
|
||||
|
||||
@Column({ length: 32, default: 'woocommerce' })
|
||||
@Column({ type: 'varchar', length: 32, default: 'woocommerce' })
|
||||
type: string; // 平台类型:woocommerce | shopyy
|
||||
|
||||
@Column({ length: 64, nullable: true })
|
||||
@Column({ type: 'varchar', length: 64, nullable: true })
|
||||
skuPrefix: string;
|
||||
|
||||
@Column({ default: false })
|
||||
isDisabled: boolean;
|
||||
@Column({ type: 'tinyint', default: 0 })
|
||||
isDisabled: number;
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -53,150 +53,42 @@ export class WpProduct {
|
|||
name: string;
|
||||
|
||||
@ApiProperty({ description: '产品状态', enum: ProductStatus })
|
||||
@Column({ type: 'enum', enum: ProductStatus, comment: '产品状态: draft, pending, private, publish' })
|
||||
@Column({ type: 'enum', enum: ProductStatus })
|
||||
status: ProductStatus;
|
||||
|
||||
@ApiProperty({ description: '是否为特色产品', type: 'boolean' })
|
||||
@Column({ default: false, comment: '是否为特色产品' })
|
||||
featured: boolean;
|
||||
|
||||
@ApiProperty({ description: '目录可见性', type: 'string' })
|
||||
@Column({ default: 'visible', comment: '目录可见性: visible, catalog, search, hidden' })
|
||||
catalog_visibility: string;
|
||||
|
||||
@ApiProperty({ description: '产品描述', type: 'string' })
|
||||
@Column({ type: 'text', nullable: true, comment: '产品描述' })
|
||||
description: string;
|
||||
|
||||
@ApiProperty({ description: '产品短描述', type: 'string' })
|
||||
@Column({ type: 'text', nullable: true, comment: '产品短描述' })
|
||||
short_description: string;
|
||||
|
||||
@ApiProperty({ description: '上下架状态', enum: ProductStockStatus })
|
||||
@Column({
|
||||
name: 'stock_status',
|
||||
type: 'enum',
|
||||
enum: ProductStockStatus,
|
||||
default: ProductStockStatus.INSTOCK,
|
||||
comment: '库存状态: instock, outofstock, onbackorder',
|
||||
default: ProductStockStatus.INSTOCK
|
||||
})
|
||||
stockStatus: ProductStockStatus;
|
||||
|
||||
@ApiProperty({ description: '库存数量', type: 'number' })
|
||||
@Column({ type: 'int', nullable: true, comment: '库存数量' })
|
||||
stock_quantity: number;
|
||||
|
||||
@ApiProperty({ description: '允许缺货下单', type: 'string' })
|
||||
@Column({ nullable: true, comment: '允许缺货下单: no, notify, yes' })
|
||||
backorders: string;
|
||||
|
||||
@ApiProperty({ description: '是否单独出售', type: 'boolean' })
|
||||
@Column({ default: false, comment: '是否单独出售' })
|
||||
sold_individually: boolean;
|
||||
|
||||
@ApiProperty({ description: '常规价格', type: Number })
|
||||
@Column('decimal', { precision: 10, scale: 2, nullable: true, comment: '常规价格' })
|
||||
regular_price: number;
|
||||
@Column('decimal', { precision: 10, scale: 2, nullable: true })
|
||||
regular_price: number; // 常规价格
|
||||
|
||||
@ApiProperty({ description: '销售价格', type: Number })
|
||||
@Column('decimal', { precision: 10, scale: 2, nullable: true, comment: '销售价格' })
|
||||
sale_price: number;
|
||||
|
||||
@ApiProperty({ description: '促销开始日期', type: 'datetime' })
|
||||
@Column({ type: 'datetime', nullable: true, comment: '促销开始日期' })
|
||||
date_on_sale_from: Date| null;
|
||||
|
||||
@ApiProperty({ description: '促销结束日期', type: 'datetime' })
|
||||
@Column({ type: 'datetime', nullable: true, comment: '促销结束日期' })
|
||||
date_on_sale_to: Date|null;
|
||||
@Column('decimal', { precision: 10, scale: 2, nullable: true })
|
||||
sale_price: number; // 销售价格
|
||||
|
||||
@ApiProperty({ description: '是否促销中', type: Boolean })
|
||||
@Column({ nullable: true, type: 'boolean', comment: '是否促销中' })
|
||||
on_sale: boolean;
|
||||
@Column({ nullable: true, type: Boolean })
|
||||
on_sale: boolean; // 是否促销中
|
||||
|
||||
@ApiProperty({ description: '税务状态', type: 'string' })
|
||||
@Column({ default: 'taxable', comment: '税务状态: taxable, shipping, none' })
|
||||
tax_status: string;
|
||||
@ApiProperty({ description: '是否删除', type: Boolean })
|
||||
@Column({ nullable: true, type: Boolean , default: false })
|
||||
on_delete: boolean; // 是否删除
|
||||
|
||||
@ApiProperty({ description: '税类', type: 'string' })
|
||||
@Column({ nullable: true, comment: '税类' })
|
||||
tax_class: string;
|
||||
|
||||
@ApiProperty({ description: '重量(g)', type: 'number' })
|
||||
@Column('decimal', { precision: 10, scale: 2, nullable: true, comment: '重量(g)' })
|
||||
weight: number;
|
||||
|
||||
@ApiProperty({ description: '尺寸(长宽高)', type: 'json' })
|
||||
@Column({ type: 'json', nullable: true, comment: '尺寸' })
|
||||
dimensions: { length: string; width: string; height: string };
|
||||
|
||||
@ApiProperty({ description: '允许评论', type: 'boolean' })
|
||||
@Column({ default: true, comment: '允许客户评论' })
|
||||
reviews_allowed: boolean;
|
||||
|
||||
@ApiProperty({ description: '购买备注', type: 'string' })
|
||||
@Column({ nullable: true, comment: '购买备注' })
|
||||
purchase_note: string;
|
||||
|
||||
@ApiProperty({ description: '菜单排序', type: 'number' })
|
||||
@Column({ default: 0, comment: '菜单排序' })
|
||||
menu_order: number;
|
||||
|
||||
@ApiProperty({ description: '产品类型', enum: ProductType })
|
||||
@Column({ type: 'enum', enum: ProductType, comment: '产品类型: simple, grouped, external, variable' })
|
||||
@ApiProperty({
|
||||
description: '产品类型',
|
||||
enum: ProductType,
|
||||
})
|
||||
@Column({ type: 'enum', enum: ProductType })
|
||||
type: ProductType;
|
||||
|
||||
@ApiProperty({ description: '父产品ID', type: 'number' })
|
||||
@Column({ default: 0, comment: '父产品ID' })
|
||||
parent_id: number;
|
||||
|
||||
@ApiProperty({ description: '外部产品URL', type: 'string' })
|
||||
@Column({ type: 'text', nullable: true, comment: '外部产品URL' })
|
||||
external_url: string;
|
||||
|
||||
@ApiProperty({ description: '外部产品按钮文本', type: 'string' })
|
||||
@Column({ nullable: true, comment: '外部产品按钮文本' })
|
||||
button_text: string;
|
||||
|
||||
@ApiProperty({ description: '分组产品', type: 'json' })
|
||||
@Column({ type: 'json', nullable: true, comment: '分组产品' })
|
||||
grouped_products: number[];
|
||||
|
||||
@ApiProperty({ description: '追加销售', type: 'json' })
|
||||
@Column({ type: 'json', nullable: true, comment: '追加销售' })
|
||||
upsell_ids: number[];
|
||||
|
||||
@ApiProperty({ description: '交叉销售', type: 'json' })
|
||||
@Column({ type: 'json', nullable: true, comment: '交叉销售' })
|
||||
cross_sell_ids: number[];
|
||||
|
||||
@ApiProperty({ description: '分类', type: 'json' })
|
||||
@Column({ type: 'json', nullable: true, comment: '分类' })
|
||||
categories: { id: number; name: string; slug: string }[];
|
||||
|
||||
@ApiProperty({ description: '标签', type: 'json' })
|
||||
@Column({ type: 'json', nullable: true, comment: '标签' })
|
||||
tags: { id: number; name: string; slug: string }[];
|
||||
|
||||
@ApiProperty({ description: '图片', type: 'json' })
|
||||
@Column({ type: 'json', nullable: true, comment: '图片' })
|
||||
images: { id: number; src: string; name: string; alt: string }[];
|
||||
|
||||
@ApiProperty({ description: '产品属性', type: 'json' })
|
||||
@Column({ type: 'json', nullable: true, comment: '产品属性' })
|
||||
attributes: { id: number; name: string; position: number; visible: boolean; variation: boolean; options: string[] }[];
|
||||
|
||||
@ApiProperty({ description: '默认属性', type: 'json' })
|
||||
@Column({ type: 'json', nullable: true, comment: '默认属性' })
|
||||
default_attributes: { id: number; name: string; option: string }[];
|
||||
|
||||
@ApiProperty({ description: 'GTIN', type: 'string' })
|
||||
@Column({ nullable: true, comment: 'GTIN, UPC, EAN, or ISBN' })
|
||||
gtin: string;
|
||||
|
||||
@ApiProperty({ description: '是否删除', type: 'boolean' })
|
||||
@Column({ nullable: true, type: 'boolean', default: false, comment: '是否删除' })
|
||||
on_delete: boolean;
|
||||
|
||||
@Column({ type: 'json', nullable: true })
|
||||
metadata: Record<string, any>; // 产品的其他扩展字段
|
||||
|
|
|
|||
|
|
@ -1,161 +0,0 @@
|
|||
import { Provide } from '@midwayjs/core';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository, Like } from 'typeorm';
|
||||
import { Dict } from '../entity/dict.entity';
|
||||
import { DictItem } from '../entity/dict_item.entity';
|
||||
import { CreateDictDTO, UpdateDictDTO } from '../dto/dict.dto';
|
||||
import { CreateDictItemDTO, UpdateDictItemDTO } from '../dto/dict.dto';
|
||||
import * as xlsx from 'xlsx';
|
||||
|
||||
@Provide()
|
||||
export class DictService {
|
||||
|
||||
@InjectEntityModel(Dict)
|
||||
dictModel: Repository<Dict>;
|
||||
|
||||
@InjectEntityModel(DictItem)
|
||||
dictItemModel: Repository<DictItem>;
|
||||
|
||||
// 生成并返回字典的XLSX模板
|
||||
getDictXLSXTemplate() {
|
||||
// 定义表头
|
||||
const headers = ['name', 'title'];
|
||||
// 创建一个新的工作表
|
||||
const ws = xlsx.utils.aoa_to_sheet([headers]);
|
||||
// 创建一个新的工作簿
|
||||
const wb = xlsx.utils.book_new();
|
||||
// 将工作表添加到工作簿
|
||||
xlsx.utils.book_append_sheet(wb, ws, 'Dicts');
|
||||
// 将工作簿写入缓冲区
|
||||
return xlsx.write(wb, { type: 'buffer', bookType: 'xlsx' });
|
||||
}
|
||||
|
||||
// 从XLSX文件导入字典
|
||||
async importDictsFromXLSX(buffer: Buffer) {
|
||||
// 读取缓冲区中的工作簿
|
||||
const wb = xlsx.read(buffer, { type: 'buffer' });
|
||||
// 获取第一个工作表的名称
|
||||
const wsname = wb.SheetNames[0];
|
||||
// 获取第一个工作表
|
||||
const ws = wb.Sheets[wsname];
|
||||
// 将工作表转换为JSON对象数组
|
||||
const data = xlsx.utils.sheet_to_json(ws, { header: ['name', 'title'] }).slice(1);
|
||||
// 创建要保存的字典实体数组
|
||||
const dicts = data.map((row: any) => {
|
||||
const dict = new Dict();
|
||||
dict.name = row.name;
|
||||
dict.title = row.title;
|
||||
return dict;
|
||||
});
|
||||
// 保存字典实体数组到数据库
|
||||
await this.dictModel.save(dicts);
|
||||
// 返回成功导入的记录数
|
||||
return { success: true, count: dicts.length };
|
||||
}
|
||||
|
||||
// 生成并返回字典项的XLSX模板
|
||||
getDictItemXLSXTemplate() {
|
||||
const headers = ['name', 'title', 'value', 'sort'];
|
||||
const ws = xlsx.utils.aoa_to_sheet([headers]);
|
||||
const wb = xlsx.utils.book_new();
|
||||
xlsx.utils.book_append_sheet(wb, ws, 'DictItems');
|
||||
return xlsx.write(wb, { type: 'buffer', bookType: 'xlsx' });
|
||||
}
|
||||
|
||||
// 从XLSX文件导入字典项
|
||||
async importDictItemsFromXLSX(buffer: Buffer, dictId: number) {
|
||||
const dict = await this.dictModel.findOneBy({ id: dictId });
|
||||
if (!dict) {
|
||||
throw new Error('指定的字典不存在');
|
||||
}
|
||||
const wb = xlsx.read(buffer, { type: 'buffer' });
|
||||
const wsname = wb.SheetNames[0];
|
||||
const ws = wb.Sheets[wsname];
|
||||
const data = xlsx.utils.sheet_to_json(ws, { header: ['name', 'title', 'value', 'sort'] }).slice(1);
|
||||
|
||||
const items = data.map((row: any) => {
|
||||
const item = new DictItem();
|
||||
item.name = row.name;
|
||||
item.title = row.title;
|
||||
item.value = row.value;
|
||||
item.sort = row.sort || 0;
|
||||
item.dict = dict;
|
||||
return item;
|
||||
});
|
||||
|
||||
await this.dictItemModel.save(items);
|
||||
return { success: true, count: items.length };
|
||||
}
|
||||
getDict(where: { name?: string; id?: number; }, relations: string[]) {
|
||||
if (!where.name && !where.id) {
|
||||
throw new Error('必须提供 name 或 id');
|
||||
}
|
||||
return this.dictModel.findOne({ where, relations });
|
||||
}
|
||||
// 获取字典列表,支持按标题搜索
|
||||
async getDicts(options: { title?: string; name?: string; }) {
|
||||
const where = {
|
||||
title: options.title ? Like(`%${options.title}%`) : undefined,
|
||||
name: options.name ? Like(`%${options.name}%`) : undefined,
|
||||
}
|
||||
return this.dictModel.find({ where });
|
||||
}
|
||||
|
||||
// 创建新字典
|
||||
async createDict(createDictDTO: CreateDictDTO) {
|
||||
const dict = new Dict();
|
||||
dict.name = createDictDTO.name;
|
||||
dict.title = createDictDTO.title;
|
||||
return this.dictModel.save(dict);
|
||||
}
|
||||
|
||||
// 更新字典
|
||||
async updateDict(id: number, updateDictDTO: UpdateDictDTO) {
|
||||
await this.dictModel.update(id, updateDictDTO);
|
||||
return this.dictModel.findOneBy({ id });
|
||||
}
|
||||
|
||||
// 删除字典及其所有字典项
|
||||
async deleteDict(id: number) {
|
||||
// 首先删除该字典下的所有字典项
|
||||
await this.dictItemModel.delete({ dict: { id } });
|
||||
// 然后删除字典本身
|
||||
const result = await this.dictModel.delete(id);
|
||||
return result.affected > 0;
|
||||
}
|
||||
|
||||
// 获取字典项列表,支持按 dictId 过滤
|
||||
async getDictItems(dictId?: number) {
|
||||
// 如果提供了 dictId,则只返回该字典下的项
|
||||
if (dictId) {
|
||||
return this.dictItemModel.find({ where: { dict: { id: dictId } } });
|
||||
}
|
||||
// 否则,返回所有字典项
|
||||
return this.dictItemModel.find();
|
||||
}
|
||||
|
||||
// 创建新字典项
|
||||
async createDictItem(createDictItemDTO: CreateDictItemDTO) {
|
||||
const dict = await this.dictModel.findOneBy({ id: createDictItemDTO.dictId });
|
||||
if (!dict) {
|
||||
throw new Error('指定的字典不存在');
|
||||
}
|
||||
const item = new DictItem();
|
||||
item.name = createDictItemDTO.name;
|
||||
item.title = createDictItemDTO.title;
|
||||
item.dict = dict;
|
||||
return this.dictItemModel.save(item);
|
||||
}
|
||||
|
||||
// 更新字典项
|
||||
async updateDictItem(id: number, updateDictItemDTO: UpdateDictItemDTO) {
|
||||
await this.dictItemModel.update(id, updateDictItemDTO);
|
||||
return this.dictItemModel.findOneBy({ id });
|
||||
}
|
||||
|
||||
// 删除字典项
|
||||
async deleteDictItem(id: number) {
|
||||
const result = await this.dictItemModel.delete(id);
|
||||
return result.affected > 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -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_refund_item.entity';
|
||||
import { OrderCoupon } from '../entity/order_coupon.entity';
|
||||
import { OrderRefundItem } from '../entity/order_retund_item.entity';
|
||||
import { OrderCoupon } from '../entity/order_copon.entity';
|
||||
import { OrderShipping } from '../entity/order_shipping.entity';
|
||||
import { Shipment } from '../entity/shipment.entity';
|
||||
import { Customer } from '../entity/customer.entity';
|
||||
|
|
|
|||
|
|
@ -1,20 +1,21 @@
|
|||
import { Inject, Provide } from '@midwayjs/core';
|
||||
import { 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 {
|
||||
CreateBrandDTO,
|
||||
CreateCategoryDTO,
|
||||
CreateFlavorsDTO,
|
||||
CreateProductDTO,
|
||||
CreateStrengthDTO,
|
||||
UpdateBrandDTO,
|
||||
UpdateCategoryDTO,
|
||||
UpdateFlavorsDTO,
|
||||
UpdateProductDTO,
|
||||
UpdateStrengthDTO,
|
||||
} from '../dto/product.dto';
|
||||
import {
|
||||
BrandPaginatedResponse,
|
||||
CategoryPaginatedResponse,
|
||||
FlavorsPaginatedResponse,
|
||||
ProductPaginatedResponse,
|
||||
StrengthPaginatedResponse,
|
||||
|
|
@ -22,27 +23,22 @@ import {
|
|||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { WpProduct } from '../entity/wp_product.entity';
|
||||
import { Variation } from '../entity/variation.entity';
|
||||
import { Dict } from '../entity/dict.entity';
|
||||
import { DictItem } from '../entity/dict_item.entity';
|
||||
import { Context } from '@midwayjs/koa';
|
||||
import { TemplateService } from './template.service';
|
||||
import { Strength } from '../entity/strength.entity';
|
||||
import { Flavors } from '../entity/flavors.entity';
|
||||
|
||||
@Provide()
|
||||
export class ProductService {
|
||||
@Inject()
|
||||
ctx: Context;
|
||||
|
||||
@Inject()
|
||||
templateService: TemplateService;
|
||||
|
||||
@InjectEntityModel(Product)
|
||||
productModel: Repository<Product>;
|
||||
|
||||
@InjectEntityModel(Dict)
|
||||
dictModel: Repository<Dict>;
|
||||
@InjectEntityModel(Category)
|
||||
categoryModel: Repository<Category>;
|
||||
|
||||
@InjectEntityModel(DictItem)
|
||||
dictItemModel: Repository<DictItem>;
|
||||
@InjectEntityModel(Strength)
|
||||
strengthModel: Repository<Strength>;
|
||||
|
||||
@InjectEntityModel(Flavors)
|
||||
flavorsModel: Repository<Flavors>;
|
||||
|
||||
@InjectEntityModel(WpProduct)
|
||||
wpProductModel: Repository<WpProduct>;
|
||||
|
|
@ -115,39 +111,39 @@ export class ProductService {
|
|||
async getProductList(
|
||||
pagination: PaginationParams,
|
||||
name?: string,
|
||||
brandId?: number
|
||||
categoryId?: number
|
||||
): Promise<ProductPaginatedResponse> {
|
||||
const nameFilter = name ? name.split(' ').filter(Boolean) : [];
|
||||
|
||||
const qb = this.productModel
|
||||
.createQueryBuilder('product')
|
||||
.leftJoinAndSelect('product.attributes', 'attribute')
|
||||
.leftJoinAndSelect('attribute.dict', 'dict');
|
||||
.leftJoin(Category, 'category', 'category.id = product.categoryId')
|
||||
.leftJoin(Strength, 'strength', 'strength.id = product.strengthId')
|
||||
.leftJoin(Flavors, 'flavors', 'flavors.id = product.flavorsId')
|
||||
.select([
|
||||
'product.id as id',
|
||||
'product.name as name',
|
||||
'product.nameCn as nameCn',
|
||||
'product.description as description',
|
||||
'product.humidity as humidity',
|
||||
'product.sku as sku',
|
||||
'product.createdAt as createdAt',
|
||||
'product.updatedAt as updatedAt',
|
||||
'category.name AS categoryName',
|
||||
'strength.name AS strengthName',
|
||||
'flavors.name AS flavorsName',
|
||||
]);
|
||||
|
||||
// 模糊搜索 name,支持多个关键词
|
||||
const nameFilter = name ? name.split(' ').filter(Boolean) : [];
|
||||
if (nameFilter.length > 0) {
|
||||
const nameConditions = nameFilter
|
||||
.map((word, index) => `product.name LIKE :name${index}`)
|
||||
.join(' AND ');
|
||||
const nameParams = nameFilter.reduce(
|
||||
(params, word, index) => ({ ...params, [`name${index}`]: `%${word}%` }),
|
||||
{}
|
||||
);
|
||||
qb.where(`(${nameConditions})`, nameParams);
|
||||
}
|
||||
|
||||
// 品牌过滤
|
||||
if (brandId) {
|
||||
qb.andWhere(qb => {
|
||||
const subQuery = qb
|
||||
.subQuery()
|
||||
.select('product_attributes_dict_item.productId')
|
||||
.from('product_attributes_dict_item', 'product_attributes_dict_item')
|
||||
.where('product_attributes_dict_item.dictItemId = :brandId', {
|
||||
brandId,
|
||||
})
|
||||
.getQuery();
|
||||
return 'product.id IN ' + subQuery;
|
||||
nameFilter.forEach((word, index) => {
|
||||
qb.andWhere(`product.name LIKE :name${index}`, {
|
||||
[`name${index}`]: `%${word}%`,
|
||||
});
|
||||
});
|
||||
|
||||
// 分类过滤
|
||||
if (categoryId) {
|
||||
qb.andWhere('product.categoryId = :categoryId', { categoryId });
|
||||
}
|
||||
|
||||
// 分页
|
||||
|
|
@ -155,120 +151,46 @@ export class ProductService {
|
|||
pagination.pageSize
|
||||
);
|
||||
|
||||
const [items, total] = await qb.getManyAndCount();
|
||||
|
||||
// 格式化返回的数据
|
||||
const formattedItems = items.map(product => {
|
||||
const getAttributeTitle = (dictName: string) =>
|
||||
product.attributes.find(a => a.dict.name === dictName)?.title || null;
|
||||
|
||||
return {
|
||||
id: product.id,
|
||||
name: product.name,
|
||||
nameCn: product.nameCn,
|
||||
description: product.description,
|
||||
humidity: getAttributeTitle('humidity'),
|
||||
sku: product.sku,
|
||||
createdAt: product.createdAt,
|
||||
updatedAt: product.updatedAt,
|
||||
brandName: getAttributeTitle('brand'),
|
||||
flavorsName: getAttributeTitle('flavor'),
|
||||
strengthName: getAttributeTitle('strength'),
|
||||
};
|
||||
});
|
||||
// 执行查询
|
||||
const items = await qb.getRawMany();
|
||||
const total = await qb.getCount();
|
||||
|
||||
return {
|
||||
items: formattedItems,
|
||||
items,
|
||||
total,
|
||||
...pagination,
|
||||
};
|
||||
}
|
||||
|
||||
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 = dict;
|
||||
await this.dictItemModel.save(item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
async createProduct(createProductDTO: CreateProductDTO): Promise<Product> {
|
||||
const { name, description, brand, flavor, strength, humidity, sku } =
|
||||
const { name, description, categoryId, strengthId, flavorsId, 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 humidityItem = await this.getOrCreateDictItem('humidity', humidity);
|
||||
|
||||
// 检查具有完全相同属性组合的产品是否已存在
|
||||
const attributesToMatch = [brandItem, flavorItem, strengthItem, humidityItem];
|
||||
const qb = this.productModel.createQueryBuilder('product');
|
||||
attributesToMatch.forEach((attr, index) => {
|
||||
qb.innerJoin(
|
||||
'product.attributes',
|
||||
`attr${index}`,
|
||||
`attr${index}.id = :attrId${index}`,
|
||||
{ [`attrId${index}`]: attr.id }
|
||||
);
|
||||
const isExit = await this.productModel.findOne({
|
||||
where: {
|
||||
categoryId,
|
||||
strengthId,
|
||||
flavorsId,
|
||||
humidity,
|
||||
},
|
||||
});
|
||||
const isExit = await qb.getOne();
|
||||
|
||||
if (isExit) throw new Error('产品已存在');
|
||||
|
||||
// 创建新产品实例
|
||||
const product = new Product();
|
||||
product.name = name;
|
||||
product.description = description;
|
||||
product.attributes = attributesToMatch;
|
||||
|
||||
// 如果用户提供了 sku,则直接使用;否则,通过模板引擎生成
|
||||
if (sku) {
|
||||
product.sku = sku;
|
||||
} else {
|
||||
// 生成 SKU
|
||||
product.sku = await this.templateService.render('product_sku', {
|
||||
brand: brandItem.name,
|
||||
flavor: flavorItem.name,
|
||||
strength: strengthItem.name,
|
||||
humidity: humidityItem.name,
|
||||
});
|
||||
}
|
||||
|
||||
// 保存产品
|
||||
product.categoryId = categoryId;
|
||||
product.strengthId = strengthId;
|
||||
product.flavorsId = flavorsId;
|
||||
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}`;
|
||||
return await this.productModel.save(product);
|
||||
}
|
||||
|
||||
|
|
@ -335,271 +257,191 @@ export class ProductService {
|
|||
return result.affected > 0; // `affected` 表示删除的行数
|
||||
}
|
||||
|
||||
|
||||
async hasAttribute(
|
||||
dictName: string,
|
||||
title: string,
|
||||
id?: number
|
||||
): Promise<boolean> {
|
||||
const dict = await this.dictModel.findOne({
|
||||
where: { name: dictName },
|
||||
async hasProductsInCategory(categoryId: number): Promise<boolean> {
|
||||
const count = await this.productModel.count({
|
||||
where: { categoryId },
|
||||
});
|
||||
if (!dict) {
|
||||
return false;
|
||||
}
|
||||
const where: any = { title, dict: { id: dict.id } };
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
async hasCategory(name: string, id?: string): Promise<boolean> {
|
||||
const where: any = { name };
|
||||
if (id) where.id = Not(id);
|
||||
const count = await this.dictItemModel.count({
|
||||
const count = await this.categoryModel.count({
|
||||
where,
|
||||
});
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
async hasProductsInAttribute(attributeId: number): Promise<boolean> {
|
||||
const count = await this.productModel
|
||||
.createQueryBuilder('product')
|
||||
.innerJoin('product.attributes', 'attribute')
|
||||
.where('attribute.id = :attributeId', { attributeId })
|
||||
.getCount();
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
async getBrandList(
|
||||
async getCategoryList(
|
||||
pagination: PaginationParams,
|
||||
title?: string
|
||||
): Promise<BrandPaginatedResponse> {
|
||||
// 查找 'brand' 字典
|
||||
const brandDict = await this.dictModel.findOne({
|
||||
where: { name: 'brand' },
|
||||
});
|
||||
|
||||
// 如果字典不存在,则返回空
|
||||
if (!brandDict) {
|
||||
return {
|
||||
items: [],
|
||||
total: 0,
|
||||
...pagination,
|
||||
};
|
||||
name?: string
|
||||
): Promise<CategoryPaginatedResponse> {
|
||||
const where: any = {};
|
||||
if (name) {
|
||||
where.name = Like(`%${name}%`);
|
||||
}
|
||||
|
||||
// 设置查询条件
|
||||
const where: any = { dict: { id: brandDict.id } };
|
||||
if (title) {
|
||||
where.title = Like(`%${title}%`);
|
||||
}
|
||||
|
||||
// 分页查询
|
||||
return await paginate(this.dictItemModel, { pagination, where });
|
||||
return await paginate(this.categoryModel, { pagination, where });
|
||||
}
|
||||
|
||||
async getBrandAll(): Promise<BrandPaginatedResponse> {
|
||||
// 查找 'brand' 字典
|
||||
const brandDict = await this.dictModel.findOne({
|
||||
where: { name: 'brand' },
|
||||
});
|
||||
|
||||
// 如果字典不存在,则返回空数组
|
||||
if (!brandDict) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 返回所有品牌
|
||||
return this.dictItemModel.find({ where: { dict: { id: brandDict.id } } });
|
||||
async getCategoryAll(): Promise<CategoryPaginatedResponse> {
|
||||
return await this.categoryModel.find();
|
||||
}
|
||||
|
||||
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 = brandDict;
|
||||
|
||||
// 保存到数据库
|
||||
return await this.dictItemModel.save(brand);
|
||||
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 updateBrand(id: number, updateBrand: UpdateBrandDTO) {
|
||||
// 确认品牌是否存在
|
||||
const brand = await this.dictItemModel.findOneBy({ id });
|
||||
if (!brand) {
|
||||
throw new Error(`品牌 ID ${id} 不存在`);
|
||||
async updateCategory(id: number, updateCategory: UpdateCategoryDTO) {
|
||||
// 确认产品是否存在
|
||||
const category = await this.categoryModel.findOneBy({ id });
|
||||
if (!category) {
|
||||
throw new Error(`产品分类 ID ${id} 不存在`);
|
||||
}
|
||||
|
||||
// 更新品牌
|
||||
await this.dictItemModel.update(id, updateBrand);
|
||||
|
||||
// 返回更新后的品牌
|
||||
return await this.dictItemModel.findOneBy({ id });
|
||||
// 更新产品
|
||||
await this.categoryModel.update(id, updateCategory);
|
||||
// 返回更新后的产品
|
||||
return await this.categoryModel.findOneBy({ id });
|
||||
}
|
||||
|
||||
async deleteBrand(id: number): Promise<boolean> {
|
||||
// 检查品牌是否存在
|
||||
const brand = await this.dictItemModel.findOneBy({ id });
|
||||
if (!brand) {
|
||||
throw new Error(`品牌 ID ${id} 不存在`);
|
||||
async deleteCategory(id: number): Promise<boolean> {
|
||||
// 检查产品是否存在
|
||||
const category = await this.categoryModel.findOneBy({ id });
|
||||
if (!category) {
|
||||
throw new Error(`产品分类 ID ${id} 不存在`);
|
||||
}
|
||||
|
||||
// 删除品牌
|
||||
const result = await this.dictItemModel.delete(id);
|
||||
// 删除产品
|
||||
const result = await this.categoryModel.delete(id);
|
||||
return result.affected > 0; // `affected` 表示删除的行数
|
||||
}
|
||||
|
||||
async hasProductsInFlavors(flavorsId: number): Promise<boolean> {
|
||||
const count = await this.productModel.count({
|
||||
where: { flavorsId },
|
||||
});
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
async hasFlavors(name: string, id?: string): Promise<boolean> {
|
||||
const where: any = { name };
|
||||
if (id) where.id = Not(id);
|
||||
const count = await this.flavorsModel.count({
|
||||
where,
|
||||
});
|
||||
return count > 0;
|
||||
}
|
||||
async getFlavorsList(
|
||||
pagination: PaginationParams,
|
||||
title?: string
|
||||
name?: string
|
||||
): Promise<FlavorsPaginatedResponse> {
|
||||
const flavorsDict = await this.dictModel.findOne({
|
||||
where: { name: 'flavor' },
|
||||
});
|
||||
if (!flavorsDict) {
|
||||
return {
|
||||
items: [],
|
||||
total: 0,
|
||||
...pagination,
|
||||
};
|
||||
const where: any = {};
|
||||
if (name) {
|
||||
where.name = Like(`%${name}%`);
|
||||
}
|
||||
const where: any = { dict: { id: flavorsDict.id } };
|
||||
if (title) {
|
||||
where.title = Like(`%${title}%`);
|
||||
}
|
||||
return await paginate(this.dictItemModel, { pagination, where });
|
||||
return await paginate(this.flavorsModel, { pagination, where });
|
||||
}
|
||||
|
||||
async getFlavorsAll(): Promise<FlavorsPaginatedResponse> {
|
||||
const flavorsDict = await this.dictModel.findOne({
|
||||
where: { name: 'flavor' },
|
||||
});
|
||||
if (!flavorsDict) {
|
||||
return [];
|
||||
}
|
||||
return this.dictItemModel.find({ where: { id: flavorsDict.id } });
|
||||
return await this.flavorsModel.find();
|
||||
}
|
||||
|
||||
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;
|
||||
async createFlavors(createFlavorsDTO: CreateFlavorsDTO): Promise<Flavors> {
|
||||
const { name, unique_key } = createFlavorsDTO;
|
||||
const flavors = new Flavors();
|
||||
flavors.name = name;
|
||||
flavors.dict = flavorsDict;
|
||||
return await this.dictItemModel.save(flavors);
|
||||
flavors.unique_key = unique_key;
|
||||
return await this.flavorsModel.save(flavors);
|
||||
}
|
||||
|
||||
async updateFlavors(id: number, updateFlavors: UpdateFlavorsDTO) {
|
||||
const flavors = await this.dictItemModel.findOneBy({ id });
|
||||
// 确认产品是否存在
|
||||
const flavors = await this.flavorsModel.findOneBy({ id });
|
||||
if (!flavors) {
|
||||
throw new Error(`口味 ID ${id} 不存在`);
|
||||
}
|
||||
await this.dictItemModel.update(id, updateFlavors);
|
||||
return await this.dictItemModel.findOneBy({ id });
|
||||
// 更新产品
|
||||
await this.flavorsModel.update(id, updateFlavors);
|
||||
// 返回更新后的产品
|
||||
return await this.flavorsModel.findOneBy({ id });
|
||||
}
|
||||
|
||||
async deleteFlavors(id: number): Promise<boolean> {
|
||||
const flavors = await this.dictItemModel.findOneBy({ id });
|
||||
// 检查产品是否存在
|
||||
const flavors = await this.flavorsModel.findOneBy({ id });
|
||||
if (!flavors) {
|
||||
throw new Error(`口味 ID ${id} 不存在`);
|
||||
}
|
||||
const result = await this.dictItemModel.delete(id);
|
||||
return result.affected > 0;
|
||||
// 删除产品
|
||||
const result = await this.flavorsModel.delete(id);
|
||||
return result.affected > 0; // `affected` 表示删除的行数
|
||||
}
|
||||
async hasProductsInStrength(strengthId: number): Promise<boolean> {
|
||||
const count = await this.productModel.count({
|
||||
where: { strengthId },
|
||||
});
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
|
||||
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 } };
|
||||
async hasStrength(name: string, id?: string): Promise<boolean> {
|
||||
const where: any = { name };
|
||||
if (id) where.id = Not(id);
|
||||
const count = await this.dictItemModel.count({
|
||||
const count = await this.strengthModel.count({
|
||||
where,
|
||||
});
|
||||
return count > 0;
|
||||
}
|
||||
async getStrengthList(
|
||||
pagination: PaginationParams,
|
||||
title?: string
|
||||
name?: string
|
||||
): Promise<StrengthPaginatedResponse> {
|
||||
const strengthDict = await this.dictModel.findOne({
|
||||
where: { name: 'strength' },
|
||||
});
|
||||
if (!strengthDict) {
|
||||
return {
|
||||
items: [],
|
||||
total: 0,
|
||||
...pagination,
|
||||
};
|
||||
const where: any = {};
|
||||
if (name) {
|
||||
where.name = Like(`%${name}%`);
|
||||
}
|
||||
const where: any = { dict: { id: strengthDict.id } };
|
||||
if (title) {
|
||||
where.title = Like(`%${title}%`);
|
||||
}
|
||||
return await paginate(this.dictItemModel, { pagination, where });
|
||||
return await paginate(this.strengthModel, { pagination, where });
|
||||
}
|
||||
|
||||
async getStrengthAll(): Promise<StrengthPaginatedResponse> {
|
||||
const strengthDict = await this.dictModel.findOne({
|
||||
where: { name: 'strength' },
|
||||
});
|
||||
if (!strengthDict) {
|
||||
return [];
|
||||
}
|
||||
return this.dictItemModel.find({ where: { dict: { id: strengthDict.id } } });
|
||||
return await this.strengthModel.find();
|
||||
}
|
||||
|
||||
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;
|
||||
async createStrength(
|
||||
createStrengthDTO: CreateStrengthDTO
|
||||
): Promise<Strength> {
|
||||
const { name, unique_key } = createStrengthDTO;
|
||||
const strength = new Strength();
|
||||
strength.name = name;
|
||||
strength.dict = strengthDict;
|
||||
return await this.dictItemModel.save(strength);
|
||||
strength.unique_key = unique_key;
|
||||
return await this.strengthModel.save(strength);
|
||||
}
|
||||
|
||||
async updateStrength(id: number, updateStrength: UpdateStrengthDTO) {
|
||||
const strength = await this.dictItemModel.findOneBy({ id });
|
||||
// 确认产品是否存在
|
||||
const strength = await this.strengthModel.findOneBy({ id });
|
||||
if (!strength) {
|
||||
throw new Error(`规格 ID ${id} 不存在`);
|
||||
throw new Error(`口味 ID ${id} 不存在`);
|
||||
}
|
||||
await this.dictItemModel.update(id, updateStrength);
|
||||
return await this.dictItemModel.findOneBy({ id });
|
||||
// 更新产品
|
||||
await this.strengthModel.update(id, updateStrength);
|
||||
// 返回更新后的产品
|
||||
return await this.strengthModel.findOneBy({ id });
|
||||
}
|
||||
|
||||
async deleteStrength(id: number): Promise<boolean> {
|
||||
const strength = await this.dictItemModel.findOneBy({ id });
|
||||
// 检查产品是否存在
|
||||
const strength = await this.strengthModel.findOneBy({ id });
|
||||
if (!strength) {
|
||||
throw new Error(`规格 ID ${id} 不存在`);
|
||||
throw new Error(`口味 ID ${id} 不存在`);
|
||||
}
|
||||
const result = await this.dictItemModel.delete(id);
|
||||
return result.affected > 0;
|
||||
// 删除产品
|
||||
const result = await this.flavorsModel.delete(id);
|
||||
return result.affected > 0; // `affected` 表示删除的行数
|
||||
}
|
||||
|
||||
async batchSetSku(skus: { productId: number; sku: string }[]) {
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ export class SiteService {
|
|||
|
||||
async disable(id: string | number, disabled: boolean) {
|
||||
// 设置站点禁用状态(true -> 1, false -> 0)
|
||||
await this.siteModel.update({ id: Number(id) }, { isDisabled: disabled });
|
||||
await this.siteModel.update({ id: Number(id) }, { isDisabled: disabled ? 1 : 0 });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
import { Provide } from '@midwayjs/core';
|
||||
import { InjectEntityModel } from '@midwayjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Template } from '../entity/template.entity';
|
||||
import { CreateTemplateDTO, UpdateTemplateDTO } from '../dto/template.dto';
|
||||
|
||||
/**
|
||||
* @service TemplateService 模板服务
|
||||
*/
|
||||
@Provide()
|
||||
export class TemplateService {
|
||||
// 注入 Template 实体模型
|
||||
@InjectEntityModel(Template)
|
||||
templateModel: Repository<Template>;
|
||||
|
||||
/**
|
||||
* 获取所有模板的列表
|
||||
* @returns {Promise<Template[]>} 模板实体数组
|
||||
*/
|
||||
async getTemplateList(): Promise<Template[]> {
|
||||
// 使用 find 方法查询所有模板
|
||||
return this.templateModel.find();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据唯一名称获取模板
|
||||
* @param {string} name - 模板名称
|
||||
* @returns {Promise<Template>} 模板实体
|
||||
*/
|
||||
async getTemplateByName(name: string): Promise<Template> {
|
||||
// 使用 findOne 方法按名称查询模板
|
||||
return this.templateModel.findOne({ where: { name } });
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建一个新模板
|
||||
* @param {CreateTemplateDTO} templateData - 创建模板所需的数据
|
||||
* @returns {Promise<Template>} 创建后的模板实体
|
||||
*/
|
||||
async createTemplate(templateData: CreateTemplateDTO): Promise<Template> {
|
||||
// 创建一个新的模板实例
|
||||
const template = new Template();
|
||||
// 设置模板的名称和值
|
||||
template.name = templateData.name;
|
||||
template.value = templateData.value;
|
||||
// 保存新模板到数据库
|
||||
return this.templateModel.save(template);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ID 更新一个现有模板
|
||||
* @param {number} id - 模板 ID
|
||||
* @param {UpdateTemplateDTO} templateData - 更新模板所需的数据
|
||||
* @returns {Promise<Template>} 更新后的模板实体
|
||||
*/
|
||||
async updateTemplate(
|
||||
id: number,
|
||||
templateData: UpdateTemplateDTO
|
||||
): Promise<Template> {
|
||||
// 首先根据 ID 查找模板
|
||||
const template = await this.templateModel.findOneBy({ id });
|
||||
// 如果模板不存在,则抛出错误
|
||||
if (!template) {
|
||||
throw new Error(`模板 ID ${id} 不存在`);
|
||||
}
|
||||
// 更新模板的名称和值
|
||||
template.name = templateData.name;
|
||||
template.value = templateData.value;
|
||||
// 保存更新后的模板到数据库
|
||||
return this.templateModel.save(template);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 ID 删除一个模板
|
||||
* @param {number} id - 模板 ID
|
||||
* @returns {Promise<boolean>} 如果删除成功则返回 true,否则返回 false
|
||||
*/
|
||||
async deleteTemplate(id: number): Promise<boolean> {
|
||||
// 执行删除操作
|
||||
const result = await this.templateModel.delete(id);
|
||||
// 如果影响的行数大于 0,则表示删除成功
|
||||
return result.affected > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据模板名称和数据对象渲染最终字符串
|
||||
* @param {string} name - 模板名称
|
||||
* @param {Record<string, any>} data - 用于替换占位符的数据对象
|
||||
* @returns {Promise<string>} 渲染后的字符串
|
||||
*/
|
||||
async render(name: string, data: Record<string, any>): Promise<string> {
|
||||
// 根据名称获取模板
|
||||
const template = await this.getTemplateByName(name);
|
||||
// 如果模板不存在,则抛出错误
|
||||
if (!template) {
|
||||
throw new Error(`模板 '${name}' 不存在`);
|
||||
}
|
||||
|
||||
// 获取模板的原始内容
|
||||
let rendered = template.value;
|
||||
// 遍历数据对象,替换模板中的占位符
|
||||
for (const key in data) {
|
||||
// 创建一个正则表达式来匹配 {{key}}
|
||||
const regex = new RegExp(`{{${key}}}`, 'g');
|
||||
// 执行替换操作
|
||||
rendered = rendered.replace(regex, data[key]);
|
||||
}
|
||||
|
||||
// 返回渲染后的字符串
|
||||
return rendered;
|
||||
}
|
||||
}
|
||||
|
|
@ -320,7 +320,7 @@ export class WPService {
|
|||
site: any,
|
||||
productId: string,
|
||||
variationId: string,
|
||||
data: Partial<UpdateVariationDTO>
|
||||
data: UpdateVariationDTO
|
||||
): Promise<Boolean> {
|
||||
const { regular_price, sale_price, ...params } = data;
|
||||
return await this.updateData(
|
||||
|
|
|
|||
Loading…
Reference in New Issue