From 7917bb71a2de0fb9a1efd3b74ac41487ec63a2c4 Mon Sep 17 00:00:00 2001 From: Jang-DongHo Date: Tue, 18 Mar 2025 18:29:31 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[Refactor]=20swagger=20=EC=84=A4=EC=A0=95?= =?UTF-8?q?=20=EB=B0=8F=20=EC=9C=A0=EC=A0=80=20=EB=AA=A8=EB=93=88=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + pnpm-lock.yaml | 111 +++++++++++------- src/auth/auth.guard.ts | 15 +++ src/auth/auth.module.ts | 15 +++ src/main.ts | 22 +++- src/modules/app/app.module.ts | 4 +- .../cafeteria/cafeteria.controller.spec.ts | 20 ---- src/modules/cafeteria/cafeteria.controller.ts | 9 -- src/modules/cafeteria/cafeteria.module.ts | 11 -- .../cafeteria/cafeteria.service.spec.ts | 18 --- src/modules/cafeteria/cafeteria.service.ts | 6 - src/modules/user/dtos/user.dto.ts | 74 ++++++++++++ src/modules/user/user.controller.ts | 33 ++---- 13 files changed, 208 insertions(+), 131 deletions(-) create mode 100644 src/auth/auth.guard.ts create mode 100644 src/auth/auth.module.ts delete mode 100644 src/modules/cafeteria/cafeteria.controller.spec.ts delete mode 100644 src/modules/cafeteria/cafeteria.controller.ts delete mode 100644 src/modules/cafeteria/cafeteria.module.ts delete mode 100644 src/modules/cafeteria/cafeteria.service.spec.ts delete mode 100644 src/modules/cafeteria/cafeteria.service.ts create mode 100644 src/modules/user/dtos/user.dto.ts diff --git a/package.json b/package.json index 89e0bfd..54db1df 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@slack/client": "^5.0.2", "@supabase/supabase-js": "^2.45.1", "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "cross-env": "^7.0.3", "dotenv": "^16.4.5", "joi": "^17.13.3", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2d811fb..12f8cf6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,28 +10,28 @@ importers: dependencies: '@nestjs/axios': specifier: ^4.0.0 - version: 4.0.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@0.21.4)(rxjs@7.8.1) + version: 4.0.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@0.21.4)(rxjs@7.8.1) '@nestjs/common': specifier: ^9.0.0 - version: 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + version: 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nestjs/config': specifier: ^3.2.3 - version: 3.2.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(rxjs@7.8.1) + version: 3.2.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(rxjs@7.8.1) '@nestjs/core': specifier: ^9.0.0 - version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nestjs/platform-express': specifier: ^9.0.0 - version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) + version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) '@nestjs/schedule': specifier: ^5.0.1 - version: 5.0.1(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)) + version: 5.0.1(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)) '@nestjs/swagger': specifier: ^7.4.0 - version: 7.4.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(reflect-metadata@0.1.14) + version: 7.4.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14) '@nestjs/typeorm': specifier: ^10.0.2 - version: 10.0.2(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(reflect-metadata@0.1.14)(rxjs@7.8.1)(typeorm@0.3.20(pg@8.12.0)(ts-node@10.9.2(@types/node@18.16.12)(typescript@5.5.4))) + version: 10.0.2(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(reflect-metadata@0.1.14)(rxjs@7.8.1)(typeorm@0.3.20(pg@8.12.0)(ts-node@10.9.2(@types/node@18.16.12)(typescript@5.5.4))) '@sentry/browser': specifier: ^8.54.0 version: 8.54.0 @@ -40,7 +40,7 @@ importers: version: 6.19.7 '@sentry/nestjs': specifier: ^8.27.0 - version: 8.27.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)) + version: 8.27.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)) '@sentry/node': specifier: ^8.54.0 version: 8.54.0 @@ -56,6 +56,9 @@ importers: class-transformer: specifier: ^0.5.1 version: 0.5.1 + class-validator: + specifier: ^0.14.1 + version: 0.14.1 cross-env: specifier: ^7.0.3 version: 7.0.3 @@ -92,7 +95,7 @@ importers: version: 9.2.0(chokidar@3.5.3)(typescript@5.5.4) '@nestjs/testing': specifier: ^9.0.0 - version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3)) + version: 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3)) '@snaplet/copycat': specifier: ^5.1.0 version: 5.1.0 @@ -1486,6 +1489,9 @@ packages: '@types/uuid@10.0.0': resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} + '@types/validator@13.12.2': + resolution: {integrity: sha512-6SlHBzUW8Jhf3liqrGGXyTJSIFe4nqlJ5A5KaMZ2l/vbM3Wh3KSybots/wfWVzNLK4D1NZluDlSQIbIEPx6oyA==} + '@types/wrap-ansi@3.0.0': resolution: {integrity: sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==} @@ -1954,6 +1960,9 @@ packages: class-transformer@0.5.1: resolution: {integrity: sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==} + class-validator@0.14.1: + resolution: {integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==} + cli-cursor@3.1.0: resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} engines: {node: '>=8'} @@ -3105,6 +3114,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + libphonenumber-js@1.12.6: + resolution: {integrity: sha512-PJiS4ETaUfCOFLpmtKzAbqZQjCCKVu2OhTV4SVNNE7c2nu/dACvtCqj4L0i/KWNnIgRv7yrILvBj5Lonv5Ncxw==} + lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -4434,6 +4446,10 @@ packages: valid-url@1.0.9: resolution: {integrity: sha512-QQDsV8OnSf5Uc30CKSwG9lnhMPe6exHtTXLRYX8uMwKENy640pU+2BgBL0LRbDh/eYRahNCS7aewCx0wf3NYVA==} + validator@13.12.0: + resolution: {integrity: sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==} + engines: {node: '>= 0.10'} + vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -5253,9 +5269,9 @@ snapshots: '@microsoft/tsdoc@0.15.0': {} - '@nestjs/axios@4.0.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@0.21.4)(rxjs@7.8.1)': + '@nestjs/axios@4.0.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(axios@0.21.4)(rxjs@7.8.1)': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) axios: 0.21.4 rxjs: 7.8.1 @@ -5289,7 +5305,7 @@ snapshots: - uglify-js - webpack-cli - '@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)': + '@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1)': dependencies: iterare: 1.2.1 reflect-metadata: 0.1.14 @@ -5298,18 +5314,19 @@ snapshots: uid: 2.0.2 optionalDependencies: class-transformer: 0.5.1 + class-validator: 0.14.1 - '@nestjs/config@3.2.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(rxjs@7.8.1)': + '@nestjs/config@3.2.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(rxjs@7.8.1)': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) dotenv: 16.4.5 dotenv-expand: 10.0.0 lodash: 4.17.21 rxjs: 7.8.1 - '@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)': + '@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1)': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@nuxtjs/opencollective': 0.3.2 fast-safe-stringify: 2.1.1 iterare: 1.2.1 @@ -5319,21 +5336,22 @@ snapshots: tslib: 2.5.3 uid: 2.0.2 optionalDependencies: - '@nestjs/platform-express': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) + '@nestjs/platform-express': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) transitivePeerDependencies: - encoding - '@nestjs/mapped-types@2.0.5(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(reflect-metadata@0.1.14)': + '@nestjs/mapped-types@2.0.5(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) reflect-metadata: 0.1.14 optionalDependencies: class-transformer: 0.5.1 + class-validator: 0.14.1 - '@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3)': + '@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3)': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) body-parser: 1.20.2 cors: 2.8.5 express: 4.18.2 @@ -5342,10 +5360,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@nestjs/schedule@5.0.1(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))': + '@nestjs/schedule@5.0.1(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) cron: 3.5.0 '@nestjs/schematics@9.2.0(chokidar@3.5.3)(typescript@4.9.5)': @@ -5368,12 +5386,12 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/swagger@7.4.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(reflect-metadata@0.1.14)': + '@nestjs/swagger@7.4.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)': dependencies: '@microsoft/tsdoc': 0.15.0 - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/mapped-types': 2.0.5(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(reflect-metadata@0.1.14) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/mapped-types': 2.0.5(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14) js-yaml: 4.1.0 lodash: 4.17.21 path-to-regexp: 3.2.0 @@ -5381,19 +5399,20 @@ snapshots: swagger-ui-dist: 5.17.14 optionalDependencies: class-transformer: 0.5.1 + class-validator: 0.14.1 - '@nestjs/testing@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3))': + '@nestjs/testing@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3))': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) tslib: 2.5.3 optionalDependencies: - '@nestjs/platform-express': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) + '@nestjs/platform-express': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3) - '@nestjs/typeorm@10.0.2(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(reflect-metadata@0.1.14)(rxjs@7.8.1)(typeorm@0.3.20(pg@8.12.0)(ts-node@10.9.2(@types/node@18.16.12)(typescript@5.5.4)))': + '@nestjs/typeorm@10.0.2(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))(reflect-metadata@0.1.14)(rxjs@7.8.1)(typeorm@0.3.20(pg@8.12.0)(ts-node@10.9.2(@types/node@18.16.12)(typescript@5.5.4)))': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) reflect-metadata: 0.1.14 rxjs: 7.8.1 typeorm: 0.3.20(pg@8.12.0)(ts-node@10.9.2(@types/node@18.16.12)(typescript@5.5.4)) @@ -6001,10 +6020,10 @@ snapshots: '@sentry/types': 6.19.7 tslib: 1.14.1 - '@sentry/nestjs@8.27.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))': + '@sentry/nestjs@8.27.0(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/core@9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1))': dependencies: - '@nestjs/common': 9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) - '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/common': 9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1) + '@nestjs/core': 9.4.3(@nestjs/common@9.4.3(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.1.14)(rxjs@7.8.1))(@nestjs/platform-express@9.4.3)(reflect-metadata@0.1.14)(rxjs@7.8.1) '@sentry/core': 8.27.0 '@sentry/node': 8.27.0 '@sentry/types': 8.27.0 @@ -6504,6 +6523,8 @@ snapshots: '@types/uuid@10.0.0': {} + '@types/validator@13.12.2': {} + '@types/wrap-ansi@3.0.0': {} '@types/ws@7.4.7': @@ -7075,6 +7096,12 @@ snapshots: class-transformer@0.5.1: {} + class-validator@0.14.1: + dependencies: + '@types/validator': 13.12.2 + libphonenumber-js: 1.12.6 + validator: 13.12.0 + cli-cursor@3.1.0: dependencies: restore-cursor: 3.1.0 @@ -8483,6 +8510,8 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + libphonenumber-js@1.12.6: {} + lines-and-columns@1.2.4: {} loader-runner@4.3.0: {} @@ -9714,6 +9743,8 @@ snapshots: valid-url@1.0.9: {} + validator@13.12.0: {} + vary@1.1.2: {} walker@1.0.8: diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts new file mode 100644 index 0000000..a4fd802 --- /dev/null +++ b/src/auth/auth.guard.ts @@ -0,0 +1,15 @@ +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { Observable } from 'rxjs'; + +@Injectable() +export class AuthGuard implements CanActivate { + canActivate(context: ExecutionContext): boolean | Promise | Observable { + const request = context.switchToHttp().getRequest(); + const userId = request.headers['x-user-id']; + if (userId) { + request.body = { ...request.body, userId }; // 기존 body에 userId 강제 삽입 + } + + return !!userId; + } +} \ No newline at end of file diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts new file mode 100644 index 0000000..0c00ca9 --- /dev/null +++ b/src/auth/auth.module.ts @@ -0,0 +1,15 @@ +import { Module } from '@nestjs/common'; +import { AuthGuard } from './auth.guard'; +import { APP_GUARD } from '@nestjs/core'; + +@Module({ + providers: [ + AuthGuard, + { + provide: APP_GUARD, + useClass: AuthGuard + } + ], + exports: [AuthGuard] +}) +export class AuthModule {} diff --git a/src/main.ts b/src/main.ts index 738ed16..03778c2 100644 --- a/src/main.ts +++ b/src/main.ts @@ -18,7 +18,6 @@ async function bootstrap() { const nodeEnv = process.env.NODE_ENV; app.useGlobalInterceptors(new SentryInterceptor()); - app.useGlobalInterceptors(new KakaoInterceptor(ResponseDTO)); const { httpAdapter } = app.get(HttpAdapterHost); @@ -29,12 +28,27 @@ async function bootstrap() { .setTitle('커넥트지누 노드 서버 API') .setDescription('커넥트지누 노드 서버 API 문서입니다.') .setVersion('1.0') + .addApiKey( + { + type: 'apiKey', + name: 'X-USER-ID', + in: 'header', + description: 'userId를 입력하세요 (예: 123456)', + }, + 'X-USER-ID', + ) + .addSecurityRequirements('X-USER-ID') .build(); const document = SwaggerModule.createDocument(app, config); - - SwaggerModule.setup('api/node/docs', app, document); - await app.listen(nodeEnv == 'production' ? 5200 : 5000); + SwaggerModule.setup('api/node/docs', app, document, + { + swaggerOptions: { + persistAuthorization: true, // 새로고침해도 인증 정보 유지 + } + } + ); + await app.listen(nodeEnv == 'production' ? 5200 : 5001); } bootstrap(); diff --git a/src/modules/app/app.module.ts b/src/modules/app/app.module.ts index c47f451..721b40d 100644 --- a/src/modules/app/app.module.ts +++ b/src/modules/app/app.module.ts @@ -8,11 +8,11 @@ import { UserModule } from '../user/user.module'; import { SupabaseModule } from '../supabase/supabase.module'; import { validationSchema } from 'src/common/utils/enviornment'; import { APP_FILTER } from '@nestjs/core'; -import { CafeteriaModule } from '../cafeteria/cafeteria.module'; import { CommonModule } from '../common/common.module'; import { ClickerModule } from '../clicker/clicker.module'; import { ScheduleModule } from '@nestjs/schedule'; import { HttpModule } from '@nestjs/axios'; +import { AuthModule } from 'src/auth/auth.module'; @Module({ imports: [ @@ -23,8 +23,8 @@ import { HttpModule } from '@nestjs/axios'; UtilsModule, CommonModule, UserModule, - CafeteriaModule, ClickerModule, + AuthModule, ConfigModule.forRoot({ isGlobal: true, envFilePath: process.env.NODE_ENV === 'production' ? '.env.prod' : '.env.dev', diff --git a/src/modules/cafeteria/cafeteria.controller.spec.ts b/src/modules/cafeteria/cafeteria.controller.spec.ts deleted file mode 100644 index de876d4..0000000 --- a/src/modules/cafeteria/cafeteria.controller.spec.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { CafeteriaController } from './cafeteria.controller'; -import { CafeteriaService } from './cafeteria.service'; - -describe('CafeteriaController', () => { - let controller: CafeteriaController; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - controllers: [CafeteriaController], - providers: [CafeteriaService], - }).compile(); - - controller = module.get(CafeteriaController); - }); - - it('should be defined', () => { - expect(controller).toBeDefined(); - }); -}); diff --git a/src/modules/cafeteria/cafeteria.controller.ts b/src/modules/cafeteria/cafeteria.controller.ts deleted file mode 100644 index 3240ac4..0000000 --- a/src/modules/cafeteria/cafeteria.controller.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Controller } from '@nestjs/common'; -import { CafeteriaService } from './cafeteria.service'; -import { ApiTags } from '@nestjs/swagger'; - -@ApiTags('cafeteria') -@Controller('cafeteria') -export class CafeteriaController { - constructor(private readonly cafeteriaService: CafeteriaService) {} -} diff --git a/src/modules/cafeteria/cafeteria.module.ts b/src/modules/cafeteria/cafeteria.module.ts deleted file mode 100644 index 8a8cfe2..0000000 --- a/src/modules/cafeteria/cafeteria.module.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Module } from '@nestjs/common'; -import { CafeteriaService } from './cafeteria.service'; -import { CafeteriaController } from './cafeteria.controller'; -import { SupabaseModule } from '../supabase/supabase.module'; - -@Module({ - imports: [SupabaseModule], - controllers: [CafeteriaController], - providers: [CafeteriaService], -}) -export class CafeteriaModule {} diff --git a/src/modules/cafeteria/cafeteria.service.spec.ts b/src/modules/cafeteria/cafeteria.service.spec.ts deleted file mode 100644 index 037a337..0000000 --- a/src/modules/cafeteria/cafeteria.service.spec.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { CafeteriaService } from './cafeteria.service'; - -describe('CafeteriaService', () => { - let service: CafeteriaService; - - beforeEach(async () => { - const module: TestingModule = await Test.createTestingModule({ - providers: [CafeteriaService], - }).compile(); - - service = module.get(CafeteriaService); - }); - - it('should be defined', () => { - expect(service).toBeDefined(); - }); -}); diff --git a/src/modules/cafeteria/cafeteria.service.ts b/src/modules/cafeteria/cafeteria.service.ts deleted file mode 100644 index cc8d4ed..0000000 --- a/src/modules/cafeteria/cafeteria.service.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Injectable } from '@nestjs/common'; - -@Injectable() -export class CafeteriaService { - constructor() {} -} diff --git a/src/modules/user/dtos/user.dto.ts b/src/modules/user/dtos/user.dto.ts new file mode 100644 index 0000000..a23a300 --- /dev/null +++ b/src/modules/user/dtos/user.dto.ts @@ -0,0 +1,74 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsString, IsNumber } from 'class-validator'; + +export class GetCollegeDto { + @IsNumber() + @Transform(({ obj }) => obj.action.clientExtra?.campusId) + @ApiProperty({ + description: '캠퍼스 ID' + }) + campusId: number; + + @IsNumber() + @Transform(({ obj }) => obj.action.clientExtra?.page) + @ApiProperty({ + description: '페이지 번호' + }) + page: number; +} + +export class GetDepartmentDto { + @IsNumber() + @Transform(({ obj }) => obj.action.clientExtra?.campusId) + @ApiProperty({ + description: '캠퍼스 ID' + }) + campusId: number; + + @IsNumber() + @Transform(({ obj }) => obj.action.clientExtra?.collegeId) + @ApiProperty({ + description: '단과대학 ID' + }) + collegeId: number; + + @IsNumber() + @Transform(({ obj }) => obj.action.clientExtra?.page) + @ApiProperty({ + description: '페이지 번호' + }) + page: number; +} + +export class GetProfileDto { + @IsString() + @Transform(({ obj }) => obj.userRequest.user?.id) + @ApiProperty({ + description: '사용자 ID' + }) + userId: string; +} + +export class UpdateDepartmentDto { + @IsString() + @Transform(({ obj }) => obj.userRequest.user?.id) + @ApiProperty({ + description: '사용자 ID' + }) + userId: string; + + @IsNumber() + @Transform(({ obj }) => obj.action.clientExtra?.campusId) + @ApiProperty({ + description: '캠퍼스 ID' + }) + campusId: number; + + @IsNumber() + @Transform(({ obj }) => obj.action.clientExtra?.departmentId) + @ApiProperty({ + description: '학과 ID' + }) + departmentId: number; +} \ No newline at end of file diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index 8757023..8150624 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -1,11 +1,10 @@ -import { Body, Controller, Post } from '@nestjs/common'; +import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common'; import { UserService } from './user.service'; import { ResponseDTO } from 'src/common/dto/response.dto'; -import { SkillPayload } from 'src/common/interfaces/request/skillPayload'; -import { ApiBody, ApiTags } from '@nestjs/swagger'; +import { ApiSecurity, ApiTags } from '@nestjs/swagger'; import { CommonService } from '../common/common.service'; import { BlockId } from 'src/common/utils/constants'; -import { RequestDTO } from 'src/common/dto/request.dto'; +import { GetCollegeDto, GetDepartmentDto, GetProfileDto, UpdateDepartmentDto } from './dtos/user.dto'; @ApiTags('user') @Controller('user') @@ -23,13 +22,11 @@ export class UserController { } @Post('get/college') - async getCollege(@Body() body: SkillPayload): Promise { - const { clientExtra } = body.action; - const capmusId = clientExtra['campusId']; - const page = clientExtra['page']; + async getCollege(@Body() body: GetCollegeDto): Promise { + const { campusId, page } = body; const blockId = BlockId.DEPARTMENT_LIST; const template = await this.commonService.getCollegeListCard( - capmusId, + campusId, page, blockId, ); @@ -37,11 +34,8 @@ export class UserController { } @Post('get/department') - async getDepartment(@Body() body: SkillPayload): Promise { - const { clientExtra } = body.action; - const campusId = clientExtra['campusId']; - const collegeId = clientExtra['collegeId']; - const page = clientExtra['page']; + async getDepartment(@Body() body: GetDepartmentDto): Promise { + const { campusId, collegeId, page } = body; const blockId = BlockId.UPDATE_DEPARTMENT; const template = await this.commonService.getDepartmentListCard( campusId, @@ -53,11 +47,8 @@ export class UserController { } @Post('update/department') - async upsertUserDepartment(@Body() body: SkillPayload): Promise { - const userId = body.userRequest.user.id; - const { clientExtra } = body.action; - const campusId = clientExtra['campusId']; - const departmentId = clientExtra['departmentId']; + async upsertUserDepartment(@Body() body: UpdateDepartmentDto): Promise { + const { userId, campusId, departmentId } = body; const template = await this.userService.upsertUserDepartment( userId, campusId, @@ -67,8 +58,8 @@ export class UserController { } @Post('get/profile') - async getUserProfile(@Body() body: RequestDTO): Promise { - const userId = body.userRequest.user.id; + async getUserProfile(@Body() body: GetProfileDto): Promise { + const { userId } = body; const template = await this.userService.getUserProfile(userId); return new ResponseDTO(template); } From d027af002aa04f5f7cd184ae24a99f9225c6311f Mon Sep 17 00:00:00 2001 From: Jang-DongHo Date: Tue, 18 Mar 2025 20:57:31 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[Refactor]=20dto=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/auth.guard.ts | 2 +- src/common/filter/http-exception.filter.ts | 40 +++++++++++++++++++++ src/main.ts | 19 ++++++++-- src/modules/app/app.module.ts | 8 +++-- src/modules/clicker/clicker.controller.ts | 11 +++--- src/modules/clicker/dtos/clicker.dto.ts | 23 ++++++++++++ src/modules/common/common.service.ts | 2 +- src/modules/user/dtos/user.dto.ts | 41 +++++++++++----------- src/modules/user/user.controller.ts | 3 +- 9 files changed, 114 insertions(+), 35 deletions(-) create mode 100644 src/common/filter/http-exception.filter.ts create mode 100644 src/modules/clicker/dtos/clicker.dto.ts diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts index a4fd802..2144621 100644 --- a/src/auth/auth.guard.ts +++ b/src/auth/auth.guard.ts @@ -5,7 +5,7 @@ import { Observable } from 'rxjs'; export class AuthGuard implements CanActivate { canActivate(context: ExecutionContext): boolean | Promise | Observable { const request = context.switchToHttp().getRequest(); - const userId = request.headers['x-user-id']; + const userId = request.headers['x-user-id'] || request.body.userRequest.user?.id; if (userId) { request.body = { ...request.body, userId }; // 기존 body에 userId 강제 삽입 } diff --git a/src/common/filter/http-exception.filter.ts b/src/common/filter/http-exception.filter.ts new file mode 100644 index 0000000..f5cbd81 --- /dev/null +++ b/src/common/filter/http-exception.filter.ts @@ -0,0 +1,40 @@ +import { ExceptionFilter, Catch, ArgumentsHost, HttpException, Logger } from '@nestjs/common'; +import { Request, Response } from 'express'; + +@Catch(HttpException) +export class HttpExceptionFilter implements ExceptionFilter { + private readonly logger = new Logger(HttpExceptionFilter.name); + + catch(exception: HttpException, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const response = ctx.getResponse(); + const request = ctx.getRequest(); + const status = exception.getStatus(); + const errorResponse = exception.getResponse(); + + this.logger.error( + JSON.stringify( + { + type: 'HTTP_VALIDATION_ERROR', + status, + url: request.url, + method: request.method, + errors: Array.isArray(errorResponse['message']) + ? errorResponse['message'] + : [errorResponse['message']], // 단일 메시지도 배열로 변환 + }, + null, + 2 // JSON 포맷 적용 (2칸 들여쓰기) + ) + ); + + response + .status(status) + .json({ + statusCode: status, + message: errorResponse['message'] || exception.message, + path: request.url, + timestamp: new Date().toISOString(), + }); + } +} \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index 03778c2..79281a4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,17 +11,30 @@ import { ResponseDTO } from './common/dto/response.dto'; import { SentryInterceptor } from './interceptors/sentry.interceptor.js'; import { SentryFilter } from './common/filter/sentry.filter'; import { initializeTransactionalContext } from 'typeorm-transactional'; +import { ValidationPipe } from '@nestjs/common'; +import { HttpExceptionFilter } from './common/filter/http-exception.filter'; async function bootstrap() { initializeTransactionalContext(); const app = await NestFactory.create(AppModule); const nodeEnv = process.env.NODE_ENV; - app.useGlobalInterceptors(new SentryInterceptor()); + if (nodeEnv == 'production') { + app.useGlobalInterceptors(new SentryInterceptor()); + const { httpAdapter } = app.get(HttpAdapterHost); + app.useGlobalFilters(new SentryFilter(httpAdapter)); + } else { + app.useGlobalFilters(new HttpExceptionFilter()); + } app.useGlobalInterceptors(new KakaoInterceptor(ResponseDTO)); - const { httpAdapter } = app.get(HttpAdapterHost); - app.useGlobalFilters(new SentryFilter(httpAdapter)); + app.useGlobalPipes(new ValidationPipe({ + whitelist: false, + forbidNonWhitelisted: false, + transform: true, + transformOptions: { enableImplicitConversion: true } + })) + app.setGlobalPrefix('api/node'); const config = new DocumentBuilder() diff --git a/src/modules/app/app.module.ts b/src/modules/app/app.module.ts index 721b40d..9750a22 100644 --- a/src/modules/app/app.module.ts +++ b/src/modules/app/app.module.ts @@ -1,5 +1,5 @@ import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup'; -import { Module } from '@nestjs/common'; +import { Module, ValidationPipe } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { UtilsModule } from 'src/common/utils/utils.module'; @@ -7,7 +7,7 @@ import { ConfigModule } from '@nestjs/config'; import { UserModule } from '../user/user.module'; import { SupabaseModule } from '../supabase/supabase.module'; import { validationSchema } from 'src/common/utils/enviornment'; -import { APP_FILTER } from '@nestjs/core'; +import { APP_FILTER, APP_PIPE } from '@nestjs/core'; import { CommonModule } from '../common/common.module'; import { ClickerModule } from '../clicker/clicker.module'; import { ScheduleModule } from '@nestjs/schedule'; @@ -37,6 +37,10 @@ import { AuthModule } from 'src/auth/auth.module'; provide: APP_FILTER, useClass: SentryGlobalFilter, }, + { + provide: APP_PIPE, + useClass: ValidationPipe, + }, AppService, ], }) diff --git a/src/modules/clicker/clicker.controller.ts b/src/modules/clicker/clicker.controller.ts index 63dc93e..ae5cea2 100644 --- a/src/modules/clicker/clicker.controller.ts +++ b/src/modules/clicker/clicker.controller.ts @@ -5,6 +5,7 @@ import { CommonService } from '../common/common.service'; import { BlockId } from 'src/common/utils/constants'; import { ResponseDTO } from 'src/common/dto/response.dto'; import { SkillPayload } from 'src/common/interfaces/request/skillPayload'; +import { GetReadingRoomDetailDto, GetReadingRoomDto } from './dtos/clicker.dto'; @ApiTags('clicker') @Controller('clicker') @@ -22,9 +23,8 @@ export class ClickerController { } @Post('get/reading-room') - async getReadingRoom(@Body() body: SkillPayload): Promise { - const { clientExtra } = body.action; - const campusId = clientExtra['campusId']; + async getReadingRoom(@Body() body: GetReadingRoomDto): Promise { + const { campusId } = body; const blockId = BlockId.READING_ROOM_DETAIL; const template = await this.clickerService.getReadingRoomListCard( campusId, @@ -34,9 +34,8 @@ export class ClickerController { } @Post('get/reading-room-detail') - async getReadingRoomDetail(@Body() body: SkillPayload): Promise { - const { clientExtra } = body.action; - const readingRoomId = clientExtra['readingRoomId']; + async getReadingRoomDetail(@Body() body: GetReadingRoomDetailDto): Promise { + const { readingRoomId } = body const template = await this.clickerService.getReadingRoomDetailCard( readingRoomId, ); diff --git a/src/modules/clicker/dtos/clicker.dto.ts b/src/modules/clicker/dtos/clicker.dto.ts new file mode 100644 index 0000000..cda62ad --- /dev/null +++ b/src/modules/clicker/dtos/clicker.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from "@nestjs/swagger" +import { Transform } from "class-transformer" +import { IsNumber } from "class-validator" + +export class GetReadingRoomDto { + @IsNumber() + @Transform(({ obj }) => obj.action.clientExtra?.campusId) + @ApiProperty({ + description: '캠퍼스 ID', + default: 1 + }) + campusId: number +} + +export class GetReadingRoomDetailDto { + @IsNumber() + @Transform(({ obj }) => obj.action.clientExtra?.readingRoomId) + @ApiProperty({ + description: '열람실 ID', + default: 1 + }) + readingRoomId: number +} \ No newline at end of file diff --git a/src/modules/common/common.service.ts b/src/modules/common/common.service.ts index 9f71e8f..71f4813 100644 --- a/src/modules/common/common.service.ts +++ b/src/modules/common/common.service.ts @@ -46,7 +46,7 @@ export class CommonService { async getCollegeListCard( campusId: number, - page: number = 1, + page: number, blockId: string, ): Promise { const [collegeEntities, total] = await this.collegeRepository.findAll(page); diff --git a/src/modules/user/dtos/user.dto.ts b/src/modules/user/dtos/user.dto.ts index a23a300..9d4312b 100644 --- a/src/modules/user/dtos/user.dto.ts +++ b/src/modules/user/dtos/user.dto.ts @@ -1,74 +1,73 @@ import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsString, IsNumber } from 'class-validator'; +import { IsString, IsNumber, IsOptional } from 'class-validator'; export class GetCollegeDto { @IsNumber() - @Transform(({ obj }) => obj.action.clientExtra?.campusId) - @ApiProperty({ - description: '캠퍼스 ID' - }) campusId: number; + @IsOptional() + @Transform(({ value, obj }) => { + console.log('value:', value); + console.log('obj:', obj); + return value ?? obj?.action?.clientExtra?.page; + }) @IsNumber() - @Transform(({ obj }) => obj.action.clientExtra?.page) @ApiProperty({ - description: '페이지 번호' + description: '페이지 번호', + default: 1 }) - page: number; + page?: number = 1; } export class GetDepartmentDto { @IsNumber() @Transform(({ obj }) => obj.action.clientExtra?.campusId) @ApiProperty({ - description: '캠퍼스 ID' + description: '캠퍼스 ID', + default: 1 }) campusId: number; @IsNumber() @Transform(({ obj }) => obj.action.clientExtra?.collegeId) @ApiProperty({ - description: '단과대학 ID' + description: '단과대학 ID', + default: 1 }) collegeId: number; @IsNumber() @Transform(({ obj }) => obj.action.clientExtra?.page) @ApiProperty({ - description: '페이지 번호' + description: '페이지 번호', + default: 1 }) page: number; } export class GetProfileDto { @IsString() - @Transform(({ obj }) => obj.userRequest.user?.id) - @ApiProperty({ - description: '사용자 ID' - }) userId: string; } export class UpdateDepartmentDto { @IsString() - @Transform(({ obj }) => obj.userRequest.user?.id) - @ApiProperty({ - description: '사용자 ID' - }) userId: string; @IsNumber() @Transform(({ obj }) => obj.action.clientExtra?.campusId) @ApiProperty({ - description: '캠퍼스 ID' + description: '캠퍼스 ID', + default: 1 }) campusId: number; @IsNumber() @Transform(({ obj }) => obj.action.clientExtra?.departmentId) @ApiProperty({ - description: '학과 ID' + description: '학과 ID', + default: 1 }) departmentId: number; } \ No newline at end of file diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index 8150624..391299d 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common'; import { UserService } from './user.service'; import { ResponseDTO } from 'src/common/dto/response.dto'; -import { ApiSecurity, ApiTags } from '@nestjs/swagger'; +import { ApiTags } from '@nestjs/swagger'; import { CommonService } from '../common/common.service'; import { BlockId } from 'src/common/utils/constants'; import { GetCollegeDto, GetDepartmentDto, GetProfileDto, UpdateDepartmentDto } from './dtos/user.dto'; @@ -24,6 +24,7 @@ export class UserController { @Post('get/college') async getCollege(@Body() body: GetCollegeDto): Promise { const { campusId, page } = body; + console.log(campusId, page); const blockId = BlockId.DEPARTMENT_LIST; const template = await this.commonService.getCollegeListCard( campusId, From 4b7ed289c90a90af26e5910758f3ffbe7d9e6c1c Mon Sep 17 00:00:00 2001 From: Jang-DongHo Date: Wed, 19 Mar 2025 17:07:39 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[Refactor]=20user=20=EC=BB=A8=ED=8A=B8?= =?UTF-8?q?=EB=A1=A4=EB=9F=AC=20dto=201=EC=B0=A8=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/auth/auth.guard.ts | 2 +- .../decorators/api-skill-body.decorator.ts | 22 +++++++++ src/common/dto/request.dto.ts | 42 ----------------- src/common/dto/request/action/action.dto.ts | 12 +++++ src/common/dto/request/action/index.ts | 1 + src/common/dto/request/index.ts | 2 + src/common/dto/request/skill-payload.dto.ts | 11 +++++ src/common/dto/request/user/index.ts | 1 + src/common/dto/request/user/user.dto.ts | 16 +++++++ src/common/dto/response.dto.ts | 13 ++---- src/instrument.ts | 2 +- src/main.ts | 3 +- src/modules/app/app.module.ts | 4 -- src/modules/clicker/clicker.controller.ts | 1 - src/modules/user/dtos/user.dto.ts | 46 +++++-------------- src/modules/user/user.controller.ts | 31 ++++++++----- 16 files changed, 105 insertions(+), 104 deletions(-) create mode 100644 src/common/decorators/api-skill-body.decorator.ts delete mode 100644 src/common/dto/request.dto.ts create mode 100644 src/common/dto/request/action/action.dto.ts create mode 100644 src/common/dto/request/action/index.ts create mode 100644 src/common/dto/request/index.ts create mode 100644 src/common/dto/request/skill-payload.dto.ts create mode 100644 src/common/dto/request/user/index.ts create mode 100644 src/common/dto/request/user/user.dto.ts diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts index 2144621..165bfa0 100644 --- a/src/auth/auth.guard.ts +++ b/src/auth/auth.guard.ts @@ -7,7 +7,7 @@ export class AuthGuard implements CanActivate { const request = context.switchToHttp().getRequest(); const userId = request.headers['x-user-id'] || request.body.userRequest.user?.id; if (userId) { - request.body = { ...request.body, userId }; // 기존 body에 userId 강제 삽입 + request['userId'] = userId; } return !!userId; diff --git a/src/common/decorators/api-skill-body.decorator.ts b/src/common/decorators/api-skill-body.decorator.ts new file mode 100644 index 0000000..ea4577e --- /dev/null +++ b/src/common/decorators/api-skill-body.decorator.ts @@ -0,0 +1,22 @@ +import { applyDecorators } from '@nestjs/common'; +import { ApiBody, ApiExtraModels, getSchemaPath } from '@nestjs/swagger'; +import { Type } from '@nestjs/common'; + +export function ApiSkillBody(dto: Type) { + return applyDecorators( + ApiExtraModels(dto), + ApiBody({ + schema: { + type: 'object', + properties: { + action: { + type: 'object', + properties: { + clientExtra: { $ref: getSchemaPath(dto) }, + }, + }, + }, + }, + }), + ); +} \ No newline at end of file diff --git a/src/common/dto/request.dto.ts b/src/common/dto/request.dto.ts deleted file mode 100644 index 60a4124..0000000 --- a/src/common/dto/request.dto.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { SkillPayload } from "src/common/interfaces/request/skillPayload"; -import { Bot } from "../interfaces/request/fields/bot"; -import { Intent } from "../interfaces/request/fields/intent"; -import { UserRequest } from "../interfaces/request/fields/userRequest"; -import { ApiProperty } from "@nestjs/swagger"; -import { Action } from "../interfaces/request/fields/action"; - -export class RequestDTO implements SkillPayload { - @ApiProperty({ - description: "봇 정보", - required: false, - }) - bot: Bot; - - @ApiProperty({ - description: "인텐트 정보", - required: false, - }) - intent: Intent; - - @ApiProperty({ - description: "액션 정보", - required: false, - }) - action: Action; - - @ApiProperty({ - description: "유저 요청 정보", - example: { - user: { - id: "74f7e7ab2bf19e63bb1ec845b760631259d7615440a2d3db7b344ac48ed1bbcde5" - } - } - }) - userRequest: UserRequest; - - @ApiProperty({ - description: "문맥 정보", - required: false, - }) - contexts?: any[]; -} \ No newline at end of file diff --git a/src/common/dto/request/action/action.dto.ts b/src/common/dto/request/action/action.dto.ts new file mode 100644 index 0000000..fb82785 --- /dev/null +++ b/src/common/dto/request/action/action.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { Allow, ValidateNested } from "class-validator"; + +export class ClientExtraDto {} + +export class ActionDto { + @ApiProperty({ description: '추가 클라이언트 정보' }) + @ValidateNested() + @Type(() => ClientExtraDto) + clientExtra: ClientExtraDto; +} \ No newline at end of file diff --git a/src/common/dto/request/action/index.ts b/src/common/dto/request/action/index.ts new file mode 100644 index 0000000..928c21e --- /dev/null +++ b/src/common/dto/request/action/index.ts @@ -0,0 +1 @@ +export * from './action.dto' \ No newline at end of file diff --git a/src/common/dto/request/index.ts b/src/common/dto/request/index.ts new file mode 100644 index 0000000..e959dc6 --- /dev/null +++ b/src/common/dto/request/index.ts @@ -0,0 +1,2 @@ +export * from './action'; +export * from './user'; \ No newline at end of file diff --git a/src/common/dto/request/skill-payload.dto.ts b/src/common/dto/request/skill-payload.dto.ts new file mode 100644 index 0000000..b57ec4e --- /dev/null +++ b/src/common/dto/request/skill-payload.dto.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { ValidateNested } from "class-validator"; +import { ActionDto } from "./action"; + +export class SkillPayloadDto { + @ApiProperty({ description: 'Action 정보' }) + @ValidateNested() + @Type(() => ActionDto) + action: ActionDto; +} \ No newline at end of file diff --git a/src/common/dto/request/user/index.ts b/src/common/dto/request/user/index.ts new file mode 100644 index 0000000..964fd11 --- /dev/null +++ b/src/common/dto/request/user/index.ts @@ -0,0 +1 @@ +export * from './user.dto'; \ No newline at end of file diff --git a/src/common/dto/request/user/user.dto.ts b/src/common/dto/request/user/user.dto.ts new file mode 100644 index 0000000..b1faa60 --- /dev/null +++ b/src/common/dto/request/user/user.dto.ts @@ -0,0 +1,16 @@ +import { ApiHideProperty, ApiProperty } from "@nestjs/swagger"; +import { Type } from "class-transformer"; +import { IsString, ValidateNested } from "class-validator"; + +export class UserDto { + @ApiProperty({ description: '사용자 ID' }) + @IsString() + id: string; +} + +export class UserRequestDto { + @ApiHideProperty() + @ValidateNested() + @Type(() => UserDto) + user: UserDto; +} \ No newline at end of file diff --git a/src/common/dto/response.dto.ts b/src/common/dto/response.dto.ts index 1515038..e3835c7 100644 --- a/src/common/dto/response.dto.ts +++ b/src/common/dto/response.dto.ts @@ -1,24 +1,21 @@ import { Expose, Exclude } from 'class-transformer'; -import { ContextControl } from 'src/common/interfaces/response/fields/context'; -import { SkillResponse } from 'src/common/interfaces/response/response'; -import { SkillTemplate } from 'src/common/interfaces/response/fields/template'; -export class ResponseDTO implements SkillResponse { +export class ResponseDTO { @Exclude() version: string; @Expose() - template: SkillTemplate; + template: any; @Expose() - context?: ContextControl; + context?: any; @Expose() data?: Map; constructor( - template: SkillTemplate, - context?: ContextControl, + template: any, + context?: any, data?: Map, ) { this.version = '2.0'; // 버전은 고정값 diff --git a/src/instrument.ts b/src/instrument.ts index 97b4ccc..b24d6ec 100644 --- a/src/instrument.ts +++ b/src/instrument.ts @@ -27,7 +27,7 @@ Sentry.init({ enableTracing: process.env.NODE_ENV !== 'development', // Enable debug mode to log event submission - debug: process.env.NODE_ENV === 'development', + // debug: process.env.NODE_ENV === 'development', // Advanced, optional: Called for message and error events beforeSend(event) { diff --git a/src/main.ts b/src/main.ts index 79281a4..d29ab87 100644 --- a/src/main.ts +++ b/src/main.ts @@ -32,7 +32,7 @@ async function bootstrap() { whitelist: false, forbidNonWhitelisted: false, transform: true, - transformOptions: { enableImplicitConversion: true } + //transformOptions: { enableImplicitConversion: true } })) @@ -58,6 +58,7 @@ async function bootstrap() { { swaggerOptions: { persistAuthorization: true, // 새로고침해도 인증 정보 유지 + defaultModelsExpandDepth: -1 } } ); diff --git a/src/modules/app/app.module.ts b/src/modules/app/app.module.ts index 9750a22..c05e80a 100644 --- a/src/modules/app/app.module.ts +++ b/src/modules/app/app.module.ts @@ -37,10 +37,6 @@ import { AuthModule } from 'src/auth/auth.module'; provide: APP_FILTER, useClass: SentryGlobalFilter, }, - { - provide: APP_PIPE, - useClass: ValidationPipe, - }, AppService, ], }) diff --git a/src/modules/clicker/clicker.controller.ts b/src/modules/clicker/clicker.controller.ts index ae5cea2..f28e29a 100644 --- a/src/modules/clicker/clicker.controller.ts +++ b/src/modules/clicker/clicker.controller.ts @@ -4,7 +4,6 @@ import { ApiBody, ApiTags } from '@nestjs/swagger'; import { CommonService } from '../common/common.service'; import { BlockId } from 'src/common/utils/constants'; import { ResponseDTO } from 'src/common/dto/response.dto'; -import { SkillPayload } from 'src/common/interfaces/request/skillPayload'; import { GetReadingRoomDetailDto, GetReadingRoomDto } from './dtos/clicker.dto'; @ApiTags('clicker') diff --git a/src/modules/user/dtos/user.dto.ts b/src/modules/user/dtos/user.dto.ts index 9d4312b..07556df 100644 --- a/src/modules/user/dtos/user.dto.ts +++ b/src/modules/user/dtos/user.dto.ts @@ -1,62 +1,41 @@ -import { ApiProperty } from '@nestjs/swagger'; +import { ApiProperty, PickType } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; import { IsString, IsNumber, IsOptional } from 'class-validator'; +import { ClientExtraDto } from 'src/common/dto/request'; -export class GetCollegeDto { +export class GetCollegeDto extends ClientExtraDto { + @ApiProperty({ description: '캠퍼스 ID', example: 1 }) @IsNumber() campusId: number; @IsOptional() - @Transform(({ value, obj }) => { - console.log('value:', value); - console.log('obj:', obj); - return value ?? obj?.action?.clientExtra?.page; - }) + @ApiProperty({ description: '페이지 번호', example: 1 }) @IsNumber() - @ApiProperty({ - description: '페이지 번호', - default: 1 - }) page?: number = 1; } export class GetDepartmentDto { + @IsOptional() + @ApiProperty({ description: '캠퍼스 ID', example: 1 }) @IsNumber() - @Transform(({ obj }) => obj.action.clientExtra?.campusId) - @ApiProperty({ - description: '캠퍼스 ID', - default: 1 - }) campusId: number; + @IsOptional() @IsNumber() - @Transform(({ obj }) => obj.action.clientExtra?.collegeId) @ApiProperty({ description: '단과대학 ID', default: 1 }) collegeId: number; - + + @IsOptional() + @ApiProperty({ description: '페이지 번호', example: 1 }) @IsNumber() - @Transform(({ obj }) => obj.action.clientExtra?.page) - @ApiProperty({ - description: '페이지 번호', - default: 1 - }) - page: number; -} - -export class GetProfileDto { - @IsString() - userId: string; + page?: number = 1; } export class UpdateDepartmentDto { - @IsString() - userId: string; - @IsNumber() - @Transform(({ obj }) => obj.action.clientExtra?.campusId) @ApiProperty({ description: '캠퍼스 ID', default: 1 @@ -64,7 +43,6 @@ export class UpdateDepartmentDto { campusId: number; @IsNumber() - @Transform(({ obj }) => obj.action.clientExtra?.departmentId) @ApiProperty({ description: '학과 ID', default: 1 diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts index 391299d..b60d41c 100644 --- a/src/modules/user/user.controller.ts +++ b/src/modules/user/user.controller.ts @@ -1,10 +1,13 @@ import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common'; import { UserService } from './user.service'; import { ResponseDTO } from 'src/common/dto/response.dto'; -import { ApiTags } from '@nestjs/swagger'; +import { ApiHideProperty, ApiTags } from '@nestjs/swagger'; import { CommonService } from '../common/common.service'; import { BlockId } from 'src/common/utils/constants'; -import { GetCollegeDto, GetDepartmentDto, GetProfileDto, UpdateDepartmentDto } from './dtos/user.dto'; +import { GetCollegeDto, GetDepartmentDto, UpdateDepartmentDto } from './dtos/user.dto'; +import { SkillPayloadDto } from 'src/common/dto/request/skill-payload.dto'; +import { plainToInstance } from 'class-transformer'; +import { ApiSkillBody } from 'src/common/decorators/api-skill-body.decorator'; @ApiTags('user') @Controller('user') @@ -20,23 +23,25 @@ export class UserController { const template = await this.commonService.getCampusListCard(blockId); return new ResponseDTO(template); } - + @Post('get/college') - async getCollege(@Body() body: GetCollegeDto): Promise { - const { campusId, page } = body; - console.log(campusId, page); + @ApiSkillBody(GetCollegeDto) + async getCollege(@Body() body: SkillPayloadDto): Promise { + const { campusId, page } = plainToInstance(GetCollegeDto, body.action.clientExtra); const blockId = BlockId.DEPARTMENT_LIST; const template = await this.commonService.getCollegeListCard( campusId, page, blockId, ); + console.log(template.quickReplies); return new ResponseDTO(template); } @Post('get/department') - async getDepartment(@Body() body: GetDepartmentDto): Promise { - const { campusId, collegeId, page } = body; + @ApiSkillBody(GetDepartmentDto) + async getDepartment(@Body() body: SkillPayloadDto): Promise { + const { campusId, collegeId, page } = plainToInstance(GetDepartmentDto, body.action.clientExtra); const blockId = BlockId.UPDATE_DEPARTMENT; const template = await this.commonService.getDepartmentListCard( campusId, @@ -48,8 +53,10 @@ export class UserController { } @Post('update/department') - async upsertUserDepartment(@Body() body: UpdateDepartmentDto): Promise { - const { userId, campusId, departmentId } = body; + @ApiSkillBody(UpdateDepartmentDto) + async upsertUserDepartment(@Req() req: Request, @Body() body: SkillPayloadDto): Promise { + const userId = req['userId']; + const { campusId, departmentId } = plainToInstance(UpdateDepartmentDto, body.action.clientExtra); const template = await this.userService.upsertUserDepartment( userId, campusId, @@ -59,8 +66,8 @@ export class UserController { } @Post('get/profile') - async getUserProfile(@Body() body: GetProfileDto): Promise { - const { userId } = body; + async getUserProfile(@Req() req: Request): Promise { + const userId = req['userId']; const template = await this.userService.getUserProfile(userId); return new ResponseDTO(template); } From 3b9989a587bf448504690f7abfaab276331e93cf Mon Sep 17 00:00:00 2001 From: Jang-DongHo Date: Wed, 19 Mar 2025 21:31:16 +0900 Subject: [PATCH 4/5] =?UTF-8?q?refactor:=20=EC=BB=A8=EB=B2=A4=EC=85=98=20?= =?UTF-8?q?=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.ts | 10 +-- src/modules/app/app.controller.ts | 2 +- src/modules/app/app.module.ts | 16 ++-- src/{ => modules}/auth/auth.guard.ts | 0 src/{ => modules}/auth/auth.module.ts | 0 src/modules/clicker/clicker.module.ts | 19 ----- .../clicker/repository/clicker.repository.ts | 21 ----- .../clickers.controller.spec.ts} | 4 +- .../clickers.controller.ts} | 22 +++--- src/modules/clickers/clickers.module.ts | 19 +++++ .../clickers.service.spec.ts} | 2 +- .../clickers.service.ts} | 14 ++-- .../dtos/clickers.dto.ts} | 0 .../entities/clickers.entity.ts} | 2 +- .../repositories/clickers.repository.ts | 21 +++++ src/modules/common/common.module.ts | 20 ++--- src/modules/common/common.service.ts | 52 ++++++------- .../decorators/api-skill-body.decorator.ts | 0 .../common/dtos}/request/action/action.dto.ts | 0 .../common/dtos}/request/action/index.ts | 0 .../common/dtos}/request/index.ts | 0 .../common/dtos}/request/skill-payload.dto.ts | 0 .../common/dtos}/request/user/index.ts | 0 .../common/dtos}/request/user/user.dto.ts | 0 .../common/dtos}/response.dto.ts | 0 src/modules/common/entities/campus.entity.ts | 2 +- src/modules/common/entities/college.entity.ts | 2 +- .../common/entities/department.entity.ts | 8 +- .../common/filters}/http-exception.filter.ts | 0 .../common/filters}/sentry.filter.ts | 0 .../interfaces/request/fields/action.ts | 0 .../common/interfaces/request/fields/bot.ts | 0 .../common/interfaces/request/fields/etc.ts | 0 .../interfaces/request/fields/intent.ts | 0 .../interfaces/request/fields/userRequest.ts | 0 .../common/interfaces/request/skillPayload.ts | 0 .../interfaces/response/fields/component.ts | 0 .../interfaces/response/fields/context.ts | 0 .../common/interfaces/response/fields/etc.ts | 0 .../interfaces/response/fields/template.ts | 0 .../common/interfaces/response/response.ts | 0 .../campuses.repository.ts} | 14 ++-- .../colleges.repository.ts} | 14 ++-- .../departments.repository.ts} | 14 ++-- src/{ => modules}/common/utils/component.ts | 0 src/{ => modules}/common/utils/constants.ts | 0 src/{ => modules}/common/utils/enviornment.ts | 0 .../common/utils/utils.module.ts | 0 .../common/utils/utils.service.ts | 0 .../interceptors/kakao.interceptor.ts | 0 .../interceptors/sentry.interceptor.ts | 0 src/modules/user/dtos/user.dto.ts | 51 ------------- src/modules/user/user.controller.ts | 74 ------------------ src/modules/user/user.module.ts | 19 ----- .../dtos/request/list-college-request.dto.ts | 15 ++++ .../request/list-department-request.dto.ts | 23 ++++++ .../request/upsert-department-request.dto.ts | 21 +++++ .../entities/users.entity.ts} | 14 ++-- .../repository/users.repository.ts} | 18 ++--- .../users.controller.spec.ts} | 4 +- src/modules/users/users.controller.ts | 76 +++++++++++++++++++ src/modules/users/users.module.ts | 19 +++++ .../users.service.spec.ts} | 2 +- .../users.service.ts} | 24 +++--- 64 files changed, 324 insertions(+), 314 deletions(-) rename src/{ => modules}/auth/auth.guard.ts (100%) rename src/{ => modules}/auth/auth.module.ts (100%) delete mode 100644 src/modules/clicker/clicker.module.ts delete mode 100644 src/modules/clicker/repository/clicker.repository.ts rename src/modules/{clicker/clicker.controller.spec.ts => clickers/clickers.controller.spec.ts} (80%) rename src/modules/{clicker/clicker.controller.ts => clickers/clickers.controller.ts} (65%) create mode 100644 src/modules/clickers/clickers.module.ts rename src/modules/{clicker/clicker.service.spec.ts => clickers/clickers.service.spec.ts} (88%) rename src/modules/{clicker/clicker.service.ts => clickers/clickers.service.ts} (77%) rename src/modules/{clicker/dtos/clicker.dto.ts => clickers/dtos/clickers.dto.ts} (100%) rename src/modules/{clicker/entities/clicker.entity.ts => clickers/entities/clickers.entity.ts} (93%) create mode 100644 src/modules/clickers/repositories/clickers.repository.ts rename src/{ => modules}/common/decorators/api-skill-body.decorator.ts (100%) rename src/{common/dto => modules/common/dtos}/request/action/action.dto.ts (100%) rename src/{common/dto => modules/common/dtos}/request/action/index.ts (100%) rename src/{common/dto => modules/common/dtos}/request/index.ts (100%) rename src/{common/dto => modules/common/dtos}/request/skill-payload.dto.ts (100%) rename src/{common/dto => modules/common/dtos}/request/user/index.ts (100%) rename src/{common/dto => modules/common/dtos}/request/user/user.dto.ts (100%) rename src/{common/dto => modules/common/dtos}/response.dto.ts (100%) rename src/{common/filter => modules/common/filters}/http-exception.filter.ts (100%) rename src/{common/filter => modules/common/filters}/sentry.filter.ts (100%) rename src/{ => modules}/common/interfaces/request/fields/action.ts (100%) rename src/{ => modules}/common/interfaces/request/fields/bot.ts (100%) rename src/{ => modules}/common/interfaces/request/fields/etc.ts (100%) rename src/{ => modules}/common/interfaces/request/fields/intent.ts (100%) rename src/{ => modules}/common/interfaces/request/fields/userRequest.ts (100%) rename src/{ => modules}/common/interfaces/request/skillPayload.ts (100%) rename src/{ => modules}/common/interfaces/response/fields/component.ts (100%) rename src/{ => modules}/common/interfaces/response/fields/context.ts (100%) rename src/{ => modules}/common/interfaces/response/fields/etc.ts (100%) rename src/{ => modules}/common/interfaces/response/fields/template.ts (100%) rename src/{ => modules}/common/interfaces/response/response.ts (100%) rename src/modules/common/{repository/campus.repository.ts => repositories/campuses.repository.ts} (53%) rename src/modules/common/{repository/college.repository.ts => repositories/colleges.repository.ts} (50%) rename src/modules/common/{repository/department.repository.ts => repositories/departments.repository.ts} (56%) rename src/{ => modules}/common/utils/component.ts (100%) rename src/{ => modules}/common/utils/constants.ts (100%) rename src/{ => modules}/common/utils/enviornment.ts (100%) rename src/{ => modules}/common/utils/utils.module.ts (100%) rename src/{ => modules}/common/utils/utils.service.ts (100%) rename src/{ => modules}/interceptors/kakao.interceptor.ts (100%) rename src/{ => modules}/interceptors/sentry.interceptor.ts (100%) delete mode 100644 src/modules/user/dtos/user.dto.ts delete mode 100644 src/modules/user/user.controller.ts delete mode 100644 src/modules/user/user.module.ts create mode 100644 src/modules/users/dtos/request/list-college-request.dto.ts create mode 100644 src/modules/users/dtos/request/list-department-request.dto.ts create mode 100644 src/modules/users/dtos/request/upsert-department-request.dto.ts rename src/modules/{user/entities/user.entity.ts => users/entities/users.entity.ts} (51%) rename src/modules/{user/repository/user.repository.ts => users/repository/users.repository.ts} (66%) rename src/modules/{user/user.controller.spec.ts => users/users.controller.spec.ts} (82%) create mode 100644 src/modules/users/users.controller.ts create mode 100644 src/modules/users/users.module.ts rename src/modules/{user/user.service.spec.ts => users/users.service.spec.ts} (89%) rename src/modules/{user/user.service.ts => users/users.service.ts} (58%) diff --git a/src/main.ts b/src/main.ts index d29ab87..faa1ac8 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,13 +6,13 @@ import * as process from 'process'; import { NestFactory, HttpAdapterHost } from '@nestjs/core'; import { AppModule } from './modules/app/app.module'; import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; -import { KakaoInterceptor } from './interceptors/kakao.interceptor'; -import { ResponseDTO } from './common/dto/response.dto'; -import { SentryInterceptor } from './interceptors/sentry.interceptor.js'; -import { SentryFilter } from './common/filter/sentry.filter'; +import { KakaoInterceptor } from './modules/interceptors/kakao.interceptor'; +import { ResponseDTO } from './modules/common/dtos/response.dto'; +import { SentryInterceptor } from './modules/interceptors/sentry.interceptor.js'; +import { SentryFilter } from './modules/common/filters/sentry.filter'; import { initializeTransactionalContext } from 'typeorm-transactional'; import { ValidationPipe } from '@nestjs/common'; -import { HttpExceptionFilter } from './common/filter/http-exception.filter'; +import { HttpExceptionFilter } from './modules/common/filters/http-exception.filter'; async function bootstrap() { initializeTransactionalContext(); diff --git a/src/modules/app/app.controller.ts b/src/modules/app/app.controller.ts index 69df980..488ce18 100644 --- a/src/modules/app/app.controller.ts +++ b/src/modules/app/app.controller.ts @@ -1,6 +1,6 @@ import { Controller } from '@nestjs/common'; import { AppService } from './app.service'; -import { ResponseDTO } from '../../common/dto/response.dto'; +import { ResponseDTO } from '../common/dtos/response.dto'; @Controller() export class AppController { diff --git a/src/modules/app/app.module.ts b/src/modules/app/app.module.ts index c05e80a..1ee935f 100644 --- a/src/modules/app/app.module.ts +++ b/src/modules/app/app.module.ts @@ -2,17 +2,17 @@ import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup'; import { Module, ValidationPipe } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; -import { UtilsModule } from 'src/common/utils/utils.module'; +import { UtilsModule } from 'src/modules/common/utils/utils.module'; import { ConfigModule } from '@nestjs/config'; -import { UserModule } from '../user/user.module'; +import { UsersModule } from '../users/users.module'; import { SupabaseModule } from '../supabase/supabase.module'; -import { validationSchema } from 'src/common/utils/enviornment'; -import { APP_FILTER, APP_PIPE } from '@nestjs/core'; +import { validationSchema } from 'src/modules/common/utils/enviornment'; +import { APP_FILTER } from '@nestjs/core'; import { CommonModule } from '../common/common.module'; -import { ClickerModule } from '../clicker/clicker.module'; +import { ClickersModule } from '../clickers/clickers.module'; import { ScheduleModule } from '@nestjs/schedule'; import { HttpModule } from '@nestjs/axios'; -import { AuthModule } from 'src/auth/auth.module'; +import { AuthModule } from 'src/modules/auth/auth.module'; @Module({ imports: [ @@ -22,8 +22,8 @@ import { AuthModule } from 'src/auth/auth.module'; SupabaseModule, UtilsModule, CommonModule, - UserModule, - ClickerModule, + UsersModule, + ClickersModule, AuthModule, ConfigModule.forRoot({ isGlobal: true, diff --git a/src/auth/auth.guard.ts b/src/modules/auth/auth.guard.ts similarity index 100% rename from src/auth/auth.guard.ts rename to src/modules/auth/auth.guard.ts diff --git a/src/auth/auth.module.ts b/src/modules/auth/auth.module.ts similarity index 100% rename from src/auth/auth.module.ts rename to src/modules/auth/auth.module.ts diff --git a/src/modules/clicker/clicker.module.ts b/src/modules/clicker/clicker.module.ts deleted file mode 100644 index d267d1b..0000000 --- a/src/modules/clicker/clicker.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ClickerService } from './clicker.service'; -import { ClickerController } from './clicker.controller'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { SupabaseModule } from '../supabase/supabase.module'; -import { CommonModule } from '../common/common.module'; -import { ClickerRepository } from './repository/clicker.repository'; -import { ClickerEntity } from './entities/clicker.entity'; - -@Module({ - imports: [ - SupabaseModule, - CommonModule, - TypeOrmModule.forFeature([ClickerEntity]), - ], - controllers: [ClickerController], - providers: [ClickerService, ClickerRepository], -}) -export class ClickerModule {} diff --git a/src/modules/clicker/repository/clicker.repository.ts b/src/modules/clicker/repository/clicker.repository.ts deleted file mode 100644 index c6cc947..0000000 --- a/src/modules/clicker/repository/clicker.repository.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { In, Repository } from 'typeorm'; -import { ClickerEntity } from '../entities/clicker.entity'; - -@Injectable() -export class ClickerRepository { - constructor( - @InjectRepository(ClickerEntity) - private readonly clickerRepository: Repository, // Inject the repository here - ) {} - - async findByCampusId(campusId: number): Promise { - return await this.clickerRepository.find({ - where: { - campusId: campusId, - isActive: true, - }, - }); - } -} diff --git a/src/modules/clicker/clicker.controller.spec.ts b/src/modules/clickers/clickers.controller.spec.ts similarity index 80% rename from src/modules/clicker/clicker.controller.spec.ts rename to src/modules/clickers/clickers.controller.spec.ts index 8c7307e..90c690f 100644 --- a/src/modules/clicker/clicker.controller.spec.ts +++ b/src/modules/clickers/clickers.controller.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { ClickerController } from './clicker.controller'; -import { ClickerService } from './clicker.service'; +import { ClickerController } from './clickers.controller'; +import { ClickerService } from './clickers.service'; describe('ClickerController', () => { let controller: ClickerController; diff --git a/src/modules/clicker/clicker.controller.ts b/src/modules/clickers/clickers.controller.ts similarity index 65% rename from src/modules/clicker/clicker.controller.ts rename to src/modules/clickers/clickers.controller.ts index f28e29a..d907b17 100644 --- a/src/modules/clicker/clicker.controller.ts +++ b/src/modules/clickers/clickers.controller.ts @@ -1,16 +1,16 @@ import { Body, Controller, Post } from '@nestjs/common'; -import { ClickerService } from './clicker.service'; -import { ApiBody, ApiTags } from '@nestjs/swagger'; +import { ClickersService } from './clickers.service'; +import { ApiTags } from '@nestjs/swagger'; import { CommonService } from '../common/common.service'; -import { BlockId } from 'src/common/utils/constants'; -import { ResponseDTO } from 'src/common/dto/response.dto'; -import { GetReadingRoomDetailDto, GetReadingRoomDto } from './dtos/clicker.dto'; +import { BlockId } from 'src/modules/common/utils/constants'; +import { ResponseDTO } from 'src/modules/common/dtos/response.dto'; +import { GetReadingRoomDetailDto, GetReadingRoomDto } from './dtos/clickers.dto'; -@ApiTags('clicker') -@Controller('clicker') -export class ClickerController { +@ApiTags('clickers') +@Controller('clickers') +export class ClickersController { constructor( - private readonly clickerService: ClickerService, + private readonly clickersService: ClickersService, private readonly commonService: CommonService, ) {} @@ -25,7 +25,7 @@ export class ClickerController { async getReadingRoom(@Body() body: GetReadingRoomDto): Promise { const { campusId } = body; const blockId = BlockId.READING_ROOM_DETAIL; - const template = await this.clickerService.getReadingRoomListCard( + const template = await this.clickersService.getReadingRoomListCard( campusId, blockId, ); @@ -35,7 +35,7 @@ export class ClickerController { @Post('get/reading-room-detail') async getReadingRoomDetail(@Body() body: GetReadingRoomDetailDto): Promise { const { readingRoomId } = body - const template = await this.clickerService.getReadingRoomDetailCard( + const template = await this.clickersService.getReadingRoomDetailCard( readingRoomId, ); return new ResponseDTO(template); diff --git a/src/modules/clickers/clickers.module.ts b/src/modules/clickers/clickers.module.ts new file mode 100644 index 0000000..0762ebd --- /dev/null +++ b/src/modules/clickers/clickers.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; +import { ClickersService } from './clickers.service'; +import { ClickersController } from './clickers.controller'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { SupabaseModule } from '../supabase/supabase.module'; +import { CommonModule } from '../common/common.module'; +import { ClickersRepository } from './repositories/clickers.repository'; +import { Clicker } from './entities/clickers.entity'; + +@Module({ + imports: [ + SupabaseModule, + CommonModule, + TypeOrmModule.forFeature([Clicker]), + ], + controllers: [ClickersController], + providers: [ClickersService, ClickersRepository], +}) +export class ClickersModule {} diff --git a/src/modules/clicker/clicker.service.spec.ts b/src/modules/clickers/clickers.service.spec.ts similarity index 88% rename from src/modules/clicker/clicker.service.spec.ts rename to src/modules/clickers/clickers.service.spec.ts index 48a65b0..7545eae 100644 --- a/src/modules/clicker/clicker.service.spec.ts +++ b/src/modules/clickers/clickers.service.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { ClickerService } from './clicker.service'; +import { ClickerService } from './clickers.service'; describe('ClickerService', () => { let service: ClickerService; diff --git a/src/modules/clicker/clicker.service.ts b/src/modules/clickers/clickers.service.ts similarity index 77% rename from src/modules/clicker/clicker.service.ts rename to src/modules/clickers/clickers.service.ts index 064e810..78b6c68 100644 --- a/src/modules/clicker/clicker.service.ts +++ b/src/modules/clickers/clickers.service.ts @@ -1,16 +1,16 @@ import { Injectable } from '@nestjs/common'; -import { ClickerRepository } from './repository/clicker.repository'; -import { ListItem } from 'src/common/interfaces/response/fields/etc'; -import { ListCard } from 'src/common/interfaces/response/fields/component'; +import { ClickersRepository } from './repositories/clickers.repository'; +import { ListItem } from 'src/modules/common/interfaces/response/fields/etc'; +import { ListCard } from 'src/modules/common/interfaces/response/fields/component'; import { createListCard, createSimpleImage, createSimpleText, -} from 'src/common/utils/component'; +} from 'src/modules/common/utils/component'; @Injectable() -export class ClickerService { - constructor(private readonly clickerRepository: ClickerRepository) {} +export class ClickersService { + constructor(private readonly clickersRepository: ClickersRepository) {} async getReadingRoomListCard( campusId: number, @@ -24,7 +24,7 @@ export class ClickerService { }; } - const readingRoomEntities = await this.clickerRepository.findByCampusId( + const readingRoomEntities = await this.clickersRepository.findByCampusId( campusId, ); diff --git a/src/modules/clicker/dtos/clicker.dto.ts b/src/modules/clickers/dtos/clickers.dto.ts similarity index 100% rename from src/modules/clicker/dtos/clicker.dto.ts rename to src/modules/clickers/dtos/clickers.dto.ts diff --git a/src/modules/clicker/entities/clicker.entity.ts b/src/modules/clickers/entities/clickers.entity.ts similarity index 93% rename from src/modules/clicker/entities/clicker.entity.ts rename to src/modules/clickers/entities/clickers.entity.ts index 15dd7e9..9f83fa4 100644 --- a/src/modules/clicker/entities/clicker.entity.ts +++ b/src/modules/clickers/entities/clickers.entity.ts @@ -1,7 +1,7 @@ import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity('reading_room') -export class ClickerEntity { +export class Clicker { @PrimaryGeneratedColumn() id: number; diff --git a/src/modules/clickers/repositories/clickers.repository.ts b/src/modules/clickers/repositories/clickers.repository.ts new file mode 100644 index 0000000..c88e8e7 --- /dev/null +++ b/src/modules/clickers/repositories/clickers.repository.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { Clicker } from '../entities/clickers.entity'; + +@Injectable() +export class ClickersRepository { + constructor( + @InjectRepository(Clicker) + private readonly clickerRepository: Repository, + ) {} + + async findByCampusId(campusId: number): Promise { + return await this.clickerRepository.find({ + where: { + campusId: campusId, + isActive: true, + }, + }); + } +} diff --git a/src/modules/common/common.module.ts b/src/modules/common/common.module.ts index ec2ede5..07550b7 100644 --- a/src/modules/common/common.module.ts +++ b/src/modules/common/common.module.ts @@ -2,23 +2,23 @@ import { Module } from '@nestjs/common'; import { CommonService } from './common.service'; import { SupabaseModule } from '../supabase/supabase.module'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { CampusEntity } from './entities/campus.entity'; -import { CollegeEntity } from './entities/college.entity'; -import { DepartmentEntity } from './entities/department.entity'; -import { CampusRepository } from './repository/campus.repository'; -import { CollegeRepository } from './repository/college.repository'; -import { DepartmentRepository } from './repository/department.repository'; +import { Campus } from './entities/campus.entity'; +import { College } from './entities/college.entity'; +import { Department } from './entities/department.entity'; +import { CampusesRepository } from './repositories/campuses.repository'; +import { CollegesRepository } from './repositories/colleges.repository'; +import { DepartmentsRepository } from './repositories/departments.repository'; @Module({ imports: [ SupabaseModule, - TypeOrmModule.forFeature([CampusEntity, CollegeEntity, DepartmentEntity]), + TypeOrmModule.forFeature([Campus, College, Department]), ], providers: [ CommonService, - CampusRepository, - CollegeRepository, - DepartmentRepository, + CampusesRepository, + CollegesRepository, + DepartmentsRepository, ], exports: [CommonService], }) diff --git a/src/modules/common/common.service.ts b/src/modules/common/common.service.ts index 71f4813..2d2a950 100644 --- a/src/modules/common/common.service.ts +++ b/src/modules/common/common.service.ts @@ -1,38 +1,38 @@ import { Injectable } from '@nestjs/common'; -import { CampusRepository } from './repository/campus.repository'; -import { CollegeRepository } from './repository/college.repository'; -import { DepartmentRepository } from './repository/department.repository'; -import { SkillTemplate } from 'src/common/interfaces/response/fields/template'; +import { CampusesRepository } from './repositories/campuses.repository'; +import { CollegesRepository } from './repositories/colleges.repository'; +import { DepartmentsRepository } from './repositories/departments.repository'; +import { SkillTemplate } from 'src/modules/common/interfaces/response/fields/template'; import { ApiTags } from '@nestjs/swagger'; -import { Button, ListItem } from 'src/common/interfaces/response/fields/etc'; -import { BlockId, ListCardConfig } from 'src/common/utils/constants'; -import { ListCard } from 'src/common/interfaces/response/fields/component'; -import { createListCard } from 'src/common/utils/component'; +import { ListItem } from 'src/modules/common/interfaces/response/fields/etc'; +import { BlockId, ListCardConfig } from 'src/modules/common/utils/constants'; +import { ListCard } from 'src/modules/common/interfaces/response/fields/component'; +import { createListCard } from 'src/modules/common/utils/component'; @ApiTags('common') @Injectable() export class CommonService { constructor( - private readonly campusRepository: CampusRepository, - private readonly collegeRepository: CollegeRepository, - private readonly departmentRepository: DepartmentRepository, + private readonly campusesRepository: CampusesRepository, + private readonly collegesRepository: CollegesRepository, + private readonly departmentsRepository: DepartmentsRepository, ) {} async getCampusListCard(blockId: string): Promise { - const campusEntities = await this.campusRepository.findAll(); + const campuses = await this.campusesRepository.findAll(); const header: ListItem = { title: '캠퍼스 선택', }; - const items: ListItem[] = campusEntities.map((campusEntity) => { + const items: ListItem[] = campuses.map((campus) => { return { - title: campusEntity.name, - imageUrl: campusEntity.thumbnailUrl, + title: campus.name, + imageUrl: campus.thumbnailUrl, action: 'block', blockId, extra: { - campusId: campusEntity.id, + campusId: campus.id, }, }; }); @@ -49,21 +49,21 @@ export class CommonService { page: number, blockId: string, ): Promise { - const [collegeEntities, total] = await this.collegeRepository.findAll(page); + const [colleges, total] = await this.collegesRepository.findAll(page); const header: ListItem = { title: '단과대학 선택', }; - const items: ListItem[] = collegeEntities.map((collegeEntity) => { + const items: ListItem[] = colleges.map((college) => { return { - title: collegeEntity.name, - imageUrl: collegeEntity.thumbnailUrl, + title: college.name, + imageUrl: college.thumbnailUrl, action: 'block', blockId, extra: { campusId: campusId, - collegeId: collegeEntity.id, + collegeId: college.id, }, }; }); @@ -109,21 +109,21 @@ export class CommonService { page: number = 1, blockId: string, ): Promise { - const [departmentEntities, total] = - await this.departmentRepository.findByCollegeId(collegeId, page); + const [departments, total] = + await this.departmentsRepository.findByCollegeId(collegeId, page); const header: ListItem = { title: '학과 선택', }; - const items: ListItem[] = departmentEntities.map((departmentEntity) => { + const items: ListItem[] = departments.map((department) => { return { - title: departmentEntity.name, + title: department.name, action: 'block', blockId, extra: { campusId, - departmentId: departmentEntity.id, + departmentId: department.id, }, }; }); diff --git a/src/common/decorators/api-skill-body.decorator.ts b/src/modules/common/decorators/api-skill-body.decorator.ts similarity index 100% rename from src/common/decorators/api-skill-body.decorator.ts rename to src/modules/common/decorators/api-skill-body.decorator.ts diff --git a/src/common/dto/request/action/action.dto.ts b/src/modules/common/dtos/request/action/action.dto.ts similarity index 100% rename from src/common/dto/request/action/action.dto.ts rename to src/modules/common/dtos/request/action/action.dto.ts diff --git a/src/common/dto/request/action/index.ts b/src/modules/common/dtos/request/action/index.ts similarity index 100% rename from src/common/dto/request/action/index.ts rename to src/modules/common/dtos/request/action/index.ts diff --git a/src/common/dto/request/index.ts b/src/modules/common/dtos/request/index.ts similarity index 100% rename from src/common/dto/request/index.ts rename to src/modules/common/dtos/request/index.ts diff --git a/src/common/dto/request/skill-payload.dto.ts b/src/modules/common/dtos/request/skill-payload.dto.ts similarity index 100% rename from src/common/dto/request/skill-payload.dto.ts rename to src/modules/common/dtos/request/skill-payload.dto.ts diff --git a/src/common/dto/request/user/index.ts b/src/modules/common/dtos/request/user/index.ts similarity index 100% rename from src/common/dto/request/user/index.ts rename to src/modules/common/dtos/request/user/index.ts diff --git a/src/common/dto/request/user/user.dto.ts b/src/modules/common/dtos/request/user/user.dto.ts similarity index 100% rename from src/common/dto/request/user/user.dto.ts rename to src/modules/common/dtos/request/user/user.dto.ts diff --git a/src/common/dto/response.dto.ts b/src/modules/common/dtos/response.dto.ts similarity index 100% rename from src/common/dto/response.dto.ts rename to src/modules/common/dtos/response.dto.ts diff --git a/src/modules/common/entities/campus.entity.ts b/src/modules/common/entities/campus.entity.ts index b0abdf4..eee88ff 100644 --- a/src/modules/common/entities/campus.entity.ts +++ b/src/modules/common/entities/campus.entity.ts @@ -1,7 +1,7 @@ import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity('campus') -export class CampusEntity { +export class Campus { @PrimaryGeneratedColumn() id: number; diff --git a/src/modules/common/entities/college.entity.ts b/src/modules/common/entities/college.entity.ts index d2fffde..e7aa0dc 100644 --- a/src/modules/common/entities/college.entity.ts +++ b/src/modules/common/entities/college.entity.ts @@ -1,7 +1,7 @@ import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity('college') -export class CollegeEntity { +export class College { @PrimaryGeneratedColumn() id: number; diff --git a/src/modules/common/entities/department.entity.ts b/src/modules/common/entities/department.entity.ts index 88dc2cd..eb2d3e7 100644 --- a/src/modules/common/entities/department.entity.ts +++ b/src/modules/common/entities/department.entity.ts @@ -5,16 +5,16 @@ import { ManyToOne, JoinColumn, } from 'typeorm'; -import { CollegeEntity } from './college.entity'; +import { College } from './college.entity'; @Entity('department') -export class DepartmentEntity { +export class Department { @PrimaryGeneratedColumn() id: number; - @ManyToOne(() => CollegeEntity, (college) => college.id) + @ManyToOne(() => College, (college) => college.id) @JoinColumn({ name: 'college_id' }) - college: CollegeEntity; + college: College; @Column({ name: 'department_ko' }) name: string; diff --git a/src/common/filter/http-exception.filter.ts b/src/modules/common/filters/http-exception.filter.ts similarity index 100% rename from src/common/filter/http-exception.filter.ts rename to src/modules/common/filters/http-exception.filter.ts diff --git a/src/common/filter/sentry.filter.ts b/src/modules/common/filters/sentry.filter.ts similarity index 100% rename from src/common/filter/sentry.filter.ts rename to src/modules/common/filters/sentry.filter.ts diff --git a/src/common/interfaces/request/fields/action.ts b/src/modules/common/interfaces/request/fields/action.ts similarity index 100% rename from src/common/interfaces/request/fields/action.ts rename to src/modules/common/interfaces/request/fields/action.ts diff --git a/src/common/interfaces/request/fields/bot.ts b/src/modules/common/interfaces/request/fields/bot.ts similarity index 100% rename from src/common/interfaces/request/fields/bot.ts rename to src/modules/common/interfaces/request/fields/bot.ts diff --git a/src/common/interfaces/request/fields/etc.ts b/src/modules/common/interfaces/request/fields/etc.ts similarity index 100% rename from src/common/interfaces/request/fields/etc.ts rename to src/modules/common/interfaces/request/fields/etc.ts diff --git a/src/common/interfaces/request/fields/intent.ts b/src/modules/common/interfaces/request/fields/intent.ts similarity index 100% rename from src/common/interfaces/request/fields/intent.ts rename to src/modules/common/interfaces/request/fields/intent.ts diff --git a/src/common/interfaces/request/fields/userRequest.ts b/src/modules/common/interfaces/request/fields/userRequest.ts similarity index 100% rename from src/common/interfaces/request/fields/userRequest.ts rename to src/modules/common/interfaces/request/fields/userRequest.ts diff --git a/src/common/interfaces/request/skillPayload.ts b/src/modules/common/interfaces/request/skillPayload.ts similarity index 100% rename from src/common/interfaces/request/skillPayload.ts rename to src/modules/common/interfaces/request/skillPayload.ts diff --git a/src/common/interfaces/response/fields/component.ts b/src/modules/common/interfaces/response/fields/component.ts similarity index 100% rename from src/common/interfaces/response/fields/component.ts rename to src/modules/common/interfaces/response/fields/component.ts diff --git a/src/common/interfaces/response/fields/context.ts b/src/modules/common/interfaces/response/fields/context.ts similarity index 100% rename from src/common/interfaces/response/fields/context.ts rename to src/modules/common/interfaces/response/fields/context.ts diff --git a/src/common/interfaces/response/fields/etc.ts b/src/modules/common/interfaces/response/fields/etc.ts similarity index 100% rename from src/common/interfaces/response/fields/etc.ts rename to src/modules/common/interfaces/response/fields/etc.ts diff --git a/src/common/interfaces/response/fields/template.ts b/src/modules/common/interfaces/response/fields/template.ts similarity index 100% rename from src/common/interfaces/response/fields/template.ts rename to src/modules/common/interfaces/response/fields/template.ts diff --git a/src/common/interfaces/response/response.ts b/src/modules/common/interfaces/response/response.ts similarity index 100% rename from src/common/interfaces/response/response.ts rename to src/modules/common/interfaces/response/response.ts diff --git a/src/modules/common/repository/campus.repository.ts b/src/modules/common/repositories/campuses.repository.ts similarity index 53% rename from src/modules/common/repository/campus.repository.ts rename to src/modules/common/repositories/campuses.repository.ts index ce14b4e..7e1b201 100644 --- a/src/modules/common/repository/campus.repository.ts +++ b/src/modules/common/repositories/campuses.repository.ts @@ -1,17 +1,17 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { In, Repository } from 'typeorm'; -import { CampusEntity } from '../entities/campus.entity'; +import { Campus } from '../entities/campus.entity'; @Injectable() -export class CampusRepository { +export class CampusesRepository { constructor( - @InjectRepository(CampusEntity) - private readonly campusRepository: Repository, // Inject the repository here + @InjectRepository(Campus) + private readonly campusesRepository: Repository, ) {} - async findAll(): Promise { - return await this.campusRepository.find({ + async findAll(): Promise { + return await this.campusesRepository.find({ select: ['id', 'name', 'thumbnailUrl'], where: { id: In([1, 2, 3, 4]), @@ -20,7 +20,7 @@ export class CampusRepository { } async getCampusName(campusId: number): Promise { - const campus = await this.campusRepository.findOne({ + const campus = await this.campusesRepository.findOne({ where: { id: campusId, }, diff --git a/src/modules/common/repository/college.repository.ts b/src/modules/common/repositories/colleges.repository.ts similarity index 50% rename from src/modules/common/repository/college.repository.ts rename to src/modules/common/repositories/colleges.repository.ts index 6936f0d..4e543d7 100644 --- a/src/modules/common/repository/college.repository.ts +++ b/src/modules/common/repositories/colleges.repository.ts @@ -1,18 +1,18 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { CollegeEntity } from '../entities/college.entity'; -import { ListCardConfig } from 'src/common/utils/constants'; +import { College } from '../entities/college.entity'; +import { ListCardConfig } from 'src/modules/common/utils/constants'; @Injectable() -export class CollegeRepository { +export class CollegesRepository { constructor( - @InjectRepository(CollegeEntity) - private readonly collegeRepository: Repository, // Inject the repository here + @InjectRepository(College) + private readonly collegesRepository: Repository, ) {} - async findAll(page: number): Promise<[CollegeEntity[], number]> { - return await this.collegeRepository + async findAll(page: number): Promise<[College[], number]> { + return await this.collegesRepository .createQueryBuilder('college') .where("college.name != '공통'") .orderBy('college.name', 'ASC') diff --git a/src/modules/common/repository/department.repository.ts b/src/modules/common/repositories/departments.repository.ts similarity index 56% rename from src/modules/common/repository/department.repository.ts rename to src/modules/common/repositories/departments.repository.ts index 8d0dd19..f3f7fd5 100644 --- a/src/modules/common/repository/department.repository.ts +++ b/src/modules/common/repositories/departments.repository.ts @@ -1,21 +1,21 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { DepartmentEntity } from '../entities/department.entity'; -import { ListCardConfig } from 'src/common/utils/constants'; +import { Department } from '../entities/department.entity'; +import { ListCardConfig } from 'src/modules/common/utils/constants'; @Injectable() -export class DepartmentRepository { +export class DepartmentsRepository { constructor( - @InjectRepository(DepartmentEntity) - private readonly departmentRepository: Repository, // Inject the repository here + @InjectRepository(Department) + private readonly departmentsRepository: Repository, ) {} async findByCollegeId( collegeId: number, page: number, - ): Promise<[DepartmentEntity[], number]> { - return await this.departmentRepository + ): Promise<[Department[], number]> { + return await this.departmentsRepository .createQueryBuilder('department') .where('department.college_id = :collegeId', { collegeId }) .orderBy('department.name', 'ASC') diff --git a/src/common/utils/component.ts b/src/modules/common/utils/component.ts similarity index 100% rename from src/common/utils/component.ts rename to src/modules/common/utils/component.ts diff --git a/src/common/utils/constants.ts b/src/modules/common/utils/constants.ts similarity index 100% rename from src/common/utils/constants.ts rename to src/modules/common/utils/constants.ts diff --git a/src/common/utils/enviornment.ts b/src/modules/common/utils/enviornment.ts similarity index 100% rename from src/common/utils/enviornment.ts rename to src/modules/common/utils/enviornment.ts diff --git a/src/common/utils/utils.module.ts b/src/modules/common/utils/utils.module.ts similarity index 100% rename from src/common/utils/utils.module.ts rename to src/modules/common/utils/utils.module.ts diff --git a/src/common/utils/utils.service.ts b/src/modules/common/utils/utils.service.ts similarity index 100% rename from src/common/utils/utils.service.ts rename to src/modules/common/utils/utils.service.ts diff --git a/src/interceptors/kakao.interceptor.ts b/src/modules/interceptors/kakao.interceptor.ts similarity index 100% rename from src/interceptors/kakao.interceptor.ts rename to src/modules/interceptors/kakao.interceptor.ts diff --git a/src/interceptors/sentry.interceptor.ts b/src/modules/interceptors/sentry.interceptor.ts similarity index 100% rename from src/interceptors/sentry.interceptor.ts rename to src/modules/interceptors/sentry.interceptor.ts diff --git a/src/modules/user/dtos/user.dto.ts b/src/modules/user/dtos/user.dto.ts deleted file mode 100644 index 07556df..0000000 --- a/src/modules/user/dtos/user.dto.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { ApiProperty, PickType } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsString, IsNumber, IsOptional } from 'class-validator'; -import { ClientExtraDto } from 'src/common/dto/request'; - -export class GetCollegeDto extends ClientExtraDto { - @ApiProperty({ description: '캠퍼스 ID', example: 1 }) - @IsNumber() - campusId: number; - - @IsOptional() - @ApiProperty({ description: '페이지 번호', example: 1 }) - @IsNumber() - page?: number = 1; -} - -export class GetDepartmentDto { - @IsOptional() - @ApiProperty({ description: '캠퍼스 ID', example: 1 }) - @IsNumber() - campusId: number; - - @IsOptional() - @IsNumber() - @ApiProperty({ - description: '단과대학 ID', - default: 1 - }) - collegeId: number; - - @IsOptional() - @ApiProperty({ description: '페이지 번호', example: 1 }) - @IsNumber() - page?: number = 1; -} - -export class UpdateDepartmentDto { - @IsNumber() - @ApiProperty({ - description: '캠퍼스 ID', - default: 1 - }) - campusId: number; - - @IsNumber() - @ApiProperty({ - description: '학과 ID', - default: 1 - }) - departmentId: number; -} \ No newline at end of file diff --git a/src/modules/user/user.controller.ts b/src/modules/user/user.controller.ts deleted file mode 100644 index b60d41c..0000000 --- a/src/modules/user/user.controller.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common'; -import { UserService } from './user.service'; -import { ResponseDTO } from 'src/common/dto/response.dto'; -import { ApiHideProperty, ApiTags } from '@nestjs/swagger'; -import { CommonService } from '../common/common.service'; -import { BlockId } from 'src/common/utils/constants'; -import { GetCollegeDto, GetDepartmentDto, UpdateDepartmentDto } from './dtos/user.dto'; -import { SkillPayloadDto } from 'src/common/dto/request/skill-payload.dto'; -import { plainToInstance } from 'class-transformer'; -import { ApiSkillBody } from 'src/common/decorators/api-skill-body.decorator'; - -@ApiTags('user') -@Controller('user') -export class UserController { - constructor( - private readonly userService: UserService, - private readonly commonService: CommonService, - ) {} - - @Post('get/campus') - async getCampus(): Promise { - const blockId = BlockId.COLLEGE_LIST; - const template = await this.commonService.getCampusListCard(blockId); - return new ResponseDTO(template); - } - - @Post('get/college') - @ApiSkillBody(GetCollegeDto) - async getCollege(@Body() body: SkillPayloadDto): Promise { - const { campusId, page } = plainToInstance(GetCollegeDto, body.action.clientExtra); - const blockId = BlockId.DEPARTMENT_LIST; - const template = await this.commonService.getCollegeListCard( - campusId, - page, - blockId, - ); - console.log(template.quickReplies); - return new ResponseDTO(template); - } - - @Post('get/department') - @ApiSkillBody(GetDepartmentDto) - async getDepartment(@Body() body: SkillPayloadDto): Promise { - const { campusId, collegeId, page } = plainToInstance(GetDepartmentDto, body.action.clientExtra); - const blockId = BlockId.UPDATE_DEPARTMENT; - const template = await this.commonService.getDepartmentListCard( - campusId, - collegeId, - page, - blockId, - ); - return new ResponseDTO(template); - } - - @Post('update/department') - @ApiSkillBody(UpdateDepartmentDto) - async upsertUserDepartment(@Req() req: Request, @Body() body: SkillPayloadDto): Promise { - const userId = req['userId']; - const { campusId, departmentId } = plainToInstance(UpdateDepartmentDto, body.action.clientExtra); - const template = await this.userService.upsertUserDepartment( - userId, - campusId, - departmentId, - ); - return new ResponseDTO(template); - } - - @Post('get/profile') - async getUserProfile(@Req() req: Request): Promise { - const userId = req['userId']; - const template = await this.userService.getUserProfile(userId); - return new ResponseDTO(template); - } -} diff --git a/src/modules/user/user.module.ts b/src/modules/user/user.module.ts deleted file mode 100644 index 69c2868..0000000 --- a/src/modules/user/user.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Module } from '@nestjs/common'; -import { UserService } from './user.service'; -import { UserController } from './user.controller'; -import { SupabaseModule } from '../supabase/supabase.module'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { UserRepository } from './repository/user.repository'; -import { UserEntity } from './entities/user.entity'; -import { CommonModule } from '../common/common.module'; - -@Module({ - imports: [ - SupabaseModule, - CommonModule, - TypeOrmModule.forFeature([UserEntity]), - ], - controllers: [UserController], - providers: [UserService, UserRepository], -}) -export class UserModule {} diff --git a/src/modules/users/dtos/request/list-college-request.dto.ts b/src/modules/users/dtos/request/list-college-request.dto.ts new file mode 100644 index 0000000..97a660a --- /dev/null +++ b/src/modules/users/dtos/request/list-college-request.dto.ts @@ -0,0 +1,15 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsNumber, IsOptional } from 'class-validator'; +import { ClientExtraDto } from 'src/modules/common/dtos/request'; + +export class ListCollegesRequestDto extends ClientExtraDto { + @IsNotEmpty() + @IsNumber() + @ApiProperty({ description: '캠퍼스 ID', example: 1 }) + campusId: number; + + @IsOptional() + @ApiProperty({ description: '페이지 번호', example: 1 }) + @IsNumber() + page?: number = 1; +} \ No newline at end of file diff --git a/src/modules/users/dtos/request/list-department-request.dto.ts b/src/modules/users/dtos/request/list-department-request.dto.ts new file mode 100644 index 0000000..ed8ebe6 --- /dev/null +++ b/src/modules/users/dtos/request/list-department-request.dto.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsNumber, IsOptional } from "class-validator"; +import { ClientExtraDto } from "src/modules/common/dtos/request"; + +export class ListDepartmentsRequestDto extends ClientExtraDto { + @IsNotEmpty() + @IsNumber() + @ApiProperty({ description: '캠퍼스 ID', example: 1 }) + campusId: number; + + @IsNotEmpty() + @IsNumber() + @ApiProperty({ + description: '단과대학 ID', + default: 1 + }) + collegeId: number; + + @IsOptional() + @IsNumber() + @ApiProperty({ description: '페이지 번호', example: 1 }) + page?: number = 1; +} \ No newline at end of file diff --git a/src/modules/users/dtos/request/upsert-department-request.dto.ts b/src/modules/users/dtos/request/upsert-department-request.dto.ts new file mode 100644 index 0000000..83ad9aa --- /dev/null +++ b/src/modules/users/dtos/request/upsert-department-request.dto.ts @@ -0,0 +1,21 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsNumber } from "class-validator"; +import { ClientExtraDto } from "src/modules/common/dtos/request"; + +export class UpsertDepartmentRequestDto extends ClientExtraDto { + @IsNotEmpty() + @IsNumber() + @ApiProperty({ + description: '캠퍼스 ID', + default: 1 + }) + campusId: number; + + @IsNotEmpty() + @IsNumber() + @ApiProperty({ + description: '학과 ID', + default: 1 + }) + departmentId: number; +} \ No newline at end of file diff --git a/src/modules/user/entities/user.entity.ts b/src/modules/users/entities/users.entity.ts similarity index 51% rename from src/modules/user/entities/user.entity.ts rename to src/modules/users/entities/users.entity.ts index 4c91816..3a67058 100644 --- a/src/modules/user/entities/user.entity.ts +++ b/src/modules/users/entities/users.entity.ts @@ -1,25 +1,25 @@ -import { CampusEntity } from 'src/modules/common/entities/campus.entity'; -import { DepartmentEntity } from 'src/modules/common/entities/department.entity'; +import { Campus } from 'src/modules/common/entities/campus.entity'; +import { Department } from 'src/modules/common/entities/department.entity'; import { Entity, Column, PrimaryColumn, ManyToOne, JoinColumn } from 'typeorm'; @Entity('kakao-user') -export class UserEntity { +export class User { @PrimaryColumn() id: string; @Column({ name: 'campus_id' }) campusId: number; - @ManyToOne(() => CampusEntity, (campus) => campus.id) + @ManyToOne(() => Campus, (campus) => campus.id) @JoinColumn({ name: 'campus_id' }) - campus: CampusEntity; + campus: Campus; @Column({ name: 'department_id' }) departmentId: number; - @ManyToOne(() => DepartmentEntity, (department) => department.id) + @ManyToOne(() => Department, (department) => department.id) @JoinColumn({ name: 'department_id' }) - department: DepartmentEntity; + department: Department; @Column({ name: 'user_type' }) userType: number; diff --git a/src/modules/user/repository/user.repository.ts b/src/modules/users/repository/users.repository.ts similarity index 66% rename from src/modules/user/repository/user.repository.ts rename to src/modules/users/repository/users.repository.ts index 9ffe86d..8588f89 100644 --- a/src/modules/user/repository/user.repository.ts +++ b/src/modules/users/repository/users.repository.ts @@ -1,17 +1,17 @@ import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; -import { UserEntity } from '../entities/user.entity'; +import { User } from '../entities/users.entity'; @Injectable() -export class UserRepository { +export class UsersRepository { constructor( - @InjectRepository(UserEntity) - private readonly userRepository: Repository, + @InjectRepository(User) + private readonly usersRepository: Repository, ) {} - async findUserProfile(userId: string): Promise { - return await this.userRepository.createQueryBuilder('user') + async findUserProfile(userId: string): Promise { + return await this.usersRepository.createQueryBuilder('user') .leftJoinAndSelect('user.campus', 'campus') .leftJoinAndSelect('user.department', 'department') .leftJoinAndSelect('department.college', 'college') @@ -27,7 +27,7 @@ export class UserRepository { } async isExistUser(userId: string): Promise { - return (await this.userRepository.findOne({ where: { id: userId } })) !== null; + return (await this.usersRepository.findOne({ where: { id: userId } })) !== null; } async createUserInfo( @@ -35,7 +35,7 @@ export class UserRepository { campusId: number, departmentId: number, ): Promise { - await this.userRepository.insert({ + await this.usersRepository.insert({ id: userId, campusId, departmentId, @@ -47,7 +47,7 @@ export class UserRepository { campusId: number, departmentId: number, ): Promise { - await this.userRepository.update(userId, { + await this.usersRepository.update(userId, { campusId, departmentId, }); diff --git a/src/modules/user/user.controller.spec.ts b/src/modules/users/users.controller.spec.ts similarity index 82% rename from src/modules/user/user.controller.spec.ts rename to src/modules/users/users.controller.spec.ts index 1f38440..dd1e5d7 100644 --- a/src/modules/user/user.controller.spec.ts +++ b/src/modules/users/users.controller.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { UserController } from './user.controller'; -import { UserService } from './user.service'; +import { UserController } from './users.controller'; +import { UserService } from './users.service'; describe('UserController', () => { let controller: UserController; diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts new file mode 100644 index 0000000..03f76f2 --- /dev/null +++ b/src/modules/users/users.controller.ts @@ -0,0 +1,76 @@ +import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common'; +import { UsersService } from './users.service'; +import { ResponseDTO } from 'src/modules/common/dtos/response.dto'; +import { ApiTags } from '@nestjs/swagger'; +import { CommonService } from '../common/common.service'; +import { BlockId } from 'src/modules/common/utils/constants'; +import { SkillPayloadDto } from 'src/modules/common/dtos/request/skill-payload.dto'; +import { plainToInstance } from 'class-transformer'; +import { ApiSkillBody } from 'src/modules/common/decorators/api-skill-body.decorator'; +import { UpsertDepartmentRequestDto } from './dtos/request/upsert-department-request.dto'; +import { ListDepartmentsRequestDto } from './dtos/request/list-department-request.dto'; +import { ListCollegesRequestDto } from './dtos/request/list-college-request.dto'; + +@ApiTags('users') +@Controller('users') +export class UsersController { + constructor( + private readonly usersService: UsersService, + private readonly commonService: CommonService, + ) {} + + @Post('get') + async getUserProfile(@Req() req: Request): Promise { + const userId = req['userId']; + const template = await this.usersService.getUserProfile(userId); + return new ResponseDTO(template); + } + + @Post('campuses/list') + async getCampuses(): Promise { + const blockId = BlockId.COLLEGE_LIST; + const template = await this.commonService.getCampusListCard(blockId); + return new ResponseDTO(template); + } + + @Post('colleges/list') + @ApiSkillBody(ListCollegesRequestDto) + async getColleges(@Body() body: SkillPayloadDto): Promise { + const { campusId, page } = plainToInstance(ListCollegesRequestDto, body.action.clientExtra); + const blockId = BlockId.DEPARTMENT_LIST; + const template = await this.commonService.getCollegeListCard( + campusId, + page, + blockId, + ); + console.log(template.quickReplies); + return new ResponseDTO(template); + } + + @Post('departments/list') + @ApiSkillBody(ListDepartmentsRequestDto) + async getDepartments(@Body() body: SkillPayloadDto): Promise { + const { campusId, collegeId, page } = plainToInstance(ListDepartmentsRequestDto, body.action.clientExtra); + const blockId = BlockId.UPDATE_DEPARTMENT; + const template = await this.commonService.getDepartmentListCard( + campusId, + collegeId, + page, + blockId, + ); + return new ResponseDTO(template); + } + + @Post('department/upsert') + @ApiSkillBody(UpsertDepartmentRequestDto) + async upsertDepartment(@Req() req: Request, @Body() body: SkillPayloadDto): Promise { + const userId = req['userId']; + const { campusId, departmentId } = plainToInstance(UpsertDepartmentRequestDto, body.action.clientExtra); + const template = await this.usersService.upsertUserDepartment( + userId, + campusId, + departmentId, + ); + return new ResponseDTO(template); + } +} diff --git a/src/modules/users/users.module.ts b/src/modules/users/users.module.ts new file mode 100644 index 0000000..c684e3d --- /dev/null +++ b/src/modules/users/users.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; +import { UsersService } from './users.service'; +import { UsersController } from './users.controller'; +import { SupabaseModule } from '../supabase/supabase.module'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { UsersRepository } from './repository/users.repository'; +import { User } from './entities/users.entity'; +import { CommonModule } from '../common/common.module'; + +@Module({ + imports: [ + SupabaseModule, + CommonModule, + TypeOrmModule.forFeature([User]), + ], + controllers: [UsersController], + providers: [UsersService, UsersRepository], +}) +export class UsersModule {} diff --git a/src/modules/user/user.service.spec.ts b/src/modules/users/users.service.spec.ts similarity index 89% rename from src/modules/user/user.service.spec.ts rename to src/modules/users/users.service.spec.ts index 873de8a..cb581bb 100644 --- a/src/modules/user/user.service.spec.ts +++ b/src/modules/users/users.service.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { UserService } from './user.service'; +import { UserService } from './users.service'; describe('UserService', () => { let service: UserService; diff --git a/src/modules/user/user.service.ts b/src/modules/users/users.service.ts similarity index 58% rename from src/modules/user/user.service.ts rename to src/modules/users/users.service.ts index 156c475..b023dc1 100644 --- a/src/modules/user/user.service.ts +++ b/src/modules/users/users.service.ts @@ -1,15 +1,15 @@ import { Injectable } from '@nestjs/common'; -import { createSimpleText, createTextCard } from 'src/common/utils/component'; -import { SkillTemplate } from 'src/common/interfaces/response/fields/template'; -import { UserRepository } from './repository/user.repository'; -import { TextCard } from 'src/common/interfaces/response/fields/component'; -import { BlockId } from 'src/common/utils/constants'; -import { Button } from 'src/common/interfaces/response/fields/etc'; +import { createSimpleText, createTextCard } from 'src/modules/common/utils/component'; +import { SkillTemplate } from 'src/modules/common/interfaces/response/fields/template'; +import { UsersRepository } from './repository/users.repository'; +import { TextCard } from 'src/modules/common/interfaces/response/fields/component'; +import { BlockId } from 'src/modules/common/utils/constants'; +import { Button } from 'src/modules/common/interfaces/response/fields/etc'; import { Transactional } from 'typeorm-transactional'; @Injectable() -export class UserService { - constructor(private readonly userRepository: UserRepository) {} +export class UsersService { + constructor(private readonly usersRepository: UsersRepository) {} @Transactional() async upsertUserDepartment( @@ -19,12 +19,12 @@ export class UserService { ): Promise { let simpleText = null; - const isExist = await this.userRepository.isExistUser(userId); + const isExist = await this.usersRepository.isExistUser(userId); if (isExist) { - await this.userRepository.updateUserInfo(userId, campusId, departmentId); + await this.usersRepository.updateUserInfo(userId, campusId, departmentId); simpleText = createSimpleText('학과 정보를 수정했어!'); } else { - await this.userRepository.createUserInfo(userId, campusId, departmentId); + await this.usersRepository.createUserInfo(userId, campusId, departmentId); simpleText = createSimpleText('학과 정보를 등록했어!'); } return { @@ -35,7 +35,7 @@ export class UserService { async getUserProfile(userId: string): Promise { let affiliation = '미등록'; let campus = '미등록'; - const user = await this.userRepository.findUserProfile(userId); + const user = await this.usersRepository.findUserProfile(userId); if (user) { campus = user.campus.name; affiliation = user.department.college.name + ' ' + user.department.name; From 48a6b33b1689bc9ebc19c0033d2a8b57c73b642a Mon Sep 17 00:00:00 2001 From: Jang-DongHo Date: Thu, 20 Mar 2025 14:22:27 +0900 Subject: [PATCH 5/5] =?UTF-8?q?refactor:=20=EC=BD=94=EB=93=9C=20=EC=BB=A8?= =?UTF-8?q?=EB=B2=A4=EC=85=98=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/modules/app/app.module.ts | 6 +-- src/modules/clickers/clickers.controller.ts | 43 ---------------- src/modules/clickers/clickers.module.ts | 19 ------- src/modules/clickers/dtos/clickers.dto.ts | 23 --------- .../repositories/clickers.repository.ts | 21 -------- src/modules/common/common.service.ts | 6 +-- .../action/action.dto.ts | 0 .../{request => requests}/action/index.ts | 0 .../dtos/{request => requests}/index.ts | 0 .../skill-payload.dto.ts | 0 .../dtos/{request => requests}/user/index.ts | 0 .../{request => requests}/user/user.dto.ts | 0 .../get-reading-room-detail-request.dto.ts | 13 +++++ .../request/list-reading-rooms-request.dto.ts | 13 +++++ .../entities/reading-rooms.entity.ts} | 2 +- .../reading-rooms.controller.spec.ts} | 4 +- .../reading-rooms/reading-rooms.controller.ts | 49 +++++++++++++++++++ .../reading-rooms/reading-rooms.module.ts | 19 +++++++ .../reading-rooms.service.spec.ts} | 2 +- .../reading-rooms.service.ts} | 20 ++++---- .../repositories/reading-rooms.repository.ts | 21 ++++++++ .../list-college-request.dto.ts | 2 +- .../list-department-request.dto.ts | 2 +- .../upsert-department-request.dto.ts | 2 +- .../users.repository.ts | 31 +++--------- src/modules/users/users.controller.ts | 31 ++++++------ src/modules/users/users.module.ts | 2 +- src/modules/users/users.service.ts | 22 ++++----- 28 files changed, 170 insertions(+), 183 deletions(-) delete mode 100644 src/modules/clickers/clickers.controller.ts delete mode 100644 src/modules/clickers/clickers.module.ts delete mode 100644 src/modules/clickers/dtos/clickers.dto.ts delete mode 100644 src/modules/clickers/repositories/clickers.repository.ts rename src/modules/common/dtos/{request => requests}/action/action.dto.ts (100%) rename src/modules/common/dtos/{request => requests}/action/index.ts (100%) rename src/modules/common/dtos/{request => requests}/index.ts (100%) rename src/modules/common/dtos/{request => requests}/skill-payload.dto.ts (100%) rename src/modules/common/dtos/{request => requests}/user/index.ts (100%) rename src/modules/common/dtos/{request => requests}/user/user.dto.ts (100%) create mode 100644 src/modules/reading-rooms/dtos/request/get-reading-room-detail-request.dto.ts create mode 100644 src/modules/reading-rooms/dtos/request/list-reading-rooms-request.dto.ts rename src/modules/{clickers/entities/clickers.entity.ts => reading-rooms/entities/reading-rooms.entity.ts} (94%) rename src/modules/{clickers/clickers.controller.spec.ts => reading-rooms/reading-rooms.controller.spec.ts} (79%) create mode 100644 src/modules/reading-rooms/reading-rooms.controller.ts create mode 100644 src/modules/reading-rooms/reading-rooms.module.ts rename src/modules/{clickers/clickers.service.spec.ts => reading-rooms/reading-rooms.service.spec.ts} (87%) rename src/modules/{clickers/clickers.service.ts => reading-rooms/reading-rooms.service.ts} (71%) create mode 100644 src/modules/reading-rooms/repositories/reading-rooms.repository.ts rename src/modules/users/dtos/{request => requests}/list-college-request.dto.ts (85%) rename src/modules/users/dtos/{request => requests}/list-department-request.dto.ts (88%) rename src/modules/users/dtos/{request => requests}/upsert-department-request.dto.ts (86%) rename src/modules/users/{repository => repositories}/users.repository.ts (61%) diff --git a/src/modules/app/app.module.ts b/src/modules/app/app.module.ts index 1ee935f..d04158c 100644 --- a/src/modules/app/app.module.ts +++ b/src/modules/app/app.module.ts @@ -1,5 +1,5 @@ import { SentryGlobalFilter, SentryModule } from '@sentry/nestjs/setup'; -import { Module, ValidationPipe } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { UtilsModule } from 'src/modules/common/utils/utils.module'; @@ -9,7 +9,7 @@ import { SupabaseModule } from '../supabase/supabase.module'; import { validationSchema } from 'src/modules/common/utils/enviornment'; import { APP_FILTER } from '@nestjs/core'; import { CommonModule } from '../common/common.module'; -import { ClickersModule } from '../clickers/clickers.module'; +import { ReadingRoomsModule } from '../reading-rooms/reading-rooms.module'; import { ScheduleModule } from '@nestjs/schedule'; import { HttpModule } from '@nestjs/axios'; import { AuthModule } from 'src/modules/auth/auth.module'; @@ -23,7 +23,7 @@ import { AuthModule } from 'src/modules/auth/auth.module'; UtilsModule, CommonModule, UsersModule, - ClickersModule, + ReadingRoomsModule, AuthModule, ConfigModule.forRoot({ isGlobal: true, diff --git a/src/modules/clickers/clickers.controller.ts b/src/modules/clickers/clickers.controller.ts deleted file mode 100644 index d907b17..0000000 --- a/src/modules/clickers/clickers.controller.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { Body, Controller, Post } from '@nestjs/common'; -import { ClickersService } from './clickers.service'; -import { ApiTags } from '@nestjs/swagger'; -import { CommonService } from '../common/common.service'; -import { BlockId } from 'src/modules/common/utils/constants'; -import { ResponseDTO } from 'src/modules/common/dtos/response.dto'; -import { GetReadingRoomDetailDto, GetReadingRoomDto } from './dtos/clickers.dto'; - -@ApiTags('clickers') -@Controller('clickers') -export class ClickersController { - constructor( - private readonly clickersService: ClickersService, - private readonly commonService: CommonService, - ) {} - - @Post('get/campus') - async getCampus(): Promise { - const blockId = BlockId.READING_ROOM_LIST; - const template = await this.commonService.getCampusListCard(blockId); - return new ResponseDTO(template); - } - - @Post('get/reading-room') - async getReadingRoom(@Body() body: GetReadingRoomDto): Promise { - const { campusId } = body; - const blockId = BlockId.READING_ROOM_DETAIL; - const template = await this.clickersService.getReadingRoomListCard( - campusId, - blockId, - ); - return new ResponseDTO(template); - } - - @Post('get/reading-room-detail') - async getReadingRoomDetail(@Body() body: GetReadingRoomDetailDto): Promise { - const { readingRoomId } = body - const template = await this.clickersService.getReadingRoomDetailCard( - readingRoomId, - ); - return new ResponseDTO(template); - } -} diff --git a/src/modules/clickers/clickers.module.ts b/src/modules/clickers/clickers.module.ts deleted file mode 100644 index 0762ebd..0000000 --- a/src/modules/clickers/clickers.module.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Module } from '@nestjs/common'; -import { ClickersService } from './clickers.service'; -import { ClickersController } from './clickers.controller'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { SupabaseModule } from '../supabase/supabase.module'; -import { CommonModule } from '../common/common.module'; -import { ClickersRepository } from './repositories/clickers.repository'; -import { Clicker } from './entities/clickers.entity'; - -@Module({ - imports: [ - SupabaseModule, - CommonModule, - TypeOrmModule.forFeature([Clicker]), - ], - controllers: [ClickersController], - providers: [ClickersService, ClickersRepository], -}) -export class ClickersModule {} diff --git a/src/modules/clickers/dtos/clickers.dto.ts b/src/modules/clickers/dtos/clickers.dto.ts deleted file mode 100644 index cda62ad..0000000 --- a/src/modules/clickers/dtos/clickers.dto.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { ApiProperty } from "@nestjs/swagger" -import { Transform } from "class-transformer" -import { IsNumber } from "class-validator" - -export class GetReadingRoomDto { - @IsNumber() - @Transform(({ obj }) => obj.action.clientExtra?.campusId) - @ApiProperty({ - description: '캠퍼스 ID', - default: 1 - }) - campusId: number -} - -export class GetReadingRoomDetailDto { - @IsNumber() - @Transform(({ obj }) => obj.action.clientExtra?.readingRoomId) - @ApiProperty({ - description: '열람실 ID', - default: 1 - }) - readingRoomId: number -} \ No newline at end of file diff --git a/src/modules/clickers/repositories/clickers.repository.ts b/src/modules/clickers/repositories/clickers.repository.ts deleted file mode 100644 index c88e8e7..0000000 --- a/src/modules/clickers/repositories/clickers.repository.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import { InjectRepository } from '@nestjs/typeorm'; -import { Repository } from 'typeorm'; -import { Clicker } from '../entities/clickers.entity'; - -@Injectable() -export class ClickersRepository { - constructor( - @InjectRepository(Clicker) - private readonly clickerRepository: Repository, - ) {} - - async findByCampusId(campusId: number): Promise { - return await this.clickerRepository.find({ - where: { - campusId: campusId, - isActive: true, - }, - }); - } -} diff --git a/src/modules/common/common.service.ts b/src/modules/common/common.service.ts index 2d2a950..44f9697 100644 --- a/src/modules/common/common.service.ts +++ b/src/modules/common/common.service.ts @@ -18,7 +18,7 @@ export class CommonService { private readonly departmentsRepository: DepartmentsRepository, ) {} - async getCampusListCard(blockId: string): Promise { + public async campusesListCard(blockId: string): Promise { const campuses = await this.campusesRepository.findAll(); const header: ListItem = { @@ -44,7 +44,7 @@ export class CommonService { }; } - async getCollegeListCard( + public async collegesListCard( campusId: number, page: number, blockId: string, @@ -103,7 +103,7 @@ export class CommonService { }; } - async getDepartmentListCard( + public async departmentsListCard( campusId: number, collegeId: number, page: number = 1, diff --git a/src/modules/common/dtos/request/action/action.dto.ts b/src/modules/common/dtos/requests/action/action.dto.ts similarity index 100% rename from src/modules/common/dtos/request/action/action.dto.ts rename to src/modules/common/dtos/requests/action/action.dto.ts diff --git a/src/modules/common/dtos/request/action/index.ts b/src/modules/common/dtos/requests/action/index.ts similarity index 100% rename from src/modules/common/dtos/request/action/index.ts rename to src/modules/common/dtos/requests/action/index.ts diff --git a/src/modules/common/dtos/request/index.ts b/src/modules/common/dtos/requests/index.ts similarity index 100% rename from src/modules/common/dtos/request/index.ts rename to src/modules/common/dtos/requests/index.ts diff --git a/src/modules/common/dtos/request/skill-payload.dto.ts b/src/modules/common/dtos/requests/skill-payload.dto.ts similarity index 100% rename from src/modules/common/dtos/request/skill-payload.dto.ts rename to src/modules/common/dtos/requests/skill-payload.dto.ts diff --git a/src/modules/common/dtos/request/user/index.ts b/src/modules/common/dtos/requests/user/index.ts similarity index 100% rename from src/modules/common/dtos/request/user/index.ts rename to src/modules/common/dtos/requests/user/index.ts diff --git a/src/modules/common/dtos/request/user/user.dto.ts b/src/modules/common/dtos/requests/user/user.dto.ts similarity index 100% rename from src/modules/common/dtos/request/user/user.dto.ts rename to src/modules/common/dtos/requests/user/user.dto.ts diff --git a/src/modules/reading-rooms/dtos/request/get-reading-room-detail-request.dto.ts b/src/modules/reading-rooms/dtos/request/get-reading-room-detail-request.dto.ts new file mode 100644 index 0000000..2b40c0f --- /dev/null +++ b/src/modules/reading-rooms/dtos/request/get-reading-room-detail-request.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsNumber } from "class-validator"; +import { ClientExtraDto } from "src/modules/common/dtos/requests"; + +export class GetReadingRoomDetailRequestDto extends ClientExtraDto { + @IsNotEmpty() + @IsNumber() + @ApiProperty({ + description: '열람실 ID', + default: 1 + }) + readingRoomId: number +} \ No newline at end of file diff --git a/src/modules/reading-rooms/dtos/request/list-reading-rooms-request.dto.ts b/src/modules/reading-rooms/dtos/request/list-reading-rooms-request.dto.ts new file mode 100644 index 0000000..5eb18a2 --- /dev/null +++ b/src/modules/reading-rooms/dtos/request/list-reading-rooms-request.dto.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from "@nestjs/swagger"; +import { IsNotEmpty, IsNumber } from "class-validator"; +import { ClientExtraDto } from "src/modules/common/dtos/requests"; + +export class ListReadingRoomsRequestDto extends ClientExtraDto { + @IsNotEmpty() + @IsNumber() + @ApiProperty({ + description: '캠퍼스 ID', + default: 1 + }) + campusId: number +} \ No newline at end of file diff --git a/src/modules/clickers/entities/clickers.entity.ts b/src/modules/reading-rooms/entities/reading-rooms.entity.ts similarity index 94% rename from src/modules/clickers/entities/clickers.entity.ts rename to src/modules/reading-rooms/entities/reading-rooms.entity.ts index 9f83fa4..9899434 100644 --- a/src/modules/clickers/entities/clickers.entity.ts +++ b/src/modules/reading-rooms/entities/reading-rooms.entity.ts @@ -1,7 +1,7 @@ import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity('reading_room') -export class Clicker { +export class ReadingRoom { @PrimaryGeneratedColumn() id: number; diff --git a/src/modules/clickers/clickers.controller.spec.ts b/src/modules/reading-rooms/reading-rooms.controller.spec.ts similarity index 79% rename from src/modules/clickers/clickers.controller.spec.ts rename to src/modules/reading-rooms/reading-rooms.controller.spec.ts index 90c690f..5a29e1a 100644 --- a/src/modules/clickers/clickers.controller.spec.ts +++ b/src/modules/reading-rooms/reading-rooms.controller.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { ClickerController } from './clickers.controller'; -import { ClickerService } from './clickers.service'; +import { ClickerController } from './reading-rooms.controller'; +import { ClickerService } from './reading-rooms.service'; describe('ClickerController', () => { let controller: ClickerController; diff --git a/src/modules/reading-rooms/reading-rooms.controller.ts b/src/modules/reading-rooms/reading-rooms.controller.ts new file mode 100644 index 0000000..2c59380 --- /dev/null +++ b/src/modules/reading-rooms/reading-rooms.controller.ts @@ -0,0 +1,49 @@ +import { Body, Controller, Post } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; +import { CommonService } from '../common/common.service'; +import { BlockId } from 'src/modules/common/utils/constants'; +import { ResponseDTO } from 'src/modules/common/dtos/response.dto'; +import { ApiSkillBody } from '../common/decorators/api-skill-body.decorator'; +import { ListReadingRoomsRequestDto } from './dtos/request/list-reading-rooms-request.dto'; +import { GetReadingRoomDetailRequestDto } from './dtos/request/get-reading-room-detail-request.dto'; +import { SkillPayloadDto } from '../common/dtos/requests/skill-payload.dto'; +import { plainToInstance } from 'class-transformer'; +import { ReadingRoomsService } from './reading-rooms.service'; + +@ApiTags('reading-rooms') +@Controller('reading-rooms') +export class ReadingRoomsController { + constructor( + private readonly readingRoomsService: ReadingRoomsService, + private readonly commonService: CommonService, + ) {} + + @Post('campuses/list') + public async listCampuses(): Promise { + const blockId = BlockId.READING_ROOM_LIST; + const template = await this.commonService.campusesListCard(blockId); + return new ResponseDTO(template); + } + + @Post('list') + @ApiSkillBody(ListReadingRoomsRequestDto) + public async listReadingRooms(@Body() body: SkillPayloadDto): Promise { + const { campusId } = plainToInstance(ListReadingRoomsRequestDto, body.action.clientExtra); + const blockId = BlockId.READING_ROOM_DETAIL; + const template = await this.readingRoomsService.readingRoomsListCard( + campusId, + blockId, + ); + return new ResponseDTO(template); + } + + @Post('detail') + @ApiSkillBody(GetReadingRoomDetailRequestDto) + public async getReadingRoomDetail(@Body() body: SkillPayloadDto): Promise { + const { readingRoomId } = plainToInstance(GetReadingRoomDetailRequestDto, body.action.clientExtra); + const template = await this.readingRoomsService.readingRoomDetailComplexCard( + readingRoomId, + ); + return new ResponseDTO(template); + } +} diff --git a/src/modules/reading-rooms/reading-rooms.module.ts b/src/modules/reading-rooms/reading-rooms.module.ts new file mode 100644 index 0000000..1bdf3d6 --- /dev/null +++ b/src/modules/reading-rooms/reading-rooms.module.ts @@ -0,0 +1,19 @@ +import { Module } from '@nestjs/common'; +import { TypeOrmModule } from '@nestjs/typeorm'; +import { SupabaseModule } from '../supabase/supabase.module'; +import { CommonModule } from '../common/common.module'; +import { ReadingRoom } from './entities/reading-rooms.entity'; +import { ReadingRoomsController } from './reading-rooms.controller'; +import { ReadingRoomsService } from './reading-rooms.service'; +import { ReadingRoomsRepository } from './repositories/reading-rooms.repository'; + +@Module({ + imports: [ + SupabaseModule, + CommonModule, + TypeOrmModule.forFeature([ReadingRoom]), + ], + controllers: [ReadingRoomsController], + providers: [ReadingRoomsService, ReadingRoomsRepository], +}) +export class ReadingRoomsModule {} diff --git a/src/modules/clickers/clickers.service.spec.ts b/src/modules/reading-rooms/reading-rooms.service.spec.ts similarity index 87% rename from src/modules/clickers/clickers.service.spec.ts rename to src/modules/reading-rooms/reading-rooms.service.spec.ts index 7545eae..9f02734 100644 --- a/src/modules/clickers/clickers.service.spec.ts +++ b/src/modules/reading-rooms/reading-rooms.service.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { ClickerService } from './clickers.service'; +import { ClickerService } from './reading-rooms.service'; describe('ClickerService', () => { let service: ClickerService; diff --git a/src/modules/clickers/clickers.service.ts b/src/modules/reading-rooms/reading-rooms.service.ts similarity index 71% rename from src/modules/clickers/clickers.service.ts rename to src/modules/reading-rooms/reading-rooms.service.ts index 78b6c68..cb0e620 100644 --- a/src/modules/clickers/clickers.service.ts +++ b/src/modules/reading-rooms/reading-rooms.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { ClickersRepository } from './repositories/clickers.repository'; +import { ReadingRoomsRepository } from './repositories/reading-rooms.repository'; import { ListItem } from 'src/modules/common/interfaces/response/fields/etc'; import { ListCard } from 'src/modules/common/interfaces/response/fields/component'; import { @@ -9,10 +9,10 @@ import { } from 'src/modules/common/utils/component'; @Injectable() -export class ClickersService { - constructor(private readonly clickersRepository: ClickersRepository) {} +export class ReadingRoomsService { + constructor(private readonly readingRoomsRepository: ReadingRoomsRepository) {} - async getReadingRoomListCard( + public async readingRoomsListCard( campusId: number, blockId: string, ): Promise { @@ -24,11 +24,11 @@ export class ClickersService { }; } - const readingRoomEntities = await this.clickersRepository.findByCampusId( + const readingRooms = await this.readingRoomsRepository.findReadingRoomsByCampusId( campusId, ); - if (readingRoomEntities.length === 0) { + if (!readingRooms.length) { return { outputs: [createSimpleText('해당 캠퍼스는 업데이트 준비중이야!')], }; @@ -38,7 +38,7 @@ export class ClickersService { title: '열람실 선택', }; - const items: ListItem[] = readingRoomEntities.map((readingRoomEntity) => { + const items: ListItem[] = readingRooms.map((readingRoomEntity) => { return { title: `[${readingRoomEntity.libraryName}] ${readingRoomEntity.roomName}`, description: `총 좌석 수: ${readingRoomEntity.totalSeats}석`, @@ -50,14 +50,14 @@ export class ClickersService { }; }); - const campusListCard: ListCard = createListCard(header, items); + const campusesListCard: ListCard = createListCard(header, items); return { - outputs: [campusListCard], + outputs: [campusesListCard], }; } - async getReadingRoomDetailCard(readingRoomId: number): Promise { + public async readingRoomDetailComplexCard(readingRoomId: number): Promise { const imageUrl = `https://zppxqcdwhqqzbwpmcjjt.supabase.co/storage/v1/object/public/clicker/clicker/${readingRoomId}.png`; return { outputs: [ diff --git a/src/modules/reading-rooms/repositories/reading-rooms.repository.ts b/src/modules/reading-rooms/repositories/reading-rooms.repository.ts new file mode 100644 index 0000000..5596590 --- /dev/null +++ b/src/modules/reading-rooms/repositories/reading-rooms.repository.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { ReadingRoom } from '../entities/reading-rooms.entity'; + +@Injectable() +export class ReadingRoomsRepository { + constructor( + @InjectRepository(ReadingRoom) + private readonly readingRoomRepository: Repository, + ) {} + + async findReadingRoomsByCampusId(campusId: number): Promise { + return await this.readingRoomRepository.find({ + where: { + campusId: campusId, + isActive: true, + }, + }); + } +} diff --git a/src/modules/users/dtos/request/list-college-request.dto.ts b/src/modules/users/dtos/requests/list-college-request.dto.ts similarity index 85% rename from src/modules/users/dtos/request/list-college-request.dto.ts rename to src/modules/users/dtos/requests/list-college-request.dto.ts index 97a660a..870cc1a 100644 --- a/src/modules/users/dtos/request/list-college-request.dto.ts +++ b/src/modules/users/dtos/requests/list-college-request.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsNumber, IsOptional } from 'class-validator'; -import { ClientExtraDto } from 'src/modules/common/dtos/request'; +import { ClientExtraDto } from 'src/modules/common/dtos/requests'; export class ListCollegesRequestDto extends ClientExtraDto { @IsNotEmpty() diff --git a/src/modules/users/dtos/request/list-department-request.dto.ts b/src/modules/users/dtos/requests/list-department-request.dto.ts similarity index 88% rename from src/modules/users/dtos/request/list-department-request.dto.ts rename to src/modules/users/dtos/requests/list-department-request.dto.ts index ed8ebe6..0d923be 100644 --- a/src/modules/users/dtos/request/list-department-request.dto.ts +++ b/src/modules/users/dtos/requests/list-department-request.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from "@nestjs/swagger"; import { IsNotEmpty, IsNumber, IsOptional } from "class-validator"; -import { ClientExtraDto } from "src/modules/common/dtos/request"; +import { ClientExtraDto } from "src/modules/common/dtos/requests"; export class ListDepartmentsRequestDto extends ClientExtraDto { @IsNotEmpty() diff --git a/src/modules/users/dtos/request/upsert-department-request.dto.ts b/src/modules/users/dtos/requests/upsert-department-request.dto.ts similarity index 86% rename from src/modules/users/dtos/request/upsert-department-request.dto.ts rename to src/modules/users/dtos/requests/upsert-department-request.dto.ts index 83ad9aa..432eb9b 100644 --- a/src/modules/users/dtos/request/upsert-department-request.dto.ts +++ b/src/modules/users/dtos/requests/upsert-department-request.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from "@nestjs/swagger"; import { IsNotEmpty, IsNumber } from "class-validator"; -import { ClientExtraDto } from "src/modules/common/dtos/request"; +import { ClientExtraDto } from "src/modules/common/dtos/requests"; export class UpsertDepartmentRequestDto extends ClientExtraDto { @IsNotEmpty() diff --git a/src/modules/users/repository/users.repository.ts b/src/modules/users/repositories/users.repository.ts similarity index 61% rename from src/modules/users/repository/users.repository.ts rename to src/modules/users/repositories/users.repository.ts index 8588f89..5e87d50 100644 --- a/src/modules/users/repository/users.repository.ts +++ b/src/modules/users/repositories/users.repository.ts @@ -10,7 +10,11 @@ export class UsersRepository { private readonly usersRepository: Repository, ) {} - async findUserProfile(userId: string): Promise { + async save(userId: string, campusId: number, departmentId: number): Promise { + return await this.usersRepository.save({id: userId, campusId, departmentId}); + } + + async findByUserId(userId: string): Promise { return await this.usersRepository.createQueryBuilder('user') .leftJoinAndSelect('user.campus', 'campus') .leftJoinAndSelect('user.department', 'department') @@ -26,30 +30,7 @@ export class UsersRepository { .getOne(); } - async isExistUser(userId: string): Promise { + async existsByUserId(userId: string): Promise { return (await this.usersRepository.findOne({ where: { id: userId } })) !== null; } - - async createUserInfo( - userId: string, - campusId: number, - departmentId: number, - ): Promise { - await this.usersRepository.insert({ - id: userId, - campusId, - departmentId, - }); - } - - async updateUserInfo( - userId: string, - campusId: number, - departmentId: number, - ): Promise { - await this.usersRepository.update(userId, { - campusId, - departmentId, - }); - } } diff --git a/src/modules/users/users.controller.ts b/src/modules/users/users.controller.ts index 03f76f2..4be3e1a 100644 --- a/src/modules/users/users.controller.ts +++ b/src/modules/users/users.controller.ts @@ -1,15 +1,15 @@ -import { Body, Controller, Post, Req, UseGuards } from '@nestjs/common'; +import { Body, Controller, Post, Req } from '@nestjs/common'; import { UsersService } from './users.service'; import { ResponseDTO } from 'src/modules/common/dtos/response.dto'; import { ApiTags } from '@nestjs/swagger'; import { CommonService } from '../common/common.service'; import { BlockId } from 'src/modules/common/utils/constants'; -import { SkillPayloadDto } from 'src/modules/common/dtos/request/skill-payload.dto'; +import { SkillPayloadDto } from 'src/modules/common/dtos/requests/skill-payload.dto'; import { plainToInstance } from 'class-transformer'; import { ApiSkillBody } from 'src/modules/common/decorators/api-skill-body.decorator'; -import { UpsertDepartmentRequestDto } from './dtos/request/upsert-department-request.dto'; -import { ListDepartmentsRequestDto } from './dtos/request/list-department-request.dto'; -import { ListCollegesRequestDto } from './dtos/request/list-college-request.dto'; +import { UpsertDepartmentRequestDto } from './dtos/requests/upsert-department-request.dto'; +import { ListDepartmentsRequestDto } from './dtos/requests/list-department-request.dto'; +import { ListCollegesRequestDto } from './dtos/requests/list-college-request.dto'; @ApiTags('users') @Controller('users') @@ -19,40 +19,39 @@ export class UsersController { private readonly commonService: CommonService, ) {} - @Post('get') - async getUserProfile(@Req() req: Request): Promise { + @Post('profile/get') + async getProfile(@Req() req: Request): Promise { const userId = req['userId']; - const template = await this.usersService.getUserProfile(userId); + const template = await this.usersService.profileTextCard(userId); return new ResponseDTO(template); } @Post('campuses/list') - async getCampuses(): Promise { + async listCampuses(): Promise { const blockId = BlockId.COLLEGE_LIST; - const template = await this.commonService.getCampusListCard(blockId); + const template = await this.commonService.campusesListCard(blockId); return new ResponseDTO(template); } @Post('colleges/list') @ApiSkillBody(ListCollegesRequestDto) - async getColleges(@Body() body: SkillPayloadDto): Promise { + async listColleges(@Body() body: SkillPayloadDto): Promise { const { campusId, page } = plainToInstance(ListCollegesRequestDto, body.action.clientExtra); const blockId = BlockId.DEPARTMENT_LIST; - const template = await this.commonService.getCollegeListCard( + const template = await this.commonService.collegesListCard( campusId, page, blockId, ); - console.log(template.quickReplies); return new ResponseDTO(template); } @Post('departments/list') @ApiSkillBody(ListDepartmentsRequestDto) - async getDepartments(@Body() body: SkillPayloadDto): Promise { + async listDepartments(@Body() body: SkillPayloadDto): Promise { const { campusId, collegeId, page } = plainToInstance(ListDepartmentsRequestDto, body.action.clientExtra); const blockId = BlockId.UPDATE_DEPARTMENT; - const template = await this.commonService.getDepartmentListCard( + const template = await this.commonService.departmentsListCard( campusId, collegeId, page, @@ -66,7 +65,7 @@ export class UsersController { async upsertDepartment(@Req() req: Request, @Body() body: SkillPayloadDto): Promise { const userId = req['userId']; const { campusId, departmentId } = plainToInstance(UpsertDepartmentRequestDto, body.action.clientExtra); - const template = await this.usersService.upsertUserDepartment( + const template = await this.usersService.upsertDepartment( userId, campusId, departmentId, diff --git a/src/modules/users/users.module.ts b/src/modules/users/users.module.ts index c684e3d..5a9287c 100644 --- a/src/modules/users/users.module.ts +++ b/src/modules/users/users.module.ts @@ -3,7 +3,7 @@ import { UsersService } from './users.service'; import { UsersController } from './users.controller'; import { SupabaseModule } from '../supabase/supabase.module'; import { TypeOrmModule } from '@nestjs/typeorm'; -import { UsersRepository } from './repository/users.repository'; +import { UsersRepository } from './repositories/users.repository'; import { User } from './entities/users.entity'; import { CommonModule } from '../common/common.module'; diff --git a/src/modules/users/users.service.ts b/src/modules/users/users.service.ts index b023dc1..784059f 100644 --- a/src/modules/users/users.service.ts +++ b/src/modules/users/users.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { createSimpleText, createTextCard } from 'src/modules/common/utils/component'; import { SkillTemplate } from 'src/modules/common/interfaces/response/fields/template'; -import { UsersRepository } from './repository/users.repository'; +import { UsersRepository } from './repositories/users.repository'; import { TextCard } from 'src/modules/common/interfaces/response/fields/component'; import { BlockId } from 'src/modules/common/utils/constants'; import { Button } from 'src/modules/common/interfaces/response/fields/etc'; @@ -12,30 +12,28 @@ export class UsersService { constructor(private readonly usersRepository: UsersRepository) {} @Transactional() - async upsertUserDepartment( + public async upsertDepartment( userId: string, campusId: number, departmentId: number, ): Promise { - let simpleText = null; - - const isExist = await this.usersRepository.isExistUser(userId); + await this.usersRepository.save(userId, campusId, departmentId); + const isExist = await this.usersRepository.existsByUserId(userId); + let message: string; if (isExist) { - await this.usersRepository.updateUserInfo(userId, campusId, departmentId); - simpleText = createSimpleText('학과 정보를 수정했어!'); + message = '학과 정보를 수정했어!'; } else { - await this.usersRepository.createUserInfo(userId, campusId, departmentId); - simpleText = createSimpleText('학과 정보를 등록했어!'); + message = '학과 정보를 등록했어!'; } return { - outputs: [simpleText], + outputs: [createSimpleText(message)], }; } - async getUserProfile(userId: string): Promise { + public async profileTextCard(userId: string): Promise { let affiliation = '미등록'; let campus = '미등록'; - const user = await this.usersRepository.findUserProfile(userId); + const user = await this.usersRepository.findByUserId(userId); if (user) { campus = user.campus.name; affiliation = user.department.college.name + ' ' + user.department.name;