diff --git a/package.json b/package.json index 4517131..4f19d4b 100644 --- a/package.json +++ b/package.json @@ -26,11 +26,11 @@ "@nestjs/platform-fastify": "^10.0.3", "@nestjs/swagger": "^7.4.2", "@nestjs/typeorm": "^11.0.0", - "better-sqlite3": "^11.10.0", "class-transformer": "^0.5.1", "class-validator": "^0.14.2", "deepmerge": "^4.3.1", "lodash": "^4.17.21", + "mysql2": "^3.14.1", "reflect-metadata": "^0.1.13", "rimraf": "^5.0.1", "rxjs": "^7.8.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index fb35f40..c822eb0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -22,10 +22,7 @@ importers: version: 7.4.2(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17)(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14) '@nestjs/typeorm': specifier: ^11.0.0 - version: 11.0.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17)(reflect-metadata@0.1.14)(rxjs@7.8.2)(typeorm@0.3.24(better-sqlite3@11.10.0)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6))) - better-sqlite3: - specifier: ^11.10.0 - version: 11.10.0 + version: 11.0.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17)(reflect-metadata@0.1.14)(rxjs@7.8.2)(typeorm@0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6))) class-transformer: specifier: ^0.5.1 version: 0.5.1 @@ -38,6 +35,9 @@ importers: lodash: specifier: ^4.17.21 version: 4.17.21 + mysql2: + specifier: ^3.14.1 + version: 3.14.1 reflect-metadata: specifier: ^0.1.13 version: 0.1.14 @@ -52,7 +52,7 @@ importers: version: 2.17.0 typeorm: specifier: ^0.3.24 - version: 0.3.24(better-sqlite3@11.10.0)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)) + version: 0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)) validator: specifier: ^13.15.0 version: 13.15.0 @@ -1170,6 +1170,10 @@ packages: avvio@8.4.0: resolution: {integrity: sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==} + aws-ssl-profiles@1.1.2: + resolution: {integrity: sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==} + engines: {node: '>= 6.0.0'} + babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1561,6 +1565,10 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} + denque@2.1.0: + resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} + engines: {node: '>=0.10'} + depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -2080,6 +2088,9 @@ packages: functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + generate-function@2.3.1: + resolution: {integrity: sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==} + gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} @@ -2224,6 +2235,10 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -2361,6 +2376,9 @@ packages: resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} engines: {node: '>=0.10.0'} + is-property@1.0.2: + resolution: {integrity: sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==} + is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} @@ -2686,6 +2704,9 @@ packages: resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} engines: {node: '>=10'} + long@5.3.2: + resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} + lowercase-keys@2.0.0: resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} engines: {node: '>=8'} @@ -2699,6 +2720,14 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + lru-cache@7.18.3: + resolution: {integrity: sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==} + engines: {node: '>=12'} + + lru.min@1.1.2: + resolution: {integrity: sha512-Nv9KddBcQSlQopmBHXSsZVY5xsdlZkdH/Iey0BlcBYggMd4two7cZnKOK9vmy3nY0O5RGH99z1PCeTpPqszUYg==} + engines: {bun: '>=1.0.0', deno: '>=1.30.0', node: '>=8.0.0'} + magic-string@0.30.8: resolution: {integrity: sha512-ISQTe55T2ao7XtlAStud6qwYPZjE4GK1S/BeVPus4jrq6JuOnQ00YKQC581RWhR122W7msZV263KzVeLoqidyQ==} engines: {node: '>=12'} @@ -2818,6 +2847,14 @@ packages: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + mysql2@3.14.1: + resolution: {integrity: sha512-7ytuPQJjQB8TNAYX/H2yhL+iQOnIBjAMam361R7UAL0lOVXWjtdrmoL9HYKqKoLp/8UUTRcvo1QPvK9KL7wA8w==} + engines: {node: '>= 8.0'} + + named-placeholders@1.1.3: + resolution: {integrity: sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==} + engines: {node: '>=12.0.0'} + nanoid@3.3.11: resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} @@ -3354,6 +3391,9 @@ packages: resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} engines: {node: '>= 0.8.0'} + seq-queue@0.0.5: + resolution: {integrity: sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==} + serialize-javascript@6.0.2: resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} @@ -3475,6 +3515,10 @@ packages: resolution: {integrity: sha512-+fLpbAbWkQ+d0JEchJT/NrRRXbYRNbG15gFpANx73EwxQB1PRjj+k/OI0GTU0J63g8ikGkJECQp9z8XEJZvPRw==} engines: {node: '>=14'} + sqlstring@2.3.3: + resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==} + engines: {node: '>= 0.6'} + stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -4685,13 +4729,13 @@ snapshots: optionalDependencies: '@nestjs/platform-express': 10.4.17(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17) - '@nestjs/typeorm@11.0.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17)(reflect-metadata@0.1.14)(rxjs@7.8.2)(typeorm@0.3.24(better-sqlite3@11.10.0)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)))': + '@nestjs/typeorm@11.0.0(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/core@10.4.17)(reflect-metadata@0.1.14)(rxjs@7.8.2)(typeorm@0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)))': dependencies: '@nestjs/common': 10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2) '@nestjs/core': 10.4.17(@nestjs/common@10.4.17(class-transformer@0.5.1)(class-validator@0.14.2)(reflect-metadata@0.1.14)(rxjs@7.8.2))(@nestjs/platform-express@10.4.17)(reflect-metadata@0.1.14)(rxjs@7.8.2) reflect-metadata: 0.1.14 rxjs: 7.8.2 - typeorm: 0.3.24(better-sqlite3@11.10.0)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)) + typeorm: 0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)) '@noble/hashes@1.8.0': {} @@ -5277,6 +5321,8 @@ snapshots: '@fastify/error': 3.4.1 fastq: 1.19.1 + aws-ssl-profiles@1.1.2: {} + babel-jest@29.7.0(@babel/core@7.27.1): dependencies: '@babel/core': 7.27.1 @@ -5340,6 +5386,7 @@ snapshots: dependencies: bindings: 1.5.0 prebuild-install: 7.1.3 + optional: true bin-check@4.1.0: dependencies: @@ -5362,6 +5409,7 @@ snapshots: bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 + optional: true bl@4.1.0: dependencies: @@ -5495,7 +5543,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chownr@1.1.4: {} + chownr@1.1.4: + optional: true chrome-trace-event@1.0.4: {} @@ -5686,7 +5735,8 @@ snapshots: dedent@1.6.0: {} - deep-extend@0.6.0: {} + deep-extend@0.6.0: + optional: true deep-is@0.1.4: {} @@ -5712,13 +5762,16 @@ snapshots: delayed-stream@1.0.0: {} + denque@2.1.0: {} + depd@2.0.0: optional: true destroy@1.2.0: optional: true - detect-libc@2.0.4: {} + detect-libc@2.0.4: + optional: true detect-newline@3.1.0: {} @@ -6105,7 +6158,8 @@ snapshots: exit@0.1.2: {} - expand-template@2.0.3: {} + expand-template@2.0.3: + optional: true expect@29.7.0: dependencies: @@ -6263,7 +6317,8 @@ snapshots: transitivePeerDependencies: - supports-color - file-uri-to-path@1.0.0: {} + file-uri-to-path@1.0.0: + optional: true filename-reserved-regex@3.0.0: {} @@ -6363,7 +6418,8 @@ snapshots: fresh@0.5.2: optional: true - fs-constants@1.0.0: {} + fs-constants@1.0.0: + optional: true fs-extra@10.1.0: dependencies: @@ -6391,6 +6447,10 @@ snapshots: functions-have-names@1.2.3: {} + generate-function@2.3.1: + dependencies: + is-property: 1.0.2 + gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} @@ -6429,7 +6489,8 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 - github-from-package@0.0.0: {} + github-from-package@0.0.0: + optional: true glob-parent@5.1.2: dependencies: @@ -6554,6 +6615,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + ieee754@1.2.1: {} ignore@5.3.2: {} @@ -6577,7 +6642,8 @@ snapshots: inherits@2.0.4: {} - ini@1.3.8: {} + ini@1.3.8: + optional: true inquirer@8.2.6: dependencies: @@ -6707,6 +6773,8 @@ snapshots: is-plain-object@5.0.0: {} + is-property@1.0.2: {} + is-regex@1.2.1: dependencies: call-bound: 1.0.4 @@ -7216,6 +7284,8 @@ snapshots: chalk: 4.1.2 is-unicode-supported: 0.1.0 + long@5.3.2: {} + lowercase-keys@2.0.0: {} lru-cache@10.4.3: {} @@ -7229,6 +7299,10 @@ snapshots: dependencies: yallist: 3.1.1 + lru-cache@7.18.3: {} + + lru.min@1.1.2: {} + magic-string@0.30.8: dependencies: '@jridgewell/sourcemap-codec': 1.5.0 @@ -7297,7 +7371,8 @@ snapshots: minipass@7.1.2: {} - mkdirp-classic@0.5.3: {} + mkdirp-classic@0.5.3: + optional: true mkdirp@0.5.6: dependencies: @@ -7328,9 +7403,26 @@ snapshots: mute-stream@1.0.0: {} + mysql2@3.14.1: + dependencies: + aws-ssl-profiles: 1.1.2 + denque: 2.1.0 + generate-function: 2.3.1 + iconv-lite: 0.6.3 + long: 5.3.2 + lru.min: 1.1.2 + named-placeholders: 1.1.3 + seq-queue: 0.0.5 + sqlstring: 2.3.3 + + named-placeholders@1.1.3: + dependencies: + lru-cache: 7.18.3 + nanoid@3.3.11: {} - napi-build-utils@2.0.0: {} + napi-build-utils@2.0.0: + optional: true natural-compare-lite@1.4.0: {} @@ -7344,6 +7436,7 @@ snapshots: node-abi@3.75.0: dependencies: semver: 7.7.1 + optional: true node-abort-controller@3.1.1: {} @@ -7590,6 +7683,7 @@ snapshots: simple-get: 4.0.1 tar-fs: 2.1.2 tunnel-agent: 0.6.0 + optional: true prelude-ls@1.2.1: {} @@ -7671,6 +7765,7 @@ snapshots: ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 + optional: true react-is@18.3.1: {} @@ -7879,6 +7974,8 @@ snapshots: - supports-color optional: true + seq-queue@0.0.5: {} + serialize-javascript@6.0.2: dependencies: randombytes: 2.1.0 @@ -7969,13 +8066,15 @@ snapshots: signal-exit@4.1.0: {} - simple-concat@1.0.1: {} + simple-concat@1.0.1: + optional: true simple-get@4.0.1: dependencies: decompress-response: 6.0.0 once: 1.4.0 simple-concat: 1.0.1 + optional: true sisteransi@1.0.5: {} @@ -8015,6 +8114,8 @@ snapshots: sql-highlight@6.0.0: {} + sqlstring@2.3.3: {} + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -8090,7 +8191,8 @@ snapshots: strip-final-newline@2.0.0: {} - strip-json-comments@2.0.1: {} + strip-json-comments@2.0.1: + optional: true strip-json-comments@3.1.1: {} @@ -8150,6 +8252,7 @@ snapshots: mkdirp-classic: 0.5.3 pump: 3.0.2 tar-stream: 2.2.0 + optional: true tar-stream@2.2.0: dependencies: @@ -8158,6 +8261,7 @@ snapshots: fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 + optional: true terser-webpack-plugin@5.3.14(@swc/core@1.11.24)(webpack@5.97.1(@swc/core@1.11.24)): dependencies: @@ -8303,6 +8407,7 @@ snapshots: tunnel-agent@0.6.0: dependencies: safe-buffer: 5.2.1 + optional: true type-check@0.4.0: dependencies: @@ -8356,7 +8461,7 @@ snapshots: typedarray@0.0.6: optional: true - typeorm@0.3.24(better-sqlite3@11.10.0)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)): + typeorm@0.3.24(better-sqlite3@11.10.0)(mysql2@3.14.1)(reflect-metadata@0.1.14)(ts-node@10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6)): dependencies: '@sqltools/formatter': 1.2.5 ansis: 3.17.0 @@ -8375,6 +8480,7 @@ snapshots: yargs: 17.7.2 optionalDependencies: better-sqlite3: 11.10.0 + mysql2: 3.14.1 ts-node: 10.9.2(@swc/core@1.11.24)(@types/node@20.17.46)(typescript@5.1.6) transitivePeerDependencies: - babel-plugin-macros diff --git a/src/config/database.config.ts b/src/config/database.config.ts index dbb57b0..09688ee 100644 --- a/src/config/database.config.ts +++ b/src/config/database.config.ts @@ -11,4 +11,5 @@ export const database = (): TypeOrmModuleOptions => ({ database: '3r', synchronize: true, autoLoadEntities: true, + timezone: '+08:00', }); diff --git a/src/modules/content/entities/post.entity.ts b/src/modules/content/entities/post.entity.ts index c56b5be..51936a8 100644 --- a/src/modules/content/entities/post.entity.ts +++ b/src/modules/content/entities/post.entity.ts @@ -6,6 +6,7 @@ import { Entity, JoinTable, ManyToMany, + ManyToOne, OneToMany, PrimaryColumn, Relation, @@ -67,7 +68,7 @@ export class PostEntity extends BaseEntity { commentCount: number; @Expose() - @OneToMany(() => CategoryEntity, (category) => category.posts, { + @ManyToOne(() => CategoryEntity, (category) => category.posts, { nullable: true, onDelete: 'SET NULL', }) diff --git a/src/modules/content/services/comment.service.ts b/src/modules/content/services/comment.service.ts index aa16a1a..2e83273 100644 --- a/src/modules/content/services/comment.service.ts +++ b/src/modules/content/services/comment.service.ts @@ -10,11 +10,9 @@ import { QueryCommentTreeDto, } from '@/modules/content/dtos/comment.dto'; import { CommentEntity } from '@/modules/content/entities/comment.entity'; +import { CommentRepository, PostRepository } from '@/modules/content/repositories'; import { treePaginate } from '@/modules/database/utils'; -import { CommentRepository } from '../repositories/comment.repository'; -import { PostRepository } from '../repositories/post.repository'; - @Injectable() export class CommentService { constructor( @@ -82,7 +80,7 @@ export class CommentService { } parent = await this.repository.findOne({ where: { id }, - relations: ['parent', 'children'], + relations: ['parent', 'children', 'post'], }); if (!parent) { throw new EntityNotFoundError(CommentEntity, `Parent Comment ${id} not found`); diff --git a/test/01-inital-data.test.ts b/test/01-inital-data.test.ts index 07a2d80..ff75d2a 100644 --- a/test/01-inital-data.test.ts +++ b/test/01-inital-data.test.ts @@ -3,26 +3,20 @@ import { describe } from 'node:test'; import { FastifyAdapter, NestFastifyApplication } from '@nestjs/platform-fastify'; import { Test, TestingModule } from '@nestjs/testing'; -import { omit, pick } from 'lodash'; +import { pick } from 'lodash'; import { DataSource } from 'typeorm'; import { database } from '@/config'; import { ContentModule } from '@/modules/content/content.module'; import { CategoryEntity, CommentEntity, PostEntity, TagEntity } from '@/modules/content/entities'; -import { CategoryRepository, PostRepository, TagRepository } from '@/modules/content/repositories'; import { DatabaseModule } from '@/modules/database/database.module'; -import { CommentRepository } from '../src/modules/content/repositories/comment.repository'; - +import { generateRandomNumber, generateUniqueRandomNumbers } from './generate-mock-data'; import { commentData, INIT_DATA, initialCategories, postData, tagData } from './test-data'; describe('category test', () => { let datasource: DataSource; let app: NestFastifyApplication; - let categoryRepository: CategoryRepository; - let tagRepository: TagRepository; - let postRepository: PostRepository; - let commentRepository: CommentRepository; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ @@ -32,16 +26,31 @@ describe('category test', () => { await app.init(); await app.getHttpAdapter().getInstance().ready(); - categoryRepository = module.get(CategoryRepository); - tagRepository = module.get(TagRepository); - postRepository = module.get(PostRepository); - commentRepository = module.get(CommentRepository); datasource = module.get(DataSource); + if (!datasource.isInitialized) { + await datasource.initialize(); + } + }); + + beforeEach(async () => { if (INIT_DATA) { - await categoryRepository.deleteAll(); - await postRepository.deleteAll(); - await tagRepository.deleteAll(); - await commentRepository.deleteAll(); + const queryRunner = datasource.createQueryRunner(); + try { + await queryRunner.query('SET FOREIGN_KEY_CHECKS = 0'); + + datasource.entityMetadatas.map(async (entity) => { + const table = entity.schema + ? `${entity.schema}.${entity.tableName}` + : `${entity.tableName}`; + console.log(`TRUNCATE TABLE ${table}`); + await queryRunner.query(`TRUNCATE TABLE ${table}`); + return table; + }); + // await queryRunner.query(`TRUNCATE TABLE ${tables}`); + } finally { + await queryRunner.query('SET FOREIGN_KEY_CHECKS = 1'); + await queryRunner.release(); + } // init category data const categories = await addCategory(app, initialCategories); @@ -50,14 +59,21 @@ describe('category test', () => { const tags = await addTag(app, tagData); console.log(tags); // init post data - addPost( + const posts = await addPost( app, postData, tags.map((tag) => tag.id), categories.map((category) => category.id), ); + console.log(posts); + console.log('='.repeat(100)); // init comment data - addComment(app, commentData); + const comments = await addComment( + app, + commentData, + posts.map((post) => post.id), + ); + console.log(comments); } }); @@ -120,11 +136,13 @@ async function addPost( if (app && data && data.length > 0) { for (let index = 0; index < data.length; index++) { const item = data[index]; - // TODO add tag and category + item.category = categories[generateRandomNumber(1, categories.length - 1)[0]]; + item.tags = generateUniqueRandomNumbers(0, tags.length - 1, 3).map((idx) => tags[idx]); + // console.log(JSON.stringify(item)); const result = await app.inject({ method: 'POST', - url: '/post', - body: omit(item, ['tags', 'category']), + url: '/posts', + body: item, }); const addedItem: PostEntity = result.json(); posts.push(addedItem); @@ -136,17 +154,32 @@ async function addPost( async function addComment( app: NestFastifyApplication, data: RecordAny[], + posts: string[], ): Promise { const comments: CommentEntity[] = []; if (app && data && data.length > 0) { for (let index = 0; index < data.length; index++) { const item = data[index]; + item.post = posts[generateRandomNumber(0, posts.length - 1)[0]]; + + const commentsFilter = comments + .filter((comment) => comment.post === item.post) + .map((comment) => comment.id); + console.log('A'.repeat(100)); + console.log(commentsFilter); + item.parent = + commentsFilter.length > 0 + ? commentsFilter[generateRandomNumber(0, commentsFilter.length - 1)[0]] + : undefined; + console.log(JSON.stringify(item)); const result = await app.inject({ method: 'POST', url: '/comment', body: item, }); - const addedItem: CommentEntity = result.json(); + const addedItem = result.json(); + console.log(addedItem); + addedItem.post = item.post; comments.push(addedItem); } } diff --git a/test/generate-mock-data.ts b/test/generate-mock-data.ts index 72b4d56..3439746 100644 --- a/test/generate-mock-data.ts +++ b/test/generate-mock-data.ts @@ -35,3 +35,59 @@ export function generateMockComment(): CreateCommentDto { parent: fakerEN.string.uuid(), }; } + +/** + * 生成指定范围内的唯一随机整数数组 + * @param start 起始范围(包含) + * @param end 结束范围(包含) + * @param n 生成数量 + * @returns 包含 n 个唯一 [start, end] 区间内随机整数的数组 + * @throws 当请求数量超出范围时抛出错误 + */ +export function generateUniqueRandomNumbers(start: number, end: number, n: number): number[] { + // 处理反向范围并计算实际区间 + const [min, max] = start <= end ? [start, end] : [end, start]; + const range = max - min + 1; + + if (n <= 0 || !Number.isInteger(n)) { + throw new Error('参数 n 必须是正整数'); + } + + if (n === 1) { + return generateRandomNumber(start, end); + } + + // 参数校验:请求数量不能超过可用唯一值总数 + if (n > range) { + throw new Error( + `Cannot generate ${n} unique numbers in range [${min}, ${max}]. Maximum possible: ${range}`, + ); + } + + // 生成所有可能的候选数字 + const candidates = Array.from({ length: range }, (_, i) => min + i); + + // Fisher-Yates 洗牌算法随机打乱数组 + for (let i = candidates.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [candidates[i], candidates[j]] = [candidates[j], candidates[i]]; + } + + // 返回前 n 个元素 + return candidates.slice(0, n); +} + +export function generateRandomNumber(start: number, end: number): number[] { + // 处理反向范围(确保 min <= max) + const [min, max] = start <= end ? [start, end] : [end, start]; + + if (min === max) { + return [min]; + } + + // 生成 1 个随机整数 + return Array.from({ length: 1 }, () => { + // 随机数公式:Math.floor(Math.random() * (max - min + 1)) + min + return Math.floor(Math.random() * (max - min + 1)) + min; + }); +}