From 5e94c68dfc904e0b9bad820fc7d3fd4a2306b121 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Thu, 18 Jan 2024 17:29:46 +0900 Subject: [PATCH 001/147] =?UTF-8?q?[FEAT]=20=EC=86=8C=EC=85=9C=20=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20API=20=EA=B5=AC=ED=98=84=20(#2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 카카오 로그인 API 구현 #1 * Feat: access token 재발급 API 구현 #1 * Feat: access token 검증 미들웨어 구현 #1 * Feat: 구글 로그인 API 구현 #1 * Feat: 네이버 로그인 API 구현 #1 --- .eslintrc.json | 4 +- package-lock.json | 291 ++++++++++++++++++++++++++++ package.json | 9 + src/app.ts | 7 +- src/config/passport.ts | 56 ++++++ src/config/response.status.ts | 50 +++++ src/controllers/auth.controller.ts | 20 ++ src/controllers/users.controller.ts | 8 - src/daos/user.dao.ts | 43 ++++ src/middlewares/auth.middleware.ts | 18 ++ src/models/user.model.ts | 29 ++- src/routes/auth.route.ts | 17 ++ src/routes/users.route.ts | 8 - src/services/auth.service.ts | 39 ++++ src/services/users.service.ts | 23 +++ src/utils/jwt.util.ts | 67 +++++++ tsconfig.json | 4 +- 17 files changed, 663 insertions(+), 30 deletions(-) create mode 100644 src/config/passport.ts create mode 100644 src/controllers/auth.controller.ts delete mode 100644 src/controllers/users.controller.ts create mode 100644 src/daos/user.dao.ts create mode 100644 src/middlewares/auth.middleware.ts create mode 100644 src/routes/auth.route.ts delete mode 100644 src/routes/users.route.ts create mode 100644 src/services/auth.service.ts create mode 100644 src/services/users.service.ts create mode 100644 src/utils/jwt.util.ts diff --git a/.eslintrc.json b/.eslintrc.json index e95dff8..500183f 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -16,6 +16,8 @@ "@typescript-eslint/no-explicit-any": "warn", "@typescript-eslint/no-unsafe-member-access": "warn", "@typescript-eslint/no-var-requires": "warn", - "@typescript-eslint/no-unsafe-call": "warn" + "@typescript-eslint/no-unsafe-call": "warn", + "@typescript-eslint/no-unsafe-argument": "warn", + "@typescript-eslint/no-unsafe-return": "warn" } } diff --git a/package-lock.json b/package-lock.json index 7632ee0..f3ca8ab 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,14 +14,23 @@ "express": "^4.18.2", "express-async-handler": "^1.2.0", "http-status-codes": "^2.3.0", + "jsonwebtoken": "^9.0.2", "mysql2": "^3.7.0", + "passport": "^0.7.0", + "passport-google-oauth20": "^2.0.0", + "passport-kakao": "^1.0.1", + "passport-naver": "^1.0.6", "sequelize": "^6.35.2", "sequelize-cli": "^6.6.2" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.21", + "@types/jsonwebtoken": "^9.0.5", "@types/node": "^20.11.0", + "@types/passport": "^1.0.16", + "@types/passport-kakao": "^1.0.3", + "@types/passport-naver": "^1.0.4", "@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/parser": "^6.18.1", "eslint": "^8.56.0", @@ -418,6 +427,15 @@ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz", + "integrity": "sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -437,6 +455,35 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/passport": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", + "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/passport-kakao": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/passport-kakao/-/passport-kakao-1.0.3.tgz", + "integrity": "sha512-McK5kpeiOptvjIPkiA/QS3k82//z5JM7Y3yBLMDvEA0QMMhFMcWUHhyebL1Qy+0i0n8jS+Oe4U6xAvkU22SXXA==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/passport": "*" + } + }, + "node_modules/@types/passport-naver": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@types/passport-naver/-/passport-naver-1.0.4.tgz", + "integrity": "sha512-QcqccZxXUjSIkF/AdGsgWXS2jOfDuhs4ClmWht3PnFoujxoMkET3eR9KbXL8gdyOAELg1yKu8LhMnMwAbza1cw==", + "dev": true, + "dependencies": { + "@types/express": "*", + "@types/passport": "*" + } + }, "node_modules/@types/qs": { "version": "6.9.11", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", @@ -924,6 +971,14 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", + "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -981,6 +1036,11 @@ "node": ">=8" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -1347,6 +1407,14 @@ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/editorconfig": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", @@ -2509,6 +2577,51 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jsonwebtoken/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -2536,12 +2649,47 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -2866,6 +3014,11 @@ "node": ">=0.10.0" } }, + "node_modules/oauth": { + "version": "0.9.15", + "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", + "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2939,6 +3092,121 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "dependencies": { + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-kakao": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz", + "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==", + "dependencies": { + "passport-oauth2": "~1.1.2", + "pkginfo": "~0.3.0" + } + }, + "node_modules/passport-kakao/node_modules/passport-oauth2": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz", + "integrity": "sha512-wpsGtJDHHQUjyc9WcV9FFB0bphFExpmKtzkQrxpH1vnSr6RcWa3ZEGHx/zGKAh2PN7Po9TKYB1fJeOiIBspNPA==", + "dependencies": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-naver": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/passport-naver/-/passport-naver-1.0.6.tgz", + "integrity": "sha512-5XEbJesiPVshwE0cTDvbbJrOiYSJ9VFRJTctqiZL1Xip6qOi9n7d49UesOqavLT+Ry8C7aw3zdzbmWosqHcQ/Q==", + "dependencies": { + "passport-oauth": "^1.0.0", + "underscore": "^1.8.3" + } + }, + "node_modules/passport-oauth": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-oauth/-/passport-oauth-1.0.0.tgz", + "integrity": "sha512-4IZNVsZbN1dkBzmEbBqUxDG8oFOIK81jqdksE3HEb/vI3ib3FMjbiZZ6MTtooyYZzmKu0BfovjvT1pdGgIq+4Q==", + "dependencies": { + "passport-oauth1": "1.x.x", + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-oauth1": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.3.0.tgz", + "integrity": "sha512-8T/nX4gwKTw0PjxP1xfD0QhrydQNakzeOpZ6M5Uqdgz9/a/Ag62RmJxnZQ4LkbdXGrRehQHIAHNAu11rCP46Sw==", + "dependencies": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-oauth2": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz", + "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2998,6 +3266,11 @@ "node": ">=8" } }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/pg-connection-string": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", @@ -3015,6 +3288,14 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pkginfo": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", + "integrity": "sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3869,6 +4150,11 @@ "node": ">=14.17" } }, + "node_modules/uid2": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", + "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" + }, "node_modules/umzug": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", @@ -3886,6 +4172,11 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, + "node_modules/underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", diff --git a/package.json b/package.json index 83b09ee..fb4bd49 100644 --- a/package.json +++ b/package.json @@ -15,14 +15,23 @@ "express": "^4.18.2", "express-async-handler": "^1.2.0", "http-status-codes": "^2.3.0", + "jsonwebtoken": "^9.0.2", "mysql2": "^3.7.0", + "passport": "^0.7.0", + "passport-google-oauth20": "^2.0.0", + "passport-kakao": "^1.0.1", + "passport-naver": "^1.0.6", "sequelize": "^6.35.2", "sequelize-cli": "^6.6.2" }, "devDependencies": { "@types/cors": "^2.8.17", "@types/express": "^4.17.21", + "@types/jsonwebtoken": "^9.0.5", "@types/node": "^20.11.0", + "@types/passport": "^1.0.16", + "@types/passport-kakao": "^1.0.3", + "@types/passport-naver": "^1.0.4", "@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/parser": "^6.18.1", "eslint": "^8.56.0", diff --git a/src/app.ts b/src/app.ts index 4848efe..c658210 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,13 +3,15 @@ dotenv.config(); import express, { Request, Response, NextFunction } from "express"; import cors from "cors"; +import passport from "passport"; +import "./config/passport"; import db from "./models/index"; import { response } from "./config/response"; import { BaseError } from "./config/error"; import { status } from "./config/response.status"; -// import { authRouter } from "./routes/auth.route"; +import { authRouter } from "./routes/auth.route"; const app = express(); @@ -26,8 +28,9 @@ app.use(cors()); app.use(express.static("public")); app.use(express.json()); app.use(express.urlencoded({ extended: false })); +app.use(passport.initialize()); -// app.use("/auth", authRouter); +app.use("/auth", authRouter); app.use((req: Request, res: Response, next: NextFunction) => { const err = new BaseError(status.NOT_FOUND); diff --git a/src/config/passport.ts b/src/config/passport.ts new file mode 100644 index 0000000..f418026 --- /dev/null +++ b/src/config/passport.ts @@ -0,0 +1,56 @@ +import passport from "passport"; +import { Strategy as GoogleStrategy } from "passport-google-oauth20"; +import { Strategy as KakaoStrategy } from "passport-kakao"; +import { Strategy as NaverStrategy } from "passport-naver"; +import { createOrReadUser } from "../services/users.service"; + +passport.use( + new GoogleStrategy( + { + clientID: process.env.GOOGLE_CLIENT_ID!, + clientSecret: process.env.GOOGLE_CLIENT_SECRET, + callbackURL: process.env.GOOGLE_CALLBACK_URL!, + }, + (accessToken, refreshToken, profile, done) => { + createOrReadUser(profile.provider, profile.id, profile._json.email) + .then((user) => done(null, user)) + .catch((err) => { + done(err); + }); + }, + ), +); + +passport.use( + new KakaoStrategy( + { + clientID: process.env.KAKAO_CLIENT_ID!, + clientSecret: process.env.KAKAO_CLIENT_SECRET, + callbackURL: process.env.KAKAO_CALLBACK_URL!, + }, + (accessToken, refreshToken, profile, done) => { + createOrReadUser(profile.provider, profile.id, profile._json.kakao_account.email) + .then((user) => done(null, user)) + .catch((err) => { + done(err); + }); + }, + ), +); + +passport.use( + new NaverStrategy( + { + clientID: process.env.NAVER_CLIENT_ID!, + clientSecret: process.env.NAVER_CLIENT_SECRET, + callbackURL: process.env.NAVER_CALLBACK_URL!, + }, + (accessToken, refreshToken, profile, done) => { + createOrReadUser(profile.provider, profile.id, profile._json.email) + .then((user) => done(null, user)) + .catch((err) => { + done(err); + }); + }, + ), +); diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 9fb6894..95f2091 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -19,4 +19,54 @@ export const status: { [key: string]: Status } = { code: "COMMON000", message: "서버 에러, 관리자에게 문의 바랍니다.", }, + NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "COMMON001", + message: "요청한 페이지를 찾을 수 없습니다. 관리자에게 문의 바랍니다.", + }, + MISSING_REQUIRED_FIELDS: { + status: StatusCodes.BAD_REQUEST, + isSuccess: false, + code: "COMMON002", + message: "요청에 필요한 정보가 누락되었습니다.", + }, + + //auth err + MISSING_ACCESS_TOKEN: { + status: StatusCodes.UNAUTHORIZED, + isSuccess: false, + code: "AUTH001", + message: "Access Token이 없습니다.", + }, + ACCESS_TOKEN_EXPIRED: { + status: StatusCodes.UNAUTHORIZED, + isSuccess: false, + code: "AUTH002", + message: "Access Token이 만료되었습니다.", + }, + ACCESS_TOKEN_NOT_EXPIRED: { + status: StatusCodes.BAD_REQUEST, + isSuccess: false, + code: "AUTH003", + message: "Access Token이 아직 만료되지 않았습니다.", + }, + ACCESS_TOKEN_VERIFICATION_FAILED: { + status: StatusCodes.UNAUTHORIZED, + isSuccess: false, + code: "AUTH004", + message: "Access Token 검증에 실패했습니다.", + }, + MISSING_REFRESH_TOKEN: { + status: StatusCodes.UNAUTHORIZED, + isSuccess: false, + code: "AUTH005", + message: "Refresh Token이 없습니다.", + }, + REFRESH_TOKEN_VERIFICATION_FAILED: { + status: StatusCodes.UNAUTHORIZED, + isSuccess: false, + code: "AUTH006", + message: "Refresh Token 검증에 실패했습니다. 다시 로그인해 주세요.", + }, }; diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts new file mode 100644 index 0000000..536e68d --- /dev/null +++ b/src/controllers/auth.controller.ts @@ -0,0 +1,20 @@ +import { Request } from "express"; +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { login, generateNewAccessToken } from "../services/auth.service"; + +export const googleCallback = async (req, res, next) => { + res.send(response(status.SUCCESS, await login(req.user))); +}; + +export const kakaoCallback = async (req, res, next) => { + res.send(response(status.SUCCESS, await login(req.user))); +}; + +export const naverCallback = async (req, res, next) => { + res.send(response(status.SUCCESS, await login(req.user))); +}; + +export const refreshAccessToken = async (req: Request, res, next) => { + res.send(response(status.SUCCESS, await generateNewAccessToken(req))); +}; diff --git a/src/controllers/users.controller.ts b/src/controllers/users.controller.ts deleted file mode 100644 index a9203d0..0000000 --- a/src/controllers/users.controller.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { response } from "../../config/response"; -import { status } from "../../config/response.status"; - -// import { ... } from "../services/users.service"; - -// export const ... = async (req, res, next) => { -// res.send(response(status.SUCCESS, await ...)); -// }; diff --git a/src/daos/user.dao.ts b/src/daos/user.dao.ts new file mode 100644 index 0000000..f0c1390 --- /dev/null +++ b/src/daos/user.dao.ts @@ -0,0 +1,43 @@ +import db from "../models"; + +export const getUserByProviderId = async (provider, providerId) => { + return await db.User.findOne({ + raw: true, + where: { + provider, + providerId, + }, + }); +}; + +export const insertUser = async (provider, providerId, email) => { + const nickname = email.split("@")[0]; + return await db.User.create({ + nickname, + provider, + providerId, + email, + }); +}; + +export const setRefreshToken = async (refreshToken: string, userId: number) => { + await db.User.update( + { refreshToken }, + { + where: { + id: userId, + }, + }, + ); +}; + +export const getRefreshToken = async (userId: number) => { + const user = await db.User.findOne({ + raw: true, + where: { + id: userId, + }, + attributes: ["refreshToken"], + }); + return user.refreshToken; +}; diff --git a/src/middlewares/auth.middleware.ts b/src/middlewares/auth.middleware.ts new file mode 100644 index 0000000..ab008a1 --- /dev/null +++ b/src/middlewares/auth.middleware.ts @@ -0,0 +1,18 @@ +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { NextFunction, Request, Response } from "express"; +import { extractAccessToken, verifyAccessToken } from "../utils/jwt.util"; + +export const verifyUser = (req: Request, res: Response, next: NextFunction) => { + const accessToken = extractAccessToken(req); + const verified = verifyAccessToken(accessToken); + if (verified.isExpired) { + throw new BaseError(status.ACCESS_TOKEN_EXPIRED); + } else { + req.user = { + id: verified.decoded.id, + nickname: verified.decoded.nickname, + }; + next(); + } +}; diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 9e0a404..54f1717 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -1,21 +1,32 @@ -import { Model, InferAttributes, InferCreationAttributes, CreationOptional, DataTypes, Sequelize } from "sequelize"; +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; class User extends Model, InferCreationAttributes> { - declare id: CreationOptional; - declare nickname: string; - static initiate(sequelize: Sequelize) { User.init( { - id: { - type: DataTypes.INTEGER, - autoIncrement: true, - primaryKey: true, - }, nickname: { type: new DataTypes.STRING(10), allowNull: false, }, + provider: { + type: new DataTypes.STRING(10), + allowNull: false, + }, + providerId: { + type: new DataTypes.STRING(50), + allowNull: false, + }, + email: { + type: new DataTypes.STRING(50), + allowNull: false, + validate: { + isEmail: true, + }, + }, + refreshToken: { + type: new DataTypes.STRING(150), + allowNull: true, + }, }, { sequelize, diff --git a/src/routes/auth.route.ts b/src/routes/auth.route.ts new file mode 100644 index 0000000..44d4ca9 --- /dev/null +++ b/src/routes/auth.route.ts @@ -0,0 +1,17 @@ +import express from "express"; +import passport from "passport"; +import asyncHandler from "express-async-handler"; +import { googleCallback, kakaoCallback, naverCallback, refreshAccessToken } from "../controllers/auth.controller"; + +export const authRouter = express.Router(); + +authRouter.get("/google", passport.authenticate("google", { scope: ["profile", "email"] })); +authRouter.get("/google/callback", passport.authenticate("google", { session: false }), asyncHandler(googleCallback)); + +authRouter.get("/kakao", passport.authenticate("kakao")); +authRouter.get("/kakao/callback", passport.authenticate("kakao", { session: false }), asyncHandler(kakaoCallback)); + +authRouter.get("/naver", passport.authenticate("naver")); +authRouter.get("/naver/callback", passport.authenticate("naver", { session: false }), asyncHandler(naverCallback)); + +authRouter.post("/refresh", asyncHandler(refreshAccessToken)); diff --git a/src/routes/users.route.ts b/src/routes/users.route.ts deleted file mode 100644 index ada81a6..0000000 --- a/src/routes/users.route.ts +++ /dev/null @@ -1,8 +0,0 @@ -import express from "express"; -import asyncHandler from "express-async-handler"; - -// import { ... } from "../controllers/users.controller"; - -export const usersRouter = express.Router(); - -// usersRouter.post("/", asyncHandler(...)); diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts new file mode 100644 index 0000000..59ae288 --- /dev/null +++ b/src/services/auth.service.ts @@ -0,0 +1,39 @@ +import { Request } from "express"; +import { Payload, updateRefreshToken } from "./users.service"; +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { getRefreshToken } from "../daos/user.dao"; +import { + generateAccessToken, + generateRefreshToken, + extractAccessToken, + extractRefreshToken, + verifyAccessToken, + isRefreshTokenValid, +} from "../utils/jwt.util"; + +export const tokenType = "Bearer "; + +export const login = async (payload: Payload) => { + const accessToken = generateAccessToken(payload); + const refreshToken = generateRefreshToken(); + await updateRefreshToken(refreshToken, payload.id); + return { tokenType, accessToken, refreshToken }; +}; + +export const generateNewAccessToken = async (req: Request) => { + const accessToken = extractAccessToken(req); + const refreshToken = extractRefreshToken(req); + const verified = verifyAccessToken(accessToken); + if (verified.isExpired) { + if (isRefreshTokenValid(refreshToken) && (await isRefreshTokenMatching(refreshToken, verified.decoded.id))) { + return await login({ id: verified.decoded.id, nickname: verified.decoded.nickname }); + } + throw new BaseError(status.REFRESH_TOKEN_VERIFICATION_FAILED); + } + throw new BaseError(status.ACCESS_TOKEN_NOT_EXPIRED); +}; + +const isRefreshTokenMatching = async (refreshToken, userId: number) => { + return refreshToken === (await getRefreshToken(userId)); +}; diff --git a/src/services/users.service.ts b/src/services/users.service.ts new file mode 100644 index 0000000..54d162a --- /dev/null +++ b/src/services/users.service.ts @@ -0,0 +1,23 @@ +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { getUserByProviderId, insertUser, setRefreshToken } from "../daos/user.dao"; + +export type Payload = { + id: number; + nickname: string; +}; + +export const createOrReadUser = async (provider, providerId, email): Promise => { + if (!provider || !providerId || !email) { + throw new BaseError(status.MISSING_REQUIRED_FIELDS); + } + let user = await getUserByProviderId(provider, providerId); + if (!user) { + user = await insertUser(provider, providerId, email); + } + return { id: user.id, nickname: user.nickname }; +}; + +export const updateRefreshToken = async (refreshToken: string, userId: number) => { + await setRefreshToken(refreshToken, userId); +}; diff --git a/src/utils/jwt.util.ts b/src/utils/jwt.util.ts new file mode 100644 index 0000000..4989d97 --- /dev/null +++ b/src/utils/jwt.util.ts @@ -0,0 +1,67 @@ +import jwt from "jsonwebtoken"; +import { tokenType } from "../services/auth.service"; +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { Payload } from "../services/users.service"; +import { Request } from "express"; + +interface Verified { + isExpired: boolean; + decoded: Decoded; +} + +export interface Decoded extends Payload { + iat: number; + exp: number; +} + +export const generateAccessToken = (payload: Payload): string => { + return jwt.sign(payload, process.env.JWT_ACCESS_SECRET!, { expiresIn: "15m" }); +}; + +export const generateRefreshToken = (): string => { + return jwt.sign({}, process.env.JWT_REFRESH_SECRET!, { expiresIn: "12h" }); +}; + +export const extractAccessToken = (req: Request): string => { + const accessToken = req.headers.authorization?.split(tokenType)[1]; + if (!accessToken) { + throw new BaseError(status.MISSING_ACCESS_TOKEN); + } + return accessToken; +}; + +export const extractRefreshToken = (req: Request) => { + const refreshToken = req.headers.refresh; + if (!refreshToken) { + throw new BaseError(status.MISSING_REFRESH_TOKEN); + } + return refreshToken; +}; + +export const verifyAccessToken = (accessToken: string): Verified => { + try { + return { + isExpired: false, + decoded: jwt.verify(accessToken, process.env.JWT_ACCESS_SECRET!) as Decoded, + }; + } catch (err) { + if (err instanceof jwt.TokenExpiredError) { + return { + isExpired: true, + decoded: jwt.decode(accessToken) as Decoded, + }; + } + throw new BaseError(status.ACCESS_TOKEN_VERIFICATION_FAILED); + } +}; + +export const isRefreshTokenValid = (refreshToken) => { + try { + jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET!); + return true; + } catch (err) { + console.log(err); + return false; + } +}; diff --git a/tsconfig.json b/tsconfig.json index ed434fb..ed39ce7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -32,7 +32,7 @@ // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */ // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */ // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */ - // "types": [], /* Specify type package names to be included without being referenced in a source file. */ + "types": ["node"] /* Specify type package names to be included without being referenced in a source file. */, // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */ // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */ @@ -106,6 +106,6 @@ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ "skipLibCheck": true /* Skip type checking all .d.ts files. */ }, - "include": ["src/**/*", "config/**/*.ts", "src/models/**/*.ts"], + "include": ["src/**/*", "src/config/**/*.ts"], "exclude": ["node_modules"] } From ab24a7ec7ec59f46c6acabcd1623d5f9f5e214c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sat, 20 Jan 2024 13:01:21 +0900 Subject: [PATCH 002/147] =?UTF-8?q?Feat:=20team,=20member=20=ED=85=8C?= =?UTF-8?q?=EC=9D=B4=EB=B8=94=20=EC=84=A4=EA=B3=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- src/models/member.model.ts | 34 ++++++++++++++++++ src/models/team.model.ts | 70 ++++++++++++++++++++++++++++++++++++++ src/models/user.model.ts | 7 ++-- 4 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 src/models/member.model.ts create mode 100644 src/models/team.model.ts diff --git a/package.json b/package.json index fb4bd49..4c88421 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "tiimmate-backend", + "name": "teammate-backend", "version": "0.0.1", "description": "", "main": "app.ts", diff --git a/src/models/member.model.ts b/src/models/member.model.ts new file mode 100644 index 0000000..44dbdd7 --- /dev/null +++ b/src/models/member.model.ts @@ -0,0 +1,34 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class Member extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + Member.init( + { + teamId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + userId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "Member", + tableName: "member", + paranoid: false, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.Member.belongsTo(db.Team, { foreignKey: "team_id" }); + db.Member.belongsTo(db.User, { foreignKey: "user_id" }); + } +} + +module.exports = Member; diff --git a/src/models/team.model.ts b/src/models/team.model.ts new file mode 100644 index 0000000..ab54585 --- /dev/null +++ b/src/models/team.model.ts @@ -0,0 +1,70 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class Team extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + Team.init( + { + logo: { + type: new DataTypes.STRING(100), + allowNull: true, + }, + name: { + type: new DataTypes.STRING(20), + allowNull: false, + }, + description: { + type: new DataTypes.STRING(400), + allowNull: true, + }, + gender: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + ageGroup: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + region: { + type: new DataTypes.STRING(200), + allowNull: false, + }, + gymName: { + type: new DataTypes.STRING(100), + allowNull: false, + }, + leaderId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + inviteCode: { + type: new DataTypes.STRING(100), + allowNull: false, + }, + skillLevel: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + mannerLevel: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "Team", + tableName: "team", + paranoid: true, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.Team.hasMany(db.Member, { foreignKey: "team_id" }); + db.Team.belongsTo(db.User, { foreignKey: "leader_id" }); + } +} + +module.exports = Team; diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 54f1717..76e3e4f 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -33,14 +33,17 @@ class User extends Model, InferCreationAttributes> { timestamps: true, underscored: true, modelName: "User", - tableName: "users", + tableName: "user", paranoid: true, charset: "utf8", collate: "utf8_general_ci", }, ); } - static associate(db) {} + static associate(db) { + db.User.hasMany(db.Team, { foreignKey: "leader_id" }); + db.User.hasMany(db.Member, { foreignKey: "user_id" }); + } } module.exports = User; From b7a633f8fad35424bd9beca881545d36a5269c7a Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Wed, 24 Jan 2024 21:04:52 +0900 Subject: [PATCH 003/147] =?UTF-8?q?[FEAT]=20=EC=A2=85=EB=AA=A9=EB=B3=84=20?= =?UTF-8?q?=EB=82=98=EC=9D=98=20=ED=8C=80=20=EC=A1=B0=ED=9A=8C=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #4 * Feat: 컨트롤러 함수 추가 #4 * Feat: Team 테이블에 category 필드 추가 #4 * Feat: 서비스 함수 추가 #4 * Feat: DAO 함수 추가 #4 --- package-lock.json | 34 ++++++++++++--- package.json | 4 +- src/app.ts | 3 ++ src/controllers/teams.controller.ts | 13 ++++++ src/daos/team.dao.ts | 68 +++++++++++++++++++++++++++++ src/models/team.model.ts | 4 ++ src/routes/teams.route.ts | 15 +++++++ src/services/teams.service.ts | 20 +++++++++ 8 files changed, 154 insertions(+), 7 deletions(-) create mode 100644 src/controllers/teams.controller.ts create mode 100644 src/daos/team.dao.ts create mode 100644 src/routes/teams.route.ts create mode 100644 src/services/teams.service.ts diff --git a/package-lock.json b/package-lock.json index f3ca8ab..def241c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "tiimmate-backend", + "name": "teammate-backend", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "tiimmate-backend", + "name": "teammate-backend", "version": "0.0.1", "license": "ISC", "dependencies": { @@ -21,7 +21,9 @@ "passport-kakao": "^1.0.1", "passport-naver": "^1.0.6", "sequelize": "^6.35.2", - "sequelize-cli": "^6.6.2" + "sequelize-cli": "^6.6.2", + "uuid": "^9.0.1", + "zod": "^3.22.4" }, "devDependencies": { "@types/cors": "^2.8.17", @@ -3741,6 +3743,14 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, + "node_modules/sequelize/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/serve-static": { "version": "1.15.0", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", @@ -4216,9 +4226,13 @@ } }, "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "bin": { "uuid": "dist/bin/uuid" } @@ -4452,6 +4466,14 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } } } } diff --git a/package.json b/package.json index 4c88421..b9e6dfa 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,9 @@ "passport-kakao": "^1.0.1", "passport-naver": "^1.0.6", "sequelize": "^6.35.2", - "sequelize-cli": "^6.6.2" + "sequelize-cli": "^6.6.2", + "uuid": "^9.0.1", + "zod": "^3.22.4" }, "devDependencies": { "@types/cors": "^2.8.17", diff --git a/src/app.ts b/src/app.ts index c658210..1cafe74 100644 --- a/src/app.ts +++ b/src/app.ts @@ -12,6 +12,7 @@ import { BaseError } from "./config/error"; import { status } from "./config/response.status"; import { authRouter } from "./routes/auth.route"; +import { teamsRouter } from "./routes/teams.route"; const app = express(); @@ -31,6 +32,7 @@ app.use(express.urlencoded({ extended: false })); app.use(passport.initialize()); app.use("/auth", authRouter); +app.use("/teams", teamsRouter); app.use((req: Request, res: Response, next: NextFunction) => { const err = new BaseError(status.NOT_FOUND); @@ -40,6 +42,7 @@ app.use((req: Request, res: Response, next: NextFunction) => { app.use((err, req: Request, res: Response, next: NextFunction) => { res.locals.message = err.message; res.locals.err = process.env.NODE_ENV !== "production" ? err : {}; + console.log(err); const error = err instanceof BaseError ? err : new BaseError(status.INTERNAL_SERVER_ERROR); res.status(error.data.status).send(response(error.data)); }); diff --git a/src/controllers/teams.controller.ts b/src/controllers/teams.controller.ts new file mode 100644 index 0000000..3020663 --- /dev/null +++ b/src/controllers/teams.controller.ts @@ -0,0 +1,13 @@ +import { Response } from "express"; +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { readTeamPreviewsByCategory } from "../services/teams.service"; +// import { createTeam } from "../services/teams.service"; + +export const fetchTeamPreviewByCategory = async (req, res, next) => { + res.send(response(status.SUCCESS, await readTeamPreviewsByCategory(req.user.id, req.params))); +}; + +// export const registerTeam = async (req, res: Response, next) => { +// res.send(response(status.SUCCESS, await createTeam(req.body))); +// }; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts new file mode 100644 index 0000000..bd1f207 --- /dev/null +++ b/src/daos/team.dao.ts @@ -0,0 +1,68 @@ +import db from "../models"; + +// const User = require("../models/user.model"); +// import { v4 as uuidv4 } from "uuid"; +// import { CreateTeamInput } from "../schemas/team.schema"; +// import { User } from "../models/user.model"; + +// import { Team, User } from "../models"; + +export const getTeamPreviewByCategory = async (userId, category) => { + const teamsAsLeader = await db.Team.findAll({ + raw: true, + where: { + category, + leaderId: userId, + }, + attributes: ["name", "logo"], + }); + const teamsAsMember = await db.Team.findAll({ + raw: true, + where: { + category, + }, + include: [ + { + model: db.Member, + where: { + userId, + }, + attributes: [], + }, + ], + attributes: ["name", "logo"], + }); + const previews = [...teamsAsLeader, ...teamsAsMember].sort((a, b) => a.name.localeCompare(b.name)); + return previews; +}; + +// export const insertTeam = async (logo: string, data, userId, inviteCode) => { +// return await db.Team.create({ +// logo, +// name: data.name, +// description: data.description, +// gender: data.gender, +// ageGroup: data.ageGroup, +// region: data.region, +// gymName: data.gymName, +// leaderId: userId, +// inviteCode: uuidv4(), +// skillLevel: 1, +// mannerLevel: 1, +// }); +// }; + +// export const insertTeam = async (data: CreateTeamInput) => { +// return await db.Team.create({ +// ...data, +// logo: "logo test", +// gender: 1, +// ageGroup: 1, +// region: "region test", +// gymName: "gym name test", +// leaderId: 1, +// inviteCode: uuidv4(), +// skillLevel: 1, +// mannerLevel: 1, +// }); +// }; diff --git a/src/models/team.model.ts b/src/models/team.model.ts index ab54585..3ee5715 100644 --- a/src/models/team.model.ts +++ b/src/models/team.model.ts @@ -48,6 +48,10 @@ class Team extends Model, InferCreationAttributes> { type: new DataTypes.INTEGER(), allowNull: false, }, + category: { + type: new DataTypes.STRING(15), + allowNull: false, + }, }, { sequelize, diff --git a/src/routes/teams.route.ts b/src/routes/teams.route.ts new file mode 100644 index 0000000..7a904b4 --- /dev/null +++ b/src/routes/teams.route.ts @@ -0,0 +1,15 @@ +import express from "express"; +import asyncHandler from "express-async-handler"; +import { fetchTeamPreviewByCategory } from "../controllers/teams.controller"; +import { verifyUser } from "../middlewares/auth.middleware"; +// import { registerTeam } from "../controllers/teams.controller"; +// import { validateInput } from "../middlewares/validate.middleware"; +// import { createTeamSchema } from "../schemas/team.schema"; + +export const teamsRouter = express.Router(); + +// teamsRouter.use(verifyUser); + +teamsRouter.get("/:category", asyncHandler(fetchTeamPreviewByCategory)); + +// teamsRouter.post("/", validateInput(createTeamSchema), asyncHandler(registerTeam)); diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts new file mode 100644 index 0000000..77ec8b1 --- /dev/null +++ b/src/services/teams.service.ts @@ -0,0 +1,20 @@ +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { getTeamPreviewByCategory } from "../daos/team.dao"; +// import { insertTeam } from "../daos/team.dao"; +// import { CreateTeamInput } from "../schemas/team.schema"; +// import { insertTeam } from "../daos/team.dao"; + +export const readTeamPreviewsByCategory = async (userId, params) => { + return await getTeamPreviewByCategory(userId, params.category); +}; + +// export const createTeam = async (body: CreateTeamInput) => { +// // //사진 gcs에 업로드 => 파일 경로 가져오기 +// console.log(body.logo); +// throw new Error(); +// const result = await insertTeam(body); +// //console.log(result); +// //return result; +// // return; +// }; From 8792b6ac6e8a7f5d90a66d8562c07148ff02cf91 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:00:52 +0900 Subject: [PATCH 004/147] =?UTF-8?q?[FEAT]=20=ED=8C=80=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84=20(#7)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 함수 추가 #6 * Feat: 컨트롤러 함수 추가 #6 * Feat: 서비스 함수 추가 #6 * Feat: DAO 함수 추가 #6 * Feat: DTO 함수 추가 #6 --- src/controllers/teams.controller.ts | 10 +++++++--- src/daos/member.dao.ts | 17 +++++++++++++++++ src/daos/team.dao.ts | 12 +++++++++++- src/daos/user.dao.ts | 14 ++++++++++++++ src/dtos/team.dto.ts | 21 +++++++++++++++++++++ src/routes/teams.route.ts | 6 ++++-- src/services/teams.service.ts | 17 ++++++++++++++--- 7 files changed, 88 insertions(+), 9 deletions(-) create mode 100644 src/daos/member.dao.ts create mode 100644 src/dtos/team.dto.ts diff --git a/src/controllers/teams.controller.ts b/src/controllers/teams.controller.ts index 3020663..54f3d03 100644 --- a/src/controllers/teams.controller.ts +++ b/src/controllers/teams.controller.ts @@ -1,13 +1,17 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readTeamPreviewsByCategory } from "../services/teams.service"; +import { readTeamPreviewsByCategory, readTeamDetail } from "../services/teams.service"; // import { createTeam } from "../services/teams.service"; -export const fetchTeamPreviewByCategory = async (req, res, next) => { - res.send(response(status.SUCCESS, await readTeamPreviewsByCategory(req.user.id, req.params))); +export const fetchTeamPreviewsByCategory = async (req, res, next) => { + res.send(response(status.SUCCESS, await readTeamPreviewsByCategory(req.user.id, req.query))); }; // export const registerTeam = async (req, res: Response, next) => { // res.send(response(status.SUCCESS, await createTeam(req.body))); // }; + +export const fetchTeamDetail = async (req, res, next) => { + res.send(response(status.SUCCESS, await readTeamDetail(req.user.id, req.params))); +}; diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts new file mode 100644 index 0000000..95bb7b1 --- /dev/null +++ b/src/daos/member.dao.ts @@ -0,0 +1,17 @@ +import db from "../models"; + +export const findMemberInfoByTeamId = async (teamId, userInfoAttributes) => { + return await db.Member.findAll({ + raw: true, + where: { + teamId, + }, + include: [ + { + model: db.User, + attributes: userInfoAttributes(), + }, + ], + attributes: [], + }); +}; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index bd1f207..594394e 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -7,7 +7,7 @@ import db from "../models"; // import { Team, User } from "../models"; -export const getTeamPreviewByCategory = async (userId, category) => { +export const findTeamPreviewByCategory = async (userId, category) => { const teamsAsLeader = await db.Team.findAll({ raw: true, where: { @@ -66,3 +66,13 @@ export const getTeamPreviewByCategory = async (userId, category) => { // mannerLevel: 1, // }); // }; + +export const getTeamDetail = async (teamId) => { + return await db.Team.findOne({ + raw: true, + where: { + id: teamId, + }, + attributes: ["name", "logo", "skillLevel", "mannerLevel", "description", "leaderId"], + }); +}; diff --git a/src/daos/user.dao.ts b/src/daos/user.dao.ts index f0c1390..aa0df7a 100644 --- a/src/daos/user.dao.ts +++ b/src/daos/user.dao.ts @@ -41,3 +41,17 @@ export const getRefreshToken = async (userId: number) => { }); return user.refreshToken; }; + +export const getUserInfoById = async (id) => { + return await db.User.findOne({ + raw: true, + where: { + id, + }, + attributes: userInfoAttributes(), + }); +}; + +export const userInfoAttributes = () => { + return ["nickname"]; //TODO: add height, weight, positon +}; diff --git a/src/dtos/team.dto.ts b/src/dtos/team.dto.ts new file mode 100644 index 0000000..2dc3984 --- /dev/null +++ b/src/dtos/team.dto.ts @@ -0,0 +1,21 @@ +export const readTeamDetailResponseDTO = (detail, leaderInfo, memberInfo, isTeamLeader) => { + const member = memberInfo.map((info) => ({ + //TODO + nickname: info["User.nickname"], + height: null, + weight: null, + position: null, + })); + return { + name: detail.name, + logo: detail.logo, + skillLevel: detail.skillLevel, + mannerLevel: detail.mannerLevel, + description: detail.description, + participants: { + leader: leaderInfo, + member, + }, + isTeamLeader, + }; +}; diff --git a/src/routes/teams.route.ts b/src/routes/teams.route.ts index 7a904b4..44a9247 100644 --- a/src/routes/teams.route.ts +++ b/src/routes/teams.route.ts @@ -1,6 +1,6 @@ import express from "express"; import asyncHandler from "express-async-handler"; -import { fetchTeamPreviewByCategory } from "../controllers/teams.controller"; +import { fetchTeamPreviewsByCategory, fetchTeamDetail } from "../controllers/teams.controller"; import { verifyUser } from "../middlewares/auth.middleware"; // import { registerTeam } from "../controllers/teams.controller"; // import { validateInput } from "../middlewares/validate.middleware"; @@ -10,6 +10,8 @@ export const teamsRouter = express.Router(); // teamsRouter.use(verifyUser); -teamsRouter.get("/:category", asyncHandler(fetchTeamPreviewByCategory)); +teamsRouter.get("/", asyncHandler(fetchTeamPreviewsByCategory)); // teamsRouter.post("/", validateInput(createTeamSchema), asyncHandler(registerTeam)); + +teamsRouter.get("/:teamId", asyncHandler(fetchTeamDetail)); diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index 77ec8b1..38f15d5 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -1,12 +1,15 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; -import { getTeamPreviewByCategory } from "../daos/team.dao"; +import { findMemberInfoByTeamId } from "../daos/member.dao"; +import { findTeamPreviewByCategory, getTeamDetail } from "../daos/team.dao"; +import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; +import { readTeamDetailResponseDTO } from "../dtos/team.dto"; // import { insertTeam } from "../daos/team.dao"; // import { CreateTeamInput } from "../schemas/team.schema"; // import { insertTeam } from "../daos/team.dao"; -export const readTeamPreviewsByCategory = async (userId, params) => { - return await getTeamPreviewByCategory(userId, params.category); +export const readTeamPreviewsByCategory = async (userId, query) => { + return await findTeamPreviewByCategory(userId, query.category); }; // export const createTeam = async (body: CreateTeamInput) => { @@ -18,3 +21,11 @@ export const readTeamPreviewsByCategory = async (userId, params) => { // //return result; // // return; // }; + +export const readTeamDetail = async (userId, params) => { + const teamId = params.teamId; + const detail = await getTeamDetail(teamId); + const leaderInfo = await getUserInfoById(detail.leaderId); + const memberInfo = await findMemberInfoByTeamId(teamId, userInfoAttributes); + return readTeamDetailResponseDTO(detail, leaderInfo, memberInfo, userId == detail.leaderId); +}; From dc50b6ec93b8f592c783c4365f845913f924afc3 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Wed, 24 Jan 2024 23:53:07 +0900 Subject: [PATCH 005/147] =?UTF-8?q?[FEAT]=20=EA=B8=B0=EC=A1=B4=20=ED=8C=80?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20API=20=EA=B5=AC=ED=98=84=20(#9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #8 * Feat: 라우트 함수 추가 #8 * Feat: 컨트롤러 함수 추가 #8 * Feat: 서비스 함수, 에러 코드 추가 #8 * Feat: DAO 함수 추가 #8 --- src/app.ts | 2 ++ src/config/response.status.ts | 16 ++++++++++++++++ src/controllers/members.controller.ts | 7 +++++++ src/daos/member.dao.ts | 17 +++++++++++++++++ src/daos/team.dao.ts | 11 +++++++++++ src/routes/members.route.ts | 10 ++++++++++ src/services/members.service.ts | 16 ++++++++++++++++ 7 files changed, 79 insertions(+) create mode 100644 src/controllers/members.controller.ts create mode 100644 src/routes/members.route.ts create mode 100644 src/services/members.service.ts diff --git a/src/app.ts b/src/app.ts index 1cafe74..a831730 100644 --- a/src/app.ts +++ b/src/app.ts @@ -13,6 +13,7 @@ import { status } from "./config/response.status"; import { authRouter } from "./routes/auth.route"; import { teamsRouter } from "./routes/teams.route"; +import { membersRouter } from "./routes/members.route"; const app = express(); @@ -33,6 +34,7 @@ app.use(passport.initialize()); app.use("/auth", authRouter); app.use("/teams", teamsRouter); +app.use("/members", membersRouter); app.use((req: Request, res: Response, next: NextFunction) => { const err = new BaseError(status.NOT_FOUND); diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 95f2091..96f1fd0 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -69,4 +69,20 @@ export const status: { [key: string]: Status } = { code: "AUTH006", message: "Refresh Token 검증에 실패했습니다. 다시 로그인해 주세요.", }, + + //team err + NO_JOINABLE_TEAM: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "TEAM001", + message: "해당 초대 코드로 가입할 수 있는 팀이 없습니다.", + }, + + //member err + ALREADY_JOINED: { + status: StatusCodes.CONFLICT, + isSuccess: false, + code: "MEMBER001", + message: "해당 팀에 이미 가입되어 있습니다.", + }, }; diff --git a/src/controllers/members.controller.ts b/src/controllers/members.controller.ts new file mode 100644 index 0000000..201ea28 --- /dev/null +++ b/src/controllers/members.controller.ts @@ -0,0 +1,7 @@ +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { createMember } from "../services/members.service"; + +export const addMember = async (req, res, next) => { + res.send(response(status.SUCCESS, await createMember(req.user.id, req.body))); +}; diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts index 95bb7b1..07105ff 100644 --- a/src/daos/member.dao.ts +++ b/src/daos/member.dao.ts @@ -15,3 +15,20 @@ export const findMemberInfoByTeamId = async (teamId, userInfoAttributes) => { attributes: [], }); }; + +export const insertMember = async (teamId, userId) => { + await db.Member.create({ + teamId, + userId, + }); +}; + +export const isMemberExist = async (teamId, userId) => { + const member = await db.Member.findOne({ + where: { + teamId, + userId, + }, + }); + return member !== null; +}; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index 594394e..5bc5503 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -76,3 +76,14 @@ export const getTeamDetail = async (teamId) => { attributes: ["name", "logo", "skillLevel", "mannerLevel", "description", "leaderId"], }); }; + +export const getTeamIdByInviteCode = async (inviteCode): Promise => { + const team = await db.Team.findOne({ + raw: true, + where: { + inviteCode, + }, + attributes: ["id"], + }); + return team?.id; +}; diff --git a/src/routes/members.route.ts b/src/routes/members.route.ts new file mode 100644 index 0000000..824887b --- /dev/null +++ b/src/routes/members.route.ts @@ -0,0 +1,10 @@ +import express from "express"; +import asyncHandler from "express-async-handler"; +import { verifyUser } from "../middlewares/auth.middleware"; +import { addMember } from "../controllers/members.controller"; + +export const membersRouter = express.Router(); + +membersRouter.use(verifyUser); + +membersRouter.post("/", asyncHandler(addMember)); diff --git a/src/services/members.service.ts b/src/services/members.service.ts new file mode 100644 index 0000000..f5defb6 --- /dev/null +++ b/src/services/members.service.ts @@ -0,0 +1,16 @@ +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { insertMember, isMemberExist } from "../daos/member.dao"; +import { getTeamIdByInviteCode } from "../daos/team.dao"; + +export const createMember = async (userId, body) => { + const teamId = await getTeamIdByInviteCode(body.inviteCode); + if (!teamId) { + throw new BaseError(status.NO_JOINABLE_TEAM); + } + if (await isMemberExist(teamId, userId)) { + throw new BaseError(status.ALREADY_JOINED); + } + await insertMember(teamId, userId); + return; +}; From 74fa8deebbb8f4c3e6ea56fa9ad47666208e5cc3 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:19:35 +0900 Subject: [PATCH 006/147] =?UTF-8?q?[FEAT]=20=EC=83=88=EB=A1=9C=EC=9A=B4=20?= =?UTF-8?q?=ED=8C=80=20=EC=83=9D=EC=84=B1=20API=20=EA=B5=AC=ED=98=84=20(#1?= =?UTF-8?q?2)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #8 * Feat: 라우트 함수 추가 #8 * Feat: 컨트롤러 함수 추가 #8 * Feat: 서비스 함수, 에러 코드 추가 #8 * Feat: DAO 함수 추가 #8 * Feat: 라우트 함수 추가 #3 * Feat: 컨트롤러 함수 추가 #3 * Feat: request body 유효성 검사 #3 * Feat: 서비스 함수 추가 #3 * Feat: DAO 함수 추가 #3 --- package-lock.json | 30 +++++++------- src/config/error.ts | 4 +- src/config/response.status.ts | 7 ++++ src/config/response.ts | 4 +- src/constants/age-group.constant.ts | 19 +++++++++ src/constants/gender.constant.ts | 17 ++++++++ src/constants/level.constant.ts | 16 ++++++++ src/controllers/teams.controller.ts | 15 ++++--- src/daos/team.dao.ts | 55 +++++++++----------------- src/middlewares/validate.middleware.ts | 19 +++++++++ src/routes/teams.route.ts | 13 +++--- src/schemas/team.schema.ts | 18 +++++++++ src/services/teams.service.ts | 24 +++++------ 13 files changed, 159 insertions(+), 82 deletions(-) create mode 100644 src/constants/age-group.constant.ts create mode 100644 src/constants/gender.constant.ts create mode 100644 src/constants/level.constant.ts create mode 100644 src/middlewares/validate.middleware.ts create mode 100644 src/schemas/team.schema.ts diff --git a/package-lock.json b/package-lock.json index def241c..f40e43f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1753,21 +1753,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/eslint/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint/node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", @@ -3074,6 +3059,21 @@ "node": ">= 0.8.0" } }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", diff --git a/src/config/error.ts b/src/config/error.ts index 6e45fb0..44e5076 100644 --- a/src/config/error.ts +++ b/src/config/error.ts @@ -3,8 +3,8 @@ import { Status } from "./response.status"; export class BaseError extends Error { public data: Status; - constructor(data: Status) { + constructor(data: Status, detail?: any) { super(data.message); - this.data = data; + this.data = detail ? { ...data, detail } : data; } } diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 96f1fd0..66d28ee 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -5,6 +5,7 @@ export interface Status { isSuccess: boolean; code: number | string; message: string; + detail?: any; } export const status: { [key: string]: Status } = { @@ -31,6 +32,12 @@ export const status: { [key: string]: Status } = { code: "COMMON002", message: "요청에 필요한 정보가 누락되었습니다.", }, + REQUEST_VALIDATION_ERROR: { + status: StatusCodes.BAD_REQUEST, + isSuccess: false, + code: "COMMON003", + message: "요청이 유효하지 않습니다.", + }, //auth err MISSING_ACCESS_TOKEN: { diff --git a/src/config/response.ts b/src/config/response.ts index 6e794f0..d93dcf0 100644 --- a/src/config/response.ts +++ b/src/config/response.ts @@ -4,14 +4,16 @@ export interface Response { isSuccess: boolean; code: number | string; message: string; + detail?: any; result?: any; } -export const response = ({ isSuccess, code, message }: Status, result?: any): Response => { +export const response = ({ isSuccess, code, message, detail }: Status, result?: any): Response => { return { isSuccess: isSuccess, code: code, message: message, + detail: detail, result: result, }; }; diff --git a/src/constants/age-group.constant.ts b/src/constants/age-group.constant.ts new file mode 100644 index 0000000..14318e2 --- /dev/null +++ b/src/constants/age-group.constant.ts @@ -0,0 +1,19 @@ +type AgeGroup = { + name: string; +}; + +const AgeGroups: { [key: number]: AgeGroup } = { + 1: { name: "10대" }, + 2: { name: "20대" }, + 3: { name: "30대" }, + 4: { name: "40대" }, + 5: { name: "50대 이상~" }, +}; + +export const getAgeGroupsLength = (): number => { + return Object.keys(AgeGroups).length; +}; + +export const getAgeGroupById = (id: number): string | undefined => { + return AgeGroups[id].name; +}; diff --git a/src/constants/gender.constant.ts b/src/constants/gender.constant.ts new file mode 100644 index 0000000..ea550ba --- /dev/null +++ b/src/constants/gender.constant.ts @@ -0,0 +1,17 @@ +type Gender = { + name: string; +}; + +const Genders: { [key: number]: Gender } = { + 1: { name: "여성" }, + 2: { name: "남성" }, + 3: { name: "혼성" }, +}; + +export const getGendersLength = (): number => { + return Object.keys(Genders).length; +}; + +export const getGenderById = (id: number): string | undefined => { + return Genders[id].name; +}; diff --git a/src/constants/level.constant.ts b/src/constants/level.constant.ts new file mode 100644 index 0000000..f39a2bf --- /dev/null +++ b/src/constants/level.constant.ts @@ -0,0 +1,16 @@ +type Level = { + name: string; +}; + +const Levels: { [key: number]: Level } = { + 1: { name: "Level 1" }, + 2: { name: "Level 2" }, + 3: { name: "Level 3" }, + 4: { name: "Level 4" }, +}; + +export const defaultLevel = 1; + +export const getLevelById = (id: number): string | undefined => { + return Levels[id].name; +}; diff --git a/src/controllers/teams.controller.ts b/src/controllers/teams.controller.ts index 54f3d03..af3960b 100644 --- a/src/controllers/teams.controller.ts +++ b/src/controllers/teams.controller.ts @@ -1,17 +1,20 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readTeamPreviewsByCategory, readTeamDetail } from "../services/teams.service"; -// import { createTeam } from "../services/teams.service"; +import { readTeamPreviewsByCategory, readTeamDetail, createTeam } from "../services/teams.service"; -export const fetchTeamPreviewsByCategory = async (req, res, next) => { +export const fetchTeamPreviewsByCategory = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readTeamPreviewsByCategory(req.user.id, req.query))); }; -// export const registerTeam = async (req, res: Response, next) => { -// res.send(response(status.SUCCESS, await createTeam(req.body))); +export const addTeam = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await createTeam(req.user.id, req.body))); +}; + +// export const modifyTeam = async (req, res: Response, next) => { +// res.send(response(status.SUCCESS, await updateTeam(req.body))); // }; -export const fetchTeamDetail = async (req, res, next) => { +export const fetchTeamDetail = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readTeamDetail(req.user.id, req.params))); }; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index 5bc5503..83c4f06 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -1,11 +1,6 @@ +import { defaultLevel } from "../constants/level.constant"; import db from "../models"; - -// const User = require("../models/user.model"); -// import { v4 as uuidv4 } from "uuid"; -// import { CreateTeamInput } from "../schemas/team.schema"; -// import { User } from "../models/user.model"; - -// import { Team, User } from "../models"; +import { CreateTeamSchema } from "../schemas/team.schema"; export const findTeamPreviewByCategory = async (userId, category) => { const teamsAsLeader = await db.Team.findAll({ @@ -36,36 +31,22 @@ export const findTeamPreviewByCategory = async (userId, category) => { return previews; }; -// export const insertTeam = async (logo: string, data, userId, inviteCode) => { -// return await db.Team.create({ -// logo, -// name: data.name, -// description: data.description, -// gender: data.gender, -// ageGroup: data.ageGroup, -// region: data.region, -// gymName: data.gymName, -// leaderId: userId, -// inviteCode: uuidv4(), -// skillLevel: 1, -// mannerLevel: 1, -// }); -// }; - -// export const insertTeam = async (data: CreateTeamInput) => { -// return await db.Team.create({ -// ...data, -// logo: "logo test", -// gender: 1, -// ageGroup: 1, -// region: "region test", -// gymName: "gym name test", -// leaderId: 1, -// inviteCode: uuidv4(), -// skillLevel: 1, -// mannerLevel: 1, -// }); -// }; +export const insertTeam = async (data: CreateTeamSchema, userId: number, inviteCode: string) => { + await db.Team.create({ + logo: data.logo, + name: data.name, + description: data.description, + gender: data.gender, + ageGroup: data.ageGroup, + region: data.region, + gymName: data.gymName, + leaderId: userId, + inviteCode, + skillLevel: defaultLevel, + mannerLevel: defaultLevel, + category: data.category, + }); +}; export const getTeamDetail = async (teamId) => { return await db.Team.findOne({ diff --git a/src/middlewares/validate.middleware.ts b/src/middlewares/validate.middleware.ts new file mode 100644 index 0000000..4896bae --- /dev/null +++ b/src/middlewares/validate.middleware.ts @@ -0,0 +1,19 @@ +import { AnyZodObject, ZodError } from "zod"; +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { NextFunction } from "express"; + +export const validateBody = (schema: AnyZodObject) => (req, res, next: NextFunction) => { + try { + schema.parse(req.body); + next(); + } catch (err: any) { + if (err instanceof ZodError) { + const detail = err.errors.map((err) => ({ + field: err.path[0], + description: err.message, + })); + throw new BaseError(status.REQUEST_VALIDATION_ERROR, detail); + } + } +}; diff --git a/src/routes/teams.route.ts b/src/routes/teams.route.ts index 44a9247..bbe4947 100644 --- a/src/routes/teams.route.ts +++ b/src/routes/teams.route.ts @@ -1,17 +1,18 @@ import express from "express"; import asyncHandler from "express-async-handler"; -import { fetchTeamPreviewsByCategory, fetchTeamDetail } from "../controllers/teams.controller"; import { verifyUser } from "../middlewares/auth.middleware"; -// import { registerTeam } from "../controllers/teams.controller"; -// import { validateInput } from "../middlewares/validate.middleware"; -// import { createTeamSchema } from "../schemas/team.schema"; +import { validateBody } from "../middlewares/validate.middleware"; +import { createTeam } from "../schemas/team.schema"; +import { fetchTeamPreviewsByCategory, fetchTeamDetail, addTeam } from "../controllers/teams.controller"; export const teamsRouter = express.Router(); -// teamsRouter.use(verifyUser); +teamsRouter.use(verifyUser); teamsRouter.get("/", asyncHandler(fetchTeamPreviewsByCategory)); -// teamsRouter.post("/", validateInput(createTeamSchema), asyncHandler(registerTeam)); +teamsRouter.post("/", validateBody(createTeam), asyncHandler(addTeam)); + +// teamsRouter.put("/:teamId", asyncHandler(modifyTeam)); teamsRouter.get("/:teamId", asyncHandler(fetchTeamDetail)); diff --git a/src/schemas/team.schema.ts b/src/schemas/team.schema.ts new file mode 100644 index 0000000..b87b3eb --- /dev/null +++ b/src/schemas/team.schema.ts @@ -0,0 +1,18 @@ +import { TypeOf, object, z } from "zod"; +import { getGendersLength } from "../constants/gender.constant"; +import { getAgeGroupsLength } from "../constants/age-group.constant"; + +const categories = z.enum(["농구", "야구", "테니스", "축구", "풋살", "배구", "볼링", "배드민턴", "탁구"]); + +export const createTeam = object({ + logo: z.optional(z.string()), + name: z.string().max(20), + description: z.optional(z.string()), + gender: z.number().int().min(1).max(getGendersLength()), + ageGroup: z.number().int().min(1).max(getAgeGroupsLength()), + region: z.string(), + gymName: z.string(), + category: categories, +}); + +export type CreateTeamSchema = TypeOf; diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index 38f15d5..ee36f77 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -1,26 +1,20 @@ -import { BaseError } from "../config/error"; -import { status } from "../config/response.status"; import { findMemberInfoByTeamId } from "../daos/member.dao"; -import { findTeamPreviewByCategory, getTeamDetail } from "../daos/team.dao"; +import { findTeamPreviewByCategory, getTeamDetail, insertTeam } from "../daos/team.dao"; import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; import { readTeamDetailResponseDTO } from "../dtos/team.dto"; -// import { insertTeam } from "../daos/team.dao"; -// import { CreateTeamInput } from "../schemas/team.schema"; -// import { insertTeam } from "../daos/team.dao"; +import { v4 as uuidv4 } from "uuid"; +import { CreateTeamSchema } from "../schemas/team.schema"; export const readTeamPreviewsByCategory = async (userId, query) => { return await findTeamPreviewByCategory(userId, query.category); }; -// export const createTeam = async (body: CreateTeamInput) => { -// // //사진 gcs에 업로드 => 파일 경로 가져오기 -// console.log(body.logo); -// throw new Error(); -// const result = await insertTeam(body); -// //console.log(result); -// //return result; -// // return; -// }; +export const createTeam = async (userId, body: CreateTeamSchema) => { + await insertTeam(body, userId, uuidv4()); + return; +}; + +// export const updateTeam = async (body) => {}; export const readTeamDetail = async (userId, params) => { const teamId = params.teamId; From ec7a52c5b0ec6b47ab3cb9981f3d2cb2f2af9b17 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Thu, 25 Jan 2024 23:50:35 +0900 Subject: [PATCH 007/147] =?UTF-8?q?[FEAT]=20=EB=82=A0=EC=A7=9C=EB=B3=84=20?= =?UTF-8?q?=EA=B2=8C=EC=8A=A4=ED=8A=B8=20=EB=AA=A8=EC=A7=91=EA=B8=80=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84=20(#14)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * [FEAT]게스트모집 조회 API 구현 * Feat: guest, guestUser 테이블 생성 #11 * Feat: 라우트 연결 #11 * Feat: guest, guest user 테이블 생성 #11 * Feat: 날짜별 게스트 모집글 조회 #11 * Style: guest user 테이블 이름 언더스코어 처리 #11 * Feat: guest 테이블 모집 인원 수 필드 추가 #11 * Feat: DAO 함수 추가 #11 * Feat: 팀의 멤버 수 조회하는 함수 추가 #11 * Feat: 서비스 함수 추가 #11 * Feat: DTO 함수 추가 #11 * Feat: API와 상관 없는 코드 주석 처리 #11 --------- Co-authored-by: ByeonYujin124 --- src/app.ts | 2 + src/controllers/guest.controller.ts | 33 ++++++++ src/daos/guest.dao.ts | 119 ++++++++++++++++++++++++++++ src/daos/member.dao.ts | 10 +++ src/dtos/guest.dto.ts | 13 +++ src/models/guest-user.model.ts | 34 ++++++++ src/models/guest.model.ts | 42 ++++++++++ src/models/team.model.ts | 1 + src/models/user.model.ts | 1 + src/routes/guests.route.ts | 22 +++++ src/services/guest.service.ts | 25 ++++++ 11 files changed, 302 insertions(+) create mode 100644 src/controllers/guest.controller.ts create mode 100644 src/daos/guest.dao.ts create mode 100644 src/dtos/guest.dto.ts create mode 100644 src/models/guest-user.model.ts create mode 100644 src/models/guest.model.ts create mode 100644 src/routes/guests.route.ts create mode 100644 src/services/guest.service.ts diff --git a/src/app.ts b/src/app.ts index a831730..8a60a07 100644 --- a/src/app.ts +++ b/src/app.ts @@ -14,6 +14,7 @@ import { status } from "./config/response.status"; import { authRouter } from "./routes/auth.route"; import { teamsRouter } from "./routes/teams.route"; import { membersRouter } from "./routes/members.route"; +import { guestsRouter } from "./routes/guests.route"; const app = express(); @@ -35,6 +36,7 @@ app.use(passport.initialize()); app.use("/auth", authRouter); app.use("/teams", teamsRouter); app.use("/members", membersRouter); +app.use("/guests", guestsRouter); app.use((req: Request, res: Response, next: NextFunction) => { const err = new BaseError(status.NOT_FOUND); diff --git a/src/controllers/guest.controller.ts b/src/controllers/guest.controller.ts new file mode 100644 index 0000000..946daa6 --- /dev/null +++ b/src/controllers/guest.controller.ts @@ -0,0 +1,33 @@ +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { + readGuesting, + // readGuestingByLevel, + // readGuestingByGender, + // readGuestingByRegion, +} from "../services/guest.service"; +// import { postGuesting } from "../services/guest.service"; + +// export const createGuesting = async (req, res, next) => { +// return res.send(response(status.SUCCESS, await postGuesting(req.params.category, req.query))); +// }; + +export const GuestingPreview = async (req, res, next) => { + return res.send(response(status.SUCCESS, await readGuesting(req.query))); +}; + +// export const GuestingPreviewByLevel = async (req, res, next) => { +// return res.send(response(status.SUCCESS, await readGuestingByLevel(req.team.id, req.query))); +// }; + +// export const GuestingPreviewByGender = async (req, res, next) => { +// return res.send(response(status.SUCCESS, await readGuestingByGender(req.team.id, req.query))); +// }; + +// export const GuestingPreviewByRegion = async (req, res, next) => { +// return res.send(response(status.SUCCESS, await readGuestingByRegion(req.team.id, req.query))); +// }; + +// export const DetailedGuestingPreview = async (req, res, next) => { +// return res.send(response(status.SUCCESS, await getDetailedGuesting(req.team.id, req.params))); +// }; diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts new file mode 100644 index 0000000..b243bc1 --- /dev/null +++ b/src/daos/guest.dao.ts @@ -0,0 +1,119 @@ +import { Sequelize } from "sequelize"; +import db from "../models"; + +export const findGuesting = async (date, category) => { + return await db.Guest.findAll({ + raw: true, + where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + include: [ + { + model: db.Team, + where: { + category, + }, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["gameTime", "recruitCount"], + }); +}; + +// export const findGuestingByGender = async (selectedDate, category, gender) => { +// const Guesting = await db.Guest.findAll({ +// raw: true, +// where: { +// gameTime: selectedDate, +// category, +// gender, +// }, +// include: [ +// { +// model: db.Team, +// as: "TeamInfo", +// attributes: ["name", "region", "gender", "age_group", "skill_level"], +// where: { +// id: db.Sequelize.col("Guest.TeamId"), +// }, +// }, +// ], +// attributes: ["game_time"], +// }); + +// return Guesting.map((guest) => guest["TeamInfo"]); +// }; + +// export const findGuestingByLevel = async (selectedDate, category, level) => { +// const Guesting = await db.Guest.findAll({ +// raw: true, +// where: { +// gameTime: selectedDate, +// category, +// level, +// }, +// include: [ +// { +// model: db.Team, +// as: "TeamInfo", +// attributes: ["name", "region", "gender", "age_group", "skill_level"], +// where: { +// id: db.Sequelize.col("Guest.TeamId"), +// }, +// }, +// ], +// attributes: ["game_time"], +// }); + +// return Guesting.map((guest) => guest["TeamInfo"]); +// }; + +// export const findGuestingByRegion = async (selectedDate, category, region) => { +// const Guesting = await db.Guest.findAll({ +// raw: true, +// where: { +// gameTime: selectedDate, +// category, +// region, +// }, +// include: [ +// { +// model: db.Team, +// as: "TeamInfo", +// attributes: ["name", "region", "gender", "age_group", "skill_level"], +// where: { +// id: db.Sequelize.col("Guest.TeamId"), +// }, +// }, +// ], +// attributes: ["game_time"], +// }); + +// return Guesting.map((guest) => guest["TeamInfo"]); +// }; + +// export const findGuestDetail = async (guestId) => { +// return await db.Guest.findOne({ +// raw: true, +// where: { +// id: guestId, +// }, +// include: [ +// { +// model: db.Team, +// as: "TeamInfo", +// attributes: ["name", "skill_level", "manner_level", "region", "description", "gender", "age_group"], +// where: { +// id: db.Sequelize.col("Guest.TeamId"), +// }, +// }, +// { +// model: db.User, +// as: "TeamMemberInfo", +// attributes: ["nickname"], +// where: { +// id: db.Sequelize.col("User.TeamId"), +// }, +// }, +// ], +// attributes: ["game_time", "dscription"], +// }); +// }; diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts index 07105ff..a5505db 100644 --- a/src/daos/member.dao.ts +++ b/src/daos/member.dao.ts @@ -32,3 +32,13 @@ export const isMemberExist = async (teamId, userId) => { }); return member !== null; }; + +export const getMemberCountByTeamId = async (teamId) => { + const count = await db.Member.count({ + where: { + teamId, + }, + }); + console.log(count); + return count; +}; diff --git a/src/dtos/guest.dto.ts b/src/dtos/guest.dto.ts new file mode 100644 index 0000000..bb4e91c --- /dev/null +++ b/src/dtos/guest.dto.ts @@ -0,0 +1,13 @@ +export const readGuestingResponseDTO = (guestings) => { + console.log(guestings); + return guestings.map((guesting) => ({ + gameTime: guesting.gameTime, + teamName: guesting["Team.name"], + teamRegion: guesting["Team.region"], + teamGender: guesting["Team.gender"], //getGenderById(guesting["Team.gender"]), + memberCount: guesting.memberCount, + teamAgeGroup: guesting["Team.ageGroup"], //getAgeGroupById(guesting["Team.ageGroup"]), + teamSkillLevel: guesting["Team.skillLevel"], //getLevelById(guesting["Team.skillLevel"]) + recruitCount: guesting.recruitCount, + })); +}; diff --git a/src/models/guest-user.model.ts b/src/models/guest-user.model.ts new file mode 100644 index 0000000..585b33a --- /dev/null +++ b/src/models/guest-user.model.ts @@ -0,0 +1,34 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class GuestUser extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + GuestUser.init( + { + guestId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + userId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "GuestUser", + tableName: "guest_User", + paranoid: false, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.GuestUser.belongsTo(db.Guest, { foreignKey: "guest_id" }); + db.GuestUser.belongsTo(db.User, { foreignKey: "user_id" }); + } +} + +module.exports = GuestUser; diff --git a/src/models/guest.model.ts b/src/models/guest.model.ts new file mode 100644 index 0000000..3d99ae0 --- /dev/null +++ b/src/models/guest.model.ts @@ -0,0 +1,42 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class Guest extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + Guest.init( + { + teamId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + gameTime: { + type: new DataTypes.DATE(6), + allowNull: false, + }, + description: { + type: new DataTypes.STRING(400), + allowNull: true, + }, + recruitCount: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "Guest", + tableName: "guest", + paranoid: true, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.Guest.hasMany(db.GuestUser, { foreignKey: "guest_id" }); + db.Guest.belongsTo(db.Team, { foreignKey: "team_id" }); + } +} + +module.exports = Guest; diff --git a/src/models/team.model.ts b/src/models/team.model.ts index 3ee5715..876e818 100644 --- a/src/models/team.model.ts +++ b/src/models/team.model.ts @@ -68,6 +68,7 @@ class Team extends Model, InferCreationAttributes> { static associate(db) { db.Team.hasMany(db.Member, { foreignKey: "team_id" }); db.Team.belongsTo(db.User, { foreignKey: "leader_id" }); + db.Team.hasMany(db.Guest, { foreignKey: "team_id" }); } } diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 76e3e4f..d66f7a6 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -43,6 +43,7 @@ class User extends Model, InferCreationAttributes> { static associate(db) { db.User.hasMany(db.Team, { foreignKey: "leader_id" }); db.User.hasMany(db.Member, { foreignKey: "user_id" }); + db.User.hasMany(db.GuestUser, { foreignKey: "user_id" }); } } diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts new file mode 100644 index 0000000..b73c911 --- /dev/null +++ b/src/routes/guests.route.ts @@ -0,0 +1,22 @@ +import express from "express"; +import asyncHandler from "express-async-handler"; +import { + GuestingPreview, + // GuestingPreviewByLevel, + // GuestingPreviewByGender, + // GuestingPreviewByRegion, +} from "../controllers/guest.controller"; + +export const guestsRouter = express.Router({ mergeParams: true }); + +// guestRouter.post("/", asyncHandler(createGuesting)); + +guestsRouter.get("/", asyncHandler(GuestingPreview)); + +// guestRouter.get("/:guestId", asyncHandler(DetailedGuestingPreview)); + +// guestsRouter.get("/level", asyncHandler(GuestingPreviewByLevel)); + +// guestsRouter.get("/gender", asyncHandler(GuestingPreviewByGender)); + +// guestsRouter.get("/region", asyncHandler(GuestingPreviewByRegion)); diff --git a/src/services/guest.service.ts b/src/services/guest.service.ts new file mode 100644 index 0000000..7b463a4 --- /dev/null +++ b/src/services/guest.service.ts @@ -0,0 +1,25 @@ +// import { BaseError } from "../config/error"; +// import { status } from "../config/response.status"; +import { findGuesting } from "../daos/guest.dao"; +import { getMemberCountByTeamId } from "../daos/member.dao"; +import { readGuestingResponseDTO } from "../dtos/guest.dto"; + +export const readGuesting = async (query) => { + const guestings = await findGuesting(query.date, query.category); + for (const guesting of guestings) { + guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; + } + return readGuestingResponseDTO(guestings); +}; + +// export const readGuestingByGender = async (selectedDate, query) => { +// return await findGuestingByGender(selectedDate, query.category, query.gender); +// }; + +// export const readGuestingByLevel = async (selectedDate, query) => { +// return await findGuestingByLevel(selectedDate, query.category, query.level); +// }; + +// export const readGuestingByRegion = async (selectedDate, query) => { +// return await findGuestingByRegion(selectedDate, query.category, query.region); +// }; From b8aa30730b4100d2841c09775bb39e6bba0776aa Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Fri, 26 Jan 2024 00:36:35 +0900 Subject: [PATCH 008/147] =?UTF-8?q?[FEAT]=20=ED=8C=80=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20API=20=EA=B5=AC=ED=98=84=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 함수 추가 #13 * Feat: 컨트롤러 함수 추가 #13 * Feat: 서비스 함수 추가 #13 * Feat: error status 추가 #13 * Feat: UpdateTeamSchema 추가 #13 * Feat: DAO 함수 추가 #13 --- src/config/response.status.ts | 12 ++++++++++++ src/controllers/teams.controller.ts | 8 ++++---- src/daos/member.dao.ts | 19 ++++++++++++++++++- src/daos/team.dao.ts | 16 ++++++++++++++++ src/routes/teams.route.ts | 6 +++--- src/schemas/team.schema.ts | 16 +++++++++++++--- src/services/teams.service.ts | 26 ++++++++++++++++++++++---- 7 files changed, 88 insertions(+), 15 deletions(-) diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 66d28ee..667836e 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -84,6 +84,12 @@ export const status: { [key: string]: Status } = { code: "TEAM001", message: "해당 초대 코드로 가입할 수 있는 팀이 없습니다.", }, + TEAM_NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "TEAM002", + message: "요청한 팀을 찾을 수 없습니다.", + }, //member err ALREADY_JOINED: { @@ -92,4 +98,10 @@ export const status: { [key: string]: Status } = { code: "MEMBER001", message: "해당 팀에 이미 가입되어 있습니다.", }, + MEMBER_NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "MEMBER002", + message: "멤버를 찾을 수 없습니다.", + }, }; diff --git a/src/controllers/teams.controller.ts b/src/controllers/teams.controller.ts index af3960b..5bdb4bb 100644 --- a/src/controllers/teams.controller.ts +++ b/src/controllers/teams.controller.ts @@ -1,7 +1,7 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readTeamPreviewsByCategory, readTeamDetail, createTeam } from "../services/teams.service"; +import { readTeamPreviewsByCategory, readTeamDetail, createTeam, updateTeam } from "../services/teams.service"; export const fetchTeamPreviewsByCategory = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readTeamPreviewsByCategory(req.user.id, req.query))); @@ -11,9 +11,9 @@ export const addTeam = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await createTeam(req.user.id, req.body))); }; -// export const modifyTeam = async (req, res: Response, next) => { -// res.send(response(status.SUCCESS, await updateTeam(req.body))); -// }; +export const modifyTeam = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await updateTeam(req.user.id, req.params, req.body))); +}; export const fetchTeamDetail = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readTeamDetail(req.user.id, req.params))); diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts index a5505db..9a45534 100644 --- a/src/daos/member.dao.ts +++ b/src/daos/member.dao.ts @@ -1,3 +1,4 @@ +import { Op } from "sequelize"; import db from "../models"; export const findMemberInfoByTeamId = async (teamId, userInfoAttributes) => { @@ -33,12 +34,28 @@ export const isMemberExist = async (teamId, userId) => { return member !== null; }; +export const findMemberToDelete = async (memberIdsToDelete, teamId) => { + return await db.Member.findAll({ + where: { + teamId, + userId: { + [Op.in]: memberIdsToDelete, + }, + }, + }); +}; + +export const deleteMembersById = async (members, teamId) => { + for (const member of members) { + await member.destroy(); + } +}; + export const getMemberCountByTeamId = async (teamId) => { const count = await db.Member.count({ where: { teamId, }, }); - console.log(count); return count; }; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index 83c4f06..d6cc0f2 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -68,3 +68,19 @@ export const getTeamIdByInviteCode = async (inviteCode): Promise => { }); return team?.id; }; + +export const getTeamById = async (teamId, userId) => { + return await db.Team.findOne({ + where: { + id: teamId, + leaderId: userId, + }, + }); +}; + +export const setTeam = async (team, body) => { + Object.keys(body).forEach((field) => { + team[field] = body[field]; + }); + await team.save(); +}; diff --git a/src/routes/teams.route.ts b/src/routes/teams.route.ts index bbe4947..948706f 100644 --- a/src/routes/teams.route.ts +++ b/src/routes/teams.route.ts @@ -2,8 +2,8 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; import { validateBody } from "../middlewares/validate.middleware"; -import { createTeam } from "../schemas/team.schema"; -import { fetchTeamPreviewsByCategory, fetchTeamDetail, addTeam } from "../controllers/teams.controller"; +import { createTeam, updateTeam } from "../schemas/team.schema"; +import { fetchTeamPreviewsByCategory, fetchTeamDetail, addTeam, modifyTeam } from "../controllers/teams.controller"; export const teamsRouter = express.Router(); @@ -13,6 +13,6 @@ teamsRouter.get("/", asyncHandler(fetchTeamPreviewsByCategory)); teamsRouter.post("/", validateBody(createTeam), asyncHandler(addTeam)); -// teamsRouter.put("/:teamId", asyncHandler(modifyTeam)); +teamsRouter.put("/:teamId", validateBody(updateTeam), asyncHandler(modifyTeam)); teamsRouter.get("/:teamId", asyncHandler(fetchTeamDetail)); diff --git a/src/schemas/team.schema.ts b/src/schemas/team.schema.ts index b87b3eb..1dbcf3e 100644 --- a/src/schemas/team.schema.ts +++ b/src/schemas/team.schema.ts @@ -2,9 +2,7 @@ import { TypeOf, object, z } from "zod"; import { getGendersLength } from "../constants/gender.constant"; import { getAgeGroupsLength } from "../constants/age-group.constant"; -const categories = z.enum(["농구", "야구", "테니스", "축구", "풋살", "배구", "볼링", "배드민턴", "탁구"]); - -export const createTeam = object({ +const fieldsWithoutCategory = { logo: z.optional(z.string()), name: z.string().max(20), description: z.optional(z.string()), @@ -12,7 +10,19 @@ export const createTeam = object({ ageGroup: z.number().int().min(1).max(getAgeGroupsLength()), region: z.string(), gymName: z.string(), +}; + +const categories = z.enum(["농구", "야구", "테니스", "축구", "풋살", "배구", "볼링", "배드민턴", "탁구"]); + +export const createTeam = object({ + ...fieldsWithoutCategory, category: categories, }); +export const updateTeam = object({ + ...fieldsWithoutCategory, + memberIdsToDelete: z.optional(z.array(z.number().int())), +}); + export type CreateTeamSchema = TypeOf; +export type UpdateTeamSchema = TypeOf; diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index ee36f77..29ac2ea 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -1,9 +1,11 @@ -import { findMemberInfoByTeamId } from "../daos/member.dao"; -import { findTeamPreviewByCategory, getTeamDetail, insertTeam } from "../daos/team.dao"; +import { deleteMembersById, findMemberInfoByTeamId, findMemberToDelete } from "../daos/member.dao"; +import { findTeamPreviewByCategory, getTeamById, getTeamDetail, insertTeam, setTeam } from "../daos/team.dao"; import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; import { readTeamDetailResponseDTO } from "../dtos/team.dto"; import { v4 as uuidv4 } from "uuid"; -import { CreateTeamSchema } from "../schemas/team.schema"; +import { CreateTeamSchema, UpdateTeamSchema } from "../schemas/team.schema"; +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; export const readTeamPreviewsByCategory = async (userId, query) => { return await findTeamPreviewByCategory(userId, query.category); @@ -14,7 +16,23 @@ export const createTeam = async (userId, body: CreateTeamSchema) => { return; }; -// export const updateTeam = async (body) => {}; +export const updateTeam = async (userId, params, body: UpdateTeamSchema) => { + const teamId = params.teamId; + const team = await getTeamById(params.teamId, userId); + if (!team) { + throw new BaseError(status.TEAM_NOT_FOUND); + } + + const { memberIdsToDelete, ...bodyWithoutMemberIdsToDelete } = body; + const members = await findMemberToDelete(memberIdsToDelete, teamId); + if (members.length !== memberIdsToDelete?.length) { + throw new BaseError(status.MEMBER_NOT_FOUND); + } + + await deleteMembersById(members, teamId); + await setTeam(team, bodyWithoutMemberIdsToDelete); + return; +}; export const readTeamDetail = async (userId, params) => { const teamId = params.teamId; From 800431718f3fb3c0170e30033a83f3869b46916a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Fri, 26 Jan 2024 00:49:34 +0900 Subject: [PATCH 009/147] =?UTF-8?q?Feat:=20=EC=84=B1=EB=B3=84,=20=EC=97=B0?= =?UTF-8?q?=EB=A0=B9=EB=8C=80,=20=EB=A0=88=EB=B2=A8=EC=9D=84=20=EC=88=AB?= =?UTF-8?q?=EC=9E=90=EC=97=90=EC=84=9C=20=EB=AC=B8=EC=9E=90=EC=97=B4?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20rea?= =?UTF-8?q?dGuestingResponseDTO=20=EC=88=98=EC=A0=95=20#11?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/guest.dto.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/dtos/guest.dto.ts b/src/dtos/guest.dto.ts index bb4e91c..5e9d97a 100644 --- a/src/dtos/guest.dto.ts +++ b/src/dtos/guest.dto.ts @@ -1,13 +1,16 @@ +import { getAgeGroupById } from "../constants/age-group.constant"; +import { getGenderById } from "../constants/gender.constant"; +import { getLevelById } from "../constants/level.constant"; + export const readGuestingResponseDTO = (guestings) => { - console.log(guestings); return guestings.map((guesting) => ({ gameTime: guesting.gameTime, teamName: guesting["Team.name"], teamRegion: guesting["Team.region"], - teamGender: guesting["Team.gender"], //getGenderById(guesting["Team.gender"]), + teamGender: getGenderById(guesting["Team.gender"]), memberCount: guesting.memberCount, - teamAgeGroup: guesting["Team.ageGroup"], //getAgeGroupById(guesting["Team.ageGroup"]), - teamSkillLevel: guesting["Team.skillLevel"], //getLevelById(guesting["Team.skillLevel"]) + teamAgeGroup: getAgeGroupById(guesting["Team.ageGroup"]), + teamSkillLevel: getLevelById(guesting["Team.skillLevel"]), recruitCount: guesting.recruitCount, })); }; From 8d3085501389d49ccf41a451a208811ac4639002 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Fri, 26 Jan 2024 12:10:22 +0900 Subject: [PATCH 010/147] =?UTF-8?q?[FEAT]=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EA=B8=80=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20API=20=EA=B5=AC=ED=98=84=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: community post, image, comment, bookmark 테이블 생성 #17 * Style: dto 파일 이름 변경 #17 * Feat: verifyUserIfExists 미들웨어 추가 #17 * Feat: 라우트 추가 #17 * Style: dto 파일 이름 변경에 따른 경로 변경 #17 * Feat: 컨트롤러 함수 추가 #17 * Feat: 서비스 함수 추가 #17 * Feat: DAO 함수 추가 #17 * Feat: DTO 함수 추가 #17 --- src/app.ts | 2 + src/controllers/community-posts.controller.ts | 8 ++++ src/daos/community-post.dao.ts | 31 +++++++++++++ src/dtos/community-posts.dto.ts | 11 +++++ src/dtos/{guest.dto.ts => guests.dto.ts} | 0 src/dtos/{team.dto.ts => teams.dto.ts} | 0 src/middlewares/auth.middleware.ts | 19 +++++++- src/models/community-bookmark.ts | 34 ++++++++++++++ src/models/community-comment.ts | 38 ++++++++++++++++ src/models/community-image.ts | 33 ++++++++++++++ src/models/community-post.model.ts | 44 +++++++++++++++++++ src/models/team.model.ts | 2 +- src/models/user.model.ts | 3 ++ src/routes/community-posts.route.ts | 8 ++++ src/services/community-posts.service.ts | 7 +++ src/services/guest.service.ts | 2 +- src/services/teams.service.ts | 2 +- src/utils/jwt.util.ts | 6 ++- 18 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 src/controllers/community-posts.controller.ts create mode 100644 src/daos/community-post.dao.ts create mode 100644 src/dtos/community-posts.dto.ts rename src/dtos/{guest.dto.ts => guests.dto.ts} (100%) rename src/dtos/{team.dto.ts => teams.dto.ts} (100%) create mode 100644 src/models/community-bookmark.ts create mode 100644 src/models/community-comment.ts create mode 100644 src/models/community-image.ts create mode 100644 src/models/community-post.model.ts create mode 100644 src/routes/community-posts.route.ts create mode 100644 src/services/community-posts.service.ts diff --git a/src/app.ts b/src/app.ts index 8a60a07..d95cc2a 100644 --- a/src/app.ts +++ b/src/app.ts @@ -15,6 +15,7 @@ import { authRouter } from "./routes/auth.route"; import { teamsRouter } from "./routes/teams.route"; import { membersRouter } from "./routes/members.route"; import { guestsRouter } from "./routes/guests.route"; +import { communityPostsRouter } from "./routes/community-posts.route"; const app = express(); @@ -37,6 +38,7 @@ app.use("/auth", authRouter); app.use("/teams", teamsRouter); app.use("/members", membersRouter); app.use("/guests", guestsRouter); +app.use("/community-posts", communityPostsRouter); app.use((req: Request, res: Response, next: NextFunction) => { const err = new BaseError(status.NOT_FOUND); diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts new file mode 100644 index 0000000..7c21177 --- /dev/null +++ b/src/controllers/community-posts.controller.ts @@ -0,0 +1,8 @@ +import { Response } from "express"; +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { readCommunityPosts } from "../services/community-posts.service"; + +export const fetchCommunityPosts = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readCommunityPosts(req.user?.id, req.query))); +}; diff --git a/src/daos/community-post.dao.ts b/src/daos/community-post.dao.ts new file mode 100644 index 0000000..1980471 --- /dev/null +++ b/src/daos/community-post.dao.ts @@ -0,0 +1,31 @@ +import { Op } from "sequelize"; +import db from "../models"; + +const defaultLimit = 20; + +export const findCommunityPost = async (userId?: number, cursorId?: number) => { + const postsBeforeCursor = cursorId ? { id: { [Op.lt]: cursorId } } : {}; + + const bookmarkInclude: any[] = []; + if (userId) { + bookmarkInclude.push({ + model: db.CommunityBookmark, + where: { + userId, + }, + required: false, + attributes: ["id"], + }); + } + + const posts = await db.CommunityPost.findAll({ + raw: true, + where: postsBeforeCursor, + order: [["createdAt", "DESC"]], + limit: defaultLimit, + attributes: ["id", "title", "createdAt"], + include: bookmarkInclude, + }); + const hasNext = posts.length === defaultLimit; + return { posts, hasNext }; +}; diff --git a/src/dtos/community-posts.dto.ts b/src/dtos/community-posts.dto.ts new file mode 100644 index 0000000..fe66d01 --- /dev/null +++ b/src/dtos/community-posts.dto.ts @@ -0,0 +1,11 @@ +export const readCommunityPostsResponseDTO = (response) => { + return { + posts: response.posts.map((post) => ({ + id: post.id, + isBookmarked: Boolean(post["CommunityBookmarks.id"]), + title: post.title, + createdAt: post.createdAt, + })), + hasNext: response.hasNext, + }; +}; diff --git a/src/dtos/guest.dto.ts b/src/dtos/guests.dto.ts similarity index 100% rename from src/dtos/guest.dto.ts rename to src/dtos/guests.dto.ts diff --git a/src/dtos/team.dto.ts b/src/dtos/teams.dto.ts similarity index 100% rename from src/dtos/team.dto.ts rename to src/dtos/teams.dto.ts diff --git a/src/middlewares/auth.middleware.ts b/src/middlewares/auth.middleware.ts index ab008a1..2520ca9 100644 --- a/src/middlewares/auth.middleware.ts +++ b/src/middlewares/auth.middleware.ts @@ -1,7 +1,7 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { NextFunction, Request, Response } from "express"; -import { extractAccessToken, verifyAccessToken } from "../utils/jwt.util"; +import { extractAccessToken, extractAccessTokenFromHeader, verifyAccessToken } from "../utils/jwt.util"; export const verifyUser = (req: Request, res: Response, next: NextFunction) => { const accessToken = extractAccessToken(req); @@ -16,3 +16,20 @@ export const verifyUser = (req: Request, res: Response, next: NextFunction) => { next(); } }; + +export const verifyUserIfExists = (req: Request, res: Response, next: NextFunction) => { + const accessToken = extractAccessTokenFromHeader(req); + if (accessToken) { + const verified = verifyAccessToken(accessToken); + if (verified.isExpired) { + throw new BaseError(status.ACCESS_TOKEN_EXPIRED); + } else { + req.user = { + id: verified.decoded.id, + nickname: verified.decoded.nickname, + }; + next(); + } + } + next(); +}; diff --git a/src/models/community-bookmark.ts b/src/models/community-bookmark.ts new file mode 100644 index 0000000..a3e2359 --- /dev/null +++ b/src/models/community-bookmark.ts @@ -0,0 +1,34 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class CommunityBookmark extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + CommunityBookmark.init( + { + postId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + userId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "CommunityBookmark", + tableName: "community_bookmark", + paranoid: true, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.CommunityBookmark.belongsTo(db.CommunityPost, { foreignKey: "post_id" }); + db.CommunityBookmark.belongsTo(db.User, { foreignKey: "user_id" }); + } +} + +module.exports = CommunityBookmark; diff --git a/src/models/community-comment.ts b/src/models/community-comment.ts new file mode 100644 index 0000000..bc6662f --- /dev/null +++ b/src/models/community-comment.ts @@ -0,0 +1,38 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class CommunityComment extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + CommunityComment.init( + { + postId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + authorId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + content: { + type: new DataTypes.STRING(500), + allowNull: true, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "CommunityComment", + tableName: "community_comment", + paranoid: false, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.CommunityComment.belongsTo(db.CommunityPost, { foreignKey: "post_id" }); + db.CommunityComment.belongsTo(db.User, { foreignKey: "author_id" }); + } +} + +module.exports = CommunityComment; diff --git a/src/models/community-image.ts b/src/models/community-image.ts new file mode 100644 index 0000000..a05490e --- /dev/null +++ b/src/models/community-image.ts @@ -0,0 +1,33 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class CommunityImage extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + CommunityImage.init( + { + postId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + url: { + type: new DataTypes.STRING(1000), + allowNull: true, + }, + }, + { + sequelize, + timestamps: false, + underscored: true, + modelName: "CommunityImage", + tableName: "community_image", + paranoid: false, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.CommunityImage.belongsTo(db.CommunityPost, { foreignKey: "post_id" }); + } +} + +module.exports = CommunityImage; diff --git a/src/models/community-post.model.ts b/src/models/community-post.model.ts new file mode 100644 index 0000000..18396fa --- /dev/null +++ b/src/models/community-post.model.ts @@ -0,0 +1,44 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class CommunityPost extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + CommunityPost.init( + { + title: { + type: new DataTypes.STRING(30), + allowNull: false, + }, + content: { + type: new DataTypes.STRING(1000), + allowNull: true, + }, + link: { + type: new DataTypes.STRING(200), + allowNull: true, + }, + authorId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "CommunityPost", + tableName: "community_post", + paranoid: false, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.CommunityPost.hasMany(db.CommunityImage, { foreignKey: "post_id" }); + db.CommunityPost.hasMany(db.CommunityComment, { foreignKey: "post_id" }); + db.CommunityPost.hasMany(db.CommunityBookmark, { foreignKey: "post_id" }); + db.CommunityPost.belongsTo(db.User, { foreignKey: "author_id" }); + } +} + +module.exports = CommunityPost; diff --git a/src/models/team.model.ts b/src/models/team.model.ts index 876e818..b4b7154 100644 --- a/src/models/team.model.ts +++ b/src/models/team.model.ts @@ -5,7 +5,7 @@ class Team extends Model, InferCreationAttributes> { Team.init( { logo: { - type: new DataTypes.STRING(100), + type: new DataTypes.STRING(200), allowNull: true, }, name: { diff --git a/src/models/user.model.ts b/src/models/user.model.ts index d66f7a6..39792aa 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -44,6 +44,9 @@ class User extends Model, InferCreationAttributes> { db.User.hasMany(db.Team, { foreignKey: "leader_id" }); db.User.hasMany(db.Member, { foreignKey: "user_id" }); db.User.hasMany(db.GuestUser, { foreignKey: "user_id" }); + db.User.hasMany(db.CommunityPost, { foreignKey: "author_id" }); + db.User.hasMany(db.CommunityComment, { foreignKey: "author_id" }); + db.User.hasMany(db.CommunityBookmark, { foreignKey: "user_id" }); } } diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts new file mode 100644 index 0000000..80842c7 --- /dev/null +++ b/src/routes/community-posts.route.ts @@ -0,0 +1,8 @@ +import express from "express"; +import asyncHandler from "express-async-handler"; +import { verifyUserIfExists } from "../middlewares/auth.middleware"; +import { fetchCommunityPosts } from "../controllers/community-posts.controller"; + +export const communityPostsRouter = express.Router(); + +communityPostsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts new file mode 100644 index 0000000..676ad6c --- /dev/null +++ b/src/services/community-posts.service.ts @@ -0,0 +1,7 @@ +import { findCommunityPost } from "../daos/community-post.dao"; +import { readCommunityPostsResponseDTO } from "../dtos/community-posts.dto"; + +export const readCommunityPosts = async (userId: number | undefined, query) => { + const response = await findCommunityPost(userId, query.cursorId); + return readCommunityPostsResponseDTO(response); +}; diff --git a/src/services/guest.service.ts b/src/services/guest.service.ts index 7b463a4..5cb4151 100644 --- a/src/services/guest.service.ts +++ b/src/services/guest.service.ts @@ -2,7 +2,7 @@ // import { status } from "../config/response.status"; import { findGuesting } from "../daos/guest.dao"; import { getMemberCountByTeamId } from "../daos/member.dao"; -import { readGuestingResponseDTO } from "../dtos/guest.dto"; +import { readGuestingResponseDTO } from "../dtos/guests.dto"; export const readGuesting = async (query) => { const guestings = await findGuesting(query.date, query.category); diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index 29ac2ea..dbc4995 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -1,7 +1,7 @@ import { deleteMembersById, findMemberInfoByTeamId, findMemberToDelete } from "../daos/member.dao"; import { findTeamPreviewByCategory, getTeamById, getTeamDetail, insertTeam, setTeam } from "../daos/team.dao"; import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; -import { readTeamDetailResponseDTO } from "../dtos/team.dto"; +import { readTeamDetailResponseDTO } from "../dtos/teams.dto"; import { v4 as uuidv4 } from "uuid"; import { CreateTeamSchema, UpdateTeamSchema } from "../schemas/team.schema"; import { BaseError } from "../config/error"; diff --git a/src/utils/jwt.util.ts b/src/utils/jwt.util.ts index 4989d97..6ef0dbf 100644 --- a/src/utils/jwt.util.ts +++ b/src/utils/jwt.util.ts @@ -24,13 +24,17 @@ export const generateRefreshToken = (): string => { }; export const extractAccessToken = (req: Request): string => { - const accessToken = req.headers.authorization?.split(tokenType)[1]; + const accessToken = extractAccessTokenFromHeader(req); if (!accessToken) { throw new BaseError(status.MISSING_ACCESS_TOKEN); } return accessToken; }; +export const extractAccessTokenFromHeader = (req: Request): string | undefined => { + return req.headers.authorization?.split(tokenType)[1]; +}; + export const extractRefreshToken = (req: Request) => { const refreshToken = req.headers.refresh; if (!refreshToken) { From 4c8bfdf2797c1ebd05d3211db824676b4fe98f1f Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Fri, 26 Jan 2024 12:34:43 +0900 Subject: [PATCH 011/147] =?UTF-8?q?[FEAT]=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EA=B8=80=20=EC=A0=80=EC=9E=A5=EA=B3=BC=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EC=B7=A8=EC=86=8C=20API=20=EA=B5=AC=ED=98=84=20(#2?= =?UTF-8?q?0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 함수 추가 #19 * Feat: 컨트롤러 함수 추가 #19 * Feat: 서비스 함수 추가 #19 * Feat: DAO 함수 추가 #19 --- src/controllers/community-posts.controller.ts | 6 +++++- src/daos/community-bookmark.dao.ts | 19 +++++++++++++++++++ src/routes/community-posts.route.ts | 6 ++++-- src/services/community-posts.service.ts | 6 ++++++ 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 src/daos/community-bookmark.dao.ts diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts index 7c21177..cc4def5 100644 --- a/src/controllers/community-posts.controller.ts +++ b/src/controllers/community-posts.controller.ts @@ -1,8 +1,12 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readCommunityPosts } from "../services/community-posts.service"; +import { createOrDeleteBookmark, readCommunityPosts } from "../services/community-posts.service"; export const fetchCommunityPosts = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readCommunityPosts(req.user?.id, req.query))); }; + +export const addOrRemoveBookmark = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await createOrDeleteBookmark(req.user.id, req.params))); +}; diff --git a/src/daos/community-bookmark.dao.ts b/src/daos/community-bookmark.dao.ts new file mode 100644 index 0000000..c6c86c4 --- /dev/null +++ b/src/daos/community-bookmark.dao.ts @@ -0,0 +1,19 @@ +import db from "../models"; + +export const insertOrDeleteBookmark = async (userId, postId) => { + const bookmark = await db.CommunityBookmark.findOne({ + where: { + postId, + userId, + }, + }); + + if (bookmark) { + await bookmark.destroy(); + } else { + await db.CommunityBookmark.create({ + postId, + userId, + }); + } +}; diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts index 80842c7..9c04bf2 100644 --- a/src/routes/community-posts.route.ts +++ b/src/routes/community-posts.route.ts @@ -1,8 +1,10 @@ import express from "express"; import asyncHandler from "express-async-handler"; -import { verifyUserIfExists } from "../middlewares/auth.middleware"; -import { fetchCommunityPosts } from "../controllers/community-posts.controller"; +import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; +import { addOrRemoveBookmark, fetchCommunityPosts } from "../controllers/community-posts.controller"; export const communityPostsRouter = express.Router(); communityPostsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); + +communityPostsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts index 676ad6c..c7be651 100644 --- a/src/services/community-posts.service.ts +++ b/src/services/community-posts.service.ts @@ -1,3 +1,4 @@ +import { insertOrDeleteBookmark } from "../daos/community-bookmark.dao"; import { findCommunityPost } from "../daos/community-post.dao"; import { readCommunityPostsResponseDTO } from "../dtos/community-posts.dto"; @@ -5,3 +6,8 @@ export const readCommunityPosts = async (userId: number | undefined, query) => { const response = await findCommunityPost(userId, query.cursorId); return readCommunityPostsResponseDTO(response); }; + +export const createOrDeleteBookmark = async (userId, params) => { + await insertOrDeleteBookmark(userId, params.postId); + return; +}; From fcd9daeb5d15f57b3cb2d43909df85715757d9f3 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Fri, 26 Jan 2024 18:54:13 +0900 Subject: [PATCH 012/147] =?UTF-8?q?[FEAT]=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EA=B8=80=20=EB=B3=B8=EB=AC=B8=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20API=20=EA=B5=AC=ED=98=84=20(#22)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 함수 추가 #21 * Feat: 컨트롤러 함수 추가 #21 * Feat: 서비스 함수, 에러 코드 추가 #21 * Feat: DAO 함수 추가 #21 * Feat: DTO 함수 추가 #21 --- src/config/response.status.ts | 8 +++++ src/controllers/community-posts.controller.ts | 6 +++- src/daos/community-bookmark.dao.ts | 16 ++++++---- src/daos/community-comment.dao.ts | 25 ++++++++++++++++ src/daos/community-image.dao.ts | 10 +++++++ src/daos/community-post.dao.ts | 17 ++++++++--- src/dtos/community-posts.dto.ts | 20 +++++++++++++ src/routes/community-posts.route.ts | 8 ++++- src/services/community-posts.service.ts | 29 +++++++++++++++++-- src/utils/paging.util.ts | 6 ++++ 10 files changed, 130 insertions(+), 15 deletions(-) create mode 100644 src/daos/community-comment.dao.ts create mode 100644 src/daos/community-image.dao.ts create mode 100644 src/utils/paging.util.ts diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 667836e..0fec805 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -104,4 +104,12 @@ export const status: { [key: string]: Status } = { code: "MEMBER002", message: "멤버를 찾을 수 없습니다.", }, + + //post err + POST_NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "POST001", + message: "요청한 글을 찾을 수 없습니다.", + }, }; diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts index cc4def5..62dc394 100644 --- a/src/controllers/community-posts.controller.ts +++ b/src/controllers/community-posts.controller.ts @@ -1,7 +1,7 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { createOrDeleteBookmark, readCommunityPosts } from "../services/community-posts.service"; +import { createOrDeleteBookmark, readCommunityPost, readCommunityPosts } from "../services/community-posts.service"; export const fetchCommunityPosts = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readCommunityPosts(req.user?.id, req.query))); @@ -10,3 +10,7 @@ export const fetchCommunityPosts = async (req, res: Response, next) => { export const addOrRemoveBookmark = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await createOrDeleteBookmark(req.user.id, req.params))); }; + +export const fetchCommunityPost = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readCommunityPost(req.user.id, req.params))); +}; diff --git a/src/daos/community-bookmark.dao.ts b/src/daos/community-bookmark.dao.ts index c6c86c4..f80453c 100644 --- a/src/daos/community-bookmark.dao.ts +++ b/src/daos/community-bookmark.dao.ts @@ -1,12 +1,7 @@ import db from "../models"; export const insertOrDeleteBookmark = async (userId, postId) => { - const bookmark = await db.CommunityBookmark.findOne({ - where: { - postId, - userId, - }, - }); + const bookmark = await getCommunityBookmark(userId, postId); if (bookmark) { await bookmark.destroy(); @@ -17,3 +12,12 @@ export const insertOrDeleteBookmark = async (userId, postId) => { }); } }; + +export const getCommunityBookmark = async (userId: number, postId: number) => { + return await db.CommunityBookmark.findOne({ + where: { + postId, + userId, + }, + }); +}; diff --git a/src/daos/community-comment.dao.ts b/src/daos/community-comment.dao.ts new file mode 100644 index 0000000..e561e9b --- /dev/null +++ b/src/daos/community-comment.dao.ts @@ -0,0 +1,25 @@ +import db from "../models"; +import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; + +const defaultLimit = 2; + +export const findCommunityComment = async (postId: number, cursorId: number | undefined) => { + const commentsBeforeCursorForPost = { postId, ...generateCursorCondition(cursorId) }; + + const comments = await db.CommunityComment.findAll({ + raw: true, + where: commentsBeforeCursorForPost, + order: [["createdAt", "DESC"]], + limit: defaultLimit, + include: [ + { + model: db.User, + attributes: ["nickname"], + }, + ], + attributes: ["id", "content", "createdAt"], + }); + + const ascendingComments = comments.sort((a, b) => a.id - b.id); + return { ascendingComments, hasNext: calculateHasNext(ascendingComments, defaultLimit) }; +}; diff --git a/src/daos/community-image.dao.ts b/src/daos/community-image.dao.ts new file mode 100644 index 0000000..c2c8e0c --- /dev/null +++ b/src/daos/community-image.dao.ts @@ -0,0 +1,10 @@ +import db from "../models"; + +export const findCommunityImage = async (postId: number) => { + return await db.CommunityImage.findAll({ + where: { + postId, + }, + attributes: ["url"], + }); +}; diff --git a/src/daos/community-post.dao.ts b/src/daos/community-post.dao.ts index 1980471..3dde88a 100644 --- a/src/daos/community-post.dao.ts +++ b/src/daos/community-post.dao.ts @@ -1,10 +1,10 @@ -import { Op } from "sequelize"; import db from "../models"; +import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; const defaultLimit = 20; export const findCommunityPost = async (userId?: number, cursorId?: number) => { - const postsBeforeCursor = cursorId ? { id: { [Op.lt]: cursorId } } : {}; + const postsBeforeCursor = generateCursorCondition(cursorId); const bookmarkInclude: any[] = []; if (userId) { @@ -26,6 +26,15 @@ export const findCommunityPost = async (userId?: number, cursorId?: number) => { attributes: ["id", "title", "createdAt"], include: bookmarkInclude, }); - const hasNext = posts.length === defaultLimit; - return { posts, hasNext }; + return { posts, hasNext: calculateHasNext(posts, defaultLimit) }; +}; + +export const getCommunityPost = async (userId: number | undefined, postId: number) => { + return await db.CommunityPost.findOne({ + raw: true, + where: { + id: postId, + }, + attributes: ["id", "title", "content", "link"], + }); }; diff --git a/src/dtos/community-posts.dto.ts b/src/dtos/community-posts.dto.ts index fe66d01..7ffece5 100644 --- a/src/dtos/community-posts.dto.ts +++ b/src/dtos/community-posts.dto.ts @@ -9,3 +9,23 @@ export const readCommunityPostsResponseDTO = (response) => { hasNext: response.hasNext, }; }; + +export const readCommunityPostResponseDTO = (post, imageUrls, comments, isBookmarked) => { + console.log(post); + return { + post: { + title: post.title, + contnet: post.content, + link: post.link, + imageUrls: imageUrls, + }, + isBookmarked, + commentCount: comments.ascendingComments.length, + comments: comments.ascendingComments.map((comment) => ({ + nickname: comment["User.nickname"], + content: comment.content, + createdAt: comment.createdAt, + })), + commentHasNext: comments.hasNext, + }; +}; diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts index 9c04bf2..ad52048 100644 --- a/src/routes/community-posts.route.ts +++ b/src/routes/community-posts.route.ts @@ -1,10 +1,16 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; -import { addOrRemoveBookmark, fetchCommunityPosts } from "../controllers/community-posts.controller"; +import { + addOrRemoveBookmark, + fetchCommunityPost, + fetchCommunityPosts, +} from "../controllers/community-posts.controller"; export const communityPostsRouter = express.Router(); communityPostsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); communityPostsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); + +communityPostsRouter.get("/:postId", verifyUser, asyncHandler(fetchCommunityPost)); diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts index c7be651..37a7c0f 100644 --- a/src/services/community-posts.service.ts +++ b/src/services/community-posts.service.ts @@ -1,6 +1,10 @@ -import { insertOrDeleteBookmark } from "../daos/community-bookmark.dao"; -import { findCommunityPost } from "../daos/community-post.dao"; -import { readCommunityPostsResponseDTO } from "../dtos/community-posts.dto"; +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { getCommunityBookmark, insertOrDeleteBookmark } from "../daos/community-bookmark.dao"; +import { findCommunityComment } from "../daos/community-comment.dao"; +import { findCommunityImage } from "../daos/community-image.dao"; +import { findCommunityPost, getCommunityPost } from "../daos/community-post.dao"; +import { readCommunityPostResponseDTO, readCommunityPostsResponseDTO } from "../dtos/community-posts.dto"; export const readCommunityPosts = async (userId: number | undefined, query) => { const response = await findCommunityPost(userId, query.cursorId); @@ -11,3 +15,22 @@ export const createOrDeleteBookmark = async (userId, params) => { await insertOrDeleteBookmark(userId, params.postId); return; }; + +export const readCommunityPost = async (userId, params) => { + const postId = params.postId; + const post = await getCommunityPost(userId, postId); + if (!post) { + throw new BaseError(status.POST_NOT_FOUND); + } + const imageUrls = await findCommunityImage(postId); + const comments = await findCommunityComment(postId, undefined); + const isBookmarked = await checkIsBookmarked(userId, postId); + return readCommunityPostResponseDTO(post, imageUrls, comments, isBookmarked); +}; + +const checkIsBookmarked = async (userId: number | undefined, postId: number) => { + if (!userId) { + return null; + } + return Boolean(await getCommunityBookmark(userId, postId)); +}; diff --git a/src/utils/paging.util.ts b/src/utils/paging.util.ts new file mode 100644 index 0000000..a8ac293 --- /dev/null +++ b/src/utils/paging.util.ts @@ -0,0 +1,6 @@ +import { Op } from "sequelize"; + +export const generateCursorCondition = (cursorId: number | undefined) => + cursorId ? { id: { [Op.lt]: cursorId } } : {}; + +export const calculateHasNext = (items: Array, limit: number) => items.length === limit; From f061665841c324c4a43ab55b5eadeb506bfe9133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Fri, 26 Jan 2024 19:17:18 +0900 Subject: [PATCH 013/147] =?UTF-8?q?Fix:=20readCommunityPostResponseDTO?= =?UTF-8?q?=EC=97=90=20comment=20id=20=EC=B6=94=EA=B0=80=20#21?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/community-posts.dto.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dtos/community-posts.dto.ts b/src/dtos/community-posts.dto.ts index 7ffece5..e96964a 100644 --- a/src/dtos/community-posts.dto.ts +++ b/src/dtos/community-posts.dto.ts @@ -22,6 +22,7 @@ export const readCommunityPostResponseDTO = (post, imageUrls, comments, isBookma isBookmarked, commentCount: comments.ascendingComments.length, comments: comments.ascendingComments.map((comment) => ({ + id: comment.id, nickname: comment["User.nickname"], content: comment.content, createdAt: comment.createdAt, From 895d7a8055e44c287e09d82f93e162de2a7b7f03 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Fri, 26 Jan 2024 20:33:28 +0900 Subject: [PATCH 014/147] =?UTF-8?q?[FEAT]=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EB=8C=93=EA=B8=80=20=EC=B6=94=EA=B0=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84=20(#24)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 함수 추가 #23 * Feat: 컨트롤러 함수 추가 #23 * Feat: 서비스 함수 추가 #23 * Fix: 조회한 댓글 수가 아니라 전체 댓글 수를 응답하도록 수정 #21 --- src/controllers/community-posts.controller.ts | 11 +++++++++- src/daos/community-comment.dao.ts | 10 +++++++++- src/daos/community-post.dao.ts | 2 +- src/dtos/community-posts.dto.ts | 11 +++++++--- src/routes/community-posts.route.ts | 3 +++ src/services/community-posts.service.ts | 20 ++++++++++++++++--- 6 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts index 62dc394..7c82591 100644 --- a/src/controllers/community-posts.controller.ts +++ b/src/controllers/community-posts.controller.ts @@ -1,7 +1,12 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { createOrDeleteBookmark, readCommunityPost, readCommunityPosts } from "../services/community-posts.service"; +import { + createOrDeleteBookmark, + readCommunityComments, + readCommunityPost, + readCommunityPosts, +} from "../services/community-posts.service"; export const fetchCommunityPosts = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readCommunityPosts(req.user?.id, req.query))); @@ -14,3 +19,7 @@ export const addOrRemoveBookmark = async (req, res: Response, next) => { export const fetchCommunityPost = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readCommunityPost(req.user.id, req.params))); }; + +export const fetchCommunityComments = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readCommunityComments(req.params, req.query))); +}; diff --git a/src/daos/community-comment.dao.ts b/src/daos/community-comment.dao.ts index e561e9b..0f54438 100644 --- a/src/daos/community-comment.dao.ts +++ b/src/daos/community-comment.dao.ts @@ -1,7 +1,7 @@ import db from "../models"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; -const defaultLimit = 2; +const defaultLimit = 20; export const findCommunityComment = async (postId: number, cursorId: number | undefined) => { const commentsBeforeCursorForPost = { postId, ...generateCursorCondition(cursorId) }; @@ -23,3 +23,11 @@ export const findCommunityComment = async (postId: number, cursorId: number | un const ascendingComments = comments.sort((a, b) => a.id - b.id); return { ascendingComments, hasNext: calculateHasNext(ascendingComments, defaultLimit) }; }; + +export const getCommentCount = async (postId: number) => { + return await db.CommunityComment.count({ + where: { + postId, + }, + }); +}; diff --git a/src/daos/community-post.dao.ts b/src/daos/community-post.dao.ts index 3dde88a..37c90a8 100644 --- a/src/daos/community-post.dao.ts +++ b/src/daos/community-post.dao.ts @@ -35,6 +35,6 @@ export const getCommunityPost = async (userId: number | undefined, postId: numbe where: { id: postId, }, - attributes: ["id", "title", "content", "link"], + attributes: ["title", "content", "link"], }); }; diff --git a/src/dtos/community-posts.dto.ts b/src/dtos/community-posts.dto.ts index e96964a..f0d623b 100644 --- a/src/dtos/community-posts.dto.ts +++ b/src/dtos/community-posts.dto.ts @@ -10,8 +10,7 @@ export const readCommunityPostsResponseDTO = (response) => { }; }; -export const readCommunityPostResponseDTO = (post, imageUrls, comments, isBookmarked) => { - console.log(post); +export const readCommunityPostResponseDTO = (post, imageUrls, commentCount, comments, isBookmarked) => { return { post: { title: post.title, @@ -20,7 +19,13 @@ export const readCommunityPostResponseDTO = (post, imageUrls, comments, isBookma imageUrls: imageUrls, }, isBookmarked, - commentCount: comments.ascendingComments.length, + commentCount, + ...readCommunityCommentsResonseDTO(comments), + }; +}; + +export const readCommunityCommentsResonseDTO = (comments) => { + return { comments: comments.ascendingComments.map((comment) => ({ id: comment.id, nickname: comment["User.nickname"], diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts index ad52048..a0220fe 100644 --- a/src/routes/community-posts.route.ts +++ b/src/routes/community-posts.route.ts @@ -3,6 +3,7 @@ import asyncHandler from "express-async-handler"; import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; import { addOrRemoveBookmark, + fetchCommunityComments, fetchCommunityPost, fetchCommunityPosts, } from "../controllers/community-posts.controller"; @@ -14,3 +15,5 @@ communityPostsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPos communityPostsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); communityPostsRouter.get("/:postId", verifyUser, asyncHandler(fetchCommunityPost)); + +communityPostsRouter.get("/:postId/comments", asyncHandler(fetchCommunityComments)); diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts index 37a7c0f..33d3676 100644 --- a/src/services/community-posts.service.ts +++ b/src/services/community-posts.service.ts @@ -1,10 +1,14 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { getCommunityBookmark, insertOrDeleteBookmark } from "../daos/community-bookmark.dao"; -import { findCommunityComment } from "../daos/community-comment.dao"; +import { findCommunityComment, getCommentCount } from "../daos/community-comment.dao"; import { findCommunityImage } from "../daos/community-image.dao"; import { findCommunityPost, getCommunityPost } from "../daos/community-post.dao"; -import { readCommunityPostResponseDTO, readCommunityPostsResponseDTO } from "../dtos/community-posts.dto"; +import { + readCommunityCommentsResonseDTO, + readCommunityPostResponseDTO, + readCommunityPostsResponseDTO, +} from "../dtos/community-posts.dto"; export const readCommunityPosts = async (userId: number | undefined, query) => { const response = await findCommunityPost(userId, query.cursorId); @@ -23,9 +27,10 @@ export const readCommunityPost = async (userId, params) => { throw new BaseError(status.POST_NOT_FOUND); } const imageUrls = await findCommunityImage(postId); + const commentCount = await getCommentCount(postId); const comments = await findCommunityComment(postId, undefined); const isBookmarked = await checkIsBookmarked(userId, postId); - return readCommunityPostResponseDTO(post, imageUrls, comments, isBookmarked); + return readCommunityPostResponseDTO(post, imageUrls, commentCount, comments, isBookmarked); }; const checkIsBookmarked = async (userId: number | undefined, postId: number) => { @@ -34,3 +39,12 @@ const checkIsBookmarked = async (userId: number | undefined, postId: number) => } return Boolean(await getCommunityBookmark(userId, postId)); }; + +export const readCommunityComments = async (params, query) => { + const cursorId = query.cursorId; + if (isNaN(Number(cursorId))) { + throw new BaseError(status.REQUEST_VALIDATION_ERROR); + } + const comments = await findCommunityComment(params.postId, cursorId); + return readCommunityCommentsResonseDTO(comments); +}; From 12a9f0851f806e0935f5f44d2ac3f2fb3c20129e Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Fri, 26 Jan 2024 22:42:00 +0900 Subject: [PATCH 015/147] =?UTF-8?q?[FEAT]=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EA=B8=80=20=EC=9E=91=EC=84=B1=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#26)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 함수 추가 #25 * Feat: 컨트롤러 함수 추가 #25 * Feat: CreateCommunityPostSchema 생성 #25 * Feat: 서비스 함수 추가 #25 * Feat: DAO 함수 추가 #25 --- src/controllers/community-posts.controller.ts | 9 +++++++-- src/daos/community-post.dao.ts | 10 ++++++++++ src/routes/community-posts.route.ts | 7 ++++++- src/schemas/community-post.schema.ts | 9 +++++++++ src/services/community-posts.service.ts | 8 +++++++- 5 files changed, 39 insertions(+), 4 deletions(-) create mode 100644 src/schemas/community-post.schema.ts diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts index 7c82591..5141cd5 100644 --- a/src/controllers/community-posts.controller.ts +++ b/src/controllers/community-posts.controller.ts @@ -1,7 +1,8 @@ -import { Response } from "express"; +import { Request, Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; import { + createCommunityPost, createOrDeleteBookmark, readCommunityComments, readCommunityPost, @@ -20,6 +21,10 @@ export const fetchCommunityPost = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readCommunityPost(req.user.id, req.params))); }; -export const fetchCommunityComments = async (req, res: Response, next) => { +export const fetchCommunityComments = async (req: Request, res: Response, next) => { res.send(response(status.SUCCESS, await readCommunityComments(req.params, req.query))); }; + +export const addCommunityPost = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await createCommunityPost(req.user.id, req.body))); +}; diff --git a/src/daos/community-post.dao.ts b/src/daos/community-post.dao.ts index 37c90a8..dc65884 100644 --- a/src/daos/community-post.dao.ts +++ b/src/daos/community-post.dao.ts @@ -1,4 +1,5 @@ import db from "../models"; +import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; const defaultLimit = 20; @@ -38,3 +39,12 @@ export const getCommunityPost = async (userId: number | undefined, postId: numbe attributes: ["title", "content", "link"], }); }; + +export const insertCommunityPost = async (userId: number, data: CreateCommunityPostSchema) => { + await db.CommunityPost.create({ + title: data.title, + content: data.content, + link: data.link, + authorId: userId, + }); +}; diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts index a0220fe..905930a 100644 --- a/src/routes/community-posts.route.ts +++ b/src/routes/community-posts.route.ts @@ -2,11 +2,14 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; import { + addCommunityPost, addOrRemoveBookmark, fetchCommunityComments, fetchCommunityPost, fetchCommunityPosts, } from "../controllers/community-posts.controller"; +import { createCommunityPost } from "../schemas/community-post.schema"; +import { validateBody } from "../middlewares/validate.middleware"; export const communityPostsRouter = express.Router(); @@ -16,4 +19,6 @@ communityPostsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRem communityPostsRouter.get("/:postId", verifyUser, asyncHandler(fetchCommunityPost)); -communityPostsRouter.get("/:postId/comments", asyncHandler(fetchCommunityComments)); +communityPostsRouter.get("/:postId/comments", verifyUser, asyncHandler(fetchCommunityComments)); + +communityPostsRouter.post("/", verifyUser, validateBody(createCommunityPost), asyncHandler(addCommunityPost)); diff --git a/src/schemas/community-post.schema.ts b/src/schemas/community-post.schema.ts new file mode 100644 index 0000000..d7645b4 --- /dev/null +++ b/src/schemas/community-post.schema.ts @@ -0,0 +1,9 @@ +import { TypeOf, object, z } from "zod"; + +export const createCommunityPost = object({ + title: z.string().max(30), + content: z.optional(z.string().max(1000)), + link: z.optional(z.string().max(200)), +}); + +export type CreateCommunityPostSchema = TypeOf; diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts index 33d3676..894800a 100644 --- a/src/services/community-posts.service.ts +++ b/src/services/community-posts.service.ts @@ -3,12 +3,13 @@ import { status } from "../config/response.status"; import { getCommunityBookmark, insertOrDeleteBookmark } from "../daos/community-bookmark.dao"; import { findCommunityComment, getCommentCount } from "../daos/community-comment.dao"; import { findCommunityImage } from "../daos/community-image.dao"; -import { findCommunityPost, getCommunityPost } from "../daos/community-post.dao"; +import { findCommunityPost, getCommunityPost, insertCommunityPost } from "../daos/community-post.dao"; import { readCommunityCommentsResonseDTO, readCommunityPostResponseDTO, readCommunityPostsResponseDTO, } from "../dtos/community-posts.dto"; +import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; export const readCommunityPosts = async (userId: number | undefined, query) => { const response = await findCommunityPost(userId, query.cursorId); @@ -48,3 +49,8 @@ export const readCommunityComments = async (params, query) => { const comments = await findCommunityComment(params.postId, cursorId); return readCommunityCommentsResonseDTO(comments); }; + +export const createCommunityPost = async (userId: number, body: CreateCommunityPostSchema) => { + await insertCommunityPost(userId, body); + return; +}; From 7adc79711355aafadd1a50338505b57e82ef3057 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Fri, 26 Jan 2024 23:09:17 +0900 Subject: [PATCH 016/147] =?UTF-8?q?[FEAT]=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EB=8C=93=EA=B8=80=20=EC=9E=91=EC=84=B1=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(#28)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 함수 추가 #27 * Feat: 컨트롤러 함수 추가 #27 * Feat: CreateCommunityCommentSchema 생성 #27 * Feat: 서비스 함수 추가 #27 * Feat: DAO 함수 추가 #27 --- src/controllers/community-posts.controller.ts | 5 +++++ src/daos/community-comment.dao.ts | 9 ++++++++ src/daos/community-post.dao.ts | 2 +- src/routes/community-posts.route.ts | 13 +++++++++-- src/schemas/community-comment.schema.ts | 7 ++++++ src/services/community-posts.service.ts | 22 ++++++++++++++----- 6 files changed, 50 insertions(+), 8 deletions(-) create mode 100644 src/schemas/community-comment.schema.ts diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts index 5141cd5..5534602 100644 --- a/src/controllers/community-posts.controller.ts +++ b/src/controllers/community-posts.controller.ts @@ -2,6 +2,7 @@ import { Request, Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; import { + createCommunityComment, createCommunityPost, createOrDeleteBookmark, readCommunityComments, @@ -28,3 +29,7 @@ export const fetchCommunityComments = async (req: Request, res: Response, next) export const addCommunityPost = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await createCommunityPost(req.user.id, req.body))); }; + +export const addCommunityComment = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await createCommunityComment(req.user.id, req.params, req.body))); +}; diff --git a/src/daos/community-comment.dao.ts b/src/daos/community-comment.dao.ts index 0f54438..9ddd952 100644 --- a/src/daos/community-comment.dao.ts +++ b/src/daos/community-comment.dao.ts @@ -1,4 +1,5 @@ import db from "../models"; +import { CreateCommunityCommentSchema } from "../schemas/community-comment.schema"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; const defaultLimit = 20; @@ -31,3 +32,11 @@ export const getCommentCount = async (postId: number) => { }, }); }; + +export const insertCommunityComment = async (userId: number, postId: number, body: CreateCommunityCommentSchema) => { + await db.CommunityComment.create({ + postId, + authorId: userId, + content: body.content, + }); +}; diff --git a/src/daos/community-post.dao.ts b/src/daos/community-post.dao.ts index dc65884..7ac6232 100644 --- a/src/daos/community-post.dao.ts +++ b/src/daos/community-post.dao.ts @@ -30,7 +30,7 @@ export const findCommunityPost = async (userId?: number, cursorId?: number) => { return { posts, hasNext: calculateHasNext(posts, defaultLimit) }; }; -export const getCommunityPost = async (userId: number | undefined, postId: number) => { +export const getCommunityPost = async (postId: number) => { return await db.CommunityPost.findOne({ raw: true, where: { diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts index 905930a..1101f57 100644 --- a/src/routes/community-posts.route.ts +++ b/src/routes/community-posts.route.ts @@ -2,6 +2,7 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; import { + addCommunityComment, addCommunityPost, addOrRemoveBookmark, fetchCommunityComments, @@ -10,15 +11,23 @@ import { } from "../controllers/community-posts.controller"; import { createCommunityPost } from "../schemas/community-post.schema"; import { validateBody } from "../middlewares/validate.middleware"; +import { createCommunityComment } from "../schemas/community-comment.schema"; export const communityPostsRouter = express.Router(); +communityPostsRouter.post("/", verifyUser, validateBody(createCommunityPost), asyncHandler(addCommunityPost)); + communityPostsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); communityPostsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); -communityPostsRouter.get("/:postId", verifyUser, asyncHandler(fetchCommunityPost)); +communityPostsRouter.post( + "/:postId/comments", + verifyUser, + validateBody(createCommunityComment), + asyncHandler(addCommunityComment), +); communityPostsRouter.get("/:postId/comments", verifyUser, asyncHandler(fetchCommunityComments)); -communityPostsRouter.post("/", verifyUser, validateBody(createCommunityPost), asyncHandler(addCommunityPost)); +communityPostsRouter.get("/:postId", verifyUser, asyncHandler(fetchCommunityPost)); diff --git a/src/schemas/community-comment.schema.ts b/src/schemas/community-comment.schema.ts new file mode 100644 index 0000000..37e3b41 --- /dev/null +++ b/src/schemas/community-comment.schema.ts @@ -0,0 +1,7 @@ +import { TypeOf, object, z } from "zod"; + +export const createCommunityComment = object({ + content: z.string().max(500), +}); + +export type CreateCommunityCommentSchema = TypeOf; diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts index 894800a..387595c 100644 --- a/src/services/community-posts.service.ts +++ b/src/services/community-posts.service.ts @@ -1,7 +1,7 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { getCommunityBookmark, insertOrDeleteBookmark } from "../daos/community-bookmark.dao"; -import { findCommunityComment, getCommentCount } from "../daos/community-comment.dao"; +import { findCommunityComment, getCommentCount, insertCommunityComment } from "../daos/community-comment.dao"; import { findCommunityImage } from "../daos/community-image.dao"; import { findCommunityPost, getCommunityPost, insertCommunityPost } from "../daos/community-post.dao"; import { @@ -9,6 +9,7 @@ import { readCommunityPostResponseDTO, readCommunityPostsResponseDTO, } from "../dtos/community-posts.dto"; +import { CreateCommunityCommentSchema } from "../schemas/community-comment.schema"; import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; export const readCommunityPosts = async (userId: number | undefined, query) => { @@ -23,10 +24,7 @@ export const createOrDeleteBookmark = async (userId, params) => { export const readCommunityPost = async (userId, params) => { const postId = params.postId; - const post = await getCommunityPost(userId, postId); - if (!post) { - throw new BaseError(status.POST_NOT_FOUND); - } + const post = handlePostNotFound(await getCommunityPost(postId)); const imageUrls = await findCommunityImage(postId); const commentCount = await getCommentCount(postId); const comments = await findCommunityComment(postId, undefined); @@ -54,3 +52,17 @@ export const createCommunityPost = async (userId: number, body: CreateCommunityP await insertCommunityPost(userId, body); return; }; + +export const createCommunityComment = async (userId: number, params, body: CreateCommunityCommentSchema) => { + const postId = params.postId; + handlePostNotFound(await getCommunityPost(postId)); + await insertCommunityComment(userId, postId, body); + return; +}; + +const handlePostNotFound = (post) => { + if (!post) { + throw new BaseError(status.POST_NOT_FOUND); + } + return post; +}; From 4027a93530341b97e276baf814305473d434d881 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Sat, 27 Jan 2024 19:16:16 +0900 Subject: [PATCH 017/147] =?UTF-8?q?[FEAT]=20=EB=82=B4=EA=B0=80=20=EC=93=B4?= =?UTF-8?q?=20=EA=B8=80=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84=20?= =?UTF-8?q?(#31)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 커뮤니티와 대관정보 통합한 post, image, bookmark 테이블 생성 #29 * Feat: 라우트 추가 #29 * Feat: 컨트롤러 함수 추가 #29 * Feat: 서비스 함수 추가 #29 * Feat: DAO 함수 추가 #29 * Feat: DTO 함수 추가 #29 --- src/app.ts | 2 ++ src/controllers/posts.controller.ts | 8 +++++ src/daos/post.dao.ts | 33 +++++++++++++++++++ src/dtos/posts.dto.ts | 11 +++++++ .../{community-bookmark.ts => bookmark.ts} | 14 ++++---- src/models/community-comment.ts | 2 +- src/models/{community-image.ts => image.ts} | 12 +++---- ...{community-post.model.ts => post.model.ts} | 23 ++++++++----- src/models/user.model.ts | 5 +-- src/routes/posts.route.ts | 10 ++++++ src/services/posts.service.ts | 7 ++++ 11 files changed, 102 insertions(+), 25 deletions(-) create mode 100644 src/controllers/posts.controller.ts create mode 100644 src/daos/post.dao.ts create mode 100644 src/dtos/posts.dto.ts rename src/models/{community-bookmark.ts => bookmark.ts} (62%) rename src/models/{community-image.ts => image.ts} (67%) rename src/models/{community-post.model.ts => post.model.ts} (60%) create mode 100644 src/routes/posts.route.ts create mode 100644 src/services/posts.service.ts diff --git a/src/app.ts b/src/app.ts index d95cc2a..ef4fbe1 100644 --- a/src/app.ts +++ b/src/app.ts @@ -15,6 +15,7 @@ import { authRouter } from "./routes/auth.route"; import { teamsRouter } from "./routes/teams.route"; import { membersRouter } from "./routes/members.route"; import { guestsRouter } from "./routes/guests.route"; +import { postsRouter } from "./routes/posts.route"; import { communityPostsRouter } from "./routes/community-posts.route"; const app = express(); @@ -38,6 +39,7 @@ app.use("/auth", authRouter); app.use("/teams", teamsRouter); app.use("/members", membersRouter); app.use("/guests", guestsRouter); +app.use("/posts", postsRouter); app.use("/community-posts", communityPostsRouter); app.use((req: Request, res: Response, next: NextFunction) => { diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts new file mode 100644 index 0000000..9ba7f89 --- /dev/null +++ b/src/controllers/posts.controller.ts @@ -0,0 +1,8 @@ +import { Response } from "express"; +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { readPostsByAuthor } from "../services/posts.service"; + +export const fetchMyPosts = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readPostsByAuthor(req.user.id, req.query))); +}; diff --git a/src/daos/post.dao.ts b/src/daos/post.dao.ts new file mode 100644 index 0000000..a200f83 --- /dev/null +++ b/src/daos/post.dao.ts @@ -0,0 +1,33 @@ +import db from "../models"; +import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; + +const defaultLimit = 20; + +export const findPostByAuthorId = async (userId: number, cursorId?: number) => { + const postsBeforeCursorForAuthor = { authorId: userId, ...generateCursorCondition(cursorId) }; + return findPost(userId, postsBeforeCursorForAuthor); +}; + +const findPost = async (userId: number | undefined, postFilter: object) => { + const bookmarkInclude: any[] = []; + if (userId) { + bookmarkInclude.push({ + model: db.Bookmark, + where: { + userId, + }, + required: false, + attributes: ["id"], + }); + } + + const posts = await db.Post.findAll({ + raw: true, + where: postFilter, + order: [["createdAt", "DESC"]], + limit: defaultLimit, + attributes: ["id", "title", "createdAt"], + include: bookmarkInclude, + }); + return { posts, hasNext: calculateHasNext(posts, defaultLimit) }; +}; diff --git a/src/dtos/posts.dto.ts b/src/dtos/posts.dto.ts new file mode 100644 index 0000000..356bb82 --- /dev/null +++ b/src/dtos/posts.dto.ts @@ -0,0 +1,11 @@ +export const readPostsResponseDTO = (result) => { + return { + posts: result.posts.map((post) => ({ + id: post.id, + isBookmarked: Boolean(post["Bookmarks.id"]), + title: post.title, + createdAt: post.createdAt, + })), + hasNext: result.hasNext, + }; +}; diff --git a/src/models/community-bookmark.ts b/src/models/bookmark.ts similarity index 62% rename from src/models/community-bookmark.ts rename to src/models/bookmark.ts index a3e2359..cd3ba43 100644 --- a/src/models/community-bookmark.ts +++ b/src/models/bookmark.ts @@ -1,8 +1,8 @@ import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; -class CommunityBookmark extends Model, InferCreationAttributes> { +class Bookmark extends Model, InferCreationAttributes> { static initiate(sequelize: Sequelize) { - CommunityBookmark.init( + Bookmark.init( { postId: { type: new DataTypes.INTEGER(), @@ -17,8 +17,8 @@ class CommunityBookmark extends Model, InferC sequelize, timestamps: true, underscored: true, - modelName: "CommunityBookmark", - tableName: "community_bookmark", + modelName: "Bookmark", + tableName: "bookmark", paranoid: true, charset: "utf8", collate: "utf8_general_ci", @@ -26,9 +26,9 @@ class CommunityBookmark extends Model, InferC ); } static associate(db) { - db.CommunityBookmark.belongsTo(db.CommunityPost, { foreignKey: "post_id" }); - db.CommunityBookmark.belongsTo(db.User, { foreignKey: "user_id" }); + db.Bookmark.belongsTo(db.Post, { foreignKey: "post_id" }); + db.Bookmark.belongsTo(db.User, { foreignKey: "user_id" }); } } -module.exports = CommunityBookmark; +module.exports = Bookmark; diff --git a/src/models/community-comment.ts b/src/models/community-comment.ts index bc6662f..ab7676a 100644 --- a/src/models/community-comment.ts +++ b/src/models/community-comment.ts @@ -30,7 +30,7 @@ class CommunityComment extends Model, InferCre ); } static associate(db) { - db.CommunityComment.belongsTo(db.CommunityPost, { foreignKey: "post_id" }); + db.CommunityComment.belongsTo(db.Post, { foreignKey: "post_id" }); db.CommunityComment.belongsTo(db.User, { foreignKey: "author_id" }); } } diff --git a/src/models/community-image.ts b/src/models/image.ts similarity index 67% rename from src/models/community-image.ts rename to src/models/image.ts index a05490e..a775137 100644 --- a/src/models/community-image.ts +++ b/src/models/image.ts @@ -1,8 +1,8 @@ import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; -class CommunityImage extends Model, InferCreationAttributes> { +class Image extends Model, InferCreationAttributes> { static initiate(sequelize: Sequelize) { - CommunityImage.init( + Image.init( { postId: { type: new DataTypes.INTEGER(), @@ -17,8 +17,8 @@ class CommunityImage extends Model, InferCreatio sequelize, timestamps: false, underscored: true, - modelName: "CommunityImage", - tableName: "community_image", + modelName: "Image", + tableName: "image", paranoid: false, charset: "utf8", collate: "utf8_general_ci", @@ -26,8 +26,8 @@ class CommunityImage extends Model, InferCreatio ); } static associate(db) { - db.CommunityImage.belongsTo(db.CommunityPost, { foreignKey: "post_id" }); + db.Image.belongsTo(db.Post, { foreignKey: "post_id" }); } } -module.exports = CommunityImage; +module.exports = Image; diff --git a/src/models/community-post.model.ts b/src/models/post.model.ts similarity index 60% rename from src/models/community-post.model.ts rename to src/models/post.model.ts index 18396fa..4be5499 100644 --- a/src/models/community-post.model.ts +++ b/src/models/post.model.ts @@ -1,8 +1,8 @@ import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; -class CommunityPost extends Model, InferCreationAttributes> { +class Post extends Model, InferCreationAttributes> { static initiate(sequelize: Sequelize) { - CommunityPost.init( + Post.init( { title: { type: new DataTypes.STRING(30), @@ -20,13 +20,17 @@ class CommunityPost extends Model, InferCreationA type: new DataTypes.INTEGER(), allowNull: false, }, + type: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, }, { sequelize, timestamps: true, underscored: true, - modelName: "CommunityPost", - tableName: "community_post", + modelName: "Post", + tableName: "post", paranoid: false, charset: "utf8", collate: "utf8_general_ci", @@ -34,11 +38,12 @@ class CommunityPost extends Model, InferCreationA ); } static associate(db) { - db.CommunityPost.hasMany(db.CommunityImage, { foreignKey: "post_id" }); - db.CommunityPost.hasMany(db.CommunityComment, { foreignKey: "post_id" }); - db.CommunityPost.hasMany(db.CommunityBookmark, { foreignKey: "post_id" }); - db.CommunityPost.belongsTo(db.User, { foreignKey: "author_id" }); + db.Post.belongsTo(db.User, { foreignKey: "author_id" }); + db.Post.hasMany(db.Image, { foreignKey: "post_id" }); + db.Post.hasMany(db.Bookmark, { foreignKey: "post_id" }); + db.Post.hasMany(db.CommunityComment, { foreignKey: "post_id" }); + //db.Post.hasMany(db.RentalInformationComment, { foreignKey: "post_id" }); } } -module.exports = CommunityPost; +module.exports = Post; diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 39792aa..01ad7d4 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -44,9 +44,10 @@ class User extends Model, InferCreationAttributes> { db.User.hasMany(db.Team, { foreignKey: "leader_id" }); db.User.hasMany(db.Member, { foreignKey: "user_id" }); db.User.hasMany(db.GuestUser, { foreignKey: "user_id" }); - db.User.hasMany(db.CommunityPost, { foreignKey: "author_id" }); + db.User.hasMany(db.Post, { foreignKey: "author_id" }); + db.User.hasMany(db.Bookmark, { foreignKey: "user_id" }); db.User.hasMany(db.CommunityComment, { foreignKey: "author_id" }); - db.User.hasMany(db.CommunityBookmark, { foreignKey: "user_id" }); + // db.User.hasMany(db.RentalInformationComment, { foreignKey: "author_id" }); } } diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts new file mode 100644 index 0000000..6d59ded --- /dev/null +++ b/src/routes/posts.route.ts @@ -0,0 +1,10 @@ +import express from "express"; +import asyncHandler from "express-async-handler"; +import { verifyUser } from "../middlewares/auth.middleware"; +import { fetchMyPosts } from "../controllers/posts.controller"; + +export const postsRouter = express.Router(); + +postsRouter.use(verifyUser); + +postsRouter.get("/my", asyncHandler(fetchMyPosts)); diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts new file mode 100644 index 0000000..b3be6b4 --- /dev/null +++ b/src/services/posts.service.ts @@ -0,0 +1,7 @@ +import { findPostByAuthorId } from "../daos/post.dao"; +import { readPostsResponseDTO } from "../dtos/posts.dto"; + +export const readPostsByAuthor = async (userId, query) => { + const result = await findPostByAuthorId(userId, query.cursorId); + return readPostsResponseDTO(result); +}; From c74bb3ab06a798f340a326d80e00a3e20040a4cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sat, 27 Jan 2024 19:38:46 +0900 Subject: [PATCH 018/147] =?UTF-8?q?Refactor:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EA=B8=80=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20API=20=EA=B5=AC=ED=98=84=20#17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/post-type.constant.ts | 4 ++ src/controllers/community-posts.controller.ts | 8 +-- src/controllers/posts.controller.ts | 6 ++- src/daos/community-post.dao.ts | 50 +++++++++---------- src/daos/post.dao.ts | 6 +++ src/routes/community-posts.route.ts | 4 +- src/routes/posts.route.ts | 8 +-- src/services/community-posts.service.ts | 16 +++--- src/services/posts.service.ts | 8 ++- 9 files changed, 67 insertions(+), 43 deletions(-) create mode 100644 src/constants/post-type.constant.ts diff --git a/src/constants/post-type.constant.ts b/src/constants/post-type.constant.ts new file mode 100644 index 0000000..e3f9676 --- /dev/null +++ b/src/constants/post-type.constant.ts @@ -0,0 +1,4 @@ +export enum PostType { + RentalInfo = 1, + Community = 2, +} diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts index 5534602..604f028 100644 --- a/src/controllers/community-posts.controller.ts +++ b/src/controllers/community-posts.controller.ts @@ -7,12 +7,12 @@ import { createOrDeleteBookmark, readCommunityComments, readCommunityPost, - readCommunityPosts, + // readCommunityPosts, } from "../services/community-posts.service"; -export const fetchCommunityPosts = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readCommunityPosts(req.user?.id, req.query))); -}; +// export const fetchCommunityPosts = async (req, res: Response, next) => { +// res.send(response(status.SUCCESS, await readCommunityPosts(req.user?.id, req.query))); +// }; export const addOrRemoveBookmark = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await createOrDeleteBookmark(req.user.id, req.params))); diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index 9ba7f89..eda2a9f 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -1,7 +1,11 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readPostsByAuthor } from "../services/posts.service"; +import { readCommunityPosts, readPostsByAuthor } from "../services/posts.service"; + +export const fetchCommunityPosts = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readCommunityPosts(req.user?.id, req.query))); +}; export const fetchMyPosts = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readPostsByAuthor(req.user.id, req.query))); diff --git a/src/daos/community-post.dao.ts b/src/daos/community-post.dao.ts index 7ac6232..2e9cc22 100644 --- a/src/daos/community-post.dao.ts +++ b/src/daos/community-post.dao.ts @@ -1,34 +1,34 @@ import db from "../models"; import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; -import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; +// import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; -const defaultLimit = 20; +// const defaultLimit = 20; -export const findCommunityPost = async (userId?: number, cursorId?: number) => { - const postsBeforeCursor = generateCursorCondition(cursorId); +// export const findCommunityPost = async (userId?: number, cursorId?: number) => { +// const postsBeforeCursor = generateCursorCondition(cursorId); - const bookmarkInclude: any[] = []; - if (userId) { - bookmarkInclude.push({ - model: db.CommunityBookmark, - where: { - userId, - }, - required: false, - attributes: ["id"], - }); - } +// const bookmarkInclude: any[] = []; +// if (userId) { +// bookmarkInclude.push({ +// model: db.CommunityBookmark, +// where: { +// userId, +// }, +// required: false, +// attributes: ["id"], +// }); +// } - const posts = await db.CommunityPost.findAll({ - raw: true, - where: postsBeforeCursor, - order: [["createdAt", "DESC"]], - limit: defaultLimit, - attributes: ["id", "title", "createdAt"], - include: bookmarkInclude, - }); - return { posts, hasNext: calculateHasNext(posts, defaultLimit) }; -}; +// const posts = await db.CommunityPost.findAll({ +// raw: true, +// where: postsBeforeCursor, +// order: [["createdAt", "DESC"]], +// limit: defaultLimit, +// attributes: ["id", "title", "createdAt"], +// include: bookmarkInclude, +// }); +// return { posts, hasNext: calculateHasNext(posts, defaultLimit) }; +// }; export const getCommunityPost = async (postId: number) => { return await db.CommunityPost.findOne({ diff --git a/src/daos/post.dao.ts b/src/daos/post.dao.ts index a200f83..ae1b9af 100644 --- a/src/daos/post.dao.ts +++ b/src/daos/post.dao.ts @@ -1,8 +1,14 @@ +import { PostType } from "../constants/post-type.constant"; import db from "../models"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; const defaultLimit = 20; +export const findPostByType = async (userId: number | undefined, cursorId: number | undefined, type: PostType) => { + const communityPostsBeforeCursor = { type, ...generateCursorCondition(cursorId) }; + return findPost(userId, communityPostsBeforeCursor); +}; + export const findPostByAuthorId = async (userId: number, cursorId?: number) => { const postsBeforeCursorForAuthor = { authorId: userId, ...generateCursorCondition(cursorId) }; return findPost(userId, postsBeforeCursorForAuthor); diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts index 1101f57..a12f009 100644 --- a/src/routes/community-posts.route.ts +++ b/src/routes/community-posts.route.ts @@ -7,7 +7,7 @@ import { addOrRemoveBookmark, fetchCommunityComments, fetchCommunityPost, - fetchCommunityPosts, + // fetchCommunityPosts, } from "../controllers/community-posts.controller"; import { createCommunityPost } from "../schemas/community-post.schema"; import { validateBody } from "../middlewares/validate.middleware"; @@ -17,7 +17,7 @@ export const communityPostsRouter = express.Router(); communityPostsRouter.post("/", verifyUser, validateBody(createCommunityPost), asyncHandler(addCommunityPost)); -communityPostsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); +// communityPostsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); communityPostsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index 6d59ded..7af07c7 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -1,10 +1,10 @@ import express from "express"; import asyncHandler from "express-async-handler"; -import { verifyUser } from "../middlewares/auth.middleware"; -import { fetchMyPosts } from "../controllers/posts.controller"; +import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; +import { fetchCommunityPosts, fetchMyPosts } from "../controllers/posts.controller"; export const postsRouter = express.Router(); -postsRouter.use(verifyUser); +postsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); -postsRouter.get("/my", asyncHandler(fetchMyPosts)); +postsRouter.get("/my", verifyUser, asyncHandler(fetchMyPosts)); diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts index 387595c..80f33a1 100644 --- a/src/services/community-posts.service.ts +++ b/src/services/community-posts.service.ts @@ -3,19 +3,23 @@ import { status } from "../config/response.status"; import { getCommunityBookmark, insertOrDeleteBookmark } from "../daos/community-bookmark.dao"; import { findCommunityComment, getCommentCount, insertCommunityComment } from "../daos/community-comment.dao"; import { findCommunityImage } from "../daos/community-image.dao"; -import { findCommunityPost, getCommunityPost, insertCommunityPost } from "../daos/community-post.dao"; +import { + // findCommunityPost, + getCommunityPost, + insertCommunityPost, +} from "../daos/community-post.dao"; import { readCommunityCommentsResonseDTO, readCommunityPostResponseDTO, - readCommunityPostsResponseDTO, + // readCommunityPostsResponseDTO, } from "../dtos/community-posts.dto"; import { CreateCommunityCommentSchema } from "../schemas/community-comment.schema"; import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; -export const readCommunityPosts = async (userId: number | undefined, query) => { - const response = await findCommunityPost(userId, query.cursorId); - return readCommunityPostsResponseDTO(response); -}; +// export const readCommunityPosts = async (userId: number | undefined, query) => { +// const response = await findCommunityPost(userId, query.cursorId); +// return readCommunityPostsResponseDTO(response); +// }; export const createOrDeleteBookmark = async (userId, params) => { await insertOrDeleteBookmark(userId, params.postId); diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index b3be6b4..dfaa56d 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -1,6 +1,12 @@ -import { findPostByAuthorId } from "../daos/post.dao"; +import { PostType } from "../constants/post-type.constant"; +import { findPostByType, findPostByAuthorId } from "../daos/post.dao"; import { readPostsResponseDTO } from "../dtos/posts.dto"; +export const readCommunityPosts = async (userId: number | undefined, query) => { + const result = await findPostByType(userId, query.cursorId, PostType.Community); + return readPostsResponseDTO(result); +}; + export const readPostsByAuthor = async (userId, query) => { const result = await findPostByAuthorId(userId, query.cursorId); return readPostsResponseDTO(result); From 70d30e968dd7378df98d5f23473b2d23c1226bd8 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Sat, 27 Jan 2024 20:23:46 +0900 Subject: [PATCH 019/147] =?UTF-8?q?[FEAT]=20=EC=A0=80=EC=9E=A5=ED=95=9C=20?= =?UTF-8?q?=EA=B8=80=20=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84=20(#3?= =?UTF-8?q?3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 함수 추가 #32 * Feat: 컨트롤러 함수 추가 #32 * Feat: 서비스 함수 추가 #32 * Feat: DAO 함수 추가 #32 --- src/controllers/posts.controller.ts | 6 +++++- src/daos/post.dao.ts | 27 ++++++++++++++++++++++----- src/routes/posts.route.ts | 4 +++- src/services/posts.service.ts | 9 +++++++-- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index eda2a9f..9672b6e 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -1,7 +1,7 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readCommunityPosts, readPostsByAuthor } from "../services/posts.service"; +import { readBookmarkedPosts, readCommunityPosts, readPostsByAuthor } from "../services/posts.service"; export const fetchCommunityPosts = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readCommunityPosts(req.user?.id, req.query))); @@ -10,3 +10,7 @@ export const fetchCommunityPosts = async (req, res: Response, next) => { export const fetchMyPosts = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readPostsByAuthor(req.user.id, req.query))); }; + +export const fetchBookmarkedPosts = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readBookmarkedPosts(req.user.id, req.query))); +}; diff --git a/src/daos/post.dao.ts b/src/daos/post.dao.ts index ae1b9af..5250275 100644 --- a/src/daos/post.dao.ts +++ b/src/daos/post.dao.ts @@ -6,18 +6,18 @@ const defaultLimit = 20; export const findPostByType = async (userId: number | undefined, cursorId: number | undefined, type: PostType) => { const communityPostsBeforeCursor = { type, ...generateCursorCondition(cursorId) }; - return findPost(userId, communityPostsBeforeCursor); + return findPostByFilter(userId, communityPostsBeforeCursor); }; export const findPostByAuthorId = async (userId: number, cursorId?: number) => { const postsBeforeCursorForAuthor = { authorId: userId, ...generateCursorCondition(cursorId) }; - return findPost(userId, postsBeforeCursorForAuthor); + return findPostByFilter(userId, postsBeforeCursorForAuthor); }; -const findPost = async (userId: number | undefined, postFilter: object) => { - const bookmarkInclude: any[] = []; +const findPostByFilter = async (userId: number | undefined, postFilter: object) => { + const includeAllPosts: any[] = []; if (userId) { - bookmarkInclude.push({ + includeAllPosts.push({ model: db.Bookmark, where: { userId, @@ -26,7 +26,24 @@ const findPost = async (userId: number | undefined, postFilter: object) => { attributes: ["id"], }); } + return findPost(postFilter, includeAllPosts); +}; + +export const findBookmarkedPost = async (userId: number, cursorId?: number) => { + const postsBeforeCursor = generateCursorCondition(cursorId); + const includeBookmarkedPosts = [ + { + model: db.Bookmark, + where: { + userId, + }, + attributes: ["id"], + }, + ]; + return findPost(postsBeforeCursor, includeBookmarkedPosts); +}; +const findPost = async (postFilter: object, bookmarkInclude: Array) => { const posts = await db.Post.findAll({ raw: true, where: postFilter, diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index 7af07c7..1d4a99d 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -1,10 +1,12 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; -import { fetchCommunityPosts, fetchMyPosts } from "../controllers/posts.controller"; +import { fetchBookmarkedPosts, fetchCommunityPosts, fetchMyPosts } from "../controllers/posts.controller"; export const postsRouter = express.Router(); postsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); postsRouter.get("/my", verifyUser, asyncHandler(fetchMyPosts)); + +postsRouter.get("/bookmarks", verifyUser, asyncHandler(fetchBookmarkedPosts)); diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index dfaa56d..3352a39 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -1,5 +1,5 @@ import { PostType } from "../constants/post-type.constant"; -import { findPostByType, findPostByAuthorId } from "../daos/post.dao"; +import { findPostByType, findPostByAuthorId, findBookmarkedPost } from "../daos/post.dao"; import { readPostsResponseDTO } from "../dtos/posts.dto"; export const readCommunityPosts = async (userId: number | undefined, query) => { @@ -7,7 +7,12 @@ export const readCommunityPosts = async (userId: number | undefined, query) => { return readPostsResponseDTO(result); }; -export const readPostsByAuthor = async (userId, query) => { +export const readPostsByAuthor = async (userId: number, query) => { const result = await findPostByAuthorId(userId, query.cursorId); return readPostsResponseDTO(result); }; + +export const readBookmarkedPosts = async (userId: number, query) => { + const result = await findBookmarkedPost(userId, query.cursorId); + return readPostsResponseDTO(result); +}; From 30dd9c3d7fe602876812e6e565d958a4ffc2c1e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sat, 27 Jan 2024 20:32:59 +0900 Subject: [PATCH 020/147] =?UTF-8?q?Refactor:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EA=B8=80=20=EC=A0=80=EC=9E=A5=EA=B3=BC=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EC=B7=A8=EC=86=8C=20API=20=EA=B5=AC=ED=98=84=20#19?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/community-posts.controller.ts | 8 ++++---- src/controllers/posts.controller.ts | 11 ++++++++++- ...{community-bookmark.dao.ts => bookmark.dao.ts} | 8 ++++---- src/routes/community-posts.route.ts | 4 ++-- src/routes/posts.route.ts | 9 ++++++++- src/services/community-posts.service.ts | 15 +++++++++------ src/services/posts.service.ts | 6 ++++++ 7 files changed, 43 insertions(+), 18 deletions(-) rename src/daos/{community-bookmark.dao.ts => bookmark.dao.ts} (56%) diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts index 604f028..7724b91 100644 --- a/src/controllers/community-posts.controller.ts +++ b/src/controllers/community-posts.controller.ts @@ -4,7 +4,7 @@ import { status } from "../config/response.status"; import { createCommunityComment, createCommunityPost, - createOrDeleteBookmark, + // createOrDeleteBookmark, readCommunityComments, readCommunityPost, // readCommunityPosts, @@ -14,9 +14,9 @@ import { // res.send(response(status.SUCCESS, await readCommunityPosts(req.user?.id, req.query))); // }; -export const addOrRemoveBookmark = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await createOrDeleteBookmark(req.user.id, req.params))); -}; +// export const addOrRemoveBookmark = async (req, res: Response, next) => { +// res.send(response(status.SUCCESS, await createOrDeleteBookmark(req.user.id, req.params))); +// }; export const fetchCommunityPost = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readCommunityPost(req.user.id, req.params))); diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index 9672b6e..2295624 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -1,7 +1,12 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readBookmarkedPosts, readCommunityPosts, readPostsByAuthor } from "../services/posts.service"; +import { + createOrDeleteBookmark, + readBookmarkedPosts, + readCommunityPosts, + readPostsByAuthor, +} from "../services/posts.service"; export const fetchCommunityPosts = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readCommunityPosts(req.user?.id, req.query))); @@ -14,3 +19,7 @@ export const fetchMyPosts = async (req, res: Response, next) => { export const fetchBookmarkedPosts = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readBookmarkedPosts(req.user.id, req.query))); }; + +export const addOrRemoveBookmark = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await createOrDeleteBookmark(req.user.id, req.params))); +}; diff --git a/src/daos/community-bookmark.dao.ts b/src/daos/bookmark.dao.ts similarity index 56% rename from src/daos/community-bookmark.dao.ts rename to src/daos/bookmark.dao.ts index f80453c..976b91f 100644 --- a/src/daos/community-bookmark.dao.ts +++ b/src/daos/bookmark.dao.ts @@ -1,20 +1,20 @@ import db from "../models"; export const insertOrDeleteBookmark = async (userId, postId) => { - const bookmark = await getCommunityBookmark(userId, postId); + const bookmark = await getBookmark(userId, postId); if (bookmark) { await bookmark.destroy(); } else { - await db.CommunityBookmark.create({ + await db.Bookmark.create({ postId, userId, }); } }; -export const getCommunityBookmark = async (userId: number, postId: number) => { - return await db.CommunityBookmark.findOne({ +export const getBookmark = async (userId: number, postId: number) => { + return await db.Bookmark.findOne({ where: { postId, userId, diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts index a12f009..06a729c 100644 --- a/src/routes/community-posts.route.ts +++ b/src/routes/community-posts.route.ts @@ -4,7 +4,7 @@ import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; import { addCommunityComment, addCommunityPost, - addOrRemoveBookmark, + // addOrRemoveBookmark, fetchCommunityComments, fetchCommunityPost, // fetchCommunityPosts, @@ -19,7 +19,7 @@ communityPostsRouter.post("/", verifyUser, validateBody(createCommunityPost), as // communityPostsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); -communityPostsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); +// communityPostsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); communityPostsRouter.post( "/:postId/comments", diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index 1d4a99d..6ede0b2 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -1,7 +1,12 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; -import { fetchBookmarkedPosts, fetchCommunityPosts, fetchMyPosts } from "../controllers/posts.controller"; +import { + addOrRemoveBookmark, + fetchBookmarkedPosts, + fetchCommunityPosts, + fetchMyPosts, +} from "../controllers/posts.controller"; export const postsRouter = express.Router(); @@ -10,3 +15,5 @@ postsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); postsRouter.get("/my", verifyUser, asyncHandler(fetchMyPosts)); postsRouter.get("/bookmarks", verifyUser, asyncHandler(fetchBookmarkedPosts)); + +postsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts index 80f33a1..a692a2a 100644 --- a/src/services/community-posts.service.ts +++ b/src/services/community-posts.service.ts @@ -1,6 +1,9 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; -import { getCommunityBookmark, insertOrDeleteBookmark } from "../daos/community-bookmark.dao"; +import { + getBookmark, + // insertOrDeleteBookmark +} from "../daos/bookmark.dao"; import { findCommunityComment, getCommentCount, insertCommunityComment } from "../daos/community-comment.dao"; import { findCommunityImage } from "../daos/community-image.dao"; import { @@ -21,10 +24,10 @@ import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; // return readCommunityPostsResponseDTO(response); // }; -export const createOrDeleteBookmark = async (userId, params) => { - await insertOrDeleteBookmark(userId, params.postId); - return; -}; +// export const createOrDeleteBookmark = async (userId, params) => { +// await insertOrDeleteBookmark(userId, params.postId); +// return; +// }; export const readCommunityPost = async (userId, params) => { const postId = params.postId; @@ -40,7 +43,7 @@ const checkIsBookmarked = async (userId: number | undefined, postId: number) => if (!userId) { return null; } - return Boolean(await getCommunityBookmark(userId, postId)); + return Boolean(await getBookmark(userId, postId)); }; export const readCommunityComments = async (params, query) => { diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index 3352a39..625f223 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -1,4 +1,5 @@ import { PostType } from "../constants/post-type.constant"; +import { insertOrDeleteBookmark } from "../daos/bookmark.dao"; import { findPostByType, findPostByAuthorId, findBookmarkedPost } from "../daos/post.dao"; import { readPostsResponseDTO } from "../dtos/posts.dto"; @@ -16,3 +17,8 @@ export const readBookmarkedPosts = async (userId: number, query) => { const result = await findBookmarkedPost(userId, query.cursorId); return readPostsResponseDTO(result); }; + +export const createOrDeleteBookmark = async (userId, params) => { + await insertOrDeleteBookmark(userId, params.postId); + return; +}; From 8e5df97ceee2d6767d324d7e84dbc558589cdfc2 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sat, 27 Jan 2024 22:16:36 +0900 Subject: [PATCH 021/147] =?UTF-8?q?FEAT)=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=A7=80=EC=97=AD=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20API=20=EA=B5=AC=ED=98=84=20#15?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/guest.controller.ts | 12 ++--- src/daos/guest.dao.ts | 80 +++++++++++------------------ src/routes/guests.route.ts | 8 +-- src/services/guest.service.ts | 33 +++++++++--- 4 files changed, 66 insertions(+), 67 deletions(-) diff --git a/src/controllers/guest.controller.ts b/src/controllers/guest.controller.ts index 946daa6..6135eae 100644 --- a/src/controllers/guest.controller.ts +++ b/src/controllers/guest.controller.ts @@ -4,7 +4,7 @@ import { readGuesting, // readGuestingByLevel, // readGuestingByGender, - // readGuestingByRegion, + readGuestingByRegion, } from "../services/guest.service"; // import { postGuesting } from "../services/guest.service"; @@ -17,16 +17,16 @@ export const GuestingPreview = async (req, res, next) => { }; // export const GuestingPreviewByLevel = async (req, res, next) => { -// return res.send(response(status.SUCCESS, await readGuestingByLevel(req.team.id, req.query))); +// return res.send(response(status.SUCCESS, await readGuestingByLevel(req.query))); // }; // export const GuestingPreviewByGender = async (req, res, next) => { -// return res.send(response(status.SUCCESS, await readGuestingByGender(req.team.id, req.query))); +// return res.send(response(status.SUCCESS, await readGuestingByGender(req.query))); // }; -// export const GuestingPreviewByRegion = async (req, res, next) => { -// return res.send(response(status.SUCCESS, await readGuestingByRegion(req.team.id, req.query))); -// }; +export const GuestingPreviewByRegion = async (req, res, next) => { + return res.send(response(status.SUCCESS, await readGuestingByRegion(req.query))); +}; // export const DetailedGuestingPreview = async (req, res, next) => { // return res.send(response(status.SUCCESS, await getDetailedGuesting(req.team.id, req.params))); diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index b243bc1..ccd90d3 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -18,77 +18,59 @@ export const findGuesting = async (date, category) => { }); }; -// export const findGuestingByGender = async (selectedDate, category, gender) => { -// const Guesting = await db.Guest.findAll({ +// export const findGuestingByGender = async (date, category, gender) => { +// return await db.Guest.findAll({ // raw: true, -// where: { -// gameTime: selectedDate, -// category, -// gender, -// }, +// where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), // include: [ // { // model: db.Team, -// as: "TeamInfo", -// attributes: ["name", "region", "gender", "age_group", "skill_level"], // where: { -// id: db.Sequelize.col("Guest.TeamId"), +// category, +// gender, // }, +// attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], // }, // ], -// attributes: ["game_time"], +// attributes: ["gameTime", "recruitCount"], // }); - -// return Guesting.map((guest) => guest["TeamInfo"]); // }; -// export const findGuestingByLevel = async (selectedDate, category, level) => { -// const Guesting = await db.Guest.findAll({ +// export const findGuestingByLevel = async (date, category, skillLevel) => { +// return await db.Guest.findAll({ // raw: true, -// where: { -// gameTime: selectedDate, -// category, -// level, -// }, +// where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), // include: [ // { // model: db.Team, -// as: "TeamInfo", -// attributes: ["name", "region", "gender", "age_group", "skill_level"], // where: { -// id: db.Sequelize.col("Guest.TeamId"), +// category, +// skillLevel, // }, +// attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], // }, // ], -// attributes: ["game_time"], +// attributes: ["gameTime", "recruitCount"], // }); - -// return Guesting.map((guest) => guest["TeamInfo"]); // }; -// export const findGuestingByRegion = async (selectedDate, category, region) => { -// const Guesting = await db.Guest.findAll({ -// raw: true, -// where: { -// gameTime: selectedDate, -// category, -// region, -// }, -// include: [ -// { -// model: db.Team, -// as: "TeamInfo", -// attributes: ["name", "region", "gender", "age_group", "skill_level"], -// where: { -// id: db.Sequelize.col("Guest.TeamId"), -// }, -// }, -// ], -// attributes: ["game_time"], -// }); - -// return Guesting.map((guest) => guest["TeamInfo"]); -// }; +export const findGuestingByRegion = async (date, category, region) => { + return await db.Guest.findAll({ + raw: true, + where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + include: [ + { + model: db.Team, + where: { + category, + region, + }, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["gameTime", "recruitCount"], + }); +}; // export const findGuestDetail = async (guestId) => { // return await db.Guest.findOne({ diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index b73c911..56d5fe5 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -4,7 +4,7 @@ import { GuestingPreview, // GuestingPreviewByLevel, // GuestingPreviewByGender, - // GuestingPreviewByRegion, + GuestingPreviewByRegion, } from "../controllers/guest.controller"; export const guestsRouter = express.Router({ mergeParams: true }); @@ -13,10 +13,10 @@ export const guestsRouter = express.Router({ mergeParams: true }); guestsRouter.get("/", asyncHandler(GuestingPreview)); -// guestRouter.get("/:guestId", asyncHandler(DetailedGuestingPreview)); - // guestsRouter.get("/level", asyncHandler(GuestingPreviewByLevel)); // guestsRouter.get("/gender", asyncHandler(GuestingPreviewByGender)); -// guestsRouter.get("/region", asyncHandler(GuestingPreviewByRegion)); +guestsRouter.get("/region", asyncHandler(GuestingPreviewByRegion)); + +// guestRouter.get("/:guestId", asyncHandler(DetailedGuestingPreview)); diff --git a/src/services/guest.service.ts b/src/services/guest.service.ts index 5cb4151..3497fbd 100644 --- a/src/services/guest.service.ts +++ b/src/services/guest.service.ts @@ -1,6 +1,11 @@ // import { BaseError } from "../config/error"; // import { status } from "../config/response.status"; -import { findGuesting } from "../daos/guest.dao"; +import { + findGuesting, + // findGuestingByGender, + // findGuestingByLevel, + findGuestingByRegion, +} from "../daos/guest.dao"; import { getMemberCountByTeamId } from "../daos/member.dao"; import { readGuestingResponseDTO } from "../dtos/guests.dto"; @@ -12,14 +17,26 @@ export const readGuesting = async (query) => { return readGuestingResponseDTO(guestings); }; -// export const readGuestingByGender = async (selectedDate, query) => { -// return await findGuestingByGender(selectedDate, query.category, query.gender); +// export const readGuestingByGender = async (query) => { +// const guestings = await findGuestingByGender(query.date, query.category, query.gender); +// for (const guesting of guestings) { +// guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; +// } +// return readGuestingResponseDTO(guestings); // }; -// export const readGuestingByLevel = async (selectedDate, query) => { -// return await findGuestingByLevel(selectedDate, query.category, query.level); +// export const readGuestingByLevel = async (query) => { +// const guestings = await findGuestingByLevel(query.date, query.category, query.skillLevel); +// for (const guesting of guestings) { +// guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; +// } +// return readGuestingResponseDTO(guestings); // }; -// export const readGuestingByRegion = async (selectedDate, query) => { -// return await findGuestingByRegion(selectedDate, query.category, query.region); -// }; +export const readGuestingByRegion = async (query) => { + const guestings = await findGuestingByRegion(query.date, query.category, query.region); + for (const guesting of guestings) { + guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; + } + return readGuestingResponseDTO(guestings); +}; From d1a6798d4ce71a1f1b6a578869d5b182a8d01c7b Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sat, 27 Jan 2024 22:25:25 +0900 Subject: [PATCH 022/147] =?UTF-8?q?FEAT)=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=84=B1=EB=B3=84=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20API=20=EA=B5=AC=ED=98=84=20#35?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/guest.controller.ts | 8 +++---- src/daos/guest.dao.ts | 34 ++++++++++++++--------------- src/routes/guests.route.ts | 4 ++-- src/services/guest.service.ts | 16 +++++++------- 4 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/controllers/guest.controller.ts b/src/controllers/guest.controller.ts index 6135eae..a2043c8 100644 --- a/src/controllers/guest.controller.ts +++ b/src/controllers/guest.controller.ts @@ -3,7 +3,7 @@ import { status } from "../config/response.status"; import { readGuesting, // readGuestingByLevel, - // readGuestingByGender, + readGuestingByGender, readGuestingByRegion, } from "../services/guest.service"; // import { postGuesting } from "../services/guest.service"; @@ -20,9 +20,9 @@ export const GuestingPreview = async (req, res, next) => { // return res.send(response(status.SUCCESS, await readGuestingByLevel(req.query))); // }; -// export const GuestingPreviewByGender = async (req, res, next) => { -// return res.send(response(status.SUCCESS, await readGuestingByGender(req.query))); -// }; +export const GuestingPreviewByGender = async (req, res, next) => { + return res.send(response(status.SUCCESS, await readGuestingByGender(req.query))); +}; export const GuestingPreviewByRegion = async (req, res, next) => { return res.send(response(status.SUCCESS, await readGuestingByRegion(req.query))); diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index ccd90d3..a06ba9a 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -18,23 +18,23 @@ export const findGuesting = async (date, category) => { }); }; -// export const findGuestingByGender = async (date, category, gender) => { -// return await db.Guest.findAll({ -// raw: true, -// where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), -// include: [ -// { -// model: db.Team, -// where: { -// category, -// gender, -// }, -// attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], -// }, -// ], -// attributes: ["gameTime", "recruitCount"], -// }); -// }; +export const findGuestingByGender = async (date, category, gender) => { + return await db.Guest.findAll({ + raw: true, + where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + include: [ + { + model: db.Team, + where: { + category, + gender, + }, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["gameTime", "recruitCount"], + }); +}; // export const findGuestingByLevel = async (date, category, skillLevel) => { // return await db.Guest.findAll({ diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index 56d5fe5..79bb208 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -3,7 +3,7 @@ import asyncHandler from "express-async-handler"; import { GuestingPreview, // GuestingPreviewByLevel, - // GuestingPreviewByGender, + GuestingPreviewByGender, GuestingPreviewByRegion, } from "../controllers/guest.controller"; @@ -15,7 +15,7 @@ guestsRouter.get("/", asyncHandler(GuestingPreview)); // guestsRouter.get("/level", asyncHandler(GuestingPreviewByLevel)); -// guestsRouter.get("/gender", asyncHandler(GuestingPreviewByGender)); +guestsRouter.get("/gender", asyncHandler(GuestingPreviewByGender)); guestsRouter.get("/region", asyncHandler(GuestingPreviewByRegion)); diff --git a/src/services/guest.service.ts b/src/services/guest.service.ts index 3497fbd..19408a8 100644 --- a/src/services/guest.service.ts +++ b/src/services/guest.service.ts @@ -2,7 +2,7 @@ // import { status } from "../config/response.status"; import { findGuesting, - // findGuestingByGender, + findGuestingByGender, // findGuestingByLevel, findGuestingByRegion, } from "../daos/guest.dao"; @@ -17,13 +17,13 @@ export const readGuesting = async (query) => { return readGuestingResponseDTO(guestings); }; -// export const readGuestingByGender = async (query) => { -// const guestings = await findGuestingByGender(query.date, query.category, query.gender); -// for (const guesting of guestings) { -// guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; -// } -// return readGuestingResponseDTO(guestings); -// }; +export const readGuestingByGender = async (query) => { + const guestings = await findGuestingByGender(query.date, query.category, query.gender); + for (const guesting of guestings) { + guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; + } + return readGuestingResponseDTO(guestings); +}; // export const readGuestingByLevel = async (query) => { // const guestings = await findGuestingByLevel(query.date, query.category, query.skillLevel); From 5d98604c96dbc56565b7e57c6eeddb5bd1586ced Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sat, 27 Jan 2024 22:29:33 +0900 Subject: [PATCH 023/147] =?UTF-8?q?FEAT)=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=EA=B8=80=20=EB=A0=88=EB=B2=A8=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20API=20=EA=B5=AC=ED=98=84=20#36?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/guest.controller.ts | 8 +++---- src/daos/guest.dao.ts | 34 ++++++++++++++--------------- src/routes/guests.route.ts | 4 ++-- src/services/guest.service.ts | 21 +++++++----------- 4 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/controllers/guest.controller.ts b/src/controllers/guest.controller.ts index a2043c8..f17890c 100644 --- a/src/controllers/guest.controller.ts +++ b/src/controllers/guest.controller.ts @@ -2,7 +2,7 @@ import { response } from "../config/response"; import { status } from "../config/response.status"; import { readGuesting, - // readGuestingByLevel, + readGuestingByLevel, readGuestingByGender, readGuestingByRegion, } from "../services/guest.service"; @@ -16,9 +16,9 @@ export const GuestingPreview = async (req, res, next) => { return res.send(response(status.SUCCESS, await readGuesting(req.query))); }; -// export const GuestingPreviewByLevel = async (req, res, next) => { -// return res.send(response(status.SUCCESS, await readGuestingByLevel(req.query))); -// }; +export const GuestingPreviewByLevel = async (req, res, next) => { + return res.send(response(status.SUCCESS, await readGuestingByLevel(req.query))); +}; export const GuestingPreviewByGender = async (req, res, next) => { return res.send(response(status.SUCCESS, await readGuestingByGender(req.query))); diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index a06ba9a..cf05fd7 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -36,23 +36,23 @@ export const findGuestingByGender = async (date, category, gender) => { }); }; -// export const findGuestingByLevel = async (date, category, skillLevel) => { -// return await db.Guest.findAll({ -// raw: true, -// where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), -// include: [ -// { -// model: db.Team, -// where: { -// category, -// skillLevel, -// }, -// attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], -// }, -// ], -// attributes: ["gameTime", "recruitCount"], -// }); -// }; +export const findGuestingByLevel = async (date, category, skillLevel) => { + return await db.Guest.findAll({ + raw: true, + where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + include: [ + { + model: db.Team, + where: { + category, + skillLevel, + }, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["gameTime", "recruitCount"], + }); +}; export const findGuestingByRegion = async (date, category, region) => { return await db.Guest.findAll({ diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index 79bb208..68e5c7f 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -2,7 +2,7 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { GuestingPreview, - // GuestingPreviewByLevel, + GuestingPreviewByLevel, GuestingPreviewByGender, GuestingPreviewByRegion, } from "../controllers/guest.controller"; @@ -13,7 +13,7 @@ export const guestsRouter = express.Router({ mergeParams: true }); guestsRouter.get("/", asyncHandler(GuestingPreview)); -// guestsRouter.get("/level", asyncHandler(GuestingPreviewByLevel)); +guestsRouter.get("/level", asyncHandler(GuestingPreviewByLevel)); guestsRouter.get("/gender", asyncHandler(GuestingPreviewByGender)); diff --git a/src/services/guest.service.ts b/src/services/guest.service.ts index 19408a8..0323b03 100644 --- a/src/services/guest.service.ts +++ b/src/services/guest.service.ts @@ -1,11 +1,6 @@ // import { BaseError } from "../config/error"; // import { status } from "../config/response.status"; -import { - findGuesting, - findGuestingByGender, - // findGuestingByLevel, - findGuestingByRegion, -} from "../daos/guest.dao"; +import { findGuesting, findGuestingByGender, findGuestingByLevel, findGuestingByRegion } from "../daos/guest.dao"; import { getMemberCountByTeamId } from "../daos/member.dao"; import { readGuestingResponseDTO } from "../dtos/guests.dto"; @@ -25,13 +20,13 @@ export const readGuestingByGender = async (query) => { return readGuestingResponseDTO(guestings); }; -// export const readGuestingByLevel = async (query) => { -// const guestings = await findGuestingByLevel(query.date, query.category, query.skillLevel); -// for (const guesting of guestings) { -// guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; -// } -// return readGuestingResponseDTO(guestings); -// }; +export const readGuestingByLevel = async (query) => { + const guestings = await findGuestingByLevel(query.date, query.category, query.skillLevel); + for (const guesting of guestings) { + guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; + } + return readGuestingResponseDTO(guestings); +}; export const readGuestingByRegion = async (query) => { const guestings = await findGuestingByRegion(query.date, query.category, query.region); From 6ff87d347edb053e328be00cae4c57ae4fa23921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sun, 28 Jan 2024 17:20:05 +0900 Subject: [PATCH 024/147] =?UTF-8?q?Refactor:=20=EC=B9=B4=EC=B9=B4=EC=98=A4?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20API=20=EA=B5=AC=ED=98=84=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 193 +---------------------------- package.json | 8 +- src/app.ts | 3 - src/config/passport.ts | 56 --------- src/controllers/auth.controller.ts | 14 +-- src/daos/user.dao.ts | 4 +- src/middlewares/auth.middleware.ts | 6 +- src/models/user.model.ts | 7 -- src/routes/auth.route.ts | 12 +- src/services/auth.service.ts | 42 ++++++- src/services/users.service.ts | 18 +-- src/types/decoded.interface.ts | 6 + src/types/payload.interface.ts | 4 + src/types/provider.enum.ts | 5 + src/types/user-info.interface.ts | 7 ++ src/types/verified.interface.ts | 6 + src/utils/jwt.util.ts | 14 +-- 17 files changed, 93 insertions(+), 312 deletions(-) delete mode 100644 src/config/passport.ts create mode 100644 src/types/decoded.interface.ts create mode 100644 src/types/payload.interface.ts create mode 100644 src/types/provider.enum.ts create mode 100644 src/types/user-info.interface.ts create mode 100644 src/types/verified.interface.ts diff --git a/package-lock.json b/package-lock.json index f40e43f..3f8cad7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,10 +16,7 @@ "http-status-codes": "^2.3.0", "jsonwebtoken": "^9.0.2", "mysql2": "^3.7.0", - "passport": "^0.7.0", - "passport-google-oauth20": "^2.0.0", - "passport-kakao": "^1.0.1", - "passport-naver": "^1.0.6", + "redaxios": "^0.5.1", "sequelize": "^6.35.2", "sequelize-cli": "^6.6.2", "uuid": "^9.0.1", @@ -30,9 +27,6 @@ "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.5", "@types/node": "^20.11.0", - "@types/passport": "^1.0.16", - "@types/passport-kakao": "^1.0.3", - "@types/passport-naver": "^1.0.4", "@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/parser": "^6.18.1", "eslint": "^8.56.0", @@ -457,35 +451,6 @@ "undici-types": "~5.26.4" } }, - "node_modules/@types/passport": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.16.tgz", - "integrity": "sha512-FD0qD5hbPWQzaM0wHUnJ/T0BBCJBxCeemtnCwc/ThhTg3x9jfrAcRUmj5Dopza+MfFS9acTe3wk7rcVnRIp/0A==", - "dev": true, - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/passport-kakao": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/passport-kakao/-/passport-kakao-1.0.3.tgz", - "integrity": "sha512-McK5kpeiOptvjIPkiA/QS3k82//z5JM7Y3yBLMDvEA0QMMhFMcWUHhyebL1Qy+0i0n8jS+Oe4U6xAvkU22SXXA==", - "dev": true, - "dependencies": { - "@types/express": "*", - "@types/passport": "*" - } - }, - "node_modules/@types/passport-naver": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@types/passport-naver/-/passport-naver-1.0.4.tgz", - "integrity": "sha512-QcqccZxXUjSIkF/AdGsgWXS2jOfDuhs4ClmWht3PnFoujxoMkET3eR9KbXL8gdyOAELg1yKu8LhMnMwAbza1cw==", - "dev": true, - "dependencies": { - "@types/express": "*", - "@types/passport": "*" - } - }, "node_modules/@types/qs": { "version": "6.9.11", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", @@ -973,14 +938,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "node_modules/base64url": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.1.tgz", - "integrity": "sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -3001,11 +2958,6 @@ "node": ">=0.10.0" } }, - "node_modules/oauth": { - "version": "0.9.15", - "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", - "integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==" - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3094,121 +3046,6 @@ "node": ">= 0.8" } }, - "node_modules/passport": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", - "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-google-oauth20": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", - "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", - "dependencies": { - "passport-oauth2": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-kakao": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz", - "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==", - "dependencies": { - "passport-oauth2": "~1.1.2", - "pkginfo": "~0.3.0" - } - }, - "node_modules/passport-kakao/node_modules/passport-oauth2": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz", - "integrity": "sha512-wpsGtJDHHQUjyc9WcV9FFB0bphFExpmKtzkQrxpH1vnSr6RcWa3ZEGHx/zGKAh2PN7Po9TKYB1fJeOiIBspNPA==", - "dependencies": { - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-naver": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/passport-naver/-/passport-naver-1.0.6.tgz", - "integrity": "sha512-5XEbJesiPVshwE0cTDvbbJrOiYSJ9VFRJTctqiZL1Xip6qOi9n7d49UesOqavLT+Ry8C7aw3zdzbmWosqHcQ/Q==", - "dependencies": { - "passport-oauth": "^1.0.0", - "underscore": "^1.8.3" - } - }, - "node_modules/passport-oauth": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-oauth/-/passport-oauth-1.0.0.tgz", - "integrity": "sha512-4IZNVsZbN1dkBzmEbBqUxDG8oFOIK81jqdksE3HEb/vI3ib3FMjbiZZ6MTtooyYZzmKu0BfovjvT1pdGgIq+4Q==", - "dependencies": { - "passport-oauth1": "1.x.x", - "passport-oauth2": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-oauth1": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.3.0.tgz", - "integrity": "sha512-8T/nX4gwKTw0PjxP1xfD0QhrydQNakzeOpZ6M5Uqdgz9/a/Ag62RmJxnZQ4LkbdXGrRehQHIAHNAu11rCP46Sw==", - "dependencies": { - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "utils-merge": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-oauth2": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz", - "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==", - "dependencies": { - "base64url": "3.x.x", - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -3268,11 +3105,6 @@ "node": ">=8" } }, - "node_modules/pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" - }, "node_modules/pg-connection-string": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz", @@ -3290,14 +3122,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pkginfo": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.3.1.tgz", - "integrity": "sha512-yO5feByMzAp96LtP58wvPKSbaKAi/1C4kV9XpTctr6EepnP6F33RBNOiVrdz9BrPA98U2BMFsTNHo44TWcbQ2A==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -3422,6 +3246,11 @@ "node": ">=8.10.0" } }, + "node_modules/redaxios": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/redaxios/-/redaxios-0.5.1.tgz", + "integrity": "sha512-FSD2AmfdbkYwl7KDExYQlVvIrFz6Yd83pGfaGjBzM9F6rpq8g652Q4Yq5QD4c+nf4g2AgeElv1y+8ajUPiOYMg==" + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -4160,11 +3989,6 @@ "node": ">=14.17" } }, - "node_modules/uid2": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.4.tgz", - "integrity": "sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==" - }, "node_modules/umzug": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", @@ -4182,11 +4006,6 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, - "node_modules/underscore": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", - "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" - }, "node_modules/undici-types": { "version": "5.26.5", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", diff --git a/package.json b/package.json index b9e6dfa..fa7f733 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,7 @@ "http-status-codes": "^2.3.0", "jsonwebtoken": "^9.0.2", "mysql2": "^3.7.0", - "passport": "^0.7.0", - "passport-google-oauth20": "^2.0.0", - "passport-kakao": "^1.0.1", - "passport-naver": "^1.0.6", + "redaxios": "^0.5.1", "sequelize": "^6.35.2", "sequelize-cli": "^6.6.2", "uuid": "^9.0.1", @@ -31,9 +28,6 @@ "@types/express": "^4.17.21", "@types/jsonwebtoken": "^9.0.5", "@types/node": "^20.11.0", - "@types/passport": "^1.0.16", - "@types/passport-kakao": "^1.0.3", - "@types/passport-naver": "^1.0.4", "@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/parser": "^6.18.1", "eslint": "^8.56.0", diff --git a/src/app.ts b/src/app.ts index ef4fbe1..f113efe 100644 --- a/src/app.ts +++ b/src/app.ts @@ -3,9 +3,7 @@ dotenv.config(); import express, { Request, Response, NextFunction } from "express"; import cors from "cors"; -import passport from "passport"; -import "./config/passport"; import db from "./models/index"; import { response } from "./config/response"; import { BaseError } from "./config/error"; @@ -33,7 +31,6 @@ app.use(cors()); app.use(express.static("public")); app.use(express.json()); app.use(express.urlencoded({ extended: false })); -app.use(passport.initialize()); app.use("/auth", authRouter); app.use("/teams", teamsRouter); diff --git a/src/config/passport.ts b/src/config/passport.ts deleted file mode 100644 index f418026..0000000 --- a/src/config/passport.ts +++ /dev/null @@ -1,56 +0,0 @@ -import passport from "passport"; -import { Strategy as GoogleStrategy } from "passport-google-oauth20"; -import { Strategy as KakaoStrategy } from "passport-kakao"; -import { Strategy as NaverStrategy } from "passport-naver"; -import { createOrReadUser } from "../services/users.service"; - -passport.use( - new GoogleStrategy( - { - clientID: process.env.GOOGLE_CLIENT_ID!, - clientSecret: process.env.GOOGLE_CLIENT_SECRET, - callbackURL: process.env.GOOGLE_CALLBACK_URL!, - }, - (accessToken, refreshToken, profile, done) => { - createOrReadUser(profile.provider, profile.id, profile._json.email) - .then((user) => done(null, user)) - .catch((err) => { - done(err); - }); - }, - ), -); - -passport.use( - new KakaoStrategy( - { - clientID: process.env.KAKAO_CLIENT_ID!, - clientSecret: process.env.KAKAO_CLIENT_SECRET, - callbackURL: process.env.KAKAO_CALLBACK_URL!, - }, - (accessToken, refreshToken, profile, done) => { - createOrReadUser(profile.provider, profile.id, profile._json.kakao_account.email) - .then((user) => done(null, user)) - .catch((err) => { - done(err); - }); - }, - ), -); - -passport.use( - new NaverStrategy( - { - clientID: process.env.NAVER_CLIENT_ID!, - clientSecret: process.env.NAVER_CLIENT_SECRET, - callbackURL: process.env.NAVER_CALLBACK_URL!, - }, - (accessToken, refreshToken, profile, done) => { - createOrReadUser(profile.provider, profile.id, profile._json.email) - .then((user) => done(null, user)) - .catch((err) => { - done(err); - }); - }, - ), -); diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts index 536e68d..26d8a77 100644 --- a/src/controllers/auth.controller.ts +++ b/src/controllers/auth.controller.ts @@ -1,18 +1,10 @@ import { Request } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { login, generateNewAccessToken } from "../services/auth.service"; +import { kakaoLogin, generateNewAccessToken } from "../services/auth.service"; -export const googleCallback = async (req, res, next) => { - res.send(response(status.SUCCESS, await login(req.user))); -}; - -export const kakaoCallback = async (req, res, next) => { - res.send(response(status.SUCCESS, await login(req.user))); -}; - -export const naverCallback = async (req, res, next) => { - res.send(response(status.SUCCESS, await login(req.user))); +export const authKakao = async (req, res, next) => { + res.send(response(status.SUCCESS, await kakaoLogin(req.body))); }; export const refreshAccessToken = async (req: Request, res, next) => { diff --git a/src/daos/user.dao.ts b/src/daos/user.dao.ts index aa0df7a..bacdb52 100644 --- a/src/daos/user.dao.ts +++ b/src/daos/user.dao.ts @@ -10,13 +10,11 @@ export const getUserByProviderId = async (provider, providerId) => { }); }; -export const insertUser = async (provider, providerId, email) => { - const nickname = email.split("@")[0]; +export const insertUser = async (provider, providerId, nickname) => { return await db.User.create({ nickname, provider, providerId, - email, }); }; diff --git a/src/middlewares/auth.middleware.ts b/src/middlewares/auth.middleware.ts index 2520ca9..21b067a 100644 --- a/src/middlewares/auth.middleware.ts +++ b/src/middlewares/auth.middleware.ts @@ -1,9 +1,9 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; -import { NextFunction, Request, Response } from "express"; +import { NextFunction, Response } from "express"; import { extractAccessToken, extractAccessTokenFromHeader, verifyAccessToken } from "../utils/jwt.util"; -export const verifyUser = (req: Request, res: Response, next: NextFunction) => { +export const verifyUser = (req, res: Response, next: NextFunction) => { const accessToken = extractAccessToken(req); const verified = verifyAccessToken(accessToken); if (verified.isExpired) { @@ -17,7 +17,7 @@ export const verifyUser = (req: Request, res: Response, next: NextFunction) => { } }; -export const verifyUserIfExists = (req: Request, res: Response, next: NextFunction) => { +export const verifyUserIfExists = (req, res: Response, next: NextFunction) => { const accessToken = extractAccessTokenFromHeader(req); if (accessToken) { const verified = verifyAccessToken(accessToken); diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 01ad7d4..1d61ac1 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -16,13 +16,6 @@ class User extends Model, InferCreationAttributes> { type: new DataTypes.STRING(50), allowNull: false, }, - email: { - type: new DataTypes.STRING(50), - allowNull: false, - validate: { - isEmail: true, - }, - }, refreshToken: { type: new DataTypes.STRING(150), allowNull: true, diff --git a/src/routes/auth.route.ts b/src/routes/auth.route.ts index 44d4ca9..668e425 100644 --- a/src/routes/auth.route.ts +++ b/src/routes/auth.route.ts @@ -1,17 +1,9 @@ import express from "express"; -import passport from "passport"; import asyncHandler from "express-async-handler"; -import { googleCallback, kakaoCallback, naverCallback, refreshAccessToken } from "../controllers/auth.controller"; +import { authKakao, refreshAccessToken } from "../controllers/auth.controller"; export const authRouter = express.Router(); -authRouter.get("/google", passport.authenticate("google", { scope: ["profile", "email"] })); -authRouter.get("/google/callback", passport.authenticate("google", { session: false }), asyncHandler(googleCallback)); - -authRouter.get("/kakao", passport.authenticate("kakao")); -authRouter.get("/kakao/callback", passport.authenticate("kakao", { session: false }), asyncHandler(kakaoCallback)); - -authRouter.get("/naver", passport.authenticate("naver")); -authRouter.get("/naver/callback", passport.authenticate("naver", { session: false }), asyncHandler(naverCallback)); +authRouter.post("/kakao", asyncHandler(authKakao)); authRouter.post("/refresh", asyncHandler(refreshAccessToken)); diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 59ae288..cd89241 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -1,5 +1,5 @@ import { Request } from "express"; -import { Payload, updateRefreshToken } from "./users.service"; +import { createOrReadUser, updateRefreshToken } from "./users.service"; import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { getRefreshToken } from "../daos/user.dao"; @@ -11,9 +11,49 @@ import { verifyAccessToken, isRefreshTokenValid, } from "../utils/jwt.util"; +import redaxios from "redaxios"; +import { UserInfo } from "../types/user-info.interface"; +import { Provider } from "../types/provider.enum"; +import { Payload } from "../types/payload.interface"; export const tokenType = "Bearer "; +export const kakaoLogin = async (body) => { + const accessToken = await getKakaoAccessToken(body.code); + const userInfo = await retrieveKakaoUserInfo(accessToken); + const payload = await createOrReadUser(userInfo); + return login(payload); +}; + +const getKakaoAccessToken = async (code: string) => { + const url = "https://kauth.kakao.com/oauth/token"; + const response = await redaxios.post( + url, + `grant_type=authorization_code&client_id=${process.env.KAKAO_CLIENT_ID}&redirect_uri=${process.env.KAKAO_REDIRECT_URI}&code=${code}`, + { + headers: { + "Content-type": "application/x-www-form-urlencoded;charset=utf-8", + }, + }, + ); + return response.data.access_token; +}; + +const retrieveKakaoUserInfo = async (accessToken: string): Promise => { + const url = "https://kapi.kakao.com/v2/user/me"; + const response = await redaxios.get(url, { + headers: { + "Authorization": `Bearer ${accessToken}`, + "Content-type": "application/x-www-form-urlencoded;charset=utf-8", + }, + }); + return { + provider: Provider.KAKAO, + providerId: response.data.id, + nickname: response.data.kakao_account.profile.nickname, + }; +}; + export const login = async (payload: Payload) => { const accessToken = generateAccessToken(payload); const refreshToken = generateRefreshToken(); diff --git a/src/services/users.service.ts b/src/services/users.service.ts index 54d162a..760c5cf 100644 --- a/src/services/users.service.ts +++ b/src/services/users.service.ts @@ -1,19 +1,11 @@ -import { BaseError } from "../config/error"; -import { status } from "../config/response.status"; import { getUserByProviderId, insertUser, setRefreshToken } from "../daos/user.dao"; +import { Payload } from "../types/payload.interface"; +import { UserInfo } from "../types/user-info.interface"; -export type Payload = { - id: number; - nickname: string; -}; - -export const createOrReadUser = async (provider, providerId, email): Promise => { - if (!provider || !providerId || !email) { - throw new BaseError(status.MISSING_REQUIRED_FIELDS); - } - let user = await getUserByProviderId(provider, providerId); +export const createOrReadUser = async (userInfo: UserInfo): Promise => { + let user = await getUserByProviderId(userInfo.provider, userInfo.providerId); if (!user) { - user = await insertUser(provider, providerId, email); + user = await insertUser(userInfo.provider, userInfo.providerId, userInfo.nickname); } return { id: user.id, nickname: user.nickname }; }; diff --git a/src/types/decoded.interface.ts b/src/types/decoded.interface.ts new file mode 100644 index 0000000..fb17d84 --- /dev/null +++ b/src/types/decoded.interface.ts @@ -0,0 +1,6 @@ +import { Payload } from "./payload.interface"; + +export interface Decoded extends Payload { + iat: number; + exp: number; +} diff --git a/src/types/payload.interface.ts b/src/types/payload.interface.ts new file mode 100644 index 0000000..5d5b907 --- /dev/null +++ b/src/types/payload.interface.ts @@ -0,0 +1,4 @@ +export interface Payload { + id: number; + nickname: string; +} diff --git a/src/types/provider.enum.ts b/src/types/provider.enum.ts new file mode 100644 index 0000000..207606a --- /dev/null +++ b/src/types/provider.enum.ts @@ -0,0 +1,5 @@ +export enum Provider { + GOOGLE = "google", + KAKAO = "kakao", + NAVER = "naver", +} diff --git a/src/types/user-info.interface.ts b/src/types/user-info.interface.ts new file mode 100644 index 0000000..379b5cd --- /dev/null +++ b/src/types/user-info.interface.ts @@ -0,0 +1,7 @@ +import { Provider } from "./provider.enum"; + +export interface UserInfo { + provider: Provider; + providerId: string; + nickname: string; +} diff --git a/src/types/verified.interface.ts b/src/types/verified.interface.ts new file mode 100644 index 0000000..e812229 --- /dev/null +++ b/src/types/verified.interface.ts @@ -0,0 +1,6 @@ +import { Decoded } from "./decoded.interface"; + +export interface Verified { + isExpired: boolean; + decoded: Decoded; +} diff --git a/src/utils/jwt.util.ts b/src/utils/jwt.util.ts index 6ef0dbf..b293df2 100644 --- a/src/utils/jwt.util.ts +++ b/src/utils/jwt.util.ts @@ -2,18 +2,10 @@ import jwt from "jsonwebtoken"; import { tokenType } from "../services/auth.service"; import { BaseError } from "../config/error"; import { status } from "../config/response.status"; -import { Payload } from "../services/users.service"; import { Request } from "express"; - -interface Verified { - isExpired: boolean; - decoded: Decoded; -} - -export interface Decoded extends Payload { - iat: number; - exp: number; -} +import { Payload } from "../types/payload.interface"; +import { Verified } from "../types/verified.interface"; +import { Decoded } from "../types/decoded.interface"; export const generateAccessToken = (payload: Payload): string => { return jwt.sign(payload, process.env.JWT_ACCESS_SECRET!, { expiresIn: "15m" }); From 345ddef33317fe0aa49826fadfa0180dda0f17c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sun, 28 Jan 2024 19:08:44 +0900 Subject: [PATCH 025/147] =?UTF-8?q?Feat:=20=EB=82=B4=EA=B0=80=20=EC=93=B4?= =?UTF-8?q?=20=EA=B8=80=20=EC=A1=B0=ED=9A=8C=20API=20=EC=97=94=EB=93=9C?= =?UTF-8?q?=ED=8F=AC=EC=9D=B8=ED=8A=B8=EC=9D=98=20URI=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=20#29?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/posts.route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index 6ede0b2..0460a65 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -12,7 +12,7 @@ export const postsRouter = express.Router(); postsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); -postsRouter.get("/my", verifyUser, asyncHandler(fetchMyPosts)); +postsRouter.get("/authors/me", verifyUser, asyncHandler(fetchMyPosts)); postsRouter.get("/bookmarks", verifyUser, asyncHandler(fetchBookmarkedPosts)); From 6b0fec5f9beb984b6fe2c791ceb92025419b6e23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sun, 28 Jan 2024 23:43:17 +0900 Subject: [PATCH 026/147] =?UTF-8?q?Refactor:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EA=B8=80=20=EB=B3=B8=EB=AC=B8=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20API=20=EA=B5=AC=ED=98=84=20#21?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/community-posts.controller.ts | 8 +-- src/controllers/posts.controller.ts | 5 ++ ...ommunity-comment.dao.ts => comment.dao.ts} | 12 ++-- src/daos/community-post.dao.ts | 18 +++--- .../{community-image.dao.ts => image.dao.ts} | 4 +- src/daos/post.dao.ts | 40 ++++++++----- src/dtos/community-posts.dto.ts | 37 ------------ src/dtos/posts.dto.ts | 26 +++++++++ .../{community-comment.ts => comment.ts} | 14 ++--- src/models/post.model.ts | 3 +- src/models/user.model.ts | 3 +- src/routes/community-posts.route.ts | 4 +- src/routes/posts.route.ts | 3 + src/services/community-posts.service.ts | 58 ++++++++++--------- src/services/posts.service.ts | 36 ++++++++++-- .../post-type.enum.ts} | 0 16 files changed, 154 insertions(+), 117 deletions(-) rename src/daos/{community-comment.dao.ts => comment.dao.ts} (67%) rename src/daos/{community-image.dao.ts => image.dao.ts} (52%) delete mode 100644 src/dtos/community-posts.dto.ts rename src/models/{community-comment.ts => comment.ts} (66%) rename src/{constants/post-type.constant.ts => types/post-type.enum.ts} (100%) diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts index 7724b91..4a5e55e 100644 --- a/src/controllers/community-posts.controller.ts +++ b/src/controllers/community-posts.controller.ts @@ -6,7 +6,7 @@ import { createCommunityPost, // createOrDeleteBookmark, readCommunityComments, - readCommunityPost, + // readCommunityPost, // readCommunityPosts, } from "../services/community-posts.service"; @@ -18,9 +18,9 @@ import { // res.send(response(status.SUCCESS, await createOrDeleteBookmark(req.user.id, req.params))); // }; -export const fetchCommunityPost = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readCommunityPost(req.user.id, req.params))); -}; +// export const fetchCommunityPost = async (req, res: Response, next) => { +// res.send(response(status.SUCCESS, await readCommunityPost(req.user.id, req.params))); +// }; export const fetchCommunityComments = async (req: Request, res: Response, next) => { res.send(response(status.SUCCESS, await readCommunityComments(req.params, req.query))); diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index 2295624..36ce877 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -4,6 +4,7 @@ import { status } from "../config/response.status"; import { createOrDeleteBookmark, readBookmarkedPosts, + readPost, readCommunityPosts, readPostsByAuthor, } from "../services/posts.service"; @@ -23,3 +24,7 @@ export const fetchBookmarkedPosts = async (req, res: Response, next) => { export const addOrRemoveBookmark = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await createOrDeleteBookmark(req.user.id, req.params))); }; + +export const fetchPost = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readPost(req.user.id, req.params))); +}; diff --git a/src/daos/community-comment.dao.ts b/src/daos/comment.dao.ts similarity index 67% rename from src/daos/community-comment.dao.ts rename to src/daos/comment.dao.ts index 9ddd952..05362c1 100644 --- a/src/daos/community-comment.dao.ts +++ b/src/daos/comment.dao.ts @@ -1,13 +1,13 @@ import db from "../models"; -import { CreateCommunityCommentSchema } from "../schemas/community-comment.schema"; +// import { CreateCommentSchema } from "../schemas/community-comment.schema"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; const defaultLimit = 20; -export const findCommunityComment = async (postId: number, cursorId: number | undefined) => { +export const findComment = async (postId: number, cursorId: number | undefined) => { const commentsBeforeCursorForPost = { postId, ...generateCursorCondition(cursorId) }; - const comments = await db.CommunityComment.findAll({ + const comments = await db.Comment.findAll({ raw: true, where: commentsBeforeCursorForPost, order: [["createdAt", "DESC"]], @@ -26,15 +26,15 @@ export const findCommunityComment = async (postId: number, cursorId: number | un }; export const getCommentCount = async (postId: number) => { - return await db.CommunityComment.count({ + return await db.Comment.count({ where: { postId, }, }); }; -export const insertCommunityComment = async (userId: number, postId: number, body: CreateCommunityCommentSchema) => { - await db.CommunityComment.create({ +export const insertComment = async (userId: number, postId: number, body /*: CreateCommentSchema*/) => { + await db.Comment.create({ postId, authorId: userId, content: body.content, diff --git a/src/daos/community-post.dao.ts b/src/daos/community-post.dao.ts index 2e9cc22..0b74695 100644 --- a/src/daos/community-post.dao.ts +++ b/src/daos/community-post.dao.ts @@ -30,15 +30,15 @@ import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; // return { posts, hasNext: calculateHasNext(posts, defaultLimit) }; // }; -export const getCommunityPost = async (postId: number) => { - return await db.CommunityPost.findOne({ - raw: true, - where: { - id: postId, - }, - attributes: ["title", "content", "link"], - }); -}; +// export const getCommunityPost = async (postId: number) => { +// return await db.CommunityPost.findOne({ +// raw: true, +// where: { +// id: postId, +// }, +// attributes: ["title", "content", "link"], +// }); +// }; export const insertCommunityPost = async (userId: number, data: CreateCommunityPostSchema) => { await db.CommunityPost.create({ diff --git a/src/daos/community-image.dao.ts b/src/daos/image.dao.ts similarity index 52% rename from src/daos/community-image.dao.ts rename to src/daos/image.dao.ts index c2c8e0c..03abdff 100644 --- a/src/daos/community-image.dao.ts +++ b/src/daos/image.dao.ts @@ -1,7 +1,7 @@ import db from "../models"; -export const findCommunityImage = async (postId: number) => { - return await db.CommunityImage.findAll({ +export const findImage = async (postId: number) => { + return await db.Image.findAll({ where: { postId, }, diff --git a/src/daos/post.dao.ts b/src/daos/post.dao.ts index 5250275..5c62f4b 100644 --- a/src/daos/post.dao.ts +++ b/src/daos/post.dao.ts @@ -1,5 +1,5 @@ -import { PostType } from "../constants/post-type.constant"; import db from "../models"; +import { PostType } from "../types/post-type.enum"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; const defaultLimit = 20; @@ -14,6 +14,30 @@ export const findPostByAuthorId = async (userId: number, cursorId?: number) => { return findPostByFilter(userId, postsBeforeCursorForAuthor); }; +export const findBookmarkedPost = async (userId: number, cursorId?: number) => { + const postsBeforeCursor = generateCursorCondition(cursorId); + const includeBookmarkedPosts = [ + { + model: db.Bookmark, + where: { + userId, + }, + attributes: ["id"], + }, + ]; + return findPost(postsBeforeCursor, includeBookmarkedPosts); +}; + +export const getPost = async (postId: number) => { + return await db.Post.findOne({ + raw: true, + where: { + id: postId, + }, + attributes: ["title", "content", "link"], + }); +}; + const findPostByFilter = async (userId: number | undefined, postFilter: object) => { const includeAllPosts: any[] = []; if (userId) { @@ -29,20 +53,6 @@ const findPostByFilter = async (userId: number | undefined, postFilter: object) return findPost(postFilter, includeAllPosts); }; -export const findBookmarkedPost = async (userId: number, cursorId?: number) => { - const postsBeforeCursor = generateCursorCondition(cursorId); - const includeBookmarkedPosts = [ - { - model: db.Bookmark, - where: { - userId, - }, - attributes: ["id"], - }, - ]; - return findPost(postsBeforeCursor, includeBookmarkedPosts); -}; - const findPost = async (postFilter: object, bookmarkInclude: Array) => { const posts = await db.Post.findAll({ raw: true, diff --git a/src/dtos/community-posts.dto.ts b/src/dtos/community-posts.dto.ts deleted file mode 100644 index f0d623b..0000000 --- a/src/dtos/community-posts.dto.ts +++ /dev/null @@ -1,37 +0,0 @@ -export const readCommunityPostsResponseDTO = (response) => { - return { - posts: response.posts.map((post) => ({ - id: post.id, - isBookmarked: Boolean(post["CommunityBookmarks.id"]), - title: post.title, - createdAt: post.createdAt, - })), - hasNext: response.hasNext, - }; -}; - -export const readCommunityPostResponseDTO = (post, imageUrls, commentCount, comments, isBookmarked) => { - return { - post: { - title: post.title, - contnet: post.content, - link: post.link, - imageUrls: imageUrls, - }, - isBookmarked, - commentCount, - ...readCommunityCommentsResonseDTO(comments), - }; -}; - -export const readCommunityCommentsResonseDTO = (comments) => { - return { - comments: comments.ascendingComments.map((comment) => ({ - id: comment.id, - nickname: comment["User.nickname"], - content: comment.content, - createdAt: comment.createdAt, - })), - commentHasNext: comments.hasNext, - }; -}; diff --git a/src/dtos/posts.dto.ts b/src/dtos/posts.dto.ts index 356bb82..409f78c 100644 --- a/src/dtos/posts.dto.ts +++ b/src/dtos/posts.dto.ts @@ -9,3 +9,29 @@ export const readPostsResponseDTO = (result) => { hasNext: result.hasNext, }; }; + +export const readPostResponseDTO = (post, imageUrls, commentCount, comments, isBookmarked) => { + return { + post: { + title: post.title, + contnet: post.content, + link: post.link, + imageUrls: imageUrls, + }, + isBookmarked, + commentCount, + ...readCommentsResonseDTO(comments), + }; +}; + +export const readCommentsResonseDTO = (comments) => { + return { + comments: comments.ascendingComments.map((comment) => ({ + id: comment.id, + nickname: comment["User.nickname"], + content: comment.content, + createdAt: comment.createdAt, + })), + commentHasNext: comments.hasNext, + }; +}; diff --git a/src/models/community-comment.ts b/src/models/comment.ts similarity index 66% rename from src/models/community-comment.ts rename to src/models/comment.ts index ab7676a..dadba02 100644 --- a/src/models/community-comment.ts +++ b/src/models/comment.ts @@ -1,8 +1,8 @@ import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; -class CommunityComment extends Model, InferCreationAttributes> { +class Comment extends Model, InferCreationAttributes> { static initiate(sequelize: Sequelize) { - CommunityComment.init( + Comment.init( { postId: { type: new DataTypes.INTEGER(), @@ -21,8 +21,8 @@ class CommunityComment extends Model, InferCre sequelize, timestamps: true, underscored: true, - modelName: "CommunityComment", - tableName: "community_comment", + modelName: "Comment", + tableName: "comment", paranoid: false, charset: "utf8", collate: "utf8_general_ci", @@ -30,9 +30,9 @@ class CommunityComment extends Model, InferCre ); } static associate(db) { - db.CommunityComment.belongsTo(db.Post, { foreignKey: "post_id" }); - db.CommunityComment.belongsTo(db.User, { foreignKey: "author_id" }); + db.Comment.belongsTo(db.Post, { foreignKey: "post_id" }); + db.Comment.belongsTo(db.User, { foreignKey: "author_id" }); } } -module.exports = CommunityComment; +module.exports = Comment; diff --git a/src/models/post.model.ts b/src/models/post.model.ts index 4be5499..97ccff0 100644 --- a/src/models/post.model.ts +++ b/src/models/post.model.ts @@ -41,8 +41,7 @@ class Post extends Model, InferCreationAttributes> { db.Post.belongsTo(db.User, { foreignKey: "author_id" }); db.Post.hasMany(db.Image, { foreignKey: "post_id" }); db.Post.hasMany(db.Bookmark, { foreignKey: "post_id" }); - db.Post.hasMany(db.CommunityComment, { foreignKey: "post_id" }); - //db.Post.hasMany(db.RentalInformationComment, { foreignKey: "post_id" }); + db.Post.hasMany(db.Comment, { foreignKey: "post_id" }); } } diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 1d61ac1..7096d39 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -39,8 +39,7 @@ class User extends Model, InferCreationAttributes> { db.User.hasMany(db.GuestUser, { foreignKey: "user_id" }); db.User.hasMany(db.Post, { foreignKey: "author_id" }); db.User.hasMany(db.Bookmark, { foreignKey: "user_id" }); - db.User.hasMany(db.CommunityComment, { foreignKey: "author_id" }); - // db.User.hasMany(db.RentalInformationComment, { foreignKey: "author_id" }); + db.User.hasMany(db.Comment, { foreignKey: "author_id" }); } } diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts index 06a729c..e697599 100644 --- a/src/routes/community-posts.route.ts +++ b/src/routes/community-posts.route.ts @@ -6,7 +6,7 @@ import { addCommunityPost, // addOrRemoveBookmark, fetchCommunityComments, - fetchCommunityPost, + // fetchCommunityPost, // fetchCommunityPosts, } from "../controllers/community-posts.controller"; import { createCommunityPost } from "../schemas/community-post.schema"; @@ -30,4 +30,4 @@ communityPostsRouter.post( communityPostsRouter.get("/:postId/comments", verifyUser, asyncHandler(fetchCommunityComments)); -communityPostsRouter.get("/:postId", verifyUser, asyncHandler(fetchCommunityPost)); +// communityPostsRouter.get("/:postId", verifyUser, asyncHandler(fetchCommunityPost)); diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index 0460a65..ee096e3 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -4,6 +4,7 @@ import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; import { addOrRemoveBookmark, fetchBookmarkedPosts, + fetchPost, fetchCommunityPosts, fetchMyPosts, } from "../controllers/posts.controller"; @@ -17,3 +18,5 @@ postsRouter.get("/authors/me", verifyUser, asyncHandler(fetchMyPosts)); postsRouter.get("/bookmarks", verifyUser, asyncHandler(fetchBookmarkedPosts)); postsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); + +postsRouter.get("/:postId", verifyUser, asyncHandler(fetchPost)); diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts index a692a2a..9b67e4c 100644 --- a/src/services/community-posts.service.ts +++ b/src/services/community-posts.service.ts @@ -4,20 +4,24 @@ import { getBookmark, // insertOrDeleteBookmark } from "../daos/bookmark.dao"; -import { findCommunityComment, getCommentCount, insertCommunityComment } from "../daos/community-comment.dao"; -import { findCommunityImage } from "../daos/community-image.dao"; +import { findComment, insertComment } from "../daos/comment.dao"; +// import { findCommunityComment, getCommentCount, insertCommunityComment } from "../daos/comment.dao"; +// import { findCommunityImage } from "../daos/image.dao"; import { // findCommunityPost, - getCommunityPost, + // getCommunityPost, insertCommunityPost, } from "../daos/community-post.dao"; -import { - readCommunityCommentsResonseDTO, - readCommunityPostResponseDTO, - // readCommunityPostsResponseDTO, -} from "../dtos/community-posts.dto"; +import { getPost } from "../daos/post.dao"; +// import { getPostByType } from "../daos/post.dao"; +import {} from // readCommunityCommentsResonseDTO, +// readCommunityPostResponseDTO, +// readCommunityPostsResponseDTO, +"../dtos/community-posts.dto"; +import { readCommentsResonseDTO } from "../dtos/posts.dto"; import { CreateCommunityCommentSchema } from "../schemas/community-comment.schema"; import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; +import { PostType } from "../types/post-type.enum"; // export const readCommunityPosts = async (userId: number | undefined, query) => { // const response = await findCommunityPost(userId, query.cursorId); @@ -29,30 +33,30 @@ import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; // return; // }; -export const readCommunityPost = async (userId, params) => { - const postId = params.postId; - const post = handlePostNotFound(await getCommunityPost(postId)); - const imageUrls = await findCommunityImage(postId); - const commentCount = await getCommentCount(postId); - const comments = await findCommunityComment(postId, undefined); - const isBookmarked = await checkIsBookmarked(userId, postId); - return readCommunityPostResponseDTO(post, imageUrls, commentCount, comments, isBookmarked); -}; +// export const readCommunityPost = async (userId, params) => { +// const postId = params.postId; +// const post = handlePostNotFound(await getCommunityPost(postId)); +// const imageUrls = await findCommunityImage(postId); +// const commentCount = await getCommentCount(postId); +// const comments = await findCommunityComment(postId, undefined); +// const isBookmarked = await checkIsBookmarked(userId, postId); +// return readCommunityPostResponseDTO(post, imageUrls, commentCount, comments, isBookmarked); +// }; -const checkIsBookmarked = async (userId: number | undefined, postId: number) => { - if (!userId) { - return null; - } - return Boolean(await getBookmark(userId, postId)); -}; +// const checkIsBookmarked = async (userId: number | undefined, postId: number) => { +// if (!userId) { +// return null; +// } +// return Boolean(await getBookmark(userId, postId)); +// }; export const readCommunityComments = async (params, query) => { const cursorId = query.cursorId; if (isNaN(Number(cursorId))) { throw new BaseError(status.REQUEST_VALIDATION_ERROR); } - const comments = await findCommunityComment(params.postId, cursorId); - return readCommunityCommentsResonseDTO(comments); + const comments = await findComment(params.postId, cursorId); + return readCommentsResonseDTO(comments); }; export const createCommunityPost = async (userId: number, body: CreateCommunityPostSchema) => { @@ -62,8 +66,8 @@ export const createCommunityPost = async (userId: number, body: CreateCommunityP export const createCommunityComment = async (userId: number, params, body: CreateCommunityCommentSchema) => { const postId = params.postId; - handlePostNotFound(await getCommunityPost(postId)); - await insertCommunityComment(userId, postId, body); + handlePostNotFound(await getPost(postId)); + await insertComment(userId, postId, body); return; }; diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index 625f223..dc6873d 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -1,7 +1,11 @@ -import { PostType } from "../constants/post-type.constant"; -import { insertOrDeleteBookmark } from "../daos/bookmark.dao"; -import { findPostByType, findPostByAuthorId, findBookmarkedPost } from "../daos/post.dao"; -import { readPostsResponseDTO } from "../dtos/posts.dto"; +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { getBookmark, insertOrDeleteBookmark } from "../daos/bookmark.dao"; +import { findComment, getCommentCount } from "../daos/comment.dao"; +import { findImage } from "../daos/image.dao"; +import { findPostByType, findPostByAuthorId, findBookmarkedPost, getPost } from "../daos/post.dao"; +import { readPostResponseDTO, readPostsResponseDTO } from "../dtos/posts.dto"; +import { PostType } from "../types/post-type.enum"; export const readCommunityPosts = async (userId: number | undefined, query) => { const result = await findPostByType(userId, query.cursorId, PostType.Community); @@ -22,3 +26,27 @@ export const createOrDeleteBookmark = async (userId, params) => { await insertOrDeleteBookmark(userId, params.postId); return; }; + +export const readPost = async (userId, params) => { + const postId = params.postId; + const post = handlePostNotFound(await getPost(postId)); + const imageUrls = await findImage(postId); + const commentCount = await getCommentCount(postId); + const comments = await findComment(postId, undefined); + const isBookmarked = await checkIsBookmarked(userId, postId); + return readPostResponseDTO(post, imageUrls, commentCount, comments, isBookmarked); +}; + +const checkIsBookmarked = async (userId: number | undefined, postId: number) => { + if (!userId) { + return null; + } + return Boolean(await getBookmark(userId, postId)); +}; + +const handlePostNotFound = (post) => { + if (!post) { + throw new BaseError(status.POST_NOT_FOUND); + } + return post; +}; diff --git a/src/constants/post-type.constant.ts b/src/types/post-type.enum.ts similarity index 100% rename from src/constants/post-type.constant.ts rename to src/types/post-type.enum.ts From 0f873d910544d73a893a1ae76afed790d5fa2ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sun, 28 Jan 2024 23:56:55 +0900 Subject: [PATCH 027/147] =?UTF-8?q?Refactor:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EA=B8=80=20=EC=9E=91=EC=84=B1=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20#25?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/community-posts.controller.ts | 8 +-- src/controllers/posts.controller.ts | 5 ++ src/daos/community-post.dao.ts | 50 ------------------- src/daos/post.dao.ts | 11 ++++ src/routes/community-posts.route.ts | 6 +-- src/routes/posts.route.ts | 7 ++- src/schemas/community-post.schema.ts | 4 +- src/services/community-posts.service.ts | 24 ++++----- src/services/posts.service.ts | 8 ++- 9 files changed, 50 insertions(+), 73 deletions(-) delete mode 100644 src/daos/community-post.dao.ts diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts index 4a5e55e..0023e19 100644 --- a/src/controllers/community-posts.controller.ts +++ b/src/controllers/community-posts.controller.ts @@ -3,7 +3,7 @@ import { response } from "../config/response"; import { status } from "../config/response.status"; import { createCommunityComment, - createCommunityPost, + // createCommunityPost, // createOrDeleteBookmark, readCommunityComments, // readCommunityPost, @@ -26,9 +26,9 @@ export const fetchCommunityComments = async (req: Request, res: Response, next) res.send(response(status.SUCCESS, await readCommunityComments(req.params, req.query))); }; -export const addCommunityPost = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await createCommunityPost(req.user.id, req.body))); -}; +// export const addCommunityPost = async (req, res: Response, next) => { +// res.send(response(status.SUCCESS, await createCommunityPost(req.user.id, req.body))); +// }; export const addCommunityComment = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await createCommunityComment(req.user.id, req.params, req.body))); diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index 36ce877..be5cf52 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -7,6 +7,7 @@ import { readPost, readCommunityPosts, readPostsByAuthor, + createCommunityPost, } from "../services/posts.service"; export const fetchCommunityPosts = async (req, res: Response, next) => { @@ -28,3 +29,7 @@ export const addOrRemoveBookmark = async (req, res: Response, next) => { export const fetchPost = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readPost(req.user.id, req.params))); }; + +export const addCommunityPost = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await createCommunityPost(req.user.id, req.body))); +}; diff --git a/src/daos/community-post.dao.ts b/src/daos/community-post.dao.ts deleted file mode 100644 index 0b74695..0000000 --- a/src/daos/community-post.dao.ts +++ /dev/null @@ -1,50 +0,0 @@ -import db from "../models"; -import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; -// import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; - -// const defaultLimit = 20; - -// export const findCommunityPost = async (userId?: number, cursorId?: number) => { -// const postsBeforeCursor = generateCursorCondition(cursorId); - -// const bookmarkInclude: any[] = []; -// if (userId) { -// bookmarkInclude.push({ -// model: db.CommunityBookmark, -// where: { -// userId, -// }, -// required: false, -// attributes: ["id"], -// }); -// } - -// const posts = await db.CommunityPost.findAll({ -// raw: true, -// where: postsBeforeCursor, -// order: [["createdAt", "DESC"]], -// limit: defaultLimit, -// attributes: ["id", "title", "createdAt"], -// include: bookmarkInclude, -// }); -// return { posts, hasNext: calculateHasNext(posts, defaultLimit) }; -// }; - -// export const getCommunityPost = async (postId: number) => { -// return await db.CommunityPost.findOne({ -// raw: true, -// where: { -// id: postId, -// }, -// attributes: ["title", "content", "link"], -// }); -// }; - -export const insertCommunityPost = async (userId: number, data: CreateCommunityPostSchema) => { - await db.CommunityPost.create({ - title: data.title, - content: data.content, - link: data.link, - authorId: userId, - }); -}; diff --git a/src/daos/post.dao.ts b/src/daos/post.dao.ts index 5c62f4b..960e5df 100644 --- a/src/daos/post.dao.ts +++ b/src/daos/post.dao.ts @@ -1,4 +1,5 @@ import db from "../models"; +import { CreatePostSchema } from "../schemas/community-post.schema"; import { PostType } from "../types/post-type.enum"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; @@ -64,3 +65,13 @@ const findPost = async (postFilter: object, bookmarkInclude: Array) => { }); return { posts, hasNext: calculateHasNext(posts, defaultLimit) }; }; + +export const insertPost = async (userId: number, data: CreatePostSchema, type: PostType) => { + await db.Post.create({ + title: data.title, + content: data.content, + link: data.link, + authorId: userId, + type, + }); +}; diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts index e697599..99dd32b 100644 --- a/src/routes/community-posts.route.ts +++ b/src/routes/community-posts.route.ts @@ -3,19 +3,19 @@ import asyncHandler from "express-async-handler"; import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; import { addCommunityComment, - addCommunityPost, + // addCommunityPost, // addOrRemoveBookmark, fetchCommunityComments, // fetchCommunityPost, // fetchCommunityPosts, } from "../controllers/community-posts.controller"; -import { createCommunityPost } from "../schemas/community-post.schema"; +// import { createCommunityPost } from "../schemas/community-post.schema"; import { validateBody } from "../middlewares/validate.middleware"; import { createCommunityComment } from "../schemas/community-comment.schema"; export const communityPostsRouter = express.Router(); -communityPostsRouter.post("/", verifyUser, validateBody(createCommunityPost), asyncHandler(addCommunityPost)); +// communityPostsRouter.post("/", verifyUser, validateBody(createCommunityPost), asyncHandler(addCommunityPost)); // communityPostsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index ee096e3..b18bcee 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -7,11 +7,16 @@ import { fetchPost, fetchCommunityPosts, fetchMyPosts, + addCommunityPost, } from "../controllers/posts.controller"; +import { validateBody } from "../middlewares/validate.middleware"; +import { createPost } from "../schemas/community-post.schema"; export const postsRouter = express.Router(); -postsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); +postsRouter.post("/community", verifyUser, validateBody(createPost), asyncHandler(addCommunityPost)); + +postsRouter.get("/community", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); postsRouter.get("/authors/me", verifyUser, asyncHandler(fetchMyPosts)); diff --git a/src/schemas/community-post.schema.ts b/src/schemas/community-post.schema.ts index d7645b4..3e9ce9f 100644 --- a/src/schemas/community-post.schema.ts +++ b/src/schemas/community-post.schema.ts @@ -1,9 +1,9 @@ import { TypeOf, object, z } from "zod"; -export const createCommunityPost = object({ +export const createPost = object({ title: z.string().max(30), content: z.optional(z.string().max(1000)), link: z.optional(z.string().max(200)), }); -export type CreateCommunityPostSchema = TypeOf; +export type CreatePostSchema = TypeOf; diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts index 9b67e4c..59fae4c 100644 --- a/src/services/community-posts.service.ts +++ b/src/services/community-posts.service.ts @@ -7,20 +7,20 @@ import { import { findComment, insertComment } from "../daos/comment.dao"; // import { findCommunityComment, getCommentCount, insertCommunityComment } from "../daos/comment.dao"; // import { findCommunityImage } from "../daos/image.dao"; -import { - // findCommunityPost, - // getCommunityPost, - insertCommunityPost, -} from "../daos/community-post.dao"; +// import { +// // findCommunityPost, +// // getCommunityPost, +// insertCommunityPost, +// } from "../daos/community-post.dao"; import { getPost } from "../daos/post.dao"; // import { getPostByType } from "../daos/post.dao"; -import {} from // readCommunityCommentsResonseDTO, +// import {} from // readCommunityCommentsResonseDTO, // readCommunityPostResponseDTO, // readCommunityPostsResponseDTO, -"../dtos/community-posts.dto"; +// "../dtos/community-posts.dto"; import { readCommentsResonseDTO } from "../dtos/posts.dto"; import { CreateCommunityCommentSchema } from "../schemas/community-comment.schema"; -import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; +// import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; import { PostType } from "../types/post-type.enum"; // export const readCommunityPosts = async (userId: number | undefined, query) => { @@ -59,10 +59,10 @@ export const readCommunityComments = async (params, query) => { return readCommentsResonseDTO(comments); }; -export const createCommunityPost = async (userId: number, body: CreateCommunityPostSchema) => { - await insertCommunityPost(userId, body); - return; -}; +// export const createCommunityPost = async (userId: number, body: CreateCommunityPostSchema) => { +// await insertCommunityPost(userId, body); +// return; +// }; export const createCommunityComment = async (userId: number, params, body: CreateCommunityCommentSchema) => { const postId = params.postId; diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index dc6873d..8210b77 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -3,8 +3,9 @@ import { status } from "../config/response.status"; import { getBookmark, insertOrDeleteBookmark } from "../daos/bookmark.dao"; import { findComment, getCommentCount } from "../daos/comment.dao"; import { findImage } from "../daos/image.dao"; -import { findPostByType, findPostByAuthorId, findBookmarkedPost, getPost } from "../daos/post.dao"; +import { findPostByType, findPostByAuthorId, findBookmarkedPost, getPost, insertPost } from "../daos/post.dao"; import { readPostResponseDTO, readPostsResponseDTO } from "../dtos/posts.dto"; +import { CreatePostSchema } from "../schemas/community-post.schema"; import { PostType } from "../types/post-type.enum"; export const readCommunityPosts = async (userId: number | undefined, query) => { @@ -50,3 +51,8 @@ const handlePostNotFound = (post) => { } return post; }; + +export const createCommunityPost = async (userId: number, body: CreatePostSchema) => { + await insertPost(userId, body, PostType.Community); + return; +}; From de997598399008f8ea1bb285a0bcb70f208c4291 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Mon, 29 Jan 2024 00:04:38 +0900 Subject: [PATCH 028/147] =?UTF-8?q?Refactor:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EB=8C=93=EA=B8=80=20=EC=9E=91=EC=84=B1=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20#27?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/community-posts.controller.ts | 8 +++---- src/controllers/posts.controller.ts | 5 +++++ src/daos/comment.dao.ts | 4 ++-- src/daos/post.dao.ts | 2 +- src/routes/community-posts.route.ts | 16 +++++++------- src/routes/posts.route.ts | 6 ++++- src/schemas/comment.schema.ts | 7 ++++++ src/schemas/community-comment.schema.ts | 7 ------ ...ommunity-post.schema.ts => post.schema.ts} | 0 src/services/community-posts.service.ts | 14 ++++++------ src/services/posts.service.ts | 22 +++++++++++++------ 11 files changed, 54 insertions(+), 37 deletions(-) create mode 100644 src/schemas/comment.schema.ts delete mode 100644 src/schemas/community-comment.schema.ts rename src/schemas/{community-post.schema.ts => post.schema.ts} (100%) diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts index 0023e19..1081028 100644 --- a/src/controllers/community-posts.controller.ts +++ b/src/controllers/community-posts.controller.ts @@ -2,7 +2,7 @@ import { Request, Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; import { - createCommunityComment, + // createCommunityComment, // createCommunityPost, // createOrDeleteBookmark, readCommunityComments, @@ -30,6 +30,6 @@ export const fetchCommunityComments = async (req: Request, res: Response, next) // res.send(response(status.SUCCESS, await createCommunityPost(req.user.id, req.body))); // }; -export const addCommunityComment = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await createCommunityComment(req.user.id, req.params, req.body))); -}; +// export const addCommunityComment = async (req, res: Response, next) => { +// res.send(response(status.SUCCESS, await createCommunityComment(req.user.id, req.params, req.body))); +// }; diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index be5cf52..6a363ef 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -8,6 +8,7 @@ import { readCommunityPosts, readPostsByAuthor, createCommunityPost, + createComment, } from "../services/posts.service"; export const fetchCommunityPosts = async (req, res: Response, next) => { @@ -33,3 +34,7 @@ export const fetchPost = async (req, res: Response, next) => { export const addCommunityPost = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await createCommunityPost(req.user.id, req.body))); }; + +export const addComment = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await createComment(req.user.id, req.params, req.body))); +}; diff --git a/src/daos/comment.dao.ts b/src/daos/comment.dao.ts index 05362c1..cce5ea7 100644 --- a/src/daos/comment.dao.ts +++ b/src/daos/comment.dao.ts @@ -1,5 +1,5 @@ import db from "../models"; -// import { CreateCommentSchema } from "../schemas/community-comment.schema"; +import { CreateCommentSchema } from "../schemas/comment.schema"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; const defaultLimit = 20; @@ -33,7 +33,7 @@ export const getCommentCount = async (postId: number) => { }); }; -export const insertComment = async (userId: number, postId: number, body /*: CreateCommentSchema*/) => { +export const insertComment = async (userId: number, postId: number, body: CreateCommentSchema) => { await db.Comment.create({ postId, authorId: userId, diff --git a/src/daos/post.dao.ts b/src/daos/post.dao.ts index 960e5df..ad5e08f 100644 --- a/src/daos/post.dao.ts +++ b/src/daos/post.dao.ts @@ -1,5 +1,5 @@ import db from "../models"; -import { CreatePostSchema } from "../schemas/community-post.schema"; +import { CreatePostSchema } from "../schemas/post.schema"; import { PostType } from "../types/post-type.enum"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts index 99dd32b..ad4feb2 100644 --- a/src/routes/community-posts.route.ts +++ b/src/routes/community-posts.route.ts @@ -2,7 +2,7 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; import { - addCommunityComment, + // addCommunityComment, // addCommunityPost, // addOrRemoveBookmark, fetchCommunityComments, @@ -11,7 +11,7 @@ import { } from "../controllers/community-posts.controller"; // import { createCommunityPost } from "../schemas/community-post.schema"; import { validateBody } from "../middlewares/validate.middleware"; -import { createCommunityComment } from "../schemas/community-comment.schema"; +// import { createCommunityComment } from "../schemas/comment.schema"; export const communityPostsRouter = express.Router(); @@ -21,12 +21,12 @@ export const communityPostsRouter = express.Router(); // communityPostsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); -communityPostsRouter.post( - "/:postId/comments", - verifyUser, - validateBody(createCommunityComment), - asyncHandler(addCommunityComment), -); +// communityPostsRouter.post( +// "/:postId/comments", +// verifyUser, +// validateBody(createCommunityComment), +// asyncHandler(addCommunityComment), +// ); communityPostsRouter.get("/:postId/comments", verifyUser, asyncHandler(fetchCommunityComments)); diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index b18bcee..1af4eda 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -8,9 +8,11 @@ import { fetchCommunityPosts, fetchMyPosts, addCommunityPost, + addComment, } from "../controllers/posts.controller"; import { validateBody } from "../middlewares/validate.middleware"; -import { createPost } from "../schemas/community-post.schema"; +import { createPost } from "../schemas/post.schema"; +import { createComment } from "../schemas/comment.schema"; export const postsRouter = express.Router(); @@ -24,4 +26,6 @@ postsRouter.get("/bookmarks", verifyUser, asyncHandler(fetchBookmarkedPosts)); postsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); +postsRouter.post("/:postId/comments", verifyUser, validateBody(createComment), asyncHandler(addComment)); + postsRouter.get("/:postId", verifyUser, asyncHandler(fetchPost)); diff --git a/src/schemas/comment.schema.ts b/src/schemas/comment.schema.ts new file mode 100644 index 0000000..907cd30 --- /dev/null +++ b/src/schemas/comment.schema.ts @@ -0,0 +1,7 @@ +import { TypeOf, object, z } from "zod"; + +export const createComment = object({ + content: z.string().max(500), +}); + +export type CreateCommentSchema = TypeOf; diff --git a/src/schemas/community-comment.schema.ts b/src/schemas/community-comment.schema.ts deleted file mode 100644 index 37e3b41..0000000 --- a/src/schemas/community-comment.schema.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { TypeOf, object, z } from "zod"; - -export const createCommunityComment = object({ - content: z.string().max(500), -}); - -export type CreateCommunityCommentSchema = TypeOf; diff --git a/src/schemas/community-post.schema.ts b/src/schemas/post.schema.ts similarity index 100% rename from src/schemas/community-post.schema.ts rename to src/schemas/post.schema.ts diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts index 59fae4c..adfd537 100644 --- a/src/services/community-posts.service.ts +++ b/src/services/community-posts.service.ts @@ -19,7 +19,7 @@ import { getPost } from "../daos/post.dao"; // readCommunityPostsResponseDTO, // "../dtos/community-posts.dto"; import { readCommentsResonseDTO } from "../dtos/posts.dto"; -import { CreateCommunityCommentSchema } from "../schemas/community-comment.schema"; +// import { CreateCommunityCommentSchema } from "../schemas/comment.schema"; // import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; import { PostType } from "../types/post-type.enum"; @@ -64,12 +64,12 @@ export const readCommunityComments = async (params, query) => { // return; // }; -export const createCommunityComment = async (userId: number, params, body: CreateCommunityCommentSchema) => { - const postId = params.postId; - handlePostNotFound(await getPost(postId)); - await insertComment(userId, postId, body); - return; -}; +// export const createCommunityComment = async (userId: number, params, body: CreateCommunityCommentSchema) => { +// const postId = params.postId; +// handlePostNotFound(await getPost(postId)); +// await insertComment(userId, postId, body); +// return; +// }; const handlePostNotFound = (post) => { if (!post) { diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index 8210b77..abfe99d 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -1,11 +1,12 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { getBookmark, insertOrDeleteBookmark } from "../daos/bookmark.dao"; -import { findComment, getCommentCount } from "../daos/comment.dao"; +import { findComment, getCommentCount, insertComment } from "../daos/comment.dao"; import { findImage } from "../daos/image.dao"; import { findPostByType, findPostByAuthorId, findBookmarkedPost, getPost, insertPost } from "../daos/post.dao"; import { readPostResponseDTO, readPostsResponseDTO } from "../dtos/posts.dto"; -import { CreatePostSchema } from "../schemas/community-post.schema"; +import { CreateCommentSchema } from "../schemas/comment.schema"; +import { CreatePostSchema } from "../schemas/post.schema"; import { PostType } from "../types/post-type.enum"; export const readCommunityPosts = async (userId: number | undefined, query) => { @@ -45,14 +46,21 @@ const checkIsBookmarked = async (userId: number | undefined, postId: number) => return Boolean(await getBookmark(userId, postId)); }; +export const createCommunityPost = async (userId: number, body: CreatePostSchema) => { + await insertPost(userId, body, PostType.Community); + return; +}; + +export const createComment = async (userId: number, params, body: CreateCommentSchema) => { + const postId = params.postId; + handlePostNotFound(await getPost(postId)); + await insertComment(userId, postId, body); + return; +}; + const handlePostNotFound = (post) => { if (!post) { throw new BaseError(status.POST_NOT_FOUND); } return post; }; - -export const createCommunityPost = async (userId: number, body: CreatePostSchema) => { - await insertPost(userId, body, PostType.Community); - return; -}; From 9c90acb5f851da420e8930cfeb8f3f89c631a34b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Mon, 29 Jan 2024 00:16:11 +0900 Subject: [PATCH 029/147] =?UTF-8?q?Refactor:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EB=8C=93=EA=B8=80=20=EC=B6=94=EA=B0=80=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84=20#23?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.ts | 2 - src/controllers/community-posts.controller.ts | 35 -------- src/controllers/posts.controller.ts | 7 +- src/daos/post.dao.ts | 4 +- src/routes/community-posts.route.ts | 33 -------- src/routes/posts.route.ts | 3 + src/services/community-posts.service.ts | 79 ------------------- src/services/posts.service.ts | 11 ++- 8 files changed, 21 insertions(+), 153 deletions(-) delete mode 100644 src/controllers/community-posts.controller.ts delete mode 100644 src/routes/community-posts.route.ts delete mode 100644 src/services/community-posts.service.ts diff --git a/src/app.ts b/src/app.ts index f113efe..9d5eeb0 100644 --- a/src/app.ts +++ b/src/app.ts @@ -14,7 +14,6 @@ import { teamsRouter } from "./routes/teams.route"; import { membersRouter } from "./routes/members.route"; import { guestsRouter } from "./routes/guests.route"; import { postsRouter } from "./routes/posts.route"; -import { communityPostsRouter } from "./routes/community-posts.route"; const app = express(); @@ -37,7 +36,6 @@ app.use("/teams", teamsRouter); app.use("/members", membersRouter); app.use("/guests", guestsRouter); app.use("/posts", postsRouter); -app.use("/community-posts", communityPostsRouter); app.use((req: Request, res: Response, next: NextFunction) => { const err = new BaseError(status.NOT_FOUND); diff --git a/src/controllers/community-posts.controller.ts b/src/controllers/community-posts.controller.ts deleted file mode 100644 index 1081028..0000000 --- a/src/controllers/community-posts.controller.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { Request, Response } from "express"; -import { response } from "../config/response"; -import { status } from "../config/response.status"; -import { - // createCommunityComment, - // createCommunityPost, - // createOrDeleteBookmark, - readCommunityComments, - // readCommunityPost, - // readCommunityPosts, -} from "../services/community-posts.service"; - -// export const fetchCommunityPosts = async (req, res: Response, next) => { -// res.send(response(status.SUCCESS, await readCommunityPosts(req.user?.id, req.query))); -// }; - -// export const addOrRemoveBookmark = async (req, res: Response, next) => { -// res.send(response(status.SUCCESS, await createOrDeleteBookmark(req.user.id, req.params))); -// }; - -// export const fetchCommunityPost = async (req, res: Response, next) => { -// res.send(response(status.SUCCESS, await readCommunityPost(req.user.id, req.params))); -// }; - -export const fetchCommunityComments = async (req: Request, res: Response, next) => { - res.send(response(status.SUCCESS, await readCommunityComments(req.params, req.query))); -}; - -// export const addCommunityPost = async (req, res: Response, next) => { -// res.send(response(status.SUCCESS, await createCommunityPost(req.user.id, req.body))); -// }; - -// export const addCommunityComment = async (req, res: Response, next) => { -// res.send(response(status.SUCCESS, await createCommunityComment(req.user.id, req.params, req.body))); -// }; diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index 6a363ef..72f6e48 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -1,4 +1,4 @@ -import { Response } from "express"; +import { Request, Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; import { @@ -9,6 +9,7 @@ import { readPostsByAuthor, createCommunityPost, createComment, + readComments, } from "../services/posts.service"; export const fetchCommunityPosts = async (req, res: Response, next) => { @@ -38,3 +39,7 @@ export const addCommunityPost = async (req, res: Response, next) => { export const addComment = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await createComment(req.user.id, req.params, req.body))); }; + +export const fetchComments = async (req: Request, res: Response, next) => { + res.send(response(status.SUCCESS, await readComments(req.params, req.query))); +}; diff --git a/src/daos/post.dao.ts b/src/daos/post.dao.ts index ad5e08f..9ecfee6 100644 --- a/src/daos/post.dao.ts +++ b/src/daos/post.dao.ts @@ -6,8 +6,8 @@ import { calculateHasNext, generateCursorCondition } from "../utils/paging.util" const defaultLimit = 20; export const findPostByType = async (userId: number | undefined, cursorId: number | undefined, type: PostType) => { - const communityPostsBeforeCursor = { type, ...generateCursorCondition(cursorId) }; - return findPostByFilter(userId, communityPostsBeforeCursor); + const postsBeforeCursorByType = { type, ...generateCursorCondition(cursorId) }; + return findPostByFilter(userId, postsBeforeCursorByType); }; export const findPostByAuthorId = async (userId: number, cursorId?: number) => { diff --git a/src/routes/community-posts.route.ts b/src/routes/community-posts.route.ts deleted file mode 100644 index ad4feb2..0000000 --- a/src/routes/community-posts.route.ts +++ /dev/null @@ -1,33 +0,0 @@ -import express from "express"; -import asyncHandler from "express-async-handler"; -import { verifyUser, verifyUserIfExists } from "../middlewares/auth.middleware"; -import { - // addCommunityComment, - // addCommunityPost, - // addOrRemoveBookmark, - fetchCommunityComments, - // fetchCommunityPost, - // fetchCommunityPosts, -} from "../controllers/community-posts.controller"; -// import { createCommunityPost } from "../schemas/community-post.schema"; -import { validateBody } from "../middlewares/validate.middleware"; -// import { createCommunityComment } from "../schemas/comment.schema"; - -export const communityPostsRouter = express.Router(); - -// communityPostsRouter.post("/", verifyUser, validateBody(createCommunityPost), asyncHandler(addCommunityPost)); - -// communityPostsRouter.get("/", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); - -// communityPostsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); - -// communityPostsRouter.post( -// "/:postId/comments", -// verifyUser, -// validateBody(createCommunityComment), -// asyncHandler(addCommunityComment), -// ); - -communityPostsRouter.get("/:postId/comments", verifyUser, asyncHandler(fetchCommunityComments)); - -// communityPostsRouter.get("/:postId", verifyUser, asyncHandler(fetchCommunityPost)); diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index 1af4eda..8b0d267 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -9,6 +9,7 @@ import { fetchMyPosts, addCommunityPost, addComment, + fetchComments, } from "../controllers/posts.controller"; import { validateBody } from "../middlewares/validate.middleware"; import { createPost } from "../schemas/post.schema"; @@ -28,4 +29,6 @@ postsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookma postsRouter.post("/:postId/comments", verifyUser, validateBody(createComment), asyncHandler(addComment)); +postsRouter.get("/:postId/comments", verifyUser, asyncHandler(fetchComments)); + postsRouter.get("/:postId", verifyUser, asyncHandler(fetchPost)); diff --git a/src/services/community-posts.service.ts b/src/services/community-posts.service.ts deleted file mode 100644 index adfd537..0000000 --- a/src/services/community-posts.service.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { BaseError } from "../config/error"; -import { status } from "../config/response.status"; -import { - getBookmark, - // insertOrDeleteBookmark -} from "../daos/bookmark.dao"; -import { findComment, insertComment } from "../daos/comment.dao"; -// import { findCommunityComment, getCommentCount, insertCommunityComment } from "../daos/comment.dao"; -// import { findCommunityImage } from "../daos/image.dao"; -// import { -// // findCommunityPost, -// // getCommunityPost, -// insertCommunityPost, -// } from "../daos/community-post.dao"; -import { getPost } from "../daos/post.dao"; -// import { getPostByType } from "../daos/post.dao"; -// import {} from // readCommunityCommentsResonseDTO, -// readCommunityPostResponseDTO, -// readCommunityPostsResponseDTO, -// "../dtos/community-posts.dto"; -import { readCommentsResonseDTO } from "../dtos/posts.dto"; -// import { CreateCommunityCommentSchema } from "../schemas/comment.schema"; -// import { CreateCommunityPostSchema } from "../schemas/community-post.schema"; -import { PostType } from "../types/post-type.enum"; - -// export const readCommunityPosts = async (userId: number | undefined, query) => { -// const response = await findCommunityPost(userId, query.cursorId); -// return readCommunityPostsResponseDTO(response); -// }; - -// export const createOrDeleteBookmark = async (userId, params) => { -// await insertOrDeleteBookmark(userId, params.postId); -// return; -// }; - -// export const readCommunityPost = async (userId, params) => { -// const postId = params.postId; -// const post = handlePostNotFound(await getCommunityPost(postId)); -// const imageUrls = await findCommunityImage(postId); -// const commentCount = await getCommentCount(postId); -// const comments = await findCommunityComment(postId, undefined); -// const isBookmarked = await checkIsBookmarked(userId, postId); -// return readCommunityPostResponseDTO(post, imageUrls, commentCount, comments, isBookmarked); -// }; - -// const checkIsBookmarked = async (userId: number | undefined, postId: number) => { -// if (!userId) { -// return null; -// } -// return Boolean(await getBookmark(userId, postId)); -// }; - -export const readCommunityComments = async (params, query) => { - const cursorId = query.cursorId; - if (isNaN(Number(cursorId))) { - throw new BaseError(status.REQUEST_VALIDATION_ERROR); - } - const comments = await findComment(params.postId, cursorId); - return readCommentsResonseDTO(comments); -}; - -// export const createCommunityPost = async (userId: number, body: CreateCommunityPostSchema) => { -// await insertCommunityPost(userId, body); -// return; -// }; - -// export const createCommunityComment = async (userId: number, params, body: CreateCommunityCommentSchema) => { -// const postId = params.postId; -// handlePostNotFound(await getPost(postId)); -// await insertComment(userId, postId, body); -// return; -// }; - -const handlePostNotFound = (post) => { - if (!post) { - throw new BaseError(status.POST_NOT_FOUND); - } - return post; -}; diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index abfe99d..c3655f4 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -4,7 +4,7 @@ import { getBookmark, insertOrDeleteBookmark } from "../daos/bookmark.dao"; import { findComment, getCommentCount, insertComment } from "../daos/comment.dao"; import { findImage } from "../daos/image.dao"; import { findPostByType, findPostByAuthorId, findBookmarkedPost, getPost, insertPost } from "../daos/post.dao"; -import { readPostResponseDTO, readPostsResponseDTO } from "../dtos/posts.dto"; +import { readCommentsResonseDTO, readPostResponseDTO, readPostsResponseDTO } from "../dtos/posts.dto"; import { CreateCommentSchema } from "../schemas/comment.schema"; import { CreatePostSchema } from "../schemas/post.schema"; import { PostType } from "../types/post-type.enum"; @@ -64,3 +64,12 @@ const handlePostNotFound = (post) => { } return post; }; + +export const readComments = async (params, query) => { + const cursorId = query.cursorId; + if (isNaN(Number(cursorId))) { + throw new BaseError(status.REQUEST_VALIDATION_ERROR); + } + const comments = await findComment(params.postId, cursorId); + return readCommentsResonseDTO(comments); +}; From 87de543aeb0afa882d3b55c936f2c57adcb50265 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Mon, 29 Jan 2024 12:11:52 +0900 Subject: [PATCH 030/147] =?UTF-8?q?[FEAT]=20=EB=82=A0=EC=A7=9C=EB=B3=84=20?= =?UTF-8?q?=EC=97=B0=EC=8A=B5=EA=B2=BD=EA=B8=B0=20=EB=AA=A8=EC=A7=91?= =?UTF-8?q?=EA=B8=80=20=EC=A1=B0=ED=9A=8C,=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EC=A7=91=EA=B8=80=20=ED=95=84=ED=84=B0?= =?UTF-8?q?=EB=A7=81(=EC=A7=80=EC=97=AD,=20=EB=A0=88=EB=B2=A8,=20=EC=84=B1?= =?UTF-8?q?=EB=B3=84)=20API=20=EA=B5=AC=ED=98=84=20(#43)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: game 테이블 설계 #10 * Feat: 라우트 추가 #10 * Feat: 라우트 함수 추가 #10 * Feat: 컨트롤러 함수 추가 #10 * Fix: Team, Game 테이블 수정 #10 * Fix: 컨트롤러 함수 수정 #10 * Feat: 서비스 함수 추가 #10 * Feat: DAO 함수 추가 #10 * Fix: 날짜별 조회 함수 수정 #10 * Fix: 서버 에러 수정 #10 * Feat: 연습경기 조회 최신순 정렬 추가 #10 * Fix: Game, Team 모델 수정 #10 * Feat: 모집 상태(status) 출력 #10 * Feat: 연습경기 모집 상태(status) 구현 #10 --------- Co-authored-by: Doeun --- src/app.ts | 2 + src/constants/status.constant.ts | 14 ++++ src/controllers/games.controller.ts | 20 ++++++ src/daos/games.dao.ts | 102 ++++++++++++++++++++++++++++ src/dtos/games.dto.ts | 16 +++++ src/models/game.model.ts | 57 ++++++++++++++++ src/models/team.model.ts | 10 +++ src/routes/games.route.ts | 18 +++++ src/services/games.service.ts | 37 ++++++++++ 9 files changed, 276 insertions(+) create mode 100644 src/constants/status.constant.ts create mode 100644 src/controllers/games.controller.ts create mode 100644 src/daos/games.dao.ts create mode 100644 src/dtos/games.dto.ts create mode 100644 src/models/game.model.ts create mode 100644 src/routes/games.route.ts create mode 100644 src/services/games.service.ts diff --git a/src/app.ts b/src/app.ts index 9d5eeb0..ef48196 100644 --- a/src/app.ts +++ b/src/app.ts @@ -12,6 +12,7 @@ import { status } from "./config/response.status"; import { authRouter } from "./routes/auth.route"; import { teamsRouter } from "./routes/teams.route"; import { membersRouter } from "./routes/members.route"; +import { gamesRouter } from "./routes/games.route"; import { guestsRouter } from "./routes/guests.route"; import { postsRouter } from "./routes/posts.route"; @@ -34,6 +35,7 @@ app.use(express.urlencoded({ extended: false })); app.use("/auth", authRouter); app.use("/teams", teamsRouter); app.use("/members", membersRouter); +app.use("/games", gamesRouter); app.use("/guests", guestsRouter); app.use("/posts", postsRouter); diff --git a/src/constants/status.constant.ts b/src/constants/status.constant.ts new file mode 100644 index 0000000..2bc8f82 --- /dev/null +++ b/src/constants/status.constant.ts @@ -0,0 +1,14 @@ +type Status = { + name: string; +}; + +const Statuss: { [key: number]: Status } = { + 0: { name: "모집 중" }, + 1: { name: "모집 완료" }, +}; + +export const defaultStatus = 0; + +export const getStatusById = (id: number): string | undefined => { + return Statuss[id].name; +}; diff --git a/src/controllers/games.controller.ts b/src/controllers/games.controller.ts new file mode 100644 index 0000000..bbf3890 --- /dev/null +++ b/src/controllers/games.controller.ts @@ -0,0 +1,20 @@ +import { Response } from "express"; +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { readGamesByDate, readGamesByGender, readGamesByLevel, readGamesByRegion } from "../services/games.service"; + +export const fetchGamesByDate = async (req, res, next) => { + res.send(response(status.SUCCESS, await readGamesByDate(req.query))); +}; + +export const fetchGamesByGender = async (req, res, next) => { + res.send(response(status.SUCCESS, await readGamesByGender(req.query))); +}; + +export const fetchGamesByLevel = async (req, res, next) => { + res.send(response(status.SUCCESS, await readGamesByLevel(req.query))); +}; + +export const fetchGamesByRegion = async (req, res, next) => { + res.send(response(status.SUCCESS, await readGamesByRegion(req.query))); +}; diff --git a/src/daos/games.dao.ts b/src/daos/games.dao.ts new file mode 100644 index 0000000..eef54fd --- /dev/null +++ b/src/daos/games.dao.ts @@ -0,0 +1,102 @@ +import db from "../models"; +import { Sequelize } from "sequelize"; +import { getStatusById } from "../constants/status.constant"; + +export const findGamesByDate = async (date, category) => { + const games = await db.Game.findAll({ + raw: true, + where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + include: [ + { + model: db.Team, + as: "HostTeam", + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + where: { + category, + }, + }, + ], + attributes: ["gameTime", "status"], + order: [["created_at", "DESC"]], + }); + + return games.map((game) => ({ + ...game, + status: getStatusById(game.status), + })); +}; + +export const findGamesByGender = async (date, category, gender) => { + const games = await db.Game.findAll({ + raw: true, + where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + include: [ + { + model: db.Team, + as: "HostTeam", + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + where: { + category, + gender, + }, + }, + ], + attributes: ["gameTime", "status"], + order: [["created_at", "DESC"]], + }); + + return games.map((game) => ({ + ...game, + status: getStatusById(game.status), + })); +}; + +export const findGamesByLevel = async (date, category, skillLevel) => { + const games = await db.Game.findAll({ + raw: true, + where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + include: [ + { + model: db.Team, + as: "HostTeam", + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + where: { + category, + skillLevel, + }, + }, + ], + attributes: ["gameTime", "status"], + order: [["created_at", "DESC"]], + }); + + return games.map((game) => ({ + ...game, + status: getStatusById(game.status), + })); +}; + +export const findGamesByRegion = async (date, category, region) => { + const games = await db.Game.findAll({ + raw: true, + where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + include: [ + { + model: db.Team, + as: "HostTeam", + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + where: { + category, + region, + }, + }, + ], + attributes: ["gameTime", "status"], + order: [["created_at", "DESC"]], + }); + + return games.map((game) => ({ + ...game, + status: getStatusById(game.status), + })); +}; diff --git a/src/dtos/games.dto.ts b/src/dtos/games.dto.ts new file mode 100644 index 0000000..a23fddf --- /dev/null +++ b/src/dtos/games.dto.ts @@ -0,0 +1,16 @@ +import { getAgeGroupById } from "../constants/age-group.constant"; +import { getGenderById } from "../constants/gender.constant"; +import { getLevelById } from "../constants/level.constant"; + +export const readGameResponseDTO = (games) => { + return games.map((game) => ({ + gameTime: game.gameTime, + teamName: game["HostTeam.name"], + teamRegion: game["HostTeam.region"], + teamGender: getGenderById(game["HostTeam.gender"]), + memberCount: game.memberCount, + teamAgeGroup: getAgeGroupById(game["HostTeam.ageGroup"]), + teamSkillLevel: getLevelById(game["HostTeam.skillLevel"]), + status: game.status, + })); +}; diff --git a/src/models/game.model.ts b/src/models/game.model.ts new file mode 100644 index 0000000..161c4aa --- /dev/null +++ b/src/models/game.model.ts @@ -0,0 +1,57 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class Game extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + Game.init( + { + hostTeamId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + applyTeamId: { + type: new DataTypes.INTEGER(), + allowNull: true, + }, + opposingTeamId: { + type: new DataTypes.INTEGER(), + allowNull: true, + }, + gameTime: { + type: new DataTypes.DATE(), + allowNull: false, + }, + category: { + type: new DataTypes.STRING(15), + allowNull: false, + }, + description: { + type: new DataTypes.STRING(400), + allowNull: false, + }, + status: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "Game", + tableName: "game", + paranoid: true, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.Game.belongsTo(db.Team, { foreignKey: "host_team_id", as: "HostTeam" }); + db.Game.belongsTo(db.Team, { foreignKey: "opposing_team_id" }); + db.Game.belongsToMany(db.Team, { + through: "game_apply", + }); + } +} + +module.exports = Game; diff --git a/src/models/team.model.ts b/src/models/team.model.ts index b4b7154..e68b7e9 100644 --- a/src/models/team.model.ts +++ b/src/models/team.model.ts @@ -68,6 +68,16 @@ class Team extends Model, InferCreationAttributes> { static associate(db) { db.Team.hasMany(db.Member, { foreignKey: "team_id" }); db.Team.belongsTo(db.User, { foreignKey: "leader_id" }); + db.Team.hasMany(db.Game, { foreignKey: "host_team_id" }); + db.Team.hasMany(db.Game, { foreignKey: "opposing_team_id" }); + // db.Team.belongsToMany(db.Game, { + // through: db.GameApply, + // foreignKey: "apply_team_id", + // constraints: false, + // }); + db.Team.belongsToMany(db.Game, { + through: "game_apply", + }); db.Team.hasMany(db.Guest, { foreignKey: "team_id" }); } } diff --git a/src/routes/games.route.ts b/src/routes/games.route.ts new file mode 100644 index 0000000..8a26f7d --- /dev/null +++ b/src/routes/games.route.ts @@ -0,0 +1,18 @@ +import express from "express"; +import asyncHandler from "express-async-handler"; +import { + fetchGamesByDate, + fetchGamesByGender, + fetchGamesByLevel, + fetchGamesByRegion, +} from "../controllers/games.controller"; + +export const gamesRouter = express.Router(); + +// 날짜별 연습경기 모집글 조회 (기본) +gamesRouter.get("/", asyncHandler(fetchGamesByDate)); + +// 연습경기 모집글 필터링 - 성별/레벨/지역 +gamesRouter.get("/by-gender", asyncHandler(fetchGamesByGender)); +gamesRouter.get("/by-level", asyncHandler(fetchGamesByLevel)); +gamesRouter.get("/by-region", asyncHandler(fetchGamesByRegion)); diff --git a/src/services/games.service.ts b/src/services/games.service.ts new file mode 100644 index 0000000..9fe26cf --- /dev/null +++ b/src/services/games.service.ts @@ -0,0 +1,37 @@ +// import { BaseError } from "../config/error"; +// import { status } from "../config/response.status"; +import { findGamesByDate, findGamesByGender, findGamesByLevel, findGamesByRegion } from "../daos/games.dao"; +import { getMemberCountByTeamId } from "../daos/member.dao"; +import { readGameResponseDTO } from "../dtos/games.dto"; + +export const readGamesByDate = async (query) => { + const games = await findGamesByDate(query.date, query.category); + for (const game of games) { + game.memberCount = await getMemberCountByTeamId(game["HostTeam.id"]); + } + return readGameResponseDTO(games); +}; + +export const readGamesByGender = async (query) => { + const games = await findGamesByGender(query.date, query.category, query.gender); + for (const game of games) { + game.memberCount = await getMemberCountByTeamId(game["HostTeam.id"]); + } + return readGameResponseDTO(games); +}; + +export const readGamesByLevel = async (query) => { + const games = await findGamesByLevel(query.date, query.category, query.skillLevel); + for (const game of games) { + game.memberCount = await getMemberCountByTeamId(game["HostTeam.id"]); + } + return readGameResponseDTO(games); +}; + +export const readGamesByRegion = async (query) => { + const games = await findGamesByRegion(query.date, query.category, query.region); + for (const game of games) { + game.memberCount = await getMemberCountByTeamId(game["HostTeam.id"]); + } + return readGameResponseDTO(games); +}; From fd9b2c4d54af910d0c96d071b613859e2cfddeee Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Mon, 29 Jan 2024 19:52:08 +0900 Subject: [PATCH 031/147] =?UTF-8?q?[FEAT]=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=20=EC=A1=B0=ED=9A=8C,=20=EC=8B=A0=EC=B2=AD?= =?UTF-8?q?=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 852 +++++++++++++++++++++++++--- package.json | 3 + src/config/response.status.ts | 8 + src/controllers/guest.controller.ts | 25 +- src/controllers/teams.controller.ts | 2 +- src/daos/guest.dao.ts | 71 ++- src/daos/team.dao.ts | 24 + src/daos/user.dao.ts | 9 + src/dtos/guests.dto.ts | 26 + src/models/guest.model.ts | 6 + src/routes/guests.route.ts | 16 +- src/schemas/guest.schema.ts | 19 + src/services/guest.service.ts | 62 +- 13 files changed, 1015 insertions(+), 108 deletions(-) create mode 100644 src/schemas/guest.schema.ts diff --git a/package-lock.json b/package-lock.json index 3f8cad7..0b6e8b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,6 +33,9 @@ "eslint-config-prettier": "^9.1.0", "nodemon": "^3.0.2", "prettier": "^3.1.1", + "swagger-cli": "^4.0.4", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.0", "ts-node": "^10.9.2", "typescript": "^5.3.3" } @@ -46,6 +49,271 @@ "node": ">=0.10.0" } }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz", + "integrity": "sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==", + "dev": true, + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "js-yaml": "^3.13.1" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-cli": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-cli/-/swagger-cli-4.0.4.tgz", + "integrity": "sha512-hdDT3B6GLVovCsRZYDi3+wMcB1HfetTU20l2DC8zD3iFRNMC6QNAZG5fo/6PYeHWBEv7ri4MvnlKodhNB0nt7g==", + "deprecated": "This package has been abandoned. Please switch to using the actively maintained @redocly/cli", + "dev": true, + "dependencies": { + "@apidevtools/swagger-parser": "^10.0.1", + "chalk": "^4.1.0", + "js-yaml": "^3.14.0", + "yargs": "^15.4.1" + }, + "bin": { + "swagger-cli": "bin/swagger-cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/@apidevtools/swagger-cli/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true + }, + "node_modules/@apidevtools/swagger-cli/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@apidevtools/swagger-cli/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", + "dev": true + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz", + "integrity": "sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==", + "dev": true, + "dependencies": { + "@apidevtools/json-schema-ref-parser": "9.0.6", + "@apidevtools/openapi-schemas": "^2.1.0", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "ajv": "^8.6.3", + "ajv-draft-04": "^1.0.0", + "call-me-maybe": "^1.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/@apidevtools/swagger-parser/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@apidevtools/swagger-parser/node_modules/ajv-draft-04": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", + "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", + "dev": true, + "peerDependencies": { + "ajv": "^8.5.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/@apidevtools/swagger-parser/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -278,6 +546,12 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", + "dev": true + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1021,6 +1295,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", + "dev": true + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1030,6 +1310,67 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -1268,6 +1609,15 @@ "ms": "2.0.0" } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -1570,21 +1920,6 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1595,22 +1930,6 @@ "concat-map": "0.0.1" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, "node_modules/eslint/node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -1668,15 +1987,6 @@ "node": ">=10.13.0" } }, - "node_modules/eslint/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/eslint/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -1725,34 +2035,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/eslint/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "has-flag": "^4.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -1775,6 +2064,19 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -1977,6 +2279,19 @@ "node": ">= 0.8" } }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -2588,11 +2903,29 @@ "node": ">= 0.8.0" } }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, + "node_modules/lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", + "dev": true + }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -2603,6 +2936,12 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true + }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -2629,6 +2968,12 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/lodash.mergewith": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", + "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", + "dev": true + }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -2994,6 +3339,13 @@ "wrappy": "1" } }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "dev": true, + "peer": true + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -3026,6 +3378,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3046,6 +3434,130 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-google-oauth20": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", + "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", + "dependencies": { + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-kakao": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz", + "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==", + "dependencies": { + "passport-oauth2": "~1.1.2", + "pkginfo": "~0.3.0" + } + }, + "node_modules/passport-kakao/node_modules/passport-oauth2": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz", + "integrity": "sha512-wpsGtJDHHQUjyc9WcV9FFB0bphFExpmKtzkQrxpH1vnSr6RcWa3ZEGHx/zGKAh2PN7Po9TKYB1fJeOiIBspNPA==", + "dependencies": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-naver": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/passport-naver/-/passport-naver-1.0.6.tgz", + "integrity": "sha512-5XEbJesiPVshwE0cTDvbbJrOiYSJ9VFRJTctqiZL1Xip6qOi9n7d49UesOqavLT+Ry8C7aw3zdzbmWosqHcQ/Q==", + "dependencies": { + "passport-oauth": "^1.0.0", + "underscore": "^1.8.3" + } + }, + "node_modules/passport-oauth": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-oauth/-/passport-oauth-1.0.0.tgz", + "integrity": "sha512-4IZNVsZbN1dkBzmEbBqUxDG8oFOIK81jqdksE3HEb/vI3ib3FMjbiZZ6MTtooyYZzmKu0BfovjvT1pdGgIq+4Q==", + "dependencies": { + "passport-oauth1": "1.x.x", + "passport-oauth2": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/passport-oauth1": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.3.0.tgz", + "integrity": "sha512-8T/nX4gwKTw0PjxP1xfD0QhrydQNakzeOpZ6M5Uqdgz9/a/Ag62RmJxnZQ4LkbdXGrRehQHIAHNAu11rCP46Sw==", + "dependencies": { + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-oauth2": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz", + "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==", + "dependencies": { + "base64url": "3.x.x", + "oauth": "0.9.x", + "passport-strategy": "1.x.x", + "uid2": "0.0.x", + "utils-merge": "1.x.x" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -3259,6 +3771,21 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -3594,6 +4121,12 @@ "node": ">= 0.8.0" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, "node_modules/set-function-length": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", @@ -3668,6 +4201,12 @@ "node": ">=10" } }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, "node_modules/sqlstring": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", @@ -3807,6 +4346,142 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swagger-cli": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/swagger-cli/-/swagger-cli-4.0.4.tgz", + "integrity": "sha512-Cp8YYuLny3RJFQ4CvOBTaqmOOgYsem52dPx1xM5S4EUWFblIh2Q8atppMZvXKUr1e9xH5RwipYpmdUzdPcxWcA==", + "dev": true, + "dependencies": { + "@apidevtools/swagger-cli": "4.0.4" + }, + "bin": { + "swagger-cli": "swagger-cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/swagger-jsdoc": { + "version": "6.2.8", + "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", + "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", + "dev": true, + "dependencies": { + "commander": "6.2.0", + "doctrine": "3.0.0", + "glob": "7.1.6", + "lodash.mergewith": "^4.6.2", + "swagger-parser": "^10.0.3", + "yaml": "2.0.0-1" + }, + "bin": { + "swagger-jsdoc": "bin/swagger-jsdoc.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/swagger-jsdoc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/swagger-jsdoc/node_modules/commander": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", + "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/swagger-jsdoc/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/swagger-jsdoc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "dev": true, + "dependencies": { + "@apidevtools/swagger-parser": "10.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/swagger-parser/node_modules/@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "dev": true, + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/swagger-ui-dist": { + "version": "5.11.1", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.1.tgz", + "integrity": "sha512-H0oTRHqAfuyjNBLILhS9OwC0WN/xyfNJQtRBR35AnLY34A5W107zRhLQMzL47uuH87+f+RwxCMeLvNFOdyZ+Tg==", + "dev": true + }, + "node_modules/swagger-ui-express": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz", + "integrity": "sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==", + "dev": true, + "dependencies": { + "swagger-ui-dist": ">=5.0.0" + }, + "engines": { + "node": ">= v0.10.32" + }, + "peerDependencies": { + "express": ">=4.0.0 || >=5.0.0-beta" + } + }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4092,6 +4767,12 @@ "node": ">= 8" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true + }, "node_modules/wkx": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", @@ -4203,6 +4884,15 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/yaml": { + "version": "2.0.0-1", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", + "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -4286,6 +4976,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dev": true, + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" + } + }, + "node_modules/z-schema/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, "node_modules/zod": { "version": "3.22.4", "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", diff --git a/package.json b/package.json index fa7f733..3d3cd43 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,9 @@ "eslint-config-prettier": "^9.1.0", "nodemon": "^3.0.2", "prettier": "^3.1.1", + "swagger-cli": "^4.0.4", + "swagger-jsdoc": "^6.2.8", + "swagger-ui-express": "^5.0.0", "ts-node": "^10.9.2", "typescript": "^5.3.3" } diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 0fec805..42dc99a 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -105,6 +105,14 @@ export const status: { [key: string]: Status } = { message: "멤버를 찾을 수 없습니다.", }, + // guest error + GUEST_NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "GUESTER001", + message: "게스팅을 찾을 수 없습니다.", + }, + //post err POST_NOT_FOUND: { status: StatusCodes.NOT_FOUND, diff --git a/src/controllers/guest.controller.ts b/src/controllers/guest.controller.ts index f17890c..14486ef 100644 --- a/src/controllers/guest.controller.ts +++ b/src/controllers/guest.controller.ts @@ -5,12 +5,19 @@ import { readGuestingByLevel, readGuestingByGender, readGuestingByRegion, + readDetailedGuesting, + createGuesting, + addGuestUser, + updateGuesting, } from "../services/guest.service"; -// import { postGuesting } from "../services/guest.service"; -// export const createGuesting = async (req, res, next) => { -// return res.send(response(status.SUCCESS, await postGuesting(req.params.category, req.query))); -// }; +export const addGuesting = async (req, res, next) => { + return res.send(response(status.SUCCESS, await createGuesting(1, req.body))); +}; + +export const modifyGuesting = async (req, res, next) => { + return res.send(response(status.SUCCESS, await updateGuesting(1, req.params, req.body))); +}; export const GuestingPreview = async (req, res, next) => { return res.send(response(status.SUCCESS, await readGuesting(req.query))); @@ -28,6 +35,10 @@ export const GuestingPreviewByRegion = async (req, res, next) => { return res.send(response(status.SUCCESS, await readGuestingByRegion(req.query))); }; -// export const DetailedGuestingPreview = async (req, res, next) => { -// return res.send(response(status.SUCCESS, await getDetailedGuesting(req.team.id, req.params))); -// }; +export const DetailedGuestingPreview = async (req, res, next) => { + return res.send(response(status.SUCCESS, await readDetailedGuesting(req.params))); +}; + +export const applicationGuesting = async (req, res, next) => { + return res.send(response(status.SUCCESS, await addGuestUser(1, req.params))); +}; diff --git a/src/controllers/teams.controller.ts b/src/controllers/teams.controller.ts index 5bdb4bb..e122083 100644 --- a/src/controllers/teams.controller.ts +++ b/src/controllers/teams.controller.ts @@ -16,5 +16,5 @@ export const modifyTeam = async (req, res: Response, next) => { }; export const fetchTeamDetail = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readTeamDetail(req.user.id, req.params))); + res.send(response(status.SUCCESS, await readTeamDetail(1, req.params))); }; diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index cf05fd7..89f5273 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -1,5 +1,23 @@ import { Sequelize } from "sequelize"; import db from "../models"; +import { CreateGuestingSchema } from "../schemas/guest.schema"; +import { getTeamIdByLeaderId } from "./team.dao"; + +export const insertGuesting = async (teamId, data: CreateGuestingSchema) => { + await db.Guest.create({ + teamId: teamId, + gameTime: data.gameTime, + description: data.description, + recruitCount: data.recruitCount, + }); +}; + +export const setGuesting = async (guesting, body) => { + Object.keys(body).forEach((field) => { + guesting[field] = body[field]; + }); + await guesting.save(); +}; export const findGuesting = async (date, category) => { return await db.Guest.findAll({ @@ -72,30 +90,29 @@ export const findGuestingByRegion = async (date, category, region) => { }); }; -// export const findGuestDetail = async (guestId) => { -// return await db.Guest.findOne({ -// raw: true, -// where: { -// id: guestId, -// }, -// include: [ -// { -// model: db.Team, -// as: "TeamInfo", -// attributes: ["name", "skill_level", "manner_level", "region", "description", "gender", "age_group"], -// where: { -// id: db.Sequelize.col("Guest.TeamId"), -// }, -// }, -// { -// model: db.User, -// as: "TeamMemberInfo", -// attributes: ["nickname"], -// where: { -// id: db.Sequelize.col("User.TeamId"), -// }, -// }, -// ], -// attributes: ["game_time", "dscription"], -// }); -// }; +export const getDetailedGuesting = async (guestingId) => { + return await db.Guest.findOne({ + raw: true, + where: { + id: guestingId, + }, + attributes: ["teamId", "gameTime", "description", "recruitCount"], + }); +}; + +export const InsertGuestUser = async (guestingId, userId) => { + await db.GuestUser.create({ + guestId: guestingId, + userId: userId, + }); +}; + +export const getGuestingById = async (guestingId, userId) => { + const teamId = await getTeamIdByLeaderId(userId); + return await db.Guest.findOne({ + where: { + id: guestingId, + teamId: teamId, + }, + }); +}; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index d6cc0f2..2ef0f4a 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -1,3 +1,5 @@ +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; import { defaultLevel } from "../constants/level.constant"; import db from "../models"; import { CreateTeamSchema } from "../schemas/team.schema"; @@ -58,6 +60,16 @@ export const getTeamDetail = async (teamId) => { }); }; +export const getTeamDetailforGuesting = async (teamId) => { + return await db.Team.findOne({ + raw: true, + where: { + id: teamId, + }, + attributes: ["name", "description", "gender", "ageGroup", "gymName", "skillLevel", "mannerLevel", "leaderId"], + }); +}; + export const getTeamIdByInviteCode = async (inviteCode): Promise => { const team = await db.Team.findOne({ raw: true, @@ -78,6 +90,18 @@ export const getTeamById = async (teamId, userId) => { }); }; +export const getTeamIdByLeaderId = async (userId) => { + const team = await db.Team.findOne({ + raw: true, + where: { + leaderId: userId, + }, + attributes: ["id"], + }); + + return team?.id; +}; + export const setTeam = async (team, body) => { Object.keys(body).forEach((field) => { team[field] = body[field]; diff --git a/src/daos/user.dao.ts b/src/daos/user.dao.ts index bacdb52..86d4de8 100644 --- a/src/daos/user.dao.ts +++ b/src/daos/user.dao.ts @@ -40,6 +40,15 @@ export const getRefreshToken = async (userId: number) => { return user.refreshToken; }; +export const getUserById = async (id) => { + return await db.User.findOne({ + raw: true, + where: { + id, + }, + }); +}; + export const getUserInfoById = async (id) => { return await db.User.findOne({ raw: true, diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index 5e9d97a..3912859 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -14,3 +14,29 @@ export const readGuestingResponseDTO = (guestings) => { recruitCount: guesting.recruitCount, })); }; + +export const readGuestingDetailResponseDTO = (guestingDetail, TeamDetail, leaderInfo, memberInfo) => { + const member = memberInfo.map((info) => ({ + nickname: info["User.nickname"], + height: null, + weight: null, + position: null, + })); + return { + name: TeamDetail.name, + skillLevel: TeamDetail.skillLevel, + mannerLevel: TeamDetail.mannerLevel, + description: TeamDetail.description, + gusting_info: { + gameTime: guestingDetail.gameTime, + gender: TeamDetail.gender, + ageGroup: TeamDetail.ageGroup, + gymName: TeamDetail.gymName, + skillLevel: TeamDetail.skillLevel, + }, + member_info: { + leader: leaderInfo, + member, + }, + }; +}; diff --git a/src/models/guest.model.ts b/src/models/guest.model.ts index 3d99ae0..e118ae2 100644 --- a/src/models/guest.model.ts +++ b/src/models/guest.model.ts @@ -4,6 +4,12 @@ class Guest extends Model, InferCreationAttributes static initiate(sequelize: Sequelize) { Guest.init( { + id: { + type: new DataTypes.INTEGER(), + allowNull: false, + primaryKey: true, + autoIncrement: true, // 이 부분을 추가 + }, teamId: { type: new DataTypes.INTEGER(), allowNull: false, diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index 68e5c7f..d7571ee 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -5,11 +5,21 @@ import { GuestingPreviewByLevel, GuestingPreviewByGender, GuestingPreviewByRegion, + DetailedGuestingPreview, + addGuesting, + modifyGuesting, + applicationGuesting, } from "../controllers/guest.controller"; +import { createGuesting, updateGuesting } from "../schemas/guest.schema"; +import { validateBody } from "../middlewares/validate.middleware"; export const guestsRouter = express.Router({ mergeParams: true }); -// guestRouter.post("/", asyncHandler(createGuesting)); +// guestsRouter.post("/", validateBody(createGuesting), asyncHandler(addGuesting)); +guestsRouter.post("/", asyncHandler(addGuesting)); + +// guestsRouter.put("/:guestingId", validateBody(updateGuesting), asyncHandler(modifyGuesting)); +guestsRouter.put("/:guestingId", asyncHandler(modifyGuesting)); guestsRouter.get("/", asyncHandler(GuestingPreview)); @@ -19,4 +29,6 @@ guestsRouter.get("/gender", asyncHandler(GuestingPreviewByGender)); guestsRouter.get("/region", asyncHandler(GuestingPreviewByRegion)); -// guestRouter.get("/:guestId", asyncHandler(DetailedGuestingPreview)); +guestsRouter.get("/:guestingId", asyncHandler(DetailedGuestingPreview)); + +guestsRouter.post("/:guestingId/application", asyncHandler(applicationGuesting)); diff --git a/src/schemas/guest.schema.ts b/src/schemas/guest.schema.ts new file mode 100644 index 0000000..8496df9 --- /dev/null +++ b/src/schemas/guest.schema.ts @@ -0,0 +1,19 @@ +import { TypeOf, object, z } from "zod"; + +const fields = { + teamId: z.number().int(), + gameTime: z.date(), + description: z.optional(z.string()), + recruitCount: z.number().int().min(1), +}; + +export const createGuesting = object({ + ...fields, +}); + +export const updateGuesting = object({ + ...fields, +}); + +export type CreateGuestingSchema = TypeOf; +export type UpdateGuestingSchema = TypeOf; diff --git a/src/services/guest.service.ts b/src/services/guest.service.ts index 0323b03..8f5f83c 100644 --- a/src/services/guest.service.ts +++ b/src/services/guest.service.ts @@ -1,8 +1,41 @@ -// import { BaseError } from "../config/error"; -// import { status } from "../config/response.status"; -import { findGuesting, findGuestingByGender, findGuestingByLevel, findGuestingByRegion } from "../daos/guest.dao"; -import { getMemberCountByTeamId } from "../daos/member.dao"; -import { readGuestingResponseDTO } from "../dtos/guests.dto"; +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { + InsertGuestUser, + findGuesting, + findGuestingByGender, + findGuestingByLevel, + findGuestingByRegion, + getDetailedGuesting, + getGuestingById, + insertGuesting, + setGuesting, +} from "../daos/guest.dao"; +import { findMemberInfoByTeamId, getMemberCountByTeamId } from "../daos/member.dao"; +import { getTeamById, getTeamDetailforGuesting, getTeamIdByLeaderId } from "../daos/team.dao"; +import { getUserById, getUserInfoById, userInfoAttributes } from "../daos/user.dao"; +import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; +import { CreateGuestingSchema, UpdateGuestingSchema } from "../schemas/guest.schema"; + +export const createGuesting = async (userId, body: CreateGuestingSchema) => { + const teamId = await getTeamIdByLeaderId(userId); + const team = await getTeamById(teamId, userId); + if (!team) { + // team 메뉴로 이동 + } + await insertGuesting(teamId, body); + return; +}; + +export const updateGuesting = async (userId, params, body: UpdateGuestingSchema) => { + const guestingId = params.guestingId; + const guesting = await getGuestingById(guestingId, userId); + if (!guesting) { + throw new BaseError(status.GUEST_NOT_FOUND); + } + await setGuesting(guesting, body); + return; +}; export const readGuesting = async (query) => { const guestings = await findGuesting(query.date, query.category); @@ -35,3 +68,22 @@ export const readGuestingByRegion = async (query) => { } return readGuestingResponseDTO(guestings); }; + +export const readDetailedGuesting = async (params) => { + const guestingId = params.guestingId; + const guestingDetail = await getDetailedGuesting(guestingId); + const TeamDetail = await getTeamDetailforGuesting(guestingDetail.teamId); + const leaderInfo = await getUserInfoById(TeamDetail.leaderId); + const memberInfo = await findMemberInfoByTeamId(guestingDetail.teamId, userInfoAttributes); + return readGuestingDetailResponseDTO(guestingDetail, TeamDetail, leaderInfo, memberInfo); +}; + +export const addGuestUser = async (userId, params) => { + const guestingId = params.guestingId; + const user = await getUserById(userId); + if (!user) { + // user 정보 수정 API 연결 + } + await InsertGuestUser(guestingId, userId); + return; +}; From e6f36693bc0b505401a6a5a1881ef269b18f18af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Mon, 29 Jan 2024 22:42:57 +0900 Subject: [PATCH 032/147] =?UTF-8?q?Refactor:=20=EB=84=A4=EC=9D=B4=EB=B2=84?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20API=20=EA=B5=AC=ED=98=84=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/auth.controller.ts | 10 +++++--- src/routes/auth.route.ts | 4 ++- src/services/auth.service.ts | 39 ++++++++++++++++++++++++++++-- 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts index 26d8a77..e38cb1f 100644 --- a/src/controllers/auth.controller.ts +++ b/src/controllers/auth.controller.ts @@ -1,12 +1,16 @@ -import { Request } from "express"; +import { Request, Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { kakaoLogin, generateNewAccessToken } from "../services/auth.service"; +import { kakaoLogin, naverLogin, generateNewAccessToken } from "../services/auth.service"; -export const authKakao = async (req, res, next) => { +export const authKakao = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await kakaoLogin(req.body))); }; +export const authNaver = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await naverLogin(req.body))); +}; + export const refreshAccessToken = async (req: Request, res, next) => { res.send(response(status.SUCCESS, await generateNewAccessToken(req))); }; diff --git a/src/routes/auth.route.ts b/src/routes/auth.route.ts index 668e425..15cdd7a 100644 --- a/src/routes/auth.route.ts +++ b/src/routes/auth.route.ts @@ -1,9 +1,11 @@ import express from "express"; import asyncHandler from "express-async-handler"; -import { authKakao, refreshAccessToken } from "../controllers/auth.controller"; +import { authKakao, authNaver, refreshAccessToken } from "../controllers/auth.controller"; export const authRouter = express.Router(); authRouter.post("/kakao", asyncHandler(authKakao)); +authRouter.post("/naver", asyncHandler(authNaver)); + authRouter.post("/refresh", asyncHandler(refreshAccessToken)); diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index cd89241..7d822d9 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -20,7 +20,7 @@ export const tokenType = "Bearer "; export const kakaoLogin = async (body) => { const accessToken = await getKakaoAccessToken(body.code); - const userInfo = await retrieveKakaoUserInfo(accessToken); + const userInfo = await getKakaoUserInfo(accessToken); const payload = await createOrReadUser(userInfo); return login(payload); }; @@ -39,7 +39,7 @@ const getKakaoAccessToken = async (code: string) => { return response.data.access_token; }; -const retrieveKakaoUserInfo = async (accessToken: string): Promise => { +const getKakaoUserInfo = async (accessToken: string): Promise => { const url = "https://kapi.kakao.com/v2/user/me"; const response = await redaxios.get(url, { headers: { @@ -54,6 +54,41 @@ const retrieveKakaoUserInfo = async (accessToken: string): Promise => }; }; +export const naverLogin = async (body) => { + const accessToken = await getNaverAccessToken(body.code, body.state); + const userInfo = await getNaverUserInfo(accessToken); + const payload = await createOrReadUser(userInfo); + return login(payload); +}; + +const getNaverAccessToken = async (code: string, state: string) => { + const url = "https://nid.naver.com/oauth2.0/token"; + const response = await redaxios.get(url, { + params: { + grant_type: "authorization_code", + client_id: process.env.NAVER_CLIENT_ID, + client_secret: process.env.NAVER_CLIENT_SECRET, + code: code, + state: state, + }, + }); + return response.data.access_token; +}; + +const getNaverUserInfo = async (accessToken: string): Promise => { + const url = "https://openapi.naver.com/v1/nid/me"; + const response = await redaxios.get(url, { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }); + return { + provider: Provider.NAVER, + providerId: response.data.response.id, + nickname: response.data.response.name, + }; +}; + export const login = async (payload: Payload) => { const accessToken = generateAccessToken(payload); const refreshToken = generateRefreshToken(); From d3616126b50de73afc147b880687f442392e6a25 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Mon, 29 Jan 2024 23:25:45 +0900 Subject: [PATCH 033/147] =?UTF-8?q?[FEAT]=20=EB=A1=9C=EA=B7=B8=EC=95=84?= =?UTF-8?q?=EC=9B=83=20API=20=EA=B5=AC=ED=98=84=20(#45)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 함수 추가 #44 * Feat: 컨트롤러 함수 추가 #44 * Feat: 서비스 함수 추가 #44 * Feat: setRefreshToken 함수의 refreshToken 매개변수 타입 변경 #44 --- src/controllers/auth.controller.ts | 8 ++++++-- src/daos/user.dao.ts | 2 +- src/routes/auth.route.ts | 5 ++++- src/services/auth.service.ts | 16 ++++++++++++++-- src/services/users.service.ts | 4 ++++ 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts index e38cb1f..daa747e 100644 --- a/src/controllers/auth.controller.ts +++ b/src/controllers/auth.controller.ts @@ -1,7 +1,7 @@ import { Request, Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { kakaoLogin, naverLogin, generateNewAccessToken } from "../services/auth.service"; +import { kakaoLogin, naverLogin, generateNewAccessToken, logoutUser } from "../services/auth.service"; export const authKakao = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await kakaoLogin(req.body))); @@ -11,6 +11,10 @@ export const authNaver = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await naverLogin(req.body))); }; -export const refreshAccessToken = async (req: Request, res, next) => { +export const refreshAccessToken = async (req: Request, res: Response, next) => { res.send(response(status.SUCCESS, await generateNewAccessToken(req))); }; + +export const logout = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await logoutUser(req.user.id))); +}; diff --git a/src/daos/user.dao.ts b/src/daos/user.dao.ts index 86d4de8..b8b8042 100644 --- a/src/daos/user.dao.ts +++ b/src/daos/user.dao.ts @@ -18,7 +18,7 @@ export const insertUser = async (provider, providerId, nickname) => { }); }; -export const setRefreshToken = async (refreshToken: string, userId: number) => { +export const setRefreshToken = async (refreshToken: string | null, userId: number) => { await db.User.update( { refreshToken }, { diff --git a/src/routes/auth.route.ts b/src/routes/auth.route.ts index 15cdd7a..833b824 100644 --- a/src/routes/auth.route.ts +++ b/src/routes/auth.route.ts @@ -1,6 +1,7 @@ import express from "express"; import asyncHandler from "express-async-handler"; -import { authKakao, authNaver, refreshAccessToken } from "../controllers/auth.controller"; +import { authKakao, authNaver, refreshAccessToken, logout } from "../controllers/auth.controller"; +import { verifyUser } from "../middlewares/auth.middleware"; export const authRouter = express.Router(); @@ -9,3 +10,5 @@ authRouter.post("/kakao", asyncHandler(authKakao)); authRouter.post("/naver", asyncHandler(authNaver)); authRouter.post("/refresh", asyncHandler(refreshAccessToken)); + +authRouter.post("/logout", verifyUser, asyncHandler(logout)); diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 7d822d9..2540310 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -1,5 +1,5 @@ import { Request } from "express"; -import { createOrReadUser, updateRefreshToken } from "./users.service"; +import { createOrReadUser, updateRefreshToken, deleteRefreshToken } from "./users.service"; import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { getRefreshToken } from "../daos/user.dao"; @@ -29,7 +29,14 @@ const getKakaoAccessToken = async (code: string) => { const url = "https://kauth.kakao.com/oauth/token"; const response = await redaxios.post( url, - `grant_type=authorization_code&client_id=${process.env.KAKAO_CLIENT_ID}&redirect_uri=${process.env.KAKAO_REDIRECT_URI}&code=${code}`, + { + params: { + grant_type: "authorization_code", + client_id: process.env.KAKAO_CLIENT_ID, + redirect_uri: process.env.KAKAO_REDIRECT_URI, + code: code, + }, + }, { headers: { "Content-type": "application/x-www-form-urlencoded;charset=utf-8", @@ -112,3 +119,8 @@ export const generateNewAccessToken = async (req: Request) => { const isRefreshTokenMatching = async (refreshToken, userId: number) => { return refreshToken === (await getRefreshToken(userId)); }; + +export const logoutUser = async (userId: number) => { + await deleteRefreshToken(userId); + return; +}; diff --git a/src/services/users.service.ts b/src/services/users.service.ts index 760c5cf..186e1cc 100644 --- a/src/services/users.service.ts +++ b/src/services/users.service.ts @@ -13,3 +13,7 @@ export const createOrReadUser = async (userInfo: UserInfo): Promise => export const updateRefreshToken = async (refreshToken: string, userId: number) => { await setRefreshToken(refreshToken, userId); }; + +export const deleteRefreshToken = async (userId: number) => { + await setRefreshToken(null, userId); +}; From 3e23e079eddd5306b2d2659cbea39f9f7484d7ee Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Tue, 30 Jan 2024 01:22:26 +0900 Subject: [PATCH 034/147] =?UTF-8?q?[Feat]=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20=EC=8B=A0=EC=B2=AD=20=EA=B0=80=EB=8A=A5=20=ED=8C=80?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20#46=20(#47)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: games 라우트 추가 #46 * Feat: games 컨트롤러 함수 추가 #46 * Feat: teams 서비스 함수 추가 #46 * Feat: teams DAO 함수 추가 #46 * Chore: package-lock.json 업데이트 #46 --- package-lock.json | 115 ---------------------------- src/controllers/games.controller.ts | 6 ++ src/daos/team.dao.ts | 11 +++ src/routes/games.route.ts | 4 + src/services/teams.service.ts | 5 ++ 5 files changed, 26 insertions(+), 115 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0b6e8b9..d567a02 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3434,121 +3434,6 @@ "node": ">= 0.8" } }, - "node_modules/passport": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", - "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", - "dependencies": { - "passport-strategy": "1.x.x", - "pause": "0.0.1", - "utils-merge": "^1.0.1" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-google-oauth20": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/passport-google-oauth20/-/passport-google-oauth20-2.0.0.tgz", - "integrity": "sha512-KSk6IJ15RoxuGq7D1UKK/8qKhNfzbLeLrG3gkLZ7p4A6DBCcv7xpyQwuXtWdpyR0+E0mwkpjY1VfPOhxQrKzdQ==", - "dependencies": { - "passport-oauth2": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-kakao": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/passport-kakao/-/passport-kakao-1.0.1.tgz", - "integrity": "sha512-uItaYRVrTHL6iGPMnMZvPa/O1GrAdh/V6EMjOHcFlQcVroZ9wgG7BZ5PonMNJCxfHQ3L2QVNRnzhKWUzSsumbw==", - "dependencies": { - "passport-oauth2": "~1.1.2", - "pkginfo": "~0.3.0" - } - }, - "node_modules/passport-kakao/node_modules/passport-oauth2": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.1.2.tgz", - "integrity": "sha512-wpsGtJDHHQUjyc9WcV9FFB0bphFExpmKtzkQrxpH1vnSr6RcWa3ZEGHx/zGKAh2PN7Po9TKYB1fJeOiIBspNPA==", - "dependencies": { - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-naver": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/passport-naver/-/passport-naver-1.0.6.tgz", - "integrity": "sha512-5XEbJesiPVshwE0cTDvbbJrOiYSJ9VFRJTctqiZL1Xip6qOi9n7d49UesOqavLT+Ry8C7aw3zdzbmWosqHcQ/Q==", - "dependencies": { - "passport-oauth": "^1.0.0", - "underscore": "^1.8.3" - } - }, - "node_modules/passport-oauth": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-oauth/-/passport-oauth-1.0.0.tgz", - "integrity": "sha512-4IZNVsZbN1dkBzmEbBqUxDG8oFOIK81jqdksE3HEb/vI3ib3FMjbiZZ6MTtooyYZzmKu0BfovjvT1pdGgIq+4Q==", - "dependencies": { - "passport-oauth1": "1.x.x", - "passport-oauth2": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/passport-oauth1": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.3.0.tgz", - "integrity": "sha512-8T/nX4gwKTw0PjxP1xfD0QhrydQNakzeOpZ6M5Uqdgz9/a/Ag62RmJxnZQ4LkbdXGrRehQHIAHNAu11rCP46Sw==", - "dependencies": { - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "utils-merge": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-oauth2": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.7.0.tgz", - "integrity": "sha512-j2gf34szdTF2Onw3+76alNnaAExlUmHvkc7cL+cmaS5NzHzDP/BvFHJruueQ9XAeNOdpI+CH+PWid8RA7KCwAQ==", - "dependencies": { - "base64url": "3.x.x", - "oauth": "0.9.x", - "passport-strategy": "1.x.x", - "uid2": "0.0.x", - "utils-merge": "1.x.x" - }, - "engines": { - "node": ">= 0.4.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/jaredhanson" - } - }, - "node_modules/passport-strategy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", - "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", - "engines": { - "node": ">= 0.4.0" - } - }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", diff --git a/src/controllers/games.controller.ts b/src/controllers/games.controller.ts index bbf3890..5fe25b8 100644 --- a/src/controllers/games.controller.ts +++ b/src/controllers/games.controller.ts @@ -2,6 +2,7 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; import { readGamesByDate, readGamesByGender, readGamesByLevel, readGamesByRegion } from "../services/games.service"; +import { readTeamAvailPreviewById } from "../services/teams.service"; export const fetchGamesByDate = async (req, res, next) => { res.send(response(status.SUCCESS, await readGamesByDate(req.query))); @@ -18,3 +19,8 @@ export const fetchGamesByLevel = async (req, res, next) => { export const fetchGamesByRegion = async (req, res, next) => { res.send(response(status.SUCCESS, await readGamesByRegion(req.query))); }; + +export const fetchTeamsAvailById = async (req, res, next) => { + // res.send(response(status.SUCCESS, await readTeamAvailPreviewById(req.user.id, req.query))); + res.send(response(status.SUCCESS, await readTeamAvailPreviewById(1, req.query))); // test +}; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index 2ef0f4a..f625966 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -108,3 +108,14 @@ export const setTeam = async (team, body) => { }); await team.save(); }; + +export const findTeamAvailPreviewById = async (userId, category) => { + return await db.Team.findAll({ + raw: true, + where: { + category, + leaderId: userId, + }, + attributes: ["name"], + }); +}; diff --git a/src/routes/games.route.ts b/src/routes/games.route.ts index 8a26f7d..d21dc38 100644 --- a/src/routes/games.route.ts +++ b/src/routes/games.route.ts @@ -5,6 +5,7 @@ import { fetchGamesByGender, fetchGamesByLevel, fetchGamesByRegion, + fetchTeamsAvailById, } from "../controllers/games.controller"; export const gamesRouter = express.Router(); @@ -16,3 +17,6 @@ gamesRouter.get("/", asyncHandler(fetchGamesByDate)); gamesRouter.get("/by-gender", asyncHandler(fetchGamesByGender)); gamesRouter.get("/by-level", asyncHandler(fetchGamesByLevel)); gamesRouter.get("/by-region", asyncHandler(fetchGamesByRegion)); + +// 연습경기 신청 가능 팀 목록 조회 +gamesRouter.get("/apply-avail", asyncHandler(fetchTeamsAvailById)); diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index dbc4995..318cabe 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -1,5 +1,6 @@ import { deleteMembersById, findMemberInfoByTeamId, findMemberToDelete } from "../daos/member.dao"; import { findTeamPreviewByCategory, getTeamById, getTeamDetail, insertTeam, setTeam } from "../daos/team.dao"; +import { findTeamAvailPreviewById } from "../daos/team.dao"; import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; import { readTeamDetailResponseDTO } from "../dtos/teams.dto"; import { v4 as uuidv4 } from "uuid"; @@ -41,3 +42,7 @@ export const readTeamDetail = async (userId, params) => { const memberInfo = await findMemberInfoByTeamId(teamId, userInfoAttributes); return readTeamDetailResponseDTO(detail, leaderInfo, memberInfo, userId == detail.leaderId); }; + +export const readTeamAvailPreviewById = async (userId, query) => { + return await findTeamAvailPreviewById(userId, query.category); +}; From b0682b27de7b9bb149306cb35270a2de2ac1f3c3 Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Tue, 30 Jan 2024 02:43:25 +0900 Subject: [PATCH 035/147] =?UTF-8?q?[FEAT]=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=83=81=EC=84=B8=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84=20(#50)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #48 * Feat: 컨트롤러 함수 추가 #48 * Feat: 서비스 함수 추가 #48 * Feat: DAO 함수 추가 #48 * Feat: DTO 함수 추가 #48 * Fix: 팀원 목록 리더 중복 제거 #48 --- src/controllers/games.controller.ts | 12 +++++++++++- src/daos/games.dao.ts | 10 ++++++++++ src/daos/member.dao.ts | 26 ++++++++++++++++++++++++++ src/dtos/games.dto.ts | 26 ++++++++++++++++++++++++++ src/routes/games.route.ts | 4 ++++ src/services/games.service.ts | 27 ++++++++++++++++++++++++--- 6 files changed, 101 insertions(+), 4 deletions(-) diff --git a/src/controllers/games.controller.ts b/src/controllers/games.controller.ts index 5fe25b8..847fecb 100644 --- a/src/controllers/games.controller.ts +++ b/src/controllers/games.controller.ts @@ -1,7 +1,13 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readGamesByDate, readGamesByGender, readGamesByLevel, readGamesByRegion } from "../services/games.service"; +import { + readGamesByDate, + readGamesByGender, + readGamesByLevel, + readGamesByRegion, + readGameDetail, +} from "../services/games.service"; import { readTeamAvailPreviewById } from "../services/teams.service"; export const fetchGamesByDate = async (req, res, next) => { @@ -24,3 +30,7 @@ export const fetchTeamsAvailById = async (req, res, next) => { // res.send(response(status.SUCCESS, await readTeamAvailPreviewById(req.user.id, req.query))); res.send(response(status.SUCCESS, await readTeamAvailPreviewById(1, req.query))); // test }; + +export const fetchGameDetail = async (req, res, next) => { + res.send(response(status.SUCCESS, await readGameDetail(req.params))); +}; diff --git a/src/daos/games.dao.ts b/src/daos/games.dao.ts index eef54fd..077690f 100644 --- a/src/daos/games.dao.ts +++ b/src/daos/games.dao.ts @@ -100,3 +100,13 @@ export const findGamesByRegion = async (date, category, region) => { status: getStatusById(game.status), })); }; + +export const getGameDetail = async (gameId) => { + return await db.Game.findOne({ + raw: true, + where: { + id: gameId, + }, + attributes: ["hostTeamId", "gameTime", "description"], + }); +}; diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts index 9a45534..c8fcdb2 100644 --- a/src/daos/member.dao.ts +++ b/src/daos/member.dao.ts @@ -17,6 +17,32 @@ export const findMemberInfoByTeamId = async (teamId, userInfoAttributes) => { }); }; +export const findMemberInfoWithoutLeaderByTeamId = async (teamId, userInfoAttributes) => { + const team = await db.Team.findByPk(teamId); + if (!team) { + throw new Error("Team not found"); + } + const leaderId = team.leaderId; + + // 리더 제외한 멤버들 조회 + return await db.Member.findAll({ + raw: true, + where: { + teamId: teamId, + userId: { + [Op.ne]: leaderId, + }, + }, + include: [ + { + model: db.User, + attributes: userInfoAttributes(), + }, + ], + attributes: [], + }); +}; + export const insertMember = async (teamId, userId) => { await db.Member.create({ teamId, diff --git a/src/dtos/games.dto.ts b/src/dtos/games.dto.ts index a23fddf..e0a394c 100644 --- a/src/dtos/games.dto.ts +++ b/src/dtos/games.dto.ts @@ -14,3 +14,29 @@ export const readGameResponseDTO = (games) => { status: game.status, })); }; + +export const readGameDetailResponseDTO = (gameDetail, teamDetail, leaderInfo, memberInfo) => { + const member = memberInfo.map((info) => ({ + nickname: info["User.nickname"], + // height: null, + // weight: null, + // position: null, + })); + return { + name: teamDetail.name, + skillLevel: teamDetail.skillLevel, + mannerLevel: teamDetail.mannerLevel, + description: teamDetail.description, + game_info: { + gymName: teamDetail.gymName, + gameTime: gameDetail.gameTime, + gender: teamDetail.gender, + ageGroup: teamDetail.ageGroup, + skillLevel: teamDetail.skillLevel, + }, + member_info: { + leader: leaderInfo, + member, + }, + }; +}; diff --git a/src/routes/games.route.ts b/src/routes/games.route.ts index d21dc38..d0a0d2e 100644 --- a/src/routes/games.route.ts +++ b/src/routes/games.route.ts @@ -6,6 +6,7 @@ import { fetchGamesByLevel, fetchGamesByRegion, fetchTeamsAvailById, + fetchGameDetail, } from "../controllers/games.controller"; export const gamesRouter = express.Router(); @@ -20,3 +21,6 @@ gamesRouter.get("/by-region", asyncHandler(fetchGamesByRegion)); // 연습경기 신청 가능 팀 목록 조회 gamesRouter.get("/apply-avail", asyncHandler(fetchTeamsAvailById)); + +// 연습경기 모집글 상세 조회 +gamesRouter.get("/:gameId", asyncHandler(fetchGameDetail)); diff --git a/src/services/games.service.ts b/src/services/games.service.ts index 9fe26cf..b3a44b8 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -1,8 +1,20 @@ // import { BaseError } from "../config/error"; // import { status } from "../config/response.status"; -import { findGamesByDate, findGamesByGender, findGamesByLevel, findGamesByRegion } from "../daos/games.dao"; -import { getMemberCountByTeamId } from "../daos/member.dao"; -import { readGameResponseDTO } from "../dtos/games.dto"; +import { + findGamesByDate, + findGamesByGender, + findGamesByLevel, + findGamesByRegion, + getGameDetail, +} from "../daos/games.dao"; +import { getTeamDetailforGuesting } from "../daos/team.dao"; +import { + findMemberInfoByTeamId, + findMemberInfoWithoutLeaderByTeamId, + getMemberCountByTeamId, +} from "../daos/member.dao"; +import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; +import { readGameResponseDTO, readGameDetailResponseDTO } from "../dtos/games.dto"; export const readGamesByDate = async (query) => { const games = await findGamesByDate(query.date, query.category); @@ -35,3 +47,12 @@ export const readGamesByRegion = async (query) => { } return readGameResponseDTO(games); }; + +export const readGameDetail = async (params) => { + const gameId = params.gameId; + const gameDetail = await getGameDetail(gameId); + const teamDetail = await getTeamDetailforGuesting(gameDetail.hostTeamId); + const leaderInfo = await getUserInfoById(teamDetail.leaderId); + const memberInfo = await findMemberInfoWithoutLeaderByTeamId(gameDetail.hostTeamId, userInfoAttributes); + return readGameDetailResponseDTO(gameDetail, teamDetail, leaderInfo, memberInfo); +}; From 11fc075596d0aa41b012d4f88823b95d89cc5fff Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Tue, 30 Jan 2024 05:14:48 +0900 Subject: [PATCH 036/147] =?UTF-8?q?[FEAT]=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?API=20=EA=B5=AC=ED=98=84=20(#52)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #51 * Feat: 컨트롤러 함수 추가 #51 * Feat: 스키마 작성 #51 * Feat: game 모델 수정 #51 * Feat: 서비스 함수 추가 #51 * Feat: DAO 함수 추가 #51 * Feat: team DAO - getTeamCategoryByLeaderId 함수 추가 #51 --- src/controllers/games.controller.ts | 10 ++++++++-- src/daos/games.dao.ts | 10 ++++++++++ src/daos/team.dao.ts | 12 ++++++++++++ src/models/game.model.ts | 1 + src/routes/games.route.ts | 6 ++++++ src/schemas/game.schema.ts | 29 +++++++++++++++++++++++++++++ src/services/games.service.ts | 23 ++++++++++++++++++++++- 7 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 src/schemas/game.schema.ts diff --git a/src/controllers/games.controller.ts b/src/controllers/games.controller.ts index 847fecb..acd0a4f 100644 --- a/src/controllers/games.controller.ts +++ b/src/controllers/games.controller.ts @@ -7,6 +7,7 @@ import { readGamesByLevel, readGamesByRegion, readGameDetail, + createGame, } from "../services/games.service"; import { readTeamAvailPreviewById } from "../services/teams.service"; @@ -27,10 +28,15 @@ export const fetchGamesByRegion = async (req, res, next) => { }; export const fetchTeamsAvailById = async (req, res, next) => { - // res.send(response(status.SUCCESS, await readTeamAvailPreviewById(req.user.id, req.query))); - res.send(response(status.SUCCESS, await readTeamAvailPreviewById(1, req.query))); // test + res.send(response(status.SUCCESS, await readTeamAvailPreviewById(req.user.id, req.query))); + // res.send(response(status.SUCCESS, await readTeamAvailPreviewById(1, req.query))); // for testing }; export const fetchGameDetail = async (req, res, next) => { res.send(response(status.SUCCESS, await readGameDetail(req.params))); }; + +export const addGame = async (req, res, next) => { + return res.send(response(status.SUCCESS, await createGame(req.user.id, req.body))); + // return res.send(response(status.SUCCESS, await createGame(2, req.body))); //for testing +}; diff --git a/src/daos/games.dao.ts b/src/daos/games.dao.ts index 077690f..858a632 100644 --- a/src/daos/games.dao.ts +++ b/src/daos/games.dao.ts @@ -1,6 +1,7 @@ import db from "../models"; import { Sequelize } from "sequelize"; import { getStatusById } from "../constants/status.constant"; +import { CreateGameSchema } from "../schemas/game.schema"; export const findGamesByDate = async (date, category) => { const games = await db.Game.findAll({ @@ -110,3 +111,12 @@ export const getGameDetail = async (gameId) => { attributes: ["hostTeamId", "gameTime", "description"], }); }; + +export const insertGame = async (hostTeamId, data: CreateGameSchema, category) => { + await db.Game.create({ + hostTeamId: hostTeamId, + gameTime: data.gameTime, + category: category, + description: data.description, + }); +}; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index f625966..10c7bc2 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -102,6 +102,18 @@ export const getTeamIdByLeaderId = async (userId) => { return team?.id; }; +export const getTeamCategoryByLeaderId = async (userId) => { + const team = await db.Team.findOne({ + raw: true, + where: { + leaderId: userId, + }, + attributes: ["category"], + }); + + return team?.category; +}; + export const setTeam = async (team, body) => { Object.keys(body).forEach((field) => { team[field] = body[field]; diff --git a/src/models/game.model.ts b/src/models/game.model.ts index 161c4aa..98e1a13 100644 --- a/src/models/game.model.ts +++ b/src/models/game.model.ts @@ -31,6 +31,7 @@ class Game extends Model, InferCreationAttributes> { status: { type: new DataTypes.INTEGER(), allowNull: false, + defaultValue: 0, }, }, { diff --git a/src/routes/games.route.ts b/src/routes/games.route.ts index d0a0d2e..4a9b2d7 100644 --- a/src/routes/games.route.ts +++ b/src/routes/games.route.ts @@ -7,7 +7,10 @@ import { fetchGamesByRegion, fetchTeamsAvailById, fetchGameDetail, + addGame, } from "../controllers/games.controller"; +import { createGame } from "../schemas/game.schema"; +import { validateBody } from "../middlewares/validate.middleware"; export const gamesRouter = express.Router(); @@ -24,3 +27,6 @@ gamesRouter.get("/apply-avail", asyncHandler(fetchTeamsAvailById)); // 연습경기 모집글 상세 조회 gamesRouter.get("/:gameId", asyncHandler(fetchGameDetail)); + +// 연습경기 모집글 작성/수정 +gamesRouter.post("/", validateBody(createGame), asyncHandler(addGame)); diff --git a/src/schemas/game.schema.ts b/src/schemas/game.schema.ts new file mode 100644 index 0000000..f025245 --- /dev/null +++ b/src/schemas/game.schema.ts @@ -0,0 +1,29 @@ +import { TypeOf, object, z } from "zod"; + +const fields = { + hostTeamId: z.number().int().optional(), + // 본인이 리더인 팀이 하나인 경우는 바로 들어가겠지만, 여러 개이면 선택하여 들어갈 듯..? + // applyTeamId: z.number().int().min(1), + // opposingTeamId: z.number().int().min(1), + gameTime: z.preprocess((arg) => { + if (typeof arg == "string") { + return new Date(arg); + } + return arg; + }, z.date()), + + // category: z.string(), + description: z.string(), + // status: z.number().int().min(0), +}; + +export const createGame = object({ + ...fields, +}); + +export const updateGame = object({ + memberIdsToDelete: z.optional(z.array(z.number().int())), +}); + +export type CreateGameSchema = TypeOf; +export type updateGameSchema = TypeOf; diff --git a/src/services/games.service.ts b/src/services/games.service.ts index b3a44b8..3846671 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -1,13 +1,20 @@ // import { BaseError } from "../config/error"; // import { status } from "../config/response.status"; +import db from "../models"; import { findGamesByDate, findGamesByGender, findGamesByLevel, findGamesByRegion, getGameDetail, + insertGame, } from "../daos/games.dao"; -import { getTeamDetailforGuesting } from "../daos/team.dao"; +import { + getTeamDetailforGuesting, + getTeamIdByLeaderId, + getTeamCategoryByLeaderId, + getTeamById, +} from "../daos/team.dao"; import { findMemberInfoByTeamId, findMemberInfoWithoutLeaderByTeamId, @@ -15,6 +22,7 @@ import { } from "../daos/member.dao"; import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; import { readGameResponseDTO, readGameDetailResponseDTO } from "../dtos/games.dto"; +import { CreateGameSchema } from "../schemas/game.schema"; export const readGamesByDate = async (query) => { const games = await findGamesByDate(query.date, query.category); @@ -56,3 +64,16 @@ export const readGameDetail = async (params) => { const memberInfo = await findMemberInfoWithoutLeaderByTeamId(gameDetail.hostTeamId, userInfoAttributes); return readGameDetailResponseDTO(gameDetail, teamDetail, leaderInfo, memberInfo); }; + +export const createGame = async (userId, body: CreateGameSchema) => { + const hostTeamId = await getTeamIdByLeaderId(userId); + const category = await getTeamCategoryByLeaderId(userId); + + const team = await getTeamById(hostTeamId, userId); + if (!team) { + // team 메뉴로 이동 + } + + await insertGame(hostTeamId, body, category); + return; +}; From 6f5783e537d8279529add2e695fa38dc95d00180 Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Tue, 30 Jan 2024 15:33:23 +0900 Subject: [PATCH 037/147] =?UTF-8?q?[FEAT]=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?API=20=EA=B5=AC=ED=98=84=20(#54)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #53 * Feat: 컨트롤러 함수 추가 #53 * Feat: 스키마 추가 #53 * Feat: game response error 추가 #53 * Feat: 서비스 함수 추가 #53 * Feat: DAO 함수 추가 #53 --- src/config/response.status.ts | 8 ++++++++ src/controllers/games.controller.ts | 6 ++++++ src/daos/games.dao.ts | 18 ++++++++++++++++++ src/routes/games.route.ts | 4 +++- src/schemas/game.schema.ts | 5 +++-- src/services/games.service.ts | 18 +++++++++++++++--- 6 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 42dc99a..ae8aa73 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -113,6 +113,14 @@ export const status: { [key: string]: Status } = { message: "게스팅을 찾을 수 없습니다.", }, + // game error + GAME_NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "GAME001", + message: "게임을 찾을 수 없습니다.", + }, + //post err POST_NOT_FOUND: { status: StatusCodes.NOT_FOUND, diff --git a/src/controllers/games.controller.ts b/src/controllers/games.controller.ts index acd0a4f..262beb4 100644 --- a/src/controllers/games.controller.ts +++ b/src/controllers/games.controller.ts @@ -8,6 +8,7 @@ import { readGamesByRegion, readGameDetail, createGame, + updateGame, } from "../services/games.service"; import { readTeamAvailPreviewById } from "../services/teams.service"; @@ -40,3 +41,8 @@ export const addGame = async (req, res, next) => { return res.send(response(status.SUCCESS, await createGame(req.user.id, req.body))); // return res.send(response(status.SUCCESS, await createGame(2, req.body))); //for testing }; + +export const modifyGame = async (req, res, next) => { + return res.send(response(status.SUCCESS, await updateGame(req.user.id, req.params, req.body))); + // return res.send(response(status.SUCCESS, await updateGame(2, req.params, req.body))); //for testing +}; diff --git a/src/daos/games.dao.ts b/src/daos/games.dao.ts index 858a632..a4ca523 100644 --- a/src/daos/games.dao.ts +++ b/src/daos/games.dao.ts @@ -2,6 +2,7 @@ import db from "../models"; import { Sequelize } from "sequelize"; import { getStatusById } from "../constants/status.constant"; import { CreateGameSchema } from "../schemas/game.schema"; +import { getTeamIdByLeaderId } from "../daos/team.dao"; export const findGamesByDate = async (date, category) => { const games = await db.Game.findAll({ @@ -120,3 +121,20 @@ export const insertGame = async (hostTeamId, data: CreateGameSchema, category) = description: data.description, }); }; + +export const setGame = async (game, body) => { + Object.keys(body).forEach((field) => { + game[field] = body[field]; + }); + await game.save(); +}; + +export const getGameById = async (gameId, userId) => { + const hostTeamId = await getTeamIdByLeaderId(userId); + return await db.Game.findOne({ + where: { + id: gameId, + hostTeamId: hostTeamId, + }, + }); +}; diff --git a/src/routes/games.route.ts b/src/routes/games.route.ts index 4a9b2d7..7c44fe8 100644 --- a/src/routes/games.route.ts +++ b/src/routes/games.route.ts @@ -8,8 +8,9 @@ import { fetchTeamsAvailById, fetchGameDetail, addGame, + modifyGame, } from "../controllers/games.controller"; -import { createGame } from "../schemas/game.schema"; +import { createGame, updateGame } from "../schemas/game.schema"; import { validateBody } from "../middlewares/validate.middleware"; export const gamesRouter = express.Router(); @@ -30,3 +31,4 @@ gamesRouter.get("/:gameId", asyncHandler(fetchGameDetail)); // 연습경기 모집글 작성/수정 gamesRouter.post("/", validateBody(createGame), asyncHandler(addGame)); +gamesRouter.put("/:gameId", validateBody(updateGame), asyncHandler(modifyGame)); diff --git a/src/schemas/game.schema.ts b/src/schemas/game.schema.ts index f025245..fd4cc0e 100644 --- a/src/schemas/game.schema.ts +++ b/src/schemas/game.schema.ts @@ -22,8 +22,9 @@ export const createGame = object({ }); export const updateGame = object({ - memberIdsToDelete: z.optional(z.array(z.number().int())), + // memberIdsToDelete: z.optional(z.array(z.number().int())), + ...fields, }); export type CreateGameSchema = TypeOf; -export type updateGameSchema = TypeOf; +export type UpdateGameSchema = TypeOf; diff --git a/src/services/games.service.ts b/src/services/games.service.ts index 3846671..ad8a612 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -1,5 +1,5 @@ -// import { BaseError } from "../config/error"; -// import { status } from "../config/response.status"; +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; import db from "../models"; import { findGamesByDate, @@ -8,6 +8,7 @@ import { findGamesByRegion, getGameDetail, insertGame, + setGame, } from "../daos/games.dao"; import { getTeamDetailforGuesting, @@ -21,8 +22,9 @@ import { getMemberCountByTeamId, } from "../daos/member.dao"; import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; +import { getGameById } from "../daos/games.dao"; import { readGameResponseDTO, readGameDetailResponseDTO } from "../dtos/games.dto"; -import { CreateGameSchema } from "../schemas/game.schema"; +import { CreateGameSchema, UpdateGameSchema } from "../schemas/game.schema"; export const readGamesByDate = async (query) => { const games = await findGamesByDate(query.date, query.category); @@ -77,3 +79,13 @@ export const createGame = async (userId, body: CreateGameSchema) => { await insertGame(hostTeamId, body, category); return; }; + +export const updateGame = async (userId, params, body: UpdateGameSchema) => { + const gameId = params.gameId; + const game = await getGameById(gameId, userId); + if (!game) { + throw new BaseError(status.GAME_NOT_FOUND); + } + await setGame(game, body); + return; +}; From 9ac744b062703ab8c835c7dc1a9c24c1b28f018c Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Tue, 30 Jan 2024 17:07:37 +0900 Subject: [PATCH 038/147] =?UTF-8?q?[FEAT]=20=EC=A2=85=EB=AA=A9=EB=B3=84=20?= =?UTF-8?q?=EC=84=A0=EC=88=98=20=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?API=20=EA=B5=AC=ED=98=84=20(#55)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #49 * Feat: body, query, params 검증하는 validate 함수 생성 #49 * Feat: profile 테이블 생성, user 테이블 수정 #49 * Refactor: 성별, 연령대, 종목 constant 수정 및 enum 파일 분리 #49 * Feat: update user profile schema 생성 #49 * Feat: team 테이블의 gender, ageGroup 필드 타입 변경 #49 * Style: enum 멤버의 이름을 첫 글자만 대문자로 변경 #49 * Feat: 컨트롤러 함수 추가 #49 * Style: enum 멤버의 이름 변경에 따른 코드 수정 #49 * Style: constant 함수의 이름 변경에 따른 코드 수정 #49 * Feat: 서비스 함수와 DAO 함수 추가 #49 * Style: constant 함수의 이름 변경에 따른 코드 수정 #49 --- src/app.ts | 2 + src/config/response.status.ts | 8 ++++ src/constants/age-group.constant.ts | 24 +++++------- src/constants/category.constant.ts | 17 +++++++++ src/constants/gender.constant.ts | 22 ++++++----- src/controllers/users.controller.ts | 8 ++++ src/daos/profile.dao.ts | 36 +++++++++++++++++ src/daos/user.dao.ts | 12 ++++++ src/dtos/games.dto.ts | 8 ++-- src/dtos/guests.dto.ts | 14 +++---- src/middlewares/validate.middleware.ts | 19 +++++++++ src/models/profile.model.ts | 53 ++++++++++++++++++++++++++ src/models/team.model.ts | 4 +- src/models/user.model.ts | 13 +++++++ src/routes/users.route.ts | 10 +++++ src/schemas/fields.ts | 46 ++++++++++++++++++++++ src/schemas/team.schema.ts | 6 +-- src/schemas/user-profile.schema.ts | 44 +++++++++++++++++++++ src/services/auth.service.ts | 4 +- src/services/users.service.ts | 28 +++++++++++++- src/types/age-group.enum.ts | 7 ++++ src/types/category.enum.ts | 11 ++++++ src/types/gender.enum.ts | 5 +++ src/types/provider.enum.ts | 6 +-- 24 files changed, 359 insertions(+), 48 deletions(-) create mode 100644 src/constants/category.constant.ts create mode 100644 src/controllers/users.controller.ts create mode 100644 src/daos/profile.dao.ts create mode 100644 src/models/profile.model.ts create mode 100644 src/routes/users.route.ts create mode 100644 src/schemas/fields.ts create mode 100644 src/schemas/user-profile.schema.ts create mode 100644 src/types/age-group.enum.ts create mode 100644 src/types/category.enum.ts create mode 100644 src/types/gender.enum.ts diff --git a/src/app.ts b/src/app.ts index ef48196..163fae9 100644 --- a/src/app.ts +++ b/src/app.ts @@ -15,6 +15,7 @@ import { membersRouter } from "./routes/members.route"; import { gamesRouter } from "./routes/games.route"; import { guestsRouter } from "./routes/guests.route"; import { postsRouter } from "./routes/posts.route"; +import { usersRouter } from "./routes/users.route"; const app = express(); @@ -38,6 +39,7 @@ app.use("/members", membersRouter); app.use("/games", gamesRouter); app.use("/guests", guestsRouter); app.use("/posts", postsRouter); +app.use("/users", usersRouter); app.use((req: Request, res: Response, next: NextFunction) => { const err = new BaseError(status.NOT_FOUND); diff --git a/src/config/response.status.ts b/src/config/response.status.ts index ae8aa73..5cd0c27 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -128,4 +128,12 @@ export const status: { [key: string]: Status } = { code: "POST001", message: "요청한 글을 찾을 수 없습니다.", }, + + //user err + USER_NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "USER001", + message: "요청한 유저를 찾을 수 없습니다.", + }, }; diff --git a/src/constants/age-group.constant.ts b/src/constants/age-group.constant.ts index 14318e2..efdc905 100644 --- a/src/constants/age-group.constant.ts +++ b/src/constants/age-group.constant.ts @@ -1,19 +1,13 @@ -type AgeGroup = { - name: string; -}; - -const AgeGroups: { [key: number]: AgeGroup } = { - 1: { name: "10대" }, - 2: { name: "20대" }, - 3: { name: "30대" }, - 4: { name: "40대" }, - 5: { name: "50대 이상~" }, -}; +import { AgeGroup } from "../types/age-group.enum"; -export const getAgeGroupsLength = (): number => { - return Object.keys(AgeGroups).length; +const AgeGroups: Record = { + [AgeGroup.Teenagers]: "10대", + [AgeGroup.Twenties]: "20대", + [AgeGroup.Thirties]: "30대", + [AgeGroup.Forties]: "40대", + [AgeGroup.FiftiesAndAbove]: "50대 이상~", }; -export const getAgeGroupById = (id: number): string | undefined => { - return AgeGroups[id].name; +export const getAgeGroup = (key: AgeGroup): string | undefined => { + return AgeGroups[key]; }; diff --git a/src/constants/category.constant.ts b/src/constants/category.constant.ts new file mode 100644 index 0000000..6605651 --- /dev/null +++ b/src/constants/category.constant.ts @@ -0,0 +1,17 @@ +import { Category } from "../types/category.enum"; + +const Categories: Record = { + [Category.Basketball]: "농구", + [Category.Baseball]: "야구", + [Category.Tennis]: "테니스", + [Category.Soccer]: "축구", + [Category.Futsal]: "풋살", + [Category.Volleyball]: "배구", + [Category.Bowling]: "볼링", + [Category.Badminton]: "배드민턴", + [Category.TableTennis]: "탁구", +}; + +export const getCategory = (key: Category): string | undefined => { + return Categories[key]; +}; diff --git a/src/constants/gender.constant.ts b/src/constants/gender.constant.ts index ea550ba..7803951 100644 --- a/src/constants/gender.constant.ts +++ b/src/constants/gender.constant.ts @@ -1,17 +1,19 @@ -type Gender = { - name: string; +import { Gender } from "../types/gender.enum"; + +const Genders: Record, string> = { + [Gender.Female]: "여성", + [Gender.Male]: "남성", }; -const Genders: { [key: number]: Gender } = { - 1: { name: "여성" }, - 2: { name: "남성" }, - 3: { name: "혼성" }, +const TeamGenders: Record = { + ...Genders, + [Gender.Mixed]: "혼성", }; -export const getGendersLength = (): number => { - return Object.keys(Genders).length; +export const getGender = (key: Exclude): string | undefined => { + return Genders[key]; }; -export const getGenderById = (id: number): string | undefined => { - return Genders[id].name; +export const getTeamGender = (key: Gender): string | undefined => { + return TeamGenders[key]; }; diff --git a/src/controllers/users.controller.ts b/src/controllers/users.controller.ts new file mode 100644 index 0000000..462a67f --- /dev/null +++ b/src/controllers/users.controller.ts @@ -0,0 +1,8 @@ +import { Response } from "express"; +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { updateUserProfile } from "../services/users.service"; + +export const modifyUserProfile = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await updateUserProfile(req.user.id, req.params, req.body))); +}; diff --git a/src/daos/profile.dao.ts b/src/daos/profile.dao.ts new file mode 100644 index 0000000..8789b15 --- /dev/null +++ b/src/daos/profile.dao.ts @@ -0,0 +1,36 @@ +import db from "../models"; +import { defaultLevel } from "../constants/level.constant"; +import { CategoryProfile } from "../schemas/user-profile.schema"; +import { Category } from "../types/category.enum"; + +export const getUserProfile = async (userId: number, category: Category) => { + return await db.Profile.findOne({ + raw: true, + where: { + userId, + category, + }, + attributes: ["id"], + }); +}; + +export const insertCategoryProfile = async (userId: number, category: Category, categoryProfile: CategoryProfile) => { + await db.Profile.create({ + userId, + category, + skillLevel: defaultLevel, + mannerLevel: defaultLevel, + ...categoryProfile, + }); +}; + +export const setCategoryProfile = async (id: number, categoryProfile: CategoryProfile) => { + await db.Profile.update( + { ...categoryProfile }, + { + where: { + id, + }, + }, + ); +}; diff --git a/src/daos/user.dao.ts b/src/daos/user.dao.ts index b8b8042..60d513a 100644 --- a/src/daos/user.dao.ts +++ b/src/daos/user.dao.ts @@ -1,4 +1,5 @@ import db from "../models"; +import { CommonProfile } from "../schemas/user-profile.schema"; export const getUserByProviderId = async (provider, providerId) => { return await db.User.findOne({ @@ -62,3 +63,14 @@ export const getUserInfoById = async (id) => { export const userInfoAttributes = () => { return ["nickname"]; //TODO: add height, weight, positon }; + +export const setCommonProfile = async (userId: number, commonProfile: CommonProfile) => { + await db.User.update( + { ...commonProfile }, + { + where: { + id: userId, + }, + }, + ); +}; diff --git a/src/dtos/games.dto.ts b/src/dtos/games.dto.ts index e0a394c..dc4bdc3 100644 --- a/src/dtos/games.dto.ts +++ b/src/dtos/games.dto.ts @@ -1,5 +1,5 @@ -import { getAgeGroupById } from "../constants/age-group.constant"; -import { getGenderById } from "../constants/gender.constant"; +import { getAgeGroup } from "../constants/age-group.constant"; +import { getGender } from "../constants/gender.constant"; import { getLevelById } from "../constants/level.constant"; export const readGameResponseDTO = (games) => { @@ -7,9 +7,9 @@ export const readGameResponseDTO = (games) => { gameTime: game.gameTime, teamName: game["HostTeam.name"], teamRegion: game["HostTeam.region"], - teamGender: getGenderById(game["HostTeam.gender"]), + teamGender: getGender(game["HostTeam.gender"]), memberCount: game.memberCount, - teamAgeGroup: getAgeGroupById(game["HostTeam.ageGroup"]), + teamAgeGroup: getAgeGroup(game["HostTeam.ageGroup"]), teamSkillLevel: getLevelById(game["HostTeam.skillLevel"]), status: game.status, })); diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index 3912859..f9f5ae1 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -1,5 +1,5 @@ -import { getAgeGroupById } from "../constants/age-group.constant"; -import { getGenderById } from "../constants/gender.constant"; +import { getAgeGroup } from "../constants/age-group.constant"; +import { getGender } from "../constants/gender.constant"; import { getLevelById } from "../constants/level.constant"; export const readGuestingResponseDTO = (guestings) => { @@ -7,9 +7,9 @@ export const readGuestingResponseDTO = (guestings) => { gameTime: guesting.gameTime, teamName: guesting["Team.name"], teamRegion: guesting["Team.region"], - teamGender: getGenderById(guesting["Team.gender"]), + teamGender: getGender(guesting["Team.gender"]), memberCount: guesting.memberCount, - teamAgeGroup: getAgeGroupById(guesting["Team.ageGroup"]), + teamAgeGroup: getAgeGroup(guesting["Team.ageGroup"]), teamSkillLevel: getLevelById(guesting["Team.skillLevel"]), recruitCount: guesting.recruitCount, })); @@ -29,10 +29,10 @@ export const readGuestingDetailResponseDTO = (guestingDetail, TeamDetail, leader description: TeamDetail.description, gusting_info: { gameTime: guestingDetail.gameTime, - gender: TeamDetail.gender, - ageGroup: TeamDetail.ageGroup, + gender: getGender(TeamDetail.gender), + ageGroup: getAgeGroup(TeamDetail.ageGroup), gymName: TeamDetail.gymName, - skillLevel: TeamDetail.skillLevel, + skillLevel: getLevelById(TeamDetail.skillLevel), }, member_info: { leader: leaderInfo, diff --git a/src/middlewares/validate.middleware.ts b/src/middlewares/validate.middleware.ts index 4896bae..043cbfa 100644 --- a/src/middlewares/validate.middleware.ts +++ b/src/middlewares/validate.middleware.ts @@ -17,3 +17,22 @@ export const validateBody = (schema: AnyZodObject) => (req, res, next: NextFunct } } }; + +export const validate = (schema: AnyZodObject) => (req, res, next: NextFunction) => { + try { + schema.parse({ + body: req.body, + query: req.query, + params: req.params, + }); + next(); + } catch (err: any) { + if (err instanceof ZodError) { + const detail = err.errors.map((err) => ({ + field: err.path.join(": "), + description: err.message, + })); + throw new BaseError(status.REQUEST_VALIDATION_ERROR, detail); + } + } +}; diff --git a/src/models/profile.model.ts b/src/models/profile.model.ts new file mode 100644 index 0000000..7aca1ae --- /dev/null +++ b/src/models/profile.model.ts @@ -0,0 +1,53 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class Profile extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + Profile.init( + { + userId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + category: { + type: new DataTypes.STRING(15), + allowNull: false, + }, + skillLevel: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + mannerLevel: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + region: { + type: new DataTypes.STRING(200), + allowNull: true, + }, + position: { + type: new DataTypes.STRING(50), + allowNull: true, + }, + description: { + type: new DataTypes.STRING(400), + allowNull: true, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "Profile", + tableName: "profile", + paranoid: false, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.Profile.belongsTo(db.User, { foreignKey: "user_id" }); + } +} + +module.exports = Profile; diff --git a/src/models/team.model.ts b/src/models/team.model.ts index e68b7e9..ca2d2f3 100644 --- a/src/models/team.model.ts +++ b/src/models/team.model.ts @@ -17,11 +17,11 @@ class Team extends Model, InferCreationAttributes> { allowNull: true, }, gender: { - type: new DataTypes.INTEGER(), + type: new DataTypes.STRING(1), allowNull: false, }, ageGroup: { - type: new DataTypes.INTEGER(), + type: new DataTypes.STRING(10), allowNull: false, }, region: { diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 7096d39..b02af96 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -20,6 +20,18 @@ class User extends Model, InferCreationAttributes> { type: new DataTypes.STRING(150), allowNull: true, }, + gender: { + type: new DataTypes.STRING(1), + allowNull: true, + }, + ageGroup: { + type: new DataTypes.STRING(10), + allowNull: true, + }, + height: { + type: new DataTypes.INTEGER(), + allowNull: true, + }, }, { sequelize, @@ -34,6 +46,7 @@ class User extends Model, InferCreationAttributes> { ); } static associate(db) { + db.User.hasMany(db.Profile, { foreignKey: "user_id" }); db.User.hasMany(db.Team, { foreignKey: "leader_id" }); db.User.hasMany(db.Member, { foreignKey: "user_id" }); db.User.hasMany(db.GuestUser, { foreignKey: "user_id" }); diff --git a/src/routes/users.route.ts b/src/routes/users.route.ts new file mode 100644 index 0000000..fc17892 --- /dev/null +++ b/src/routes/users.route.ts @@ -0,0 +1,10 @@ +import express from "express"; +import asyncHandler from "express-async-handler"; +import { verifyUser } from "../middlewares/auth.middleware"; +import { modifyUserProfile } from "../controllers/users.controller"; +import { updateUserProfileSchema } from "../schemas/user-profile.schema"; +import { validate } from "../middlewares/validate.middleware"; + +export const usersRouter = express.Router(); + +usersRouter.put("/profiles/:category", verifyUser, validate(updateUserProfileSchema), asyncHandler(modifyUserProfile)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts new file mode 100644 index 0000000..79b72fc --- /dev/null +++ b/src/schemas/fields.ts @@ -0,0 +1,46 @@ +import { z } from "zod"; +import { Gender } from "../types/gender.enum"; +import { Category } from "../types/category.enum"; +import { AgeGroup } from "../types/age-group.enum"; + +export const nicknameField = { nickname: z.string().max(10) }; + +export const descriptionField = { description: z.optional(z.string().max(400)) }; + +export const genderFieldInUser = { gender: z.optional(z.enum([Gender.Female, Gender.Male])) }; + +export const genderFieldInTeam = { gender: z.enum([Gender.Female, Gender.Male, Gender.Mixed]) }; + +const ageGroup = z.enum([ + AgeGroup.Teenagers, + AgeGroup.Twenties, + AgeGroup.Thirties, + AgeGroup.Forties, + AgeGroup.FiftiesAndAbove, +]); + +export const ageGroupFieldInUser = { ageGroup: z.optional(ageGroup) }; + +export const ageGroupFieldInTeam = { ageGroup: ageGroup }; + +export const heightField = { height: z.optional(z.number().int()) }; + +export const regionFieldInProfile = { region: z.optional(z.string()) }; + +export const regionFieldInTeam = { region: z.string() }; + +export const positionField = { position: z.optional(z.string()) }; //TODO: 추후 enum으로 변경 + +export const categoryField = { + category: z.enum([ + Category.Basketball, + Category.Baseball, + Category.Tennis, + Category.Soccer, + Category.Futsal, + Category.Volleyball, + Category.Bowling, + Category.Badminton, + Category.TableTennis, + ]), +}; diff --git a/src/schemas/team.schema.ts b/src/schemas/team.schema.ts index 1dbcf3e..a6d0048 100644 --- a/src/schemas/team.schema.ts +++ b/src/schemas/team.schema.ts @@ -1,13 +1,11 @@ import { TypeOf, object, z } from "zod"; -import { getGendersLength } from "../constants/gender.constant"; -import { getAgeGroupsLength } from "../constants/age-group.constant"; const fieldsWithoutCategory = { logo: z.optional(z.string()), name: z.string().max(20), description: z.optional(z.string()), - gender: z.number().int().min(1).max(getGendersLength()), - ageGroup: z.number().int().min(1).max(getAgeGroupsLength()), + gender: z.number().int().min(1), //.max(getGendersLength()), + ageGroup: z.number().int().min(1), //.max(getAgeGroupsLength()), region: z.string(), gymName: z.string(), }; diff --git a/src/schemas/user-profile.schema.ts b/src/schemas/user-profile.schema.ts new file mode 100644 index 0000000..5291989 --- /dev/null +++ b/src/schemas/user-profile.schema.ts @@ -0,0 +1,44 @@ +import { TypeOf, object } from "zod"; +import { + ageGroupFieldInUser, + categoryField, + descriptionField, + genderFieldInUser, + nicknameField, + positionField, + regionFieldInProfile, + heightField, +} from "./fields"; + +const commonFields = { + ...nicknameField, + ...genderFieldInUser, + ...ageGroupFieldInUser, + ...heightField, +}; + +const categoryFields = { + ...descriptionField, + ...regionFieldInProfile, + ...positionField, +}; + +const body = object({ + ...commonFields, + ...categoryFields, +}); + +const commonProfile = object(commonFields); + +const categoryProfile = object(categoryFields); + +export const updateUserProfileSchema = object({ + params: object({ + ...categoryField, + }), + body: body, +}); + +export type UpdateUserProfileBody = TypeOf; +export type CommonProfile = TypeOf; +export type CategoryProfile = TypeOf; diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index 2540310..db44f47 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -55,7 +55,7 @@ const getKakaoUserInfo = async (accessToken: string): Promise => { }, }); return { - provider: Provider.KAKAO, + provider: Provider.Kakao, providerId: response.data.id, nickname: response.data.kakao_account.profile.nickname, }; @@ -90,7 +90,7 @@ const getNaverUserInfo = async (accessToken: string): Promise => { }, }); return { - provider: Provider.NAVER, + provider: Provider.Naver, providerId: response.data.response.id, nickname: response.data.response.name, }; diff --git a/src/services/users.service.ts b/src/services/users.service.ts index 186e1cc..fd13e87 100644 --- a/src/services/users.service.ts +++ b/src/services/users.service.ts @@ -1,4 +1,9 @@ -import { getUserByProviderId, insertUser, setRefreshToken } from "../daos/user.dao"; +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { getUserProfile, insertCategoryProfile, setCategoryProfile } from "../daos/profile.dao"; +import { getUserById, getUserByProviderId, insertUser, setCommonProfile, setRefreshToken } from "../daos/user.dao"; +import { UpdateUserProfileBody, CategoryProfile } from "../schemas/user-profile.schema"; +import { Category } from "../types/category.enum"; import { Payload } from "../types/payload.interface"; import { UserInfo } from "../types/user-info.interface"; @@ -17,3 +22,24 @@ export const updateRefreshToken = async (refreshToken: string, userId: number) = export const deleteRefreshToken = async (userId: number) => { await setRefreshToken(null, userId); }; + +export const updateUserProfile = async (userId, params, body: UpdateUserProfileBody) => { + const user = await getUserById(userId); + if (!user) { + throw new BaseError(status.USER_NOT_FOUND); + } + + const { description, region, position, ...commonProfile } = body; + await setCommonProfile(userId, commonProfile); + await createOrUpdateCategoryProfile(userId, params.category, { description, region, position }); + return; +}; + +const createOrUpdateCategoryProfile = async (userId: number, category: Category, categoryProfile: CategoryProfile) => { + const profile = await getUserProfile(userId, category); + if (!profile) { + await insertCategoryProfile(userId, category, categoryProfile); + } else { + await setCategoryProfile(profile.id, categoryProfile); + } +}; diff --git a/src/types/age-group.enum.ts b/src/types/age-group.enum.ts new file mode 100644 index 0000000..26a4a29 --- /dev/null +++ b/src/types/age-group.enum.ts @@ -0,0 +1,7 @@ +export enum AgeGroup { + Teenagers = "-10", + Twenties = "20-29", + Thirties = "30-39", + Forties = "40-49", + FiftiesAndAbove = "50-", +} diff --git a/src/types/category.enum.ts b/src/types/category.enum.ts new file mode 100644 index 0000000..2544aa4 --- /dev/null +++ b/src/types/category.enum.ts @@ -0,0 +1,11 @@ +export enum Category { + Basketball = "basketball", + Baseball = "baseball", + Tennis = "tennis", + Soccer = "soccer", + Futsal = "futsal", + Volleyball = "volleyball", + Bowling = "bowling", + Badminton = "badminton", + TableTennis = "table-tennis", +} diff --git a/src/types/gender.enum.ts b/src/types/gender.enum.ts new file mode 100644 index 0000000..ef747d7 --- /dev/null +++ b/src/types/gender.enum.ts @@ -0,0 +1,5 @@ +export enum Gender { + Female = "F", + Male = "M", + Mixed = "MX", +} diff --git a/src/types/provider.enum.ts b/src/types/provider.enum.ts index 207606a..3894318 100644 --- a/src/types/provider.enum.ts +++ b/src/types/provider.enum.ts @@ -1,5 +1,5 @@ export enum Provider { - GOOGLE = "google", - KAKAO = "kakao", - NAVER = "naver", + Google = "google", + Kakao = "kakao", + Naver = "naver", } From c8392076ba01b78ec4dc02fe4ae4cb41d1afe935 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Tue, 30 Jan 2024 17:32:57 +0900 Subject: [PATCH 039/147] =?UTF-8?q?[FEAT]=20=EC=A2=85=EB=AA=A9=EB=B3=84=20?= =?UTF-8?q?=EC=84=A0=EC=88=98=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?API=20=EA=B5=AC=ED=98=84=20(#42)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: profile 테이블 생성 #34 * Feat: 라우트 추가 #34 * Feat: validateParams 함수 생성 #34 * Feat: 컨트롤러 함수 추가 #34 * Feat: 서비스 함수 추가 #34 * Feat: DAO 함수 추가 #34 * Feat: DTO 함수 추가 #34 --- src/constants/age-group.constant.ts | 2 +- src/constants/level.constant.ts | 2 +- src/controllers/users.controller.ts | 6 ++++++ src/daos/user.dao.ts | 20 ++++++++++++++++++++ src/dtos/users.dto.ts | 16 ++++++++++++++++ src/routes/users.route.ts | 12 ++++++++++-- src/schemas/team.schema.ts | 4 ++++ src/services/users.service.ts | 7 +++++++ 8 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 src/dtos/users.dto.ts diff --git a/src/constants/age-group.constant.ts b/src/constants/age-group.constant.ts index efdc905..7bf35ce 100644 --- a/src/constants/age-group.constant.ts +++ b/src/constants/age-group.constant.ts @@ -10,4 +10,4 @@ const AgeGroups: Record = { export const getAgeGroup = (key: AgeGroup): string | undefined => { return AgeGroups[key]; -}; +}; \ No newline at end of file diff --git a/src/constants/level.constant.ts b/src/constants/level.constant.ts index f39a2bf..49849e9 100644 --- a/src/constants/level.constant.ts +++ b/src/constants/level.constant.ts @@ -12,5 +12,5 @@ const Levels: { [key: number]: Level } = { export const defaultLevel = 1; export const getLevelById = (id: number): string | undefined => { - return Levels[id].name; + return Levels[id]?.name; }; diff --git a/src/controllers/users.controller.ts b/src/controllers/users.controller.ts index 462a67f..38e69ff 100644 --- a/src/controllers/users.controller.ts +++ b/src/controllers/users.controller.ts @@ -1,8 +1,14 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; +import { readUserProfileByCategory } from "../services/users.service"; import { updateUserProfile } from "../services/users.service"; +export const fetchUserProfileByCategory = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readUserProfileByCategory(req.user.id, req.params))); +}; + export const modifyUserProfile = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await updateUserProfile(req.user.id, req.params, req.body))); }; + diff --git a/src/daos/user.dao.ts b/src/daos/user.dao.ts index 60d513a..30b401f 100644 --- a/src/daos/user.dao.ts +++ b/src/daos/user.dao.ts @@ -64,6 +64,26 @@ export const userInfoAttributes = () => { return ["nickname"]; //TODO: add height, weight, positon }; +export const getUserProfileByCategory = async (userId: number, category) => { + return await db.User.findOne({ + raw: true, + where: { + id: userId, + }, + include: [ + { + model: db.Profile, + where: { + category, + }, + required: false, + attributes: ["skillLevel", "mannerLevel", "region", "position", "description"], + }, + ], + attributes: ["nickname", "gender", "ageGroup"], + }); +}; + export const setCommonProfile = async (userId: number, commonProfile: CommonProfile) => { await db.User.update( { ...commonProfile }, diff --git a/src/dtos/users.dto.ts b/src/dtos/users.dto.ts new file mode 100644 index 0000000..7bdb94c --- /dev/null +++ b/src/dtos/users.dto.ts @@ -0,0 +1,16 @@ +import { getAgeGroupById } from "../constants/age-group.constant"; +import { getGenderById } from "../constants/gender.constant"; +import { getLevelById } from "../constants/level.constant"; + +export const readUserProfileByCategoryResponseDTO = (profile) => { + return { + nickname: profile.nickname, + skillLevel: profile["Profiles.skillLevel"] == null ? null : getLevelById(profile["Profiles.skillLevel"]), + mannerLevel: profile["Profiles.mannerLevel"] == null ? null : getLevelById(profile["Profiles.mannerLevel"]), + gender: profile.gender == null ? null : getGenderById(profile.gender), + ageGroup: profile.ageGroup == null ? null : getAgeGroupById(profile.ageGroup), + region: profile["Profiles.region"], + position: profile["Profiles.position"], + description: profile["Profiles.description"], + }; +}; diff --git a/src/routes/users.route.ts b/src/routes/users.route.ts index fc17892..264026e 100644 --- a/src/routes/users.route.ts +++ b/src/routes/users.route.ts @@ -1,10 +1,18 @@ import express from "express"; import asyncHandler from "express-async-handler"; +import { fetchUserProfileByCategory } from "../controllers/users.controller"; +import { validateParams } from "../middlewares/validate.middleware"; +import { category } from "../schemas/team.schema"; import { verifyUser } from "../middlewares/auth.middleware"; +import { validate } from "../middlewares/validate.middleware"; import { modifyUserProfile } from "../controllers/users.controller"; import { updateUserProfileSchema } from "../schemas/user-profile.schema"; -import { validate } from "../middlewares/validate.middleware"; export const usersRouter = express.Router(); -usersRouter.put("/profiles/:category", verifyUser, validate(updateUserProfileSchema), asyncHandler(modifyUserProfile)); +usersRouter.use(verifyUser); + +usersRouter.get("/profiles/:category", validateParams(category), asyncHandler(fetchUserProfileByCategory)); + +usersRouter.put("/profiles/:category", validate(updateUserProfileSchema), asyncHandler(modifyUserProfile)); + diff --git a/src/schemas/team.schema.ts b/src/schemas/team.schema.ts index a6d0048..783cc55 100644 --- a/src/schemas/team.schema.ts +++ b/src/schemas/team.schema.ts @@ -12,6 +12,10 @@ const fieldsWithoutCategory = { const categories = z.enum(["농구", "야구", "테니스", "축구", "풋살", "배구", "볼링", "배드민턴", "탁구"]); +export const category = object({ + category: categories, +}); + export const createTeam = object({ ...fieldsWithoutCategory, category: categories, diff --git a/src/services/users.service.ts b/src/services/users.service.ts index fd13e87..151559c 100644 --- a/src/services/users.service.ts +++ b/src/services/users.service.ts @@ -1,3 +1,5 @@ +import { getUserByProviderId, getUserProfileByCategory, insertUser, setRefreshToken } from "../daos/user.dao"; +import { readUserProfileByCategoryResponseDTO } from "../dtos/users.dto"; import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { getUserProfile, insertCategoryProfile, setCategoryProfile } from "../daos/profile.dao"; @@ -19,6 +21,11 @@ export const updateRefreshToken = async (refreshToken: string, userId: number) = await setRefreshToken(refreshToken, userId); }; +export const readUserProfileByCategory = async (userId, params) => { + const profile = await getUserProfileByCategory(userId, params.category); + return readUserProfileByCategoryResponseDTO(profile); +}; + export const deleteRefreshToken = async (userId: number) => { await setRefreshToken(null, userId); }; From 25920e998b2686b31fb83c29cfc903b60dc0a462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 30 Jan 2024 17:52:48 +0900 Subject: [PATCH 040/147] =?UTF-8?q?Fix:=20=EC=A2=85=EB=AA=A9=EB=B3=84=20?= =?UTF-8?q?=EC=84=A0=EC=88=98=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?API=20=EA=B5=AC=ED=98=84=20=EB=A8=B8=EC=A7=80=20=EA=B3=BC?= =?UTF-8?q?=EC=A0=95=EC=97=90=EC=84=9C=20=EC=A4=91=EB=B3=B5=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20#34?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/users.controller.ts | 1 - src/dtos/users.dto.ts | 8 ++++---- src/routes/users.route.ts | 10 +++------- src/schemas/user-profile.schema.ts | 6 ++++++ src/services/users.service.ts | 16 +++++++++++----- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/controllers/users.controller.ts b/src/controllers/users.controller.ts index 38e69ff..2dce0df 100644 --- a/src/controllers/users.controller.ts +++ b/src/controllers/users.controller.ts @@ -11,4 +11,3 @@ export const fetchUserProfileByCategory = async (req, res: Response, next) => { export const modifyUserProfile = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await updateUserProfile(req.user.id, req.params, req.body))); }; - diff --git a/src/dtos/users.dto.ts b/src/dtos/users.dto.ts index 7bdb94c..6510eca 100644 --- a/src/dtos/users.dto.ts +++ b/src/dtos/users.dto.ts @@ -1,5 +1,5 @@ -import { getAgeGroupById } from "../constants/age-group.constant"; -import { getGenderById } from "../constants/gender.constant"; +import { getAgeGroup } from "../constants/age-group.constant"; +import { getGender } from "../constants/gender.constant"; import { getLevelById } from "../constants/level.constant"; export const readUserProfileByCategoryResponseDTO = (profile) => { @@ -7,8 +7,8 @@ export const readUserProfileByCategoryResponseDTO = (profile) => { nickname: profile.nickname, skillLevel: profile["Profiles.skillLevel"] == null ? null : getLevelById(profile["Profiles.skillLevel"]), mannerLevel: profile["Profiles.mannerLevel"] == null ? null : getLevelById(profile["Profiles.mannerLevel"]), - gender: profile.gender == null ? null : getGenderById(profile.gender), - ageGroup: profile.ageGroup == null ? null : getAgeGroupById(profile.ageGroup), + gender: profile.gender == null ? null : getGender(profile.gender), + ageGroup: profile.ageGroup == null ? null : getAgeGroup(profile.ageGroup), region: profile["Profiles.region"], position: profile["Profiles.position"], description: profile["Profiles.description"], diff --git a/src/routes/users.route.ts b/src/routes/users.route.ts index 264026e..fd4b911 100644 --- a/src/routes/users.route.ts +++ b/src/routes/users.route.ts @@ -1,18 +1,14 @@ import express from "express"; import asyncHandler from "express-async-handler"; -import { fetchUserProfileByCategory } from "../controllers/users.controller"; -import { validateParams } from "../middlewares/validate.middleware"; -import { category } from "../schemas/team.schema"; import { verifyUser } from "../middlewares/auth.middleware"; import { validate } from "../middlewares/validate.middleware"; -import { modifyUserProfile } from "../controllers/users.controller"; -import { updateUserProfileSchema } from "../schemas/user-profile.schema"; +import { readUserProfileSchema, updateUserProfileSchema } from "../schemas/user-profile.schema"; +import { fetchUserProfileByCategory, modifyUserProfile } from "../controllers/users.controller"; export const usersRouter = express.Router(); usersRouter.use(verifyUser); -usersRouter.get("/profiles/:category", validateParams(category), asyncHandler(fetchUserProfileByCategory)); +usersRouter.get("/profiles/:category", validate(readUserProfileSchema), asyncHandler(fetchUserProfileByCategory)); usersRouter.put("/profiles/:category", validate(updateUserProfileSchema), asyncHandler(modifyUserProfile)); - diff --git a/src/schemas/user-profile.schema.ts b/src/schemas/user-profile.schema.ts index 5291989..319c700 100644 --- a/src/schemas/user-profile.schema.ts +++ b/src/schemas/user-profile.schema.ts @@ -39,6 +39,12 @@ export const updateUserProfileSchema = object({ body: body, }); +export const readUserProfileSchema = object({ + params: object({ + ...categoryField, + }), +}); + export type UpdateUserProfileBody = TypeOf; export type CommonProfile = TypeOf; export type CategoryProfile = TypeOf; diff --git a/src/services/users.service.ts b/src/services/users.service.ts index 151559c..8c4359a 100644 --- a/src/services/users.service.ts +++ b/src/services/users.service.ts @@ -1,13 +1,19 @@ -import { getUserByProviderId, getUserProfileByCategory, insertUser, setRefreshToken } from "../daos/user.dao"; -import { readUserProfileByCategoryResponseDTO } from "../dtos/users.dto"; import { BaseError } from "../config/error"; import { status } from "../config/response.status"; -import { getUserProfile, insertCategoryProfile, setCategoryProfile } from "../daos/profile.dao"; -import { getUserById, getUserByProviderId, insertUser, setCommonProfile, setRefreshToken } from "../daos/user.dao"; -import { UpdateUserProfileBody, CategoryProfile } from "../schemas/user-profile.schema"; import { Category } from "../types/category.enum"; import { Payload } from "../types/payload.interface"; import { UserInfo } from "../types/user-info.interface"; +import { UpdateUserProfileBody, CategoryProfile } from "../schemas/user-profile.schema"; +import { + getUserById, + getUserByProviderId, + getUserProfileByCategory, + insertUser, + setCommonProfile, + setRefreshToken, +} from "../daos/user.dao"; +import { getUserProfile, insertCategoryProfile, setCategoryProfile } from "../daos/profile.dao"; +import { readUserProfileByCategoryResponseDTO } from "../dtos/users.dto"; export const createOrReadUser = async (userInfo: UserInfo): Promise => { let user = await getUserByProviderId(userInfo.provider, userInfo.providerId); From ae2f15d10bdfd0f46dd63905bfe3845faf6fa28b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 30 Jan 2024 19:02:47 +0900 Subject: [PATCH 041/147] =?UTF-8?q?Feat:=20users=20=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=ED=8A=B8,=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC,=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4,=20DAO=20=ED=83=80=EC=9E=85=20=EC=A7=80?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20#34=20#49?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/users.controller.ts | 7 +++--- src/daos/user.dao.ts | 39 +++++++++++++++++++++++------ src/dtos/users.dto.ts | 25 ++++++++++++++---- src/routes/users.route.ts | 4 +-- src/services/guest.service.ts | 4 +-- src/services/users.service.ts | 13 ++++++---- 6 files changed, 66 insertions(+), 26 deletions(-) diff --git a/src/controllers/users.controller.ts b/src/controllers/users.controller.ts index 2dce0df..3d63587 100644 --- a/src/controllers/users.controller.ts +++ b/src/controllers/users.controller.ts @@ -1,11 +1,10 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readUserProfileByCategory } from "../services/users.service"; -import { updateUserProfile } from "../services/users.service"; +import { readUserProfile, updateUserProfile } from "../services/users.service"; -export const fetchUserProfileByCategory = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readUserProfileByCategory(req.user.id, req.params))); +export const fetchUserProfile = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readUserProfile(req.user.id, req.params))); }; export const modifyUserProfile = async (req, res: Response, next) => { diff --git a/src/daos/user.dao.ts b/src/daos/user.dao.ts index 30b401f..2ae74a1 100644 --- a/src/daos/user.dao.ts +++ b/src/daos/user.dao.ts @@ -1,7 +1,9 @@ import db from "../models"; import { CommonProfile } from "../schemas/user-profile.schema"; +import { Category } from "../types/category.enum"; +import { Provider } from "../types/provider.enum"; -export const getUserByProviderId = async (provider, providerId) => { +export const getUserByProviderId = async (provider: Provider, providerId: string) => { return await db.User.findOne({ raw: true, where: { @@ -11,7 +13,7 @@ export const getUserByProviderId = async (provider, providerId) => { }); }; -export const insertUser = async (provider, providerId, nickname) => { +export const insertUser = async (provider: Provider, providerId: string, nickname: string) => { return await db.User.create({ nickname, provider, @@ -41,30 +43,51 @@ export const getRefreshToken = async (userId: number) => { return user.refreshToken; }; -export const getUserById = async (id) => { +export const getUser = async (userId: number) => { return await db.User.findOne({ raw: true, where: { - id, + id: userId, }, }); }; -export const getUserInfoById = async (id) => { +export const getUserInfoByCategory = async (userId: number, category: Category) => { return await db.User.findOne({ raw: true, where: { - id, + id: userId, + }, + attributes: ["nickname", "height"], + include: [ + { + model: db.Profile, + where: { + category, + }, + required: false, + attributes: ["position"], + }, + ], + }); +}; + +export const getUserInfoById = async (userId: number) => { + throw new Error("더 이상 사용되지 않는 함수"); + return await db.User.findOne({ + raw: true, + where: { + id: userId, }, attributes: userInfoAttributes(), }); }; export const userInfoAttributes = () => { - return ["nickname"]; //TODO: add height, weight, positon + return ["nickname", "height"]; }; -export const getUserProfileByCategory = async (userId: number, category) => { +export const getUserProfileByCategory = async (userId: number, category: Category) => { return await db.User.findOne({ raw: true, where: { diff --git a/src/dtos/users.dto.ts b/src/dtos/users.dto.ts index 6510eca..f5a95a2 100644 --- a/src/dtos/users.dto.ts +++ b/src/dtos/users.dto.ts @@ -1,14 +1,29 @@ import { getAgeGroup } from "../constants/age-group.constant"; import { getGender } from "../constants/gender.constant"; import { getLevelById } from "../constants/level.constant"; +import { AgeGroup } from "../types/age-group.enum"; +import { Gender } from "../types/gender.enum"; -export const readUserProfileByCategoryResponseDTO = (profile) => { +interface ReadUserProfile { + nickname: string; + gender: Exclude | null; + ageGroup: AgeGroup | null; + Profiles: { + skillLevel: number; + mannerLevel: number; + region: string | null; + position: string | null; + description: string | null; + }; +} + +export const readUserProfileResponseDTO = (profile: ReadUserProfile) => { return { nickname: profile.nickname, - skillLevel: profile["Profiles.skillLevel"] == null ? null : getLevelById(profile["Profiles.skillLevel"]), - mannerLevel: profile["Profiles.mannerLevel"] == null ? null : getLevelById(profile["Profiles.mannerLevel"]), - gender: profile.gender == null ? null : getGender(profile.gender), - ageGroup: profile.ageGroup == null ? null : getAgeGroup(profile.ageGroup), + skillLevel: !profile["Profiles.skillLevel"] ? null : getLevelById(profile["Profiles.skillLevel"]), + mannerLevel: !profile["Profiles.mannerLevel"] ? null : getLevelById(profile["Profiles.mannerLevel"]), + gender: !profile.gender ? null : getGender(profile.gender), + ageGroup: !profile.ageGroup ? null : getAgeGroup(profile.ageGroup), region: profile["Profiles.region"], position: profile["Profiles.position"], description: profile["Profiles.description"], diff --git a/src/routes/users.route.ts b/src/routes/users.route.ts index fd4b911..8d1385c 100644 --- a/src/routes/users.route.ts +++ b/src/routes/users.route.ts @@ -3,12 +3,12 @@ import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; import { validate } from "../middlewares/validate.middleware"; import { readUserProfileSchema, updateUserProfileSchema } from "../schemas/user-profile.schema"; -import { fetchUserProfileByCategory, modifyUserProfile } from "../controllers/users.controller"; +import { fetchUserProfile, modifyUserProfile } from "../controllers/users.controller"; export const usersRouter = express.Router(); usersRouter.use(verifyUser); -usersRouter.get("/profiles/:category", validate(readUserProfileSchema), asyncHandler(fetchUserProfileByCategory)); +usersRouter.get("/profiles/:category", validate(readUserProfileSchema), asyncHandler(fetchUserProfile)); usersRouter.put("/profiles/:category", validate(updateUserProfileSchema), asyncHandler(modifyUserProfile)); diff --git a/src/services/guest.service.ts b/src/services/guest.service.ts index 8f5f83c..a6235c0 100644 --- a/src/services/guest.service.ts +++ b/src/services/guest.service.ts @@ -13,7 +13,7 @@ import { } from "../daos/guest.dao"; import { findMemberInfoByTeamId, getMemberCountByTeamId } from "../daos/member.dao"; import { getTeamById, getTeamDetailforGuesting, getTeamIdByLeaderId } from "../daos/team.dao"; -import { getUserById, getUserInfoById, userInfoAttributes } from "../daos/user.dao"; +import { getUser, getUserInfoById, userInfoAttributes } from "../daos/user.dao"; import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; import { CreateGuestingSchema, UpdateGuestingSchema } from "../schemas/guest.schema"; @@ -80,7 +80,7 @@ export const readDetailedGuesting = async (params) => { export const addGuestUser = async (userId, params) => { const guestingId = params.guestingId; - const user = await getUserById(userId); + const user = await getUser(userId); if (!user) { // user 정보 수정 API 연결 } diff --git a/src/services/users.service.ts b/src/services/users.service.ts index 8c4359a..78c969f 100644 --- a/src/services/users.service.ts +++ b/src/services/users.service.ts @@ -5,7 +5,7 @@ import { Payload } from "../types/payload.interface"; import { UserInfo } from "../types/user-info.interface"; import { UpdateUserProfileBody, CategoryProfile } from "../schemas/user-profile.schema"; import { - getUserById, + getUser, getUserByProviderId, getUserProfileByCategory, insertUser, @@ -13,7 +13,7 @@ import { setRefreshToken, } from "../daos/user.dao"; import { getUserProfile, insertCategoryProfile, setCategoryProfile } from "../daos/profile.dao"; -import { readUserProfileByCategoryResponseDTO } from "../dtos/users.dto"; +import { readUserProfileResponseDTO } from "../dtos/users.dto"; export const createOrReadUser = async (userInfo: UserInfo): Promise => { let user = await getUserByProviderId(userInfo.provider, userInfo.providerId); @@ -27,9 +27,12 @@ export const updateRefreshToken = async (refreshToken: string, userId: number) = await setRefreshToken(refreshToken, userId); }; -export const readUserProfileByCategory = async (userId, params) => { +export const readUserProfile = async (userId: number, params) => { const profile = await getUserProfileByCategory(userId, params.category); - return readUserProfileByCategoryResponseDTO(profile); + if (!profile) { + throw new BaseError(status.USER_NOT_FOUND); + } + return readUserProfileResponseDTO(profile); }; export const deleteRefreshToken = async (userId: number) => { @@ -37,7 +40,7 @@ export const deleteRefreshToken = async (userId: number) => { }; export const updateUserProfile = async (userId, params, body: UpdateUserProfileBody) => { - const user = await getUserById(userId); + const user = await getUser(userId); if (!user) { throw new BaseError(status.USER_NOT_FOUND); } From 65965e1ab935de477d63d1e6cd89a95e674010ee Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Tue, 30 Jan 2024 19:32:16 +0900 Subject: [PATCH 042/147] =?UTF-8?q?[FEAT]=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=20API=20=EA=B5=AC=ED=98=84=20(#57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FEAT) 게스트 모집글 상세조회 API 구현 #37 * FEAT) 게스트 모집글 작성 API 구현 #38 * FEAT) 게스트 모집글 작성 API 수정 #38 * FEAT) 게스트 모집글 수정 API 구현 #39 * FEAT) 게스트 신청 API 구현 #40 * Fix: where parameter "id" has invalid "undefined" value 에러 해결 #37 * Fix: Incorrect integer value: '[object Promise]' for column 'team_id' 에러 해결 #38 * fix: GUEST_NOT_FOUND 발생 해결 #39 * fix) 게스트 모집글 작성 API TEAM 유무 확인 추가 #38 * Fix) 게스트 신청 API 승인여부 컬럼 추가 * Chore: 사용하지 않는 라이브러리 제거 * Fix: 사용할 수 없는 함수 제거 --------- Co-authored-by: ByeonYujin124 Co-authored-by: ByeonYujin124 <52813483+ByeonYujin124@users.noreply.github.com> --- package-lock.json | 605 ---------------------------- package.json | 3 - src/controllers/guest.controller.ts | 6 +- src/daos/guest.dao.ts | 1 + src/dtos/games.dto.ts | 8 +- src/dtos/guests.dto.ts | 12 +- src/models/guest-user.model.ts | 4 + src/routes/guests.route.ts | 9 +- 8 files changed, 23 insertions(+), 625 deletions(-) diff --git a/package-lock.json b/package-lock.json index d567a02..33c4454 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,9 +33,6 @@ "eslint-config-prettier": "^9.1.0", "nodemon": "^3.0.2", "prettier": "^3.1.1", - "swagger-cli": "^4.0.4", - "swagger-jsdoc": "^6.2.8", - "swagger-ui-express": "^5.0.0", "ts-node": "^10.9.2", "typescript": "^5.3.3" } @@ -49,271 +46,6 @@ "node": ">=0.10.0" } }, - "node_modules/@apidevtools/json-schema-ref-parser": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.0.6.tgz", - "integrity": "sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==", - "dev": true, - "dependencies": { - "@jsdevtools/ono": "^7.1.3", - "call-me-maybe": "^1.0.1", - "js-yaml": "^3.13.1" - } - }, - "node_modules/@apidevtools/json-schema-ref-parser/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@apidevtools/json-schema-ref-parser/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@apidevtools/openapi-schemas": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", - "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/@apidevtools/swagger-cli": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-cli/-/swagger-cli-4.0.4.tgz", - "integrity": "sha512-hdDT3B6GLVovCsRZYDi3+wMcB1HfetTU20l2DC8zD3iFRNMC6QNAZG5fo/6PYeHWBEv7ri4MvnlKodhNB0nt7g==", - "deprecated": "This package has been abandoned. Please switch to using the actively maintained @redocly/cli", - "dev": true, - "dependencies": { - "@apidevtools/swagger-parser": "^10.0.1", - "chalk": "^4.1.0", - "js-yaml": "^3.14.0", - "yargs": "^15.4.1" - }, - "bin": { - "swagger-cli": "bin/swagger-cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@apidevtools/swagger-cli/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@apidevtools/swagger-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@apidevtools/swagger-cli/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@apidevtools/swagger-cli/node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "node_modules/@apidevtools/swagger-cli/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "node_modules/@apidevtools/swagger-cli/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@apidevtools/swagger-cli/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@apidevtools/swagger-cli/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@apidevtools/swagger-cli/node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@apidevtools/swagger-cli/node_modules/y18n": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", - "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", - "dev": true - }, - "node_modules/@apidevtools/swagger-cli/node_modules/yargs": { - "version": "15.4.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", - "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@apidevtools/swagger-cli/node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@apidevtools/swagger-methods": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", - "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==", - "dev": true - }, - "node_modules/@apidevtools/swagger-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.1.0.tgz", - "integrity": "sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==", - "dev": true, - "dependencies": { - "@apidevtools/json-schema-ref-parser": "9.0.6", - "@apidevtools/openapi-schemas": "^2.1.0", - "@apidevtools/swagger-methods": "^3.0.2", - "@jsdevtools/ono": "^7.1.3", - "ajv": "^8.6.3", - "ajv-draft-04": "^1.0.0", - "call-me-maybe": "^1.0.1" - }, - "peerDependencies": { - "openapi-types": ">=7" - } - }, - "node_modules/@apidevtools/swagger-parser/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@apidevtools/swagger-parser/node_modules/ajv-draft-04": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz", - "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==", - "dev": true, - "peerDependencies": { - "ajv": "^8.5.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/@apidevtools/swagger-parser/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -546,12 +278,6 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@jsdevtools/ono": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", - "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==", - "dev": true - }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -1295,12 +1021,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/call-me-maybe": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", - "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==", - "dev": true - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -1310,15 +1030,6 @@ "node": ">=6" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -1609,15 +1320,6 @@ "ms": "2.0.0" } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2064,19 +1766,6 @@ "url": "https://opencollective.com/eslint" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/esquery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", @@ -2279,19 +1968,6 @@ "node": ">= 0.8" } }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", @@ -2903,29 +2579,11 @@ "node": ">= 0.8.0" } }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true - }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -2936,12 +2594,6 @@ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, - "node_modules/lodash.isequal": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", - "dev": true - }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", @@ -2968,12 +2620,6 @@ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, - "node_modules/lodash.mergewith": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", - "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true - }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -3339,13 +2985,6 @@ "wrappy": "1" } }, - "node_modules/openapi-types": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", - "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", - "dev": true, - "peer": true - }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -3378,42 +3017,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3656,21 +3259,6 @@ "node": ">=0.10.0" } }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -4006,12 +3594,6 @@ "node": ">= 0.8.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "dev": true - }, "node_modules/set-function-length": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", @@ -4086,12 +3668,6 @@ "node": ">=10" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true - }, "node_modules/sqlstring": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", @@ -4231,142 +3807,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/swagger-cli": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/swagger-cli/-/swagger-cli-4.0.4.tgz", - "integrity": "sha512-Cp8YYuLny3RJFQ4CvOBTaqmOOgYsem52dPx1xM5S4EUWFblIh2Q8atppMZvXKUr1e9xH5RwipYpmdUzdPcxWcA==", - "dev": true, - "dependencies": { - "@apidevtools/swagger-cli": "4.0.4" - }, - "bin": { - "swagger-cli": "swagger-cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/swagger-jsdoc": { - "version": "6.2.8", - "resolved": "https://registry.npmjs.org/swagger-jsdoc/-/swagger-jsdoc-6.2.8.tgz", - "integrity": "sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==", - "dev": true, - "dependencies": { - "commander": "6.2.0", - "doctrine": "3.0.0", - "glob": "7.1.6", - "lodash.mergewith": "^4.6.2", - "swagger-parser": "^10.0.3", - "yaml": "2.0.0-1" - }, - "bin": { - "swagger-jsdoc": "bin/swagger-jsdoc.js" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/swagger-jsdoc/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/swagger-jsdoc/node_modules/commander": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.0.tgz", - "integrity": "sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/swagger-jsdoc/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/swagger-jsdoc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/swagger-parser": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", - "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", - "dev": true, - "dependencies": { - "@apidevtools/swagger-parser": "10.0.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/swagger-parser/node_modules/@apidevtools/swagger-parser": { - "version": "10.0.3", - "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", - "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", - "dev": true, - "dependencies": { - "@apidevtools/json-schema-ref-parser": "^9.0.6", - "@apidevtools/openapi-schemas": "^2.0.4", - "@apidevtools/swagger-methods": "^3.0.2", - "@jsdevtools/ono": "^7.1.3", - "call-me-maybe": "^1.0.1", - "z-schema": "^5.0.1" - }, - "peerDependencies": { - "openapi-types": ">=7" - } - }, - "node_modules/swagger-ui-dist": { - "version": "5.11.1", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.1.tgz", - "integrity": "sha512-H0oTRHqAfuyjNBLILhS9OwC0WN/xyfNJQtRBR35AnLY34A5W107zRhLQMzL47uuH87+f+RwxCMeLvNFOdyZ+Tg==", - "dev": true - }, - "node_modules/swagger-ui-express": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-5.0.0.tgz", - "integrity": "sha512-tsU9tODVvhyfkNSvf03E6FAk+z+5cU3lXAzMy6Pv4av2Gt2xA0++fogwC4qo19XuFf6hdxevPuVCSKFuMHJhFA==", - "dev": true, - "dependencies": { - "swagger-ui-dist": ">=5.0.0" - }, - "engines": { - "node": ">= v0.10.32" - }, - "peerDependencies": { - "express": ">=4.0.0 || >=5.0.0-beta" - } - }, "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", @@ -4652,12 +4092,6 @@ "node": ">= 8" } }, - "node_modules/which-module": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", - "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", - "dev": true - }, "node_modules/wkx": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", @@ -4769,15 +4203,6 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "node_modules/yaml": { - "version": "2.0.0-1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.0.0-1.tgz", - "integrity": "sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==", - "dev": true, - "engines": { - "node": ">= 6" - } - }, "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", @@ -4861,36 +4286,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/z-schema": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", - "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", - "dev": true, - "dependencies": { - "lodash.get": "^4.4.2", - "lodash.isequal": "^4.5.0", - "validator": "^13.7.0" - }, - "bin": { - "z-schema": "bin/z-schema" - }, - "engines": { - "node": ">=8.0.0" - }, - "optionalDependencies": { - "commander": "^9.4.1" - } - }, - "node_modules/z-schema/node_modules/commander": { - "version": "9.5.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", - "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", - "dev": true, - "optional": true, - "engines": { - "node": "^12.20.0 || >=14" - } - }, "node_modules/zod": { "version": "3.22.4", "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", diff --git a/package.json b/package.json index 3d3cd43..fa7f733 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,6 @@ "eslint-config-prettier": "^9.1.0", "nodemon": "^3.0.2", "prettier": "^3.1.1", - "swagger-cli": "^4.0.4", - "swagger-jsdoc": "^6.2.8", - "swagger-ui-express": "^5.0.0", "ts-node": "^10.9.2", "typescript": "^5.3.3" } diff --git a/src/controllers/guest.controller.ts b/src/controllers/guest.controller.ts index 14486ef..94ed276 100644 --- a/src/controllers/guest.controller.ts +++ b/src/controllers/guest.controller.ts @@ -12,11 +12,11 @@ import { } from "../services/guest.service"; export const addGuesting = async (req, res, next) => { - return res.send(response(status.SUCCESS, await createGuesting(1, req.body))); + return res.send(response(status.SUCCESS, await createGuesting(req.user.id, req.body))); }; export const modifyGuesting = async (req, res, next) => { - return res.send(response(status.SUCCESS, await updateGuesting(1, req.params, req.body))); + return res.send(response(status.SUCCESS, await updateGuesting(req.user.id, req.params, req.body))); }; export const GuestingPreview = async (req, res, next) => { @@ -40,5 +40,5 @@ export const DetailedGuestingPreview = async (req, res, next) => { }; export const applicationGuesting = async (req, res, next) => { - return res.send(response(status.SUCCESS, await addGuestUser(1, req.params))); + return res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.params))); }; diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index 89f5273..70c2a6c 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -104,6 +104,7 @@ export const InsertGuestUser = async (guestingId, userId) => { await db.GuestUser.create({ guestId: guestingId, userId: userId, + status: 0, }); }; diff --git a/src/dtos/games.dto.ts b/src/dtos/games.dto.ts index dc4bdc3..b6b5b22 100644 --- a/src/dtos/games.dto.ts +++ b/src/dtos/games.dto.ts @@ -1,15 +1,15 @@ -import { getAgeGroup } from "../constants/age-group.constant"; -import { getGender } from "../constants/gender.constant"; import { getLevelById } from "../constants/level.constant"; +//TODO: getGender, getAgeGroup 함수 사용 + export const readGameResponseDTO = (games) => { return games.map((game) => ({ gameTime: game.gameTime, teamName: game["HostTeam.name"], teamRegion: game["HostTeam.region"], - teamGender: getGender(game["HostTeam.gender"]), + teamGender: game["HostTeam.gender"], memberCount: game.memberCount, - teamAgeGroup: getAgeGroup(game["HostTeam.ageGroup"]), + teamAgeGroup: game["HostTeam.ageGroup"], teamSkillLevel: getLevelById(game["HostTeam.skillLevel"]), status: game.status, })); diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index f9f5ae1..acee494 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -1,15 +1,15 @@ -import { getAgeGroup } from "../constants/age-group.constant"; -import { getGender } from "../constants/gender.constant"; import { getLevelById } from "../constants/level.constant"; +//TODO: getGender, getAgeGroup 함수 사용 + export const readGuestingResponseDTO = (guestings) => { return guestings.map((guesting) => ({ gameTime: guesting.gameTime, teamName: guesting["Team.name"], teamRegion: guesting["Team.region"], - teamGender: getGender(guesting["Team.gender"]), + teamGender: guesting["Team.gender"], memberCount: guesting.memberCount, - teamAgeGroup: getAgeGroup(guesting["Team.ageGroup"]), + teamAgeGroup: guesting["Team.ageGroup"], teamSkillLevel: getLevelById(guesting["Team.skillLevel"]), recruitCount: guesting.recruitCount, })); @@ -29,8 +29,8 @@ export const readGuestingDetailResponseDTO = (guestingDetail, TeamDetail, leader description: TeamDetail.description, gusting_info: { gameTime: guestingDetail.gameTime, - gender: getGender(TeamDetail.gender), - ageGroup: getAgeGroup(TeamDetail.ageGroup), + gender: TeamDetail.gender, + ageGroup: TeamDetail.ageGroup, gymName: TeamDetail.gymName, skillLevel: getLevelById(TeamDetail.skillLevel), }, diff --git a/src/models/guest-user.model.ts b/src/models/guest-user.model.ts index 585b33a..cd0bbf9 100644 --- a/src/models/guest-user.model.ts +++ b/src/models/guest-user.model.ts @@ -12,6 +12,10 @@ class GuestUser extends Model, InferCreationAttribute type: new DataTypes.INTEGER(), allowNull: false, }, + status: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, }, { sequelize, diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index d7571ee..c336dcd 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -15,11 +15,12 @@ import { validateBody } from "../middlewares/validate.middleware"; export const guestsRouter = express.Router({ mergeParams: true }); -// guestsRouter.post("/", validateBody(createGuesting), asyncHandler(addGuesting)); -guestsRouter.post("/", asyncHandler(addGuesting)); +guestsRouter.post("/", validateBody(createGuesting), asyncHandler(addGuesting)); +// guestsRouter.post("/", asyncHandler(addGuesting)); + +guestsRouter.put("/:guestingId", validateBody(updateGuesting), asyncHandler(modifyGuesting)); +// guestsRouter.put("/:guestingId", asyncHandler(modifyGuesting)); -// guestsRouter.put("/:guestingId", validateBody(updateGuesting), asyncHandler(modifyGuesting)); -guestsRouter.put("/:guestingId", asyncHandler(modifyGuesting)); guestsRouter.get("/", asyncHandler(GuestingPreview)); From 5195b51930d5a2caa31ce82b98a4de53de63a17f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 30 Jan 2024 21:36:02 +0900 Subject: [PATCH 043/147] =?UTF-8?q?Feat:=20=EB=82=A0=EC=A7=9C=EB=B3=84=20?= =?UTF-8?q?=EA=B2=8C=EC=8A=A4=ED=8C=85=20=EB=82=B4=EC=97=AD=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84=20=EC=BB=A4=EB=B0=8B=20?= =?UTF-8?q?=EB=B3=B5=EC=9B=90=20#41?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/matchings.controller.ts | 16 +++++ src/daos/matching.dao.ts | 80 +++++++++++++++++++++++++ src/dtos/matchings.dto.ts | 15 +++++ src/routes/matchings.route.ts | 12 ++++ src/services/matchings.service.ts | 34 +++++++++++ 5 files changed, 157 insertions(+) create mode 100644 src/controllers/matchings.controller.ts create mode 100644 src/daos/matching.dao.ts create mode 100644 src/dtos/matchings.dto.ts create mode 100644 src/routes/matchings.route.ts create mode 100644 src/services/matchings.service.ts diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts new file mode 100644 index 0000000..e5356fe --- /dev/null +++ b/src/controllers/matchings.controller.ts @@ -0,0 +1,16 @@ +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { + readMatchingGuesting, + // readMatchingHosting +} from "../services/matchings.service"; + +export const matchingGuestingPreview = async (req, res, next) => { + return res.send(response(status.SUCCESS, await readMatchingGuesting(2, req.query))); + // return res.send(response(status.SUCCESS, await readMatchingGuesting(req.user.id, req.query))); +}; + +// export const matchingHostingPreview = async (req, res, next) => { +// return res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); +// // return res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); +// }; diff --git a/src/daos/matching.dao.ts b/src/daos/matching.dao.ts new file mode 100644 index 0000000..aa86581 --- /dev/null +++ b/src/daos/matching.dao.ts @@ -0,0 +1,80 @@ +import { Sequelize } from "sequelize"; +import db from "../models"; +import { getTeamIdByLeaderId } from "./team.dao"; + +const { Op } = require("sequelize"); + +export const findGuestsOfMatchingGuesting = async (userId, date) => { + const guestUserResults = await db.GuestUser.findAll({ + raw: true, + where: { + userId: userId, + status: 1, + }, + attributes: ["guestId"], + }); + + const guestIds = guestUserResults.map((guestUser) => guestUser.guestId); + + const guestResults = await db.Guest.findAll({ + raw: true, + where: { + id: { + [Op.in]: guestIds, + }, + [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + }, + include: [ + { + model: db.Team, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["gameTime"], + }); + + return guestResults; +}; + +// export const findGuestsOfMatchingHosting = async (userId, date) => { +// const guestResults = await db.Guest.findAll({ +// raw: true, +// where: { +// teamId: getTeamIdByLeaderId(userId), +// [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), +// }, +// include: [ +// { +// model: db.Team, +// attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], +// }, +// ], +// attributes: ["gameTime"], +// }); + +// return guestResults; +// }; + +// export const findGamesOfMatchingGuesting = async (userId, date) => { +// const guestUserResults = await db.game.findAll({ +// raw: true, +// where: { +// userId: userId, +// }, +// attributes: ["guestId"], +// }); + +// const guestIds = guestUserResults.map((guestUser) => guestUser.guestId); +// const games = await db.Game.findAll({ +// raw: true, +// where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), +// include: [ +// { +// model: db.Team, +// as: "HostTeam", +// attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], +// }, +// ], +// attributes: ["gameTime", "status"], +// order: [["created_at", "DESC"]], +// }); diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts new file mode 100644 index 0000000..7c010d4 --- /dev/null +++ b/src/dtos/matchings.dto.ts @@ -0,0 +1,15 @@ +import { getAgeGroup } from "../constants/age-group.constant"; +import { getGender } from "../constants/gender.constant"; +import { getLevelById } from "../constants/level.constant"; + +export const readMatchingGuestingResponseDTO = (guestings) => { + return guestings.map((guesting) => ({ + gameTime: guesting.gameTime, + teamName: guesting["Team.name"], + teamRegion: guesting["Team.region"], + teamGender: getGender(guesting["Team.gender"]), + memberCount: guesting.memberCount, + teamAgeGroup: getAgeGroup(guesting["Team.ageGroup"]), + teamSkillLevel: getLevelById(guesting["Team.skillLevel"]), + })); +}; diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts new file mode 100644 index 0000000..2c61500 --- /dev/null +++ b/src/routes/matchings.route.ts @@ -0,0 +1,12 @@ +import express from "express"; +import asyncHandler from "express-async-handler"; +import { + matchingGuestingPreview, + // matchingHostingPreview +} from "../controllers/matchings.controller"; + +export const matchingsRouter = express.Router({ mergeParams: true }); + +matchingsRouter.get("/guesting", asyncHandler(matchingGuestingPreview)); + +// matchingsRouter.get("/hosting", asyncHandler(matchingHostingPreview)); diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts new file mode 100644 index 0000000..3957759 --- /dev/null +++ b/src/services/matchings.service.ts @@ -0,0 +1,34 @@ +import { + findGuestsOfMatchingGuesting, + // findGuestsOfMatchingHosting +} from "../daos/matching.dao"; +import { getMemberCountByTeamId } from "../daos/member.dao"; +import { readMatchingGuestingResponseDTO } from "../dtos/matchings.dto"; + +export const readMatchingGuesting = async (userId, query) => { + const matchingGuestings = await findGuestsOfMatchingGuesting(userId, query.date); + for (const matchingGuesting of matchingGuestings) { + matchingGuestings.memberCount = (await getMemberCountByTeamId(matchingGuesting["Team.id"])) + 1; + } + + // const matchingGames = await findGamesOfMatchingGuesting(userId, query.date); + // for (const matchingGame of matchingGames) { + // matchingGames.memberCount = (await getMemberCountByTeamId(matchingGame["HostTeam.id"])) + 1; + // } + + return readMatchingGuestingResponseDTO(matchingGuestings); +}; + +// export const readMatchingHosting = async (userId, query) => { +// const matchingHostings = await findGuestsOfMatchingHosting(userId, query.date); +// for (const matchingHosting of matchingHostings) { +// matchingHostings.memberCount = (await getMemberCountByTeamId(matchingHosting["Team.id"])) + 1; +// } + +// // const matchingGames = await findGamesOfMatchingGuesting(userId, query.date); +// // for (const matchingGame of matchingGames) { +// // matchingGames.memberCount = (await getMemberCountByTeamId(matchingGame["HostTeam.id"])) + 1; +// // } + +// return readMatchingGuestingResponseDTO(matchingHostings); +// }; From 75b504994b7f42167a568123c8cd5716cddb85a6 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Tue, 30 Jan 2024 22:05:48 +0900 Subject: [PATCH 044/147] =?UTF-8?q?Feat:=20=EB=82=A0=EC=A7=9C=EB=B3=84=20?= =?UTF-8?q?=ED=98=B8=EC=8A=A4=ED=8C=85=20=EB=82=B4=EC=97=AD=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84=20=EC=BB=A4=EB=B0=8B=20?= =?UTF-8?q?=EB=B3=B5=EC=9B=90=20#58=20(#60)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app.ts | 2 ++ src/controllers/matchings.controller.ts | 13 ++++----- src/daos/matching.dao.ts | 35 ++++++++++++------------- src/dtos/matchings.dto.ts | 18 ++++++------- src/routes/matchings.route.ts | 7 ++--- src/services/matchings.service.ts | 27 +++++++++---------- 6 files changed, 47 insertions(+), 55 deletions(-) diff --git a/src/app.ts b/src/app.ts index 163fae9..605ed53 100644 --- a/src/app.ts +++ b/src/app.ts @@ -16,6 +16,7 @@ import { gamesRouter } from "./routes/games.route"; import { guestsRouter } from "./routes/guests.route"; import { postsRouter } from "./routes/posts.route"; import { usersRouter } from "./routes/users.route"; +import { matchingsRouter } from "./routes/matchings.route"; const app = express(); @@ -40,6 +41,7 @@ app.use("/games", gamesRouter); app.use("/guests", guestsRouter); app.use("/posts", postsRouter); app.use("/users", usersRouter); +app.use("/matchings", matchingsRouter); app.use((req: Request, res: Response, next: NextFunction) => { const err = new BaseError(status.NOT_FOUND); diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts index e5356fe..d75eb70 100644 --- a/src/controllers/matchings.controller.ts +++ b/src/controllers/matchings.controller.ts @@ -1,16 +1,13 @@ import { response } from "../config/response"; import { status } from "../config/response.status"; -import { - readMatchingGuesting, - // readMatchingHosting -} from "../services/matchings.service"; +import { readMatchingGuesting, readMatchingHosting } from "../services/matchings.service"; export const matchingGuestingPreview = async (req, res, next) => { return res.send(response(status.SUCCESS, await readMatchingGuesting(2, req.query))); // return res.send(response(status.SUCCESS, await readMatchingGuesting(req.user.id, req.query))); }; -// export const matchingHostingPreview = async (req, res, next) => { -// return res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); -// // return res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); -// }; +export const matchingHostingPreview = async (req, res, next) => { + return res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); + // return res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); +}; diff --git a/src/daos/matching.dao.ts b/src/daos/matching.dao.ts index aa86581..73308bb 100644 --- a/src/daos/matching.dao.ts +++ b/src/daos/matching.dao.ts @@ -36,24 +36,23 @@ export const findGuestsOfMatchingGuesting = async (userId, date) => { return guestResults; }; -// export const findGuestsOfMatchingHosting = async (userId, date) => { -// const guestResults = await db.Guest.findAll({ -// raw: true, -// where: { -// teamId: getTeamIdByLeaderId(userId), -// [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), -// }, -// include: [ -// { -// model: db.Team, -// attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], -// }, -// ], -// attributes: ["gameTime"], -// }); - -// return guestResults; -// }; +export const findGuestsOfMatchingHosting = async (userId, date) => { + const teamId = await getTeamIdByLeaderId(userId); + return await db.Guest.findAll({ + raw: true, + where: { + teamId: teamId, + [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + }, + include: [ + { + model: db.Team, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["gameTime"], + }); +}; // export const findGamesOfMatchingGuesting = async (userId, date) => { // const guestUserResults = await db.game.findAll({ diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index 7c010d4..a867a0e 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -2,14 +2,14 @@ import { getAgeGroup } from "../constants/age-group.constant"; import { getGender } from "../constants/gender.constant"; import { getLevelById } from "../constants/level.constant"; -export const readMatchingGuestingResponseDTO = (guestings) => { - return guestings.map((guesting) => ({ - gameTime: guesting.gameTime, - teamName: guesting["Team.name"], - teamRegion: guesting["Team.region"], - teamGender: getGender(guesting["Team.gender"]), - memberCount: guesting.memberCount, - teamAgeGroup: getAgeGroup(guesting["Team.ageGroup"]), - teamSkillLevel: getLevelById(guesting["Team.skillLevel"]), +export const readMatchingGuestingResponseDTO = (matchings) => { + return matchings.map((matching) => ({ + gameTime: matching.gameTime, + teamName: matching["Team.name"], + teamRegion: matching["Team.region"], + teamGender: getGender(matching["Team.gender"]), + memberCount: matching.memberCount, + teamAgeGroup: getAgeGroup(matching["Team.ageGroup"]), + teamSkillLevel: getLevelById(matching["Team.skillLevel"]), })); }; diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts index 2c61500..c7f115e 100644 --- a/src/routes/matchings.route.ts +++ b/src/routes/matchings.route.ts @@ -1,12 +1,9 @@ import express from "express"; import asyncHandler from "express-async-handler"; -import { - matchingGuestingPreview, - // matchingHostingPreview -} from "../controllers/matchings.controller"; +import { matchingGuestingPreview, matchingHostingPreview } from "../controllers/matchings.controller"; export const matchingsRouter = express.Router({ mergeParams: true }); matchingsRouter.get("/guesting", asyncHandler(matchingGuestingPreview)); -// matchingsRouter.get("/hosting", asyncHandler(matchingHostingPreview)); +matchingsRouter.get("/hosting", asyncHandler(matchingHostingPreview)); diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index 3957759..2ddb451 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -1,7 +1,4 @@ -import { - findGuestsOfMatchingGuesting, - // findGuestsOfMatchingHosting -} from "../daos/matching.dao"; +import { findGuestsOfMatchingGuesting, findGuestsOfMatchingHosting } from "../daos/matching.dao"; import { getMemberCountByTeamId } from "../daos/member.dao"; import { readMatchingGuestingResponseDTO } from "../dtos/matchings.dto"; @@ -19,16 +16,16 @@ export const readMatchingGuesting = async (userId, query) => { return readMatchingGuestingResponseDTO(matchingGuestings); }; -// export const readMatchingHosting = async (userId, query) => { -// const matchingHostings = await findGuestsOfMatchingHosting(userId, query.date); -// for (const matchingHosting of matchingHostings) { -// matchingHostings.memberCount = (await getMemberCountByTeamId(matchingHosting["Team.id"])) + 1; -// } +export const readMatchingHosting = async (userId, query) => { + const matchingHostings = await findGuestsOfMatchingHosting(userId, query.date); + for (const matchingHosting of matchingHostings) { + matchingHostings.memberCount = (await getMemberCountByTeamId(matchingHosting["Team.id"])) + 1; + } -// // const matchingGames = await findGamesOfMatchingGuesting(userId, query.date); -// // for (const matchingGame of matchingGames) { -// // matchingGames.memberCount = (await getMemberCountByTeamId(matchingGame["HostTeam.id"])) + 1; -// // } + // const matchingGames = await findGamesOfMatchingGuesting(userId, query.date); + // for (const matchingGame of matchingGames) { + // matchingGames.memberCount = (await getMemberCountByTeamId(matchingGame["HostTeam.id"])) + 1; + // } -// return readMatchingGuestingResponseDTO(matchingHostings); -// }; + return readMatchingGuestingResponseDTO(matchingHostings); +}; From b81092aba2beb9accfb2be72c59e7c6fd2f9e781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 30 Jan 2024 22:32:18 +0900 Subject: [PATCH 045/147] =?UTF-8?q?Refactor:=20teams=20=EB=9D=BC=EC=9A=B0?= =?UTF-8?q?=ED=8A=B8,=20=EC=BB=A8=ED=8A=B8=EB=A1=A4=EB=9F=AC,=20=EC=84=9C?= =?UTF-8?q?=EB=B9=84=EC=8A=A4,=20DAO,=20DTO=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?=EC=A7=80=EC=A0=95=20=EB=B0=8F=20=EC=9D=B4=EB=A6=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20#3=20#4=20#6=20#13?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/teams.controller.ts | 8 ++-- src/daos/member.dao.ts | 9 ++-- src/daos/team.dao.ts | 40 ++++++++++-------- src/dtos/teams.dto.ts | 39 +++++++++++++---- src/routes/teams.route.ts | 13 +++--- src/routes/users.route.ts | 5 ++- src/schemas/fields.ts | 16 ++++++- src/schemas/team.schema.ts | 56 ++++++++++++++++--------- src/schemas/user-profile.schema.ts | 6 --- src/services/games.service.ts | 11 ++--- src/services/guest.service.ts | 4 +- src/services/teams.service.ts | 65 +++++++++++++++++++---------- 12 files changed, 172 insertions(+), 100 deletions(-) diff --git a/src/controllers/teams.controller.ts b/src/controllers/teams.controller.ts index e122083..f3bb0e4 100644 --- a/src/controllers/teams.controller.ts +++ b/src/controllers/teams.controller.ts @@ -1,10 +1,10 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readTeamPreviewsByCategory, readTeamDetail, createTeam, updateTeam } from "../services/teams.service"; +import { readTeamPreviews, readTeamDetail, createTeam, updateTeam } from "../services/teams.service"; -export const fetchTeamPreviewsByCategory = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readTeamPreviewsByCategory(req.user.id, req.query))); +export const fetchTeamPreviews = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readTeamPreviews(req.user.id, req.query))); }; export const addTeam = async (req, res: Response, next) => { @@ -16,5 +16,5 @@ export const modifyTeam = async (req, res: Response, next) => { }; export const fetchTeamDetail = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readTeamDetail(1, req.params))); + res.send(response(status.SUCCESS, await readTeamDetail(req.user.id, req.params))); }; diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts index c8fcdb2..729ff65 100644 --- a/src/daos/member.dao.ts +++ b/src/daos/member.dao.ts @@ -2,6 +2,7 @@ import { Op } from "sequelize"; import db from "../models"; export const findMemberInfoByTeamId = async (teamId, userInfoAttributes) => { + throw new Error("더 이상 사용하지 않는 함수"); return await db.Member.findAll({ raw: true, where: { @@ -43,14 +44,14 @@ export const findMemberInfoWithoutLeaderByTeamId = async (teamId, userInfoAttrib }); }; -export const insertMember = async (teamId, userId) => { +export const insertMember = async (teamId: number, userId: number) => { await db.Member.create({ teamId, userId, }); }; -export const isMemberExist = async (teamId, userId) => { +export const isMemberExist = async (teamId: number, userId: number) => { const member = await db.Member.findOne({ where: { teamId, @@ -60,7 +61,7 @@ export const isMemberExist = async (teamId, userId) => { return member !== null; }; -export const findMemberToDelete = async (memberIdsToDelete, teamId) => { +export const findMemberToDelete = async (memberIdsToDelete: number[], teamId: number) => { return await db.Member.findAll({ where: { teamId, @@ -71,7 +72,7 @@ export const findMemberToDelete = async (memberIdsToDelete, teamId) => { }); }; -export const deleteMembersById = async (members, teamId) => { +export const deleteMembers = async (members) => { for (const member of members) { await member.destroy(); } diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index 10c7bc2..f517796 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -1,10 +1,9 @@ -import { BaseError } from "../config/error"; -import { status } from "../config/response.status"; -import { defaultLevel } from "../constants/level.constant"; import db from "../models"; -import { CreateTeamSchema } from "../schemas/team.schema"; +import { defaultLevel } from "../constants/level.constant"; +import { CreateTeamBody, UpdateTeamBodyWithoutMemberIdsToDelete } from "../schemas/team.schema"; +import { Category } from "../types/category.enum"; -export const findTeamPreviewByCategory = async (userId, category) => { +export const findTeamPreviewByCategory = async (userId: number, category: Category) => { const teamsAsLeader = await db.Team.findAll({ raw: true, where: { @@ -33,7 +32,7 @@ export const findTeamPreviewByCategory = async (userId, category) => { return previews; }; -export const insertTeam = async (data: CreateTeamSchema, userId: number, inviteCode: string) => { +export const insertTeam = async (data: CreateTeamBody, userId: number, inviteCode: string) => { await db.Team.create({ logo: data.logo, name: data.name, @@ -50,17 +49,24 @@ export const insertTeam = async (data: CreateTeamSchema, userId: number, inviteC }); }; -export const getTeamDetail = async (teamId) => { - return await db.Team.findOne({ +export const getTeamDetail = async (teamId: number) => { + return await db.Team.findAll({ raw: true, where: { id: teamId, }, - attributes: ["name", "logo", "skillLevel", "mannerLevel", "description", "leaderId"], + include: [ + { + model: db.Member, + attributes: ["userId"], + required: false, + }, + ], + attributes: ["name", "logo", "skillLevel", "mannerLevel", "description", "leaderId", "category"], }); }; -export const getTeamDetailforGuesting = async (teamId) => { +export const getTeamDetailforGuesting = async (teamId: number) => { return await db.Team.findOne({ raw: true, where: { @@ -70,7 +76,7 @@ export const getTeamDetailforGuesting = async (teamId) => { }); }; -export const getTeamIdByInviteCode = async (inviteCode): Promise => { +export const getTeamIdByInviteCode = async (inviteCode: string): Promise => { const team = await db.Team.findOne({ raw: true, where: { @@ -81,7 +87,7 @@ export const getTeamIdByInviteCode = async (inviteCode): Promise => { return team?.id; }; -export const getTeamById = async (teamId, userId) => { +export const getTeamByLeaderId = async (teamId: number, userId: number) => { return await db.Team.findOne({ where: { id: teamId, @@ -90,7 +96,7 @@ export const getTeamById = async (teamId, userId) => { }); }; -export const getTeamIdByLeaderId = async (userId) => { +export const getTeamIdByLeaderId = async (userId: number) => { const team = await db.Team.findOne({ raw: true, where: { @@ -98,11 +104,10 @@ export const getTeamIdByLeaderId = async (userId) => { }, attributes: ["id"], }); - return team?.id; }; -export const getTeamCategoryByLeaderId = async (userId) => { +export const getTeamCategoryByLeaderId = async (userId: number) => { const team = await db.Team.findOne({ raw: true, where: { @@ -110,18 +115,17 @@ export const getTeamCategoryByLeaderId = async (userId) => { }, attributes: ["category"], }); - return team?.category; }; -export const setTeam = async (team, body) => { +export const setTeam = async (team, body: UpdateTeamBodyWithoutMemberIdsToDelete) => { Object.keys(body).forEach((field) => { team[field] = body[field]; }); await team.save(); }; -export const findTeamAvailPreviewById = async (userId, category) => { +export const findTeamPreviewByCategoryForLeader = async (userId: number, category: Category) => { return await db.Team.findAll({ raw: true, where: { diff --git a/src/dtos/teams.dto.ts b/src/dtos/teams.dto.ts index 2dc3984..e1e196b 100644 --- a/src/dtos/teams.dto.ts +++ b/src/dtos/teams.dto.ts @@ -1,10 +1,29 @@ -export const readTeamDetailResponseDTO = (detail, leaderInfo, memberInfo, isTeamLeader) => { - const member = memberInfo.map((info) => ({ - //TODO - nickname: info["User.nickname"], - height: null, - weight: null, - position: null, +interface TeamDetail { + name: string; + logo: string | null; + skillLevel: number | null; + mannerLevel: number | null; + description: string | null; +} + +interface UserInfo { + nickname: string; + height: number | null; + Profiles: { + position: string | null; + }; +} + +export const readTeamDetailResponseDTO = ( + detail: TeamDetail, + leaderInfo: UserInfo, + membersInfo: UserInfo[], + isTeamLeader: boolean, +) => { + const member = membersInfo.map((memberInfo: UserInfo) => ({ + nickname: memberInfo.nickname, + height: memberInfo.height, + position: memberInfo["Profiles.position"], })); return { name: detail.name, @@ -13,7 +32,11 @@ export const readTeamDetailResponseDTO = (detail, leaderInfo, memberInfo, isTeam mannerLevel: detail.mannerLevel, description: detail.description, participants: { - leader: leaderInfo, + leader: { + nickname: leaderInfo.nickname, + height: leaderInfo.height, + position: leaderInfo["Profiles.position"], + }, member, }, isTeamLeader, diff --git a/src/routes/teams.route.ts b/src/routes/teams.route.ts index 948706f..4631abd 100644 --- a/src/routes/teams.route.ts +++ b/src/routes/teams.route.ts @@ -1,18 +1,19 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; -import { validateBody } from "../middlewares/validate.middleware"; -import { createTeam, updateTeam } from "../schemas/team.schema"; -import { fetchTeamPreviewsByCategory, fetchTeamDetail, addTeam, modifyTeam } from "../controllers/teams.controller"; +import { validate } from "../middlewares/validate.middleware"; +import { createTeamSchema, updateTeamSchema } from "../schemas/team.schema"; +import { fetchTeamPreviews, fetchTeamDetail, addTeam, modifyTeam } from "../controllers/teams.controller"; +import { categoryParam } from "../schemas/fields"; export const teamsRouter = express.Router(); teamsRouter.use(verifyUser); -teamsRouter.get("/", asyncHandler(fetchTeamPreviewsByCategory)); +teamsRouter.post("/", validate(createTeamSchema), asyncHandler(addTeam)); -teamsRouter.post("/", validateBody(createTeam), asyncHandler(addTeam)); +teamsRouter.get("/", validate(categoryParam), asyncHandler(fetchTeamPreviews)); -teamsRouter.put("/:teamId", validateBody(updateTeam), asyncHandler(modifyTeam)); +teamsRouter.put("/:teamId", validate(updateTeamSchema), asyncHandler(modifyTeam)); teamsRouter.get("/:teamId", asyncHandler(fetchTeamDetail)); diff --git a/src/routes/users.route.ts b/src/routes/users.route.ts index 8d1385c..29a90b6 100644 --- a/src/routes/users.route.ts +++ b/src/routes/users.route.ts @@ -2,13 +2,14 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; import { validate } from "../middlewares/validate.middleware"; -import { readUserProfileSchema, updateUserProfileSchema } from "../schemas/user-profile.schema"; +import { updateUserProfileSchema } from "../schemas/user-profile.schema"; import { fetchUserProfile, modifyUserProfile } from "../controllers/users.controller"; +import { categoryParam } from "../schemas/fields"; export const usersRouter = express.Router(); usersRouter.use(verifyUser); -usersRouter.get("/profiles/:category", validate(readUserProfileSchema), asyncHandler(fetchUserProfile)); +usersRouter.get("/profiles/:category", validate(categoryParam), asyncHandler(fetchUserProfile)); usersRouter.put("/profiles/:category", validate(updateUserProfileSchema), asyncHandler(modifyUserProfile)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 79b72fc..8970039 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -1,4 +1,4 @@ -import { z } from "zod"; +import { object, z } from "zod"; import { Gender } from "../types/gender.enum"; import { Category } from "../types/category.enum"; import { AgeGroup } from "../types/age-group.enum"; @@ -44,3 +44,17 @@ export const categoryField = { Category.TableTennis, ]), }; + +export const logoField = { logo: z.optional(z.string().max(200)) }; + +export const nameField = { name: z.string().max(20) }; + +export const gymNameField = { gymName: z.string().max(100) }; + +export const memberIdsToDeleteField = { memberIdsToDelete: z.optional(z.array(z.number().int())) }; + +export const categoryParam = object({ + params: object({ + ...categoryField, + }), +}); diff --git a/src/schemas/team.schema.ts b/src/schemas/team.schema.ts index 783cc55..7cede39 100644 --- a/src/schemas/team.schema.ts +++ b/src/schemas/team.schema.ts @@ -1,30 +1,48 @@ import { TypeOf, object, z } from "zod"; +import { + ageGroupFieldInTeam, + categoryField, + descriptionField, + genderFieldInTeam, + gymNameField, + logoField, + memberIdsToDeleteField, + nameField, + regionFieldInTeam, +} from "./fields"; -const fieldsWithoutCategory = { - logo: z.optional(z.string()), - name: z.string().max(20), - description: z.optional(z.string()), - gender: z.number().int().min(1), //.max(getGendersLength()), - ageGroup: z.number().int().min(1), //.max(getAgeGroupsLength()), - region: z.string(), - gymName: z.string(), +const commonFields = { + ...logoField, + ...nameField, + ...descriptionField, + ...genderFieldInTeam, + ...ageGroupFieldInTeam, + ...regionFieldInTeam, + ...gymNameField, }; -const categories = z.enum(["농구", "야구", "테니스", "축구", "풋살", "배구", "볼링", "배드민턴", "탁구"]); +const createTeamBody = object({ + ...commonFields, + ...categoryField, +}); + +const updateTeamBody = object({ + ...commonFields, + ...memberIdsToDeleteField, +}); -export const category = object({ - category: categories, +const updateTeamBodyWithoutMemberIdsToDelete = object({ + ...commonFields, }); -export const createTeam = object({ - ...fieldsWithoutCategory, - category: categories, +export const createTeamSchema = object({ + body: createTeamBody, }); -export const updateTeam = object({ - ...fieldsWithoutCategory, - memberIdsToDelete: z.optional(z.array(z.number().int())), +export const updateTeamSchema = object({ + body: updateTeamBody, }); -export type CreateTeamSchema = TypeOf; -export type UpdateTeamSchema = TypeOf; +export type CreateTeamBody = TypeOf; +export type UpdateTeamBody = TypeOf; +export type UpdateTeamBodyWithoutMemberIdsToDelete = TypeOf; diff --git a/src/schemas/user-profile.schema.ts b/src/schemas/user-profile.schema.ts index 319c700..5291989 100644 --- a/src/schemas/user-profile.schema.ts +++ b/src/schemas/user-profile.schema.ts @@ -39,12 +39,6 @@ export const updateUserProfileSchema = object({ body: body, }); -export const readUserProfileSchema = object({ - params: object({ - ...categoryField, - }), -}); - export type UpdateUserProfileBody = TypeOf; export type CommonProfile = TypeOf; export type CategoryProfile = TypeOf; diff --git a/src/services/games.service.ts b/src/services/games.service.ts index ad8a612..ff8d517 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -1,6 +1,5 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; -import db from "../models"; import { findGamesByDate, findGamesByGender, @@ -14,13 +13,9 @@ import { getTeamDetailforGuesting, getTeamIdByLeaderId, getTeamCategoryByLeaderId, - getTeamById, + getTeamByLeaderId, } from "../daos/team.dao"; -import { - findMemberInfoByTeamId, - findMemberInfoWithoutLeaderByTeamId, - getMemberCountByTeamId, -} from "../daos/member.dao"; +import { findMemberInfoWithoutLeaderByTeamId, getMemberCountByTeamId } from "../daos/member.dao"; import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; import { getGameById } from "../daos/games.dao"; import { readGameResponseDTO, readGameDetailResponseDTO } from "../dtos/games.dto"; @@ -71,7 +66,7 @@ export const createGame = async (userId, body: CreateGameSchema) => { const hostTeamId = await getTeamIdByLeaderId(userId); const category = await getTeamCategoryByLeaderId(userId); - const team = await getTeamById(hostTeamId, userId); + const team = await getTeamByLeaderId(hostTeamId, userId); if (!team) { // team 메뉴로 이동 } diff --git a/src/services/guest.service.ts b/src/services/guest.service.ts index a6235c0..a6430ad 100644 --- a/src/services/guest.service.ts +++ b/src/services/guest.service.ts @@ -12,14 +12,14 @@ import { setGuesting, } from "../daos/guest.dao"; import { findMemberInfoByTeamId, getMemberCountByTeamId } from "../daos/member.dao"; -import { getTeamById, getTeamDetailforGuesting, getTeamIdByLeaderId } from "../daos/team.dao"; +import { getTeamByLeaderId, getTeamDetailforGuesting, getTeamIdByLeaderId } from "../daos/team.dao"; import { getUser, getUserInfoById, userInfoAttributes } from "../daos/user.dao"; import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; import { CreateGuestingSchema, UpdateGuestingSchema } from "../schemas/guest.schema"; export const createGuesting = async (userId, body: CreateGuestingSchema) => { const teamId = await getTeamIdByLeaderId(userId); - const team = await getTeamById(teamId, userId); + const team = await getTeamByLeaderId(teamId, userId); if (!team) { // team 메뉴로 이동 } diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index 318cabe..66d78a9 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -1,48 +1,69 @@ -import { deleteMembersById, findMemberInfoByTeamId, findMemberToDelete } from "../daos/member.dao"; -import { findTeamPreviewByCategory, getTeamById, getTeamDetail, insertTeam, setTeam } from "../daos/team.dao"; -import { findTeamAvailPreviewById } from "../daos/team.dao"; -import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; -import { readTeamDetailResponseDTO } from "../dtos/teams.dto"; -import { v4 as uuidv4 } from "uuid"; -import { CreateTeamSchema, UpdateTeamSchema } from "../schemas/team.schema"; import { BaseError } from "../config/error"; import { status } from "../config/response.status"; +import { v4 as uuidv4 } from "uuid"; +import { Category } from "../types/category.enum"; +import { CreateTeamBody, UpdateTeamBody } from "../schemas/team.schema"; +import { + findTeamPreviewByCategory, + getTeamByLeaderId, + getTeamDetail, + insertTeam, + setTeam, + findTeamPreviewByCategoryForLeader, +} from "../daos/team.dao"; +import { deleteMembers, findMemberToDelete } from "../daos/member.dao"; +import { getUserInfoByCategory } from "../daos/user.dao"; +import { readTeamDetailResponseDTO } from "../dtos/teams.dto"; -export const readTeamPreviewsByCategory = async (userId, query) => { +export const readTeamPreviews = async (userId: number, query) => { return await findTeamPreviewByCategory(userId, query.category); }; -export const createTeam = async (userId, body: CreateTeamSchema) => { +export const createTeam = async (userId: number, body: CreateTeamBody) => { await insertTeam(body, userId, uuidv4()); return; }; -export const updateTeam = async (userId, params, body: UpdateTeamSchema) => { +export const updateTeam = async (userId: number, params, body: UpdateTeamBody) => { const teamId = params.teamId; - const team = await getTeamById(params.teamId, userId); + const team = await getTeamByLeaderId(params.teamId, userId); if (!team) { throw new BaseError(status.TEAM_NOT_FOUND); } const { memberIdsToDelete, ...bodyWithoutMemberIdsToDelete } = body; - const members = await findMemberToDelete(memberIdsToDelete, teamId); - if (members.length !== memberIdsToDelete?.length) { - throw new BaseError(status.MEMBER_NOT_FOUND); + if (memberIdsToDelete !== undefined) { + const members = await findMemberToDelete(memberIdsToDelete, teamId); + if (members.length !== memberIdsToDelete?.length) { + throw new BaseError(status.MEMBER_NOT_FOUND); + } + await deleteMembers(members); } - - await deleteMembersById(members, teamId); await setTeam(team, bodyWithoutMemberIdsToDelete); return; }; -export const readTeamDetail = async (userId, params) => { +export const readTeamDetail = async (userId: number, params) => { const teamId = params.teamId; - const detail = await getTeamDetail(teamId); - const leaderInfo = await getUserInfoById(detail.leaderId); - const memberInfo = await findMemberInfoByTeamId(teamId, userInfoAttributes); - return readTeamDetailResponseDTO(detail, leaderInfo, memberInfo, userId == detail.leaderId); + const details = await getTeamDetail(teamId); + const detail = details[0]; + if (!detail) { + throw new BaseError(status.TEAM_NOT_FOUND); + } + const leaderInfo = await getUserInfoByCategory(detail.leaderId, detail.category); + const membersInfo = await readMembersInfo(details, detail.category); + return readTeamDetailResponseDTO(detail, leaderInfo, membersInfo, userId == detail.leaderId); +}; + +const readMembersInfo = async (details, category: Category) => { + const membersInfoPromises = details + .filter((detail) => detail["Members.userId"] !== null) + .map(async (detail) => { + return await getUserInfoByCategory(detail["Members.userId"], category); + }); + return await Promise.all(membersInfoPromises); }; export const readTeamAvailPreviewById = async (userId, query) => { - return await findTeamAvailPreviewById(userId, query.category); + return await findTeamPreviewByCategoryForLeader(userId, query.category); }; From b5fbdba5c40df5f1473693f92de291246d5f8c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 30 Jan 2024 22:57:14 +0900 Subject: [PATCH 046/147] =?UTF-8?q?Refactor:=20validate=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EC=97=90=20=EC=82=AC=EC=9A=A9=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EB=8F=84=EB=A1=9D=20post,=20comment=20schema=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20#25=20#27?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/posts.controller.ts | 4 ++-- src/daos/bookmark.dao.ts | 3 +-- src/daos/comment.dao.ts | 4 ++-- src/daos/post.dao.ts | 4 ++-- src/routes/posts.route.ts | 10 +++++----- src/schemas/comment.schema.ts | 11 ++++++++--- src/schemas/fields.ts | 8 ++++++++ src/schemas/post.schema.ts | 13 ++++++++++++- src/services/posts.service.ts | 18 +++++++++--------- 9 files changed, 49 insertions(+), 26 deletions(-) diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index 72f6e48..1a6ed47 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -6,7 +6,7 @@ import { readBookmarkedPosts, readPost, readCommunityPosts, - readPostsByAuthor, + readMyPosts, createCommunityPost, createComment, readComments, @@ -17,7 +17,7 @@ export const fetchCommunityPosts = async (req, res: Response, next) => { }; export const fetchMyPosts = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readPostsByAuthor(req.user.id, req.query))); + res.send(response(status.SUCCESS, await readMyPosts(req.user.id, req.query))); }; export const fetchBookmarkedPosts = async (req, res: Response, next) => { diff --git a/src/daos/bookmark.dao.ts b/src/daos/bookmark.dao.ts index 976b91f..b92e503 100644 --- a/src/daos/bookmark.dao.ts +++ b/src/daos/bookmark.dao.ts @@ -1,8 +1,7 @@ import db from "../models"; -export const insertOrDeleteBookmark = async (userId, postId) => { +export const insertOrDeleteBookmark = async (userId: number, postId: number) => { const bookmark = await getBookmark(userId, postId); - if (bookmark) { await bookmark.destroy(); } else { diff --git a/src/daos/comment.dao.ts b/src/daos/comment.dao.ts index cce5ea7..dbf76ad 100644 --- a/src/daos/comment.dao.ts +++ b/src/daos/comment.dao.ts @@ -1,5 +1,5 @@ import db from "../models"; -import { CreateCommentSchema } from "../schemas/comment.schema"; +import { CreateCommentBody } from "../schemas/comment.schema"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; const defaultLimit = 20; @@ -33,7 +33,7 @@ export const getCommentCount = async (postId: number) => { }); }; -export const insertComment = async (userId: number, postId: number, body: CreateCommentSchema) => { +export const insertComment = async (userId: number, postId: number, body: CreateCommentBody) => { await db.Comment.create({ postId, authorId: userId, diff --git a/src/daos/post.dao.ts b/src/daos/post.dao.ts index 9ecfee6..7864619 100644 --- a/src/daos/post.dao.ts +++ b/src/daos/post.dao.ts @@ -1,5 +1,5 @@ import db from "../models"; -import { CreatePostSchema } from "../schemas/post.schema"; +import { CreatePostBody } from "../schemas/post.schema"; import { PostType } from "../types/post-type.enum"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; @@ -66,7 +66,7 @@ const findPost = async (postFilter: object, bookmarkInclude: Array) => { return { posts, hasNext: calculateHasNext(posts, defaultLimit) }; }; -export const insertPost = async (userId: number, data: CreatePostSchema, type: PostType) => { +export const insertPost = async (userId: number, data: CreatePostBody, type: PostType) => { await db.Post.create({ title: data.title, content: data.content, diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index 8b0d267..595cc58 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -11,13 +11,13 @@ import { addComment, fetchComments, } from "../controllers/posts.controller"; -import { validateBody } from "../middlewares/validate.middleware"; -import { createPost } from "../schemas/post.schema"; -import { createComment } from "../schemas/comment.schema"; +import { validate } from "../middlewares/validate.middleware"; +import { createPostSchema } from "../schemas/post.schema"; +import { createCommentSchema } from "../schemas/comment.schema"; export const postsRouter = express.Router(); -postsRouter.post("/community", verifyUser, validateBody(createPost), asyncHandler(addCommunityPost)); +postsRouter.post("/community", verifyUser, validate(createPostSchema), asyncHandler(addCommunityPost)); postsRouter.get("/community", verifyUserIfExists, asyncHandler(fetchCommunityPosts)); @@ -27,7 +27,7 @@ postsRouter.get("/bookmarks", verifyUser, asyncHandler(fetchBookmarkedPosts)); postsRouter.post("/:postId/bookmark", verifyUser, asyncHandler(addOrRemoveBookmark)); -postsRouter.post("/:postId/comments", verifyUser, validateBody(createComment), asyncHandler(addComment)); +postsRouter.post("/:postId/comments", verifyUser, validate(createCommentSchema), asyncHandler(addComment)); postsRouter.get("/:postId/comments", verifyUser, asyncHandler(fetchComments)); diff --git a/src/schemas/comment.schema.ts b/src/schemas/comment.schema.ts index 907cd30..70df907 100644 --- a/src/schemas/comment.schema.ts +++ b/src/schemas/comment.schema.ts @@ -1,7 +1,12 @@ import { TypeOf, object, z } from "zod"; +import { contentFieldInComment } from "./fields"; -export const createComment = object({ - content: z.string().max(500), +const body = object({ + ...contentFieldInComment, }); -export type CreateCommentSchema = TypeOf; +export const createCommentSchema = object({ + body: body, +}); + +export type CreateCommentBody = TypeOf; diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 8970039..0b67513 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -53,6 +53,14 @@ export const gymNameField = { gymName: z.string().max(100) }; export const memberIdsToDeleteField = { memberIdsToDelete: z.optional(z.array(z.number().int())) }; +export const contentFieldInPost = { content: z.string().max(1000) }; + +export const contentFieldInComment = { content: z.string().max(500) }; + +export const titleField = { title: z.string().max(30) }; + +export const linkField = { link: z.optional(z.string().max(200)) }; + export const categoryParam = object({ params: object({ ...categoryField, diff --git a/src/schemas/post.schema.ts b/src/schemas/post.schema.ts index 3e9ce9f..701e25d 100644 --- a/src/schemas/post.schema.ts +++ b/src/schemas/post.schema.ts @@ -1,4 +1,5 @@ import { TypeOf, object, z } from "zod"; +import { contentFieldInPost, linkField, titleField } from "./fields"; export const createPost = object({ title: z.string().max(30), @@ -6,4 +7,14 @@ export const createPost = object({ link: z.optional(z.string().max(200)), }); -export type CreatePostSchema = TypeOf; +const body = object({ + ...titleField, + ...contentFieldInPost, + ...linkField, +}); + +export const createPostSchema = object({ + body: body, +}); + +export type CreatePostBody = TypeOf; diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index c3655f4..d437e89 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -1,20 +1,20 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; +import { PostType } from "../types/post-type.enum"; +import { CreateCommentBody } from "../schemas/comment.schema"; +import { CreatePostBody } from "../schemas/post.schema"; import { getBookmark, insertOrDeleteBookmark } from "../daos/bookmark.dao"; import { findComment, getCommentCount, insertComment } from "../daos/comment.dao"; import { findImage } from "../daos/image.dao"; import { findPostByType, findPostByAuthorId, findBookmarkedPost, getPost, insertPost } from "../daos/post.dao"; import { readCommentsResonseDTO, readPostResponseDTO, readPostsResponseDTO } from "../dtos/posts.dto"; -import { CreateCommentSchema } from "../schemas/comment.schema"; -import { CreatePostSchema } from "../schemas/post.schema"; -import { PostType } from "../types/post-type.enum"; export const readCommunityPosts = async (userId: number | undefined, query) => { const result = await findPostByType(userId, query.cursorId, PostType.Community); return readPostsResponseDTO(result); }; -export const readPostsByAuthor = async (userId: number, query) => { +export const readMyPosts = async (userId: number, query) => { const result = await findPostByAuthorId(userId, query.cursorId); return readPostsResponseDTO(result); }; @@ -24,12 +24,12 @@ export const readBookmarkedPosts = async (userId: number, query) => { return readPostsResponseDTO(result); }; -export const createOrDeleteBookmark = async (userId, params) => { +export const createOrDeleteBookmark = async (userId: number, params) => { await insertOrDeleteBookmark(userId, params.postId); return; }; -export const readPost = async (userId, params) => { +export const readPost = async (userId: number, params) => { const postId = params.postId; const post = handlePostNotFound(await getPost(postId)); const imageUrls = await findImage(postId); @@ -46,13 +46,13 @@ const checkIsBookmarked = async (userId: number | undefined, postId: number) => return Boolean(await getBookmark(userId, postId)); }; -export const createCommunityPost = async (userId: number, body: CreatePostSchema) => { +export const createCommunityPost = async (userId: number, body: CreatePostBody) => { await insertPost(userId, body, PostType.Community); return; }; -export const createComment = async (userId: number, params, body: CreateCommentSchema) => { - const postId = params.postId; +export const createComment = async (userId: number, params, body: CreateCommentBody) => { + const postId: number = params.postId; handlePostNotFound(await getPost(postId)); await insertComment(userId, postId, body); return; From 4c60e5340d3c5e3ba9ad17796b295922941a2d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 30 Jan 2024 23:04:01 +0900 Subject: [PATCH 047/147] =?UTF-8?q?Feat:=20createMemberSchema=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20#8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/members.route.ts | 4 +++- src/schemas/fields.ts | 2 ++ src/schemas/post.schema.ts | 8 +------- src/schemas/team.schema.ts | 10 ++++++++++ 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/routes/members.route.ts b/src/routes/members.route.ts index 824887b..58f0bab 100644 --- a/src/routes/members.route.ts +++ b/src/routes/members.route.ts @@ -2,9 +2,11 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; import { addMember } from "../controllers/members.controller"; +import { validate } from "../middlewares/validate.middleware"; +import { createMemberSchema } from "../schemas/team.schema"; export const membersRouter = express.Router(); membersRouter.use(verifyUser); -membersRouter.post("/", asyncHandler(addMember)); +membersRouter.post("/", validate(createMemberSchema), asyncHandler(addMember)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 0b67513..e112796 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -61,6 +61,8 @@ export const titleField = { title: z.string().max(30) }; export const linkField = { link: z.optional(z.string().max(200)) }; +export const inviteCodeField = { inviteCode: z.string().max(100) }; + export const categoryParam = object({ params: object({ ...categoryField, diff --git a/src/schemas/post.schema.ts b/src/schemas/post.schema.ts index 701e25d..116f700 100644 --- a/src/schemas/post.schema.ts +++ b/src/schemas/post.schema.ts @@ -1,12 +1,6 @@ -import { TypeOf, object, z } from "zod"; +import { TypeOf, object } from "zod"; import { contentFieldInPost, linkField, titleField } from "./fields"; -export const createPost = object({ - title: z.string().max(30), - content: z.optional(z.string().max(1000)), - link: z.optional(z.string().max(200)), -}); - const body = object({ ...titleField, ...contentFieldInPost, diff --git a/src/schemas/team.schema.ts b/src/schemas/team.schema.ts index 7cede39..2ed4782 100644 --- a/src/schemas/team.schema.ts +++ b/src/schemas/team.schema.ts @@ -5,6 +5,7 @@ import { descriptionField, genderFieldInTeam, gymNameField, + inviteCodeField, logoField, memberIdsToDeleteField, nameField, @@ -35,6 +36,10 @@ const updateTeamBodyWithoutMemberIdsToDelete = object({ ...commonFields, }); +const createMemberBody = object({ + ...inviteCodeField, +}); + export const createTeamSchema = object({ body: createTeamBody, }); @@ -43,6 +48,11 @@ export const updateTeamSchema = object({ body: updateTeamBody, }); +export const createMemberSchema = object({ + body: createMemberBody, +}); + export type CreateTeamBody = TypeOf; export type UpdateTeamBody = TypeOf; export type UpdateTeamBodyWithoutMemberIdsToDelete = TypeOf; +export type CreateMemberBody = TypeOf; From 42b757d24ddbb4de05210bc12f25632385f3f44a Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Wed, 31 Jan 2024 03:20:48 +0900 Subject: [PATCH 048/147] =?UTF-8?q?Refactor:=20validate=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=82=AC=EC=9A=A9=20=EA=B0=80=EB=8A=A5=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20game=20schema=20=EC=88=98=EC=A0=95=20#51?= =?UTF-8?q?=20#53=20(#64)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/games.dao.ts | 4 ++-- src/routes/games.route.ts | 8 ++++---- src/schemas/fields.ts | 13 +++++++++++++ src/schemas/game.schema.ts | 21 ++++++++++++++++++--- src/services/games.service.ts | 6 +++--- 5 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/daos/games.dao.ts b/src/daos/games.dao.ts index a4ca523..7bbfff5 100644 --- a/src/daos/games.dao.ts +++ b/src/daos/games.dao.ts @@ -1,7 +1,7 @@ import db from "../models"; import { Sequelize } from "sequelize"; import { getStatusById } from "../constants/status.constant"; -import { CreateGameSchema } from "../schemas/game.schema"; +import { CreateGameBody } from "../schemas/game.schema"; import { getTeamIdByLeaderId } from "../daos/team.dao"; export const findGamesByDate = async (date, category) => { @@ -113,7 +113,7 @@ export const getGameDetail = async (gameId) => { }); }; -export const insertGame = async (hostTeamId, data: CreateGameSchema, category) => { +export const insertGame = async (hostTeamId, data: CreateGameBody, category) => { await db.Game.create({ hostTeamId: hostTeamId, gameTime: data.gameTime, diff --git a/src/routes/games.route.ts b/src/routes/games.route.ts index 7c44fe8..15cfd09 100644 --- a/src/routes/games.route.ts +++ b/src/routes/games.route.ts @@ -10,8 +10,8 @@ import { addGame, modifyGame, } from "../controllers/games.controller"; -import { createGame, updateGame } from "../schemas/game.schema"; -import { validateBody } from "../middlewares/validate.middleware"; +import { createGameSchema, updateGameSchema } from "../schemas/game.schema"; +import { validate } from "../middlewares/validate.middleware"; export const gamesRouter = express.Router(); @@ -30,5 +30,5 @@ gamesRouter.get("/apply-avail", asyncHandler(fetchTeamsAvailById)); gamesRouter.get("/:gameId", asyncHandler(fetchGameDetail)); // 연습경기 모집글 작성/수정 -gamesRouter.post("/", validateBody(createGame), asyncHandler(addGame)); -gamesRouter.put("/:gameId", validateBody(updateGame), asyncHandler(modifyGame)); +gamesRouter.post("/", validate(createGameSchema), asyncHandler(addGame)); +gamesRouter.put("/:gameId", validate(updateGameSchema), asyncHandler(modifyGame)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index e112796..8096b0b 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -68,3 +68,16 @@ export const categoryParam = object({ ...categoryField, }), }); + +export const hostTeamIdField = { hostTeamId: z.number().int().optional() }; + +export const gameTimeField = { + gameTime: z.preprocess((arg) => { + if (typeof arg == "string") { + return new Date(arg); + } + return arg; + }, z.date()), +}; + +export const descriptionFieldInGame = { description: z.string() }; diff --git a/src/schemas/game.schema.ts b/src/schemas/game.schema.ts index fd4cc0e..fddbdc3 100644 --- a/src/schemas/game.schema.ts +++ b/src/schemas/game.schema.ts @@ -1,4 +1,5 @@ import { TypeOf, object, z } from "zod"; +import { hostTeamIdField, gameTimeField, descriptionFieldInGame } from "./fields"; const fields = { hostTeamId: z.number().int().optional(), @@ -17,14 +18,28 @@ const fields = { // status: z.number().int().min(0), }; +const body = object({ + ...hostTeamIdField, + ...gameTimeField, + ...descriptionFieldInGame, +}); + export const createGame = object({ ...fields, }); +export const createGameSchema = object({ + body: body, +}); + +export type CreateGameBody = TypeOf; + export const updateGame = object({ - // memberIdsToDelete: z.optional(z.array(z.number().int())), ...fields, }); -export type CreateGameSchema = TypeOf; -export type UpdateGameSchema = TypeOf; +export const updateGameSchema = object({ + body: body, +}); + +export type UpdateGameBody = TypeOf; diff --git a/src/services/games.service.ts b/src/services/games.service.ts index ff8d517..c4a5594 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -19,7 +19,7 @@ import { findMemberInfoWithoutLeaderByTeamId, getMemberCountByTeamId } from "../ import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; import { getGameById } from "../daos/games.dao"; import { readGameResponseDTO, readGameDetailResponseDTO } from "../dtos/games.dto"; -import { CreateGameSchema, UpdateGameSchema } from "../schemas/game.schema"; +import { CreateGameBody, UpdateGameBody } from "../schemas/game.schema"; export const readGamesByDate = async (query) => { const games = await findGamesByDate(query.date, query.category); @@ -62,7 +62,7 @@ export const readGameDetail = async (params) => { return readGameDetailResponseDTO(gameDetail, teamDetail, leaderInfo, memberInfo); }; -export const createGame = async (userId, body: CreateGameSchema) => { +export const createGame = async (userId, body: CreateGameBody) => { const hostTeamId = await getTeamIdByLeaderId(userId); const category = await getTeamCategoryByLeaderId(userId); @@ -75,7 +75,7 @@ export const createGame = async (userId, body: CreateGameSchema) => { return; }; -export const updateGame = async (userId, params, body: UpdateGameSchema) => { +export const updateGame = async (userId, params, body: UpdateGameBody) => { const gameId = params.gameId; const game = await getGameById(gameId, userId); if (!game) { From 2b841d9c0b15cac535e275c3a3882793181cce2f Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Wed, 31 Jan 2024 03:58:33 +0900 Subject: [PATCH 049/147] =?UTF-8?q?[FEAT]=20=EB=8C=80=EA=B4=80=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EA=B8=80=20=EC=9E=91=EC=84=B1=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#67)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #66 * Feat: 컨트롤러 함수 추가 #66 --- src/controllers/posts.controller.ts | 4 ++++ src/routes/posts.route.ts | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index 1a6ed47..3d78aed 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -43,3 +43,7 @@ export const addComment = async (req, res: Response, next) => { export const fetchComments = async (req: Request, res: Response, next) => { res.send(response(status.SUCCESS, await readComments(req.params, req.query))); }; + +export const addRentPost = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await createCommunityPost(req.user.id, req.body))); +}; diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index 595cc58..0838413 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -10,6 +10,7 @@ import { addCommunityPost, addComment, fetchComments, + addRentPost, } from "../controllers/posts.controller"; import { validate } from "../middlewares/validate.middleware"; import { createPostSchema } from "../schemas/post.schema"; @@ -32,3 +33,6 @@ postsRouter.post("/:postId/comments", verifyUser, validate(createCommentSchema), postsRouter.get("/:postId/comments", verifyUser, asyncHandler(fetchComments)); postsRouter.get("/:postId", verifyUser, asyncHandler(fetchPost)); + +// 대관정보 글 작성 +postsRouter.post("/rent", verifyUser, validate(createPostSchema), asyncHandler(addRentPost)); From 455b3be3c841c16d15bb9fa894bd6a464f3b14b4 Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Wed, 31 Jan 2024 04:11:09 +0900 Subject: [PATCH 050/147] =?UTF-8?q?Revert=20"[FEAT]=20=EB=8C=80=EA=B4=80?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EA=B8=80=20=EC=9E=91=EC=84=B1=20API=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84"=20(#69)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/posts.controller.ts | 4 ---- src/routes/posts.route.ts | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index 3d78aed..1a6ed47 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -43,7 +43,3 @@ export const addComment = async (req, res: Response, next) => { export const fetchComments = async (req: Request, res: Response, next) => { res.send(response(status.SUCCESS, await readComments(req.params, req.query))); }; - -export const addRentPost = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await createCommunityPost(req.user.id, req.body))); -}; diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index 0838413..595cc58 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -10,7 +10,6 @@ import { addCommunityPost, addComment, fetchComments, - addRentPost, } from "../controllers/posts.controller"; import { validate } from "../middlewares/validate.middleware"; import { createPostSchema } from "../schemas/post.schema"; @@ -33,6 +32,3 @@ postsRouter.post("/:postId/comments", verifyUser, validate(createCommentSchema), postsRouter.get("/:postId/comments", verifyUser, asyncHandler(fetchComments)); postsRouter.get("/:postId", verifyUser, asyncHandler(fetchPost)); - -// 대관정보 글 작성 -postsRouter.post("/rent", verifyUser, validate(createPostSchema), asyncHandler(addRentPost)); From 059dcd52668f918e3367ad9b320bda4d61bf045c Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Wed, 31 Jan 2024 04:12:01 +0900 Subject: [PATCH 051/147] =?UTF-8?q?[FEAT]=20=EB=8C=80=EA=B4=80=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EA=B8=80=20=EC=9E=91=EC=84=B1=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#70)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #66 * Feat: 컨트롤러 함수 추가 #66 * Fix: 컨트롤러 함수 수정 #66 * Feat: 서비스 함수 추가 #66 --- src/controllers/posts.controller.ts | 5 +++++ src/routes/posts.route.ts | 4 ++++ src/services/posts.service.ts | 5 +++++ 3 files changed, 14 insertions(+) diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index 1a6ed47..aef6326 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -10,6 +10,7 @@ import { createCommunityPost, createComment, readComments, + createRentPost, } from "../services/posts.service"; export const fetchCommunityPosts = async (req, res: Response, next) => { @@ -43,3 +44,7 @@ export const addComment = async (req, res: Response, next) => { export const fetchComments = async (req: Request, res: Response, next) => { res.send(response(status.SUCCESS, await readComments(req.params, req.query))); }; + +export const addRentPost = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await createRentPost(req.user.id, req.body))); +}; diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index 595cc58..0838413 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -10,6 +10,7 @@ import { addCommunityPost, addComment, fetchComments, + addRentPost, } from "../controllers/posts.controller"; import { validate } from "../middlewares/validate.middleware"; import { createPostSchema } from "../schemas/post.schema"; @@ -32,3 +33,6 @@ postsRouter.post("/:postId/comments", verifyUser, validate(createCommentSchema), postsRouter.get("/:postId/comments", verifyUser, asyncHandler(fetchComments)); postsRouter.get("/:postId", verifyUser, asyncHandler(fetchPost)); + +// 대관정보 글 작성 +postsRouter.post("/rent", verifyUser, validate(createPostSchema), asyncHandler(addRentPost)); diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index d437e89..9bd3139 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -73,3 +73,8 @@ export const readComments = async (params, query) => { const comments = await findComment(params.postId, cursorId); return readCommentsResonseDTO(comments); }; + +export const createRentPost = async (userId: number, body: CreatePostBody) => { + await insertPost(userId, body, PostType.RentalInfo); + return; +}; From 1cbfe900329ac5c7ae6cf11c8023b39aa17e3274 Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Wed, 31 Jan 2024 04:52:22 +0900 Subject: [PATCH 052/147] =?UTF-8?q?[FEAT]=20=EB=8C=80=EA=B4=80=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EA=B8=80=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20API=20=EA=B5=AC=ED=98=84=20(#71)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #68 * Feat: 컨트롤러 함수 추가 #68 * Feat: 서비스 함수 추가 #68 --- src/controllers/posts.controller.ts | 6 ++++++ src/routes/posts.route.ts | 4 ++++ src/services/posts.service.ts | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index aef6326..c9c0eee 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -11,6 +11,7 @@ import { createComment, readComments, createRentPost, + readRentPosts, } from "../services/posts.service"; export const fetchCommunityPosts = async (req, res: Response, next) => { @@ -47,4 +48,9 @@ export const fetchComments = async (req: Request, res: Response, next) => { export const addRentPost = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await createRentPost(req.user.id, req.body))); + // res.send(response(status.SUCCESS, await createRentPost(2, req.body))); +}; + +export const fetchRentPosts = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readRentPosts(req.user?.id, req.query))); }; diff --git a/src/routes/posts.route.ts b/src/routes/posts.route.ts index 0838413..57923fa 100644 --- a/src/routes/posts.route.ts +++ b/src/routes/posts.route.ts @@ -11,6 +11,7 @@ import { addComment, fetchComments, addRentPost, + fetchRentPosts, } from "../controllers/posts.controller"; import { validate } from "../middlewares/validate.middleware"; import { createPostSchema } from "../schemas/post.schema"; @@ -36,3 +37,6 @@ postsRouter.get("/:postId", verifyUser, asyncHandler(fetchPost)); // 대관정보 글 작성 postsRouter.post("/rent", verifyUser, validate(createPostSchema), asyncHandler(addRentPost)); + +// 대관정보 글 목록 조회 +postsRouter.get("/rent", verifyUserIfExists, asyncHandler(fetchRentPosts)); diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index 9bd3139..0e59973 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -78,3 +78,8 @@ export const createRentPost = async (userId: number, body: CreatePostBody) => { await insertPost(userId, body, PostType.RentalInfo); return; }; + +export const readRentPosts = async (userId: number | undefined, query) => { + const result = await findPostByType(userId, query.cursorId, PostType.RentalInfo); + return readPostsResponseDTO(result); +}; From cee3d7162641cb8447ad8607af59a2b21941218a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Wed, 31 Jan 2024 18:21:18 +0900 Subject: [PATCH 053/147] =?UTF-8?q?Refactor:=20=EA=B5=AC=EA=B8=80=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20API=20=EA=B5=AC=ED=98=84=20#1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/auth.controller.ts | 6 +++++- src/routes/auth.route.ts | 4 +++- src/services/auth.service.ts | 25 +++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/controllers/auth.controller.ts b/src/controllers/auth.controller.ts index daa747e..3e3b8ad 100644 --- a/src/controllers/auth.controller.ts +++ b/src/controllers/auth.controller.ts @@ -1,7 +1,11 @@ import { Request, Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { kakaoLogin, naverLogin, generateNewAccessToken, logoutUser } from "../services/auth.service"; +import { googleLogin, kakaoLogin, naverLogin, generateNewAccessToken, logoutUser } from "../services/auth.service"; + +export const authGoogle = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await googleLogin(req.body))); +}; export const authKakao = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await kakaoLogin(req.body))); diff --git a/src/routes/auth.route.ts b/src/routes/auth.route.ts index 833b824..8553d63 100644 --- a/src/routes/auth.route.ts +++ b/src/routes/auth.route.ts @@ -1,10 +1,12 @@ import express from "express"; import asyncHandler from "express-async-handler"; -import { authKakao, authNaver, refreshAccessToken, logout } from "../controllers/auth.controller"; +import { authGoogle, authKakao, authNaver, refreshAccessToken, logout } from "../controllers/auth.controller"; import { verifyUser } from "../middlewares/auth.middleware"; export const authRouter = express.Router(); +authRouter.post("/google", asyncHandler(authGoogle)); + authRouter.post("/kakao", asyncHandler(authKakao)); authRouter.post("/naver", asyncHandler(authNaver)); diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index db44f47..d9f1717 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -18,6 +18,31 @@ import { Payload } from "../types/payload.interface"; export const tokenType = "Bearer "; +export const googleLogin = async (body) => { + const accessToken = await getGoogleAccessToken(body.code); + const userInfo = await getGoogleUserInfo(accessToken); + const payload = await createOrReadUser(userInfo); + return login(payload); +}; + +const getGoogleAccessToken = async (code: string) => { + const url = "https://oauth2.googleapis.com/token"; + const response = await redaxios.post( + `${url}?client_id=${process.env.GOOGLE_CLIENT_ID}&client_secret=${process.env.GOOGLE_CLIENT_SECRET}&code=${code}&grant_type=authorization_code&redirect_uri=${process.env.GOOGLE_REDIRECT_URI}`, + ); + return response.data.access_token; +}; + +const getGoogleUserInfo = async (accessToken: string) => { + const url = "https://www.googleapis.com/userinfo/v2/me"; + const response = await redaxios.get(`${url}?access_token=${accessToken}`); + return { + provider: Provider.Google, + providerId: response.data.id, + nickname: response.data.name, + }; +}; + export const kakaoLogin = async (body) => { const accessToken = await getKakaoAccessToken(body.code); const userInfo = await getKakaoUserInfo(accessToken); From 26432c66e46f38fdd8ed3b213904aff1e4685722 Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Wed, 31 Jan 2024 21:53:50 +0900 Subject: [PATCH 054/147] =?UTF-8?q?[FEAT]=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20=EC=8B=A0=EC=B2=AD=20API=20=EA=B5=AC=ED=98=84=20(#7?= =?UTF-8?q?4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #65 * Feat: 컨트롤러 함수 추가 #65 * Feat: game-apply 스키마 & 필드 추가 #65 * Feat: 서비스 함수 추가 #65 * Feat: DAO 함수 추가 #65 * Feat: DAO 함수 수정 #65 --- src/controllers/games.controller.ts | 6 ++++++ src/daos/games.dao.ts | 13 +++++++++++++ src/routes/games.route.ts | 4 ++++ src/schemas/fields.ts | 2 ++ src/schemas/game-apply.schema.ts | 16 ++++++++++++++++ src/services/games.service.ts | 9 +++++++++ 6 files changed, 50 insertions(+) create mode 100644 src/schemas/game-apply.schema.ts diff --git a/src/controllers/games.controller.ts b/src/controllers/games.controller.ts index 262beb4..4f04ed5 100644 --- a/src/controllers/games.controller.ts +++ b/src/controllers/games.controller.ts @@ -1,3 +1,4 @@ +import { application } from "express"; import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; @@ -9,6 +10,7 @@ import { readGameDetail, createGame, updateGame, + addGameApplication, } from "../services/games.service"; import { readTeamAvailPreviewById } from "../services/teams.service"; @@ -46,3 +48,7 @@ export const modifyGame = async (req, res, next) => { return res.send(response(status.SUCCESS, await updateGame(req.user.id, req.params, req.body))); // return res.send(response(status.SUCCESS, await updateGame(2, req.params, req.body))); //for testing }; + +export const applyGame = async (req, res, next) => { + return res.send(response(status.SUCCESS, await addGameApplication(req.params, req.body))); +}; diff --git a/src/daos/games.dao.ts b/src/daos/games.dao.ts index 7bbfff5..22b9526 100644 --- a/src/daos/games.dao.ts +++ b/src/daos/games.dao.ts @@ -2,6 +2,7 @@ import db from "../models"; import { Sequelize } from "sequelize"; import { getStatusById } from "../constants/status.constant"; import { CreateGameBody } from "../schemas/game.schema"; +import { ApplyGameBody } from "../schemas/game-apply.schema"; import { getTeamIdByLeaderId } from "../daos/team.dao"; export const findGamesByDate = async (date, category) => { @@ -138,3 +139,15 @@ export const getGameById = async (gameId, userId) => { }, }); }; + +export const insertGameApplication = async (gameId: number, body: ApplyGameBody) => { + const teamId = body.teamId; // ApplyGameBody 내의 teamId를 추출 + + const insertQuery = + "INSERT INTO game_apply (game_id, team_id, created_at, updated_at) VALUES (:gameId, :teamId, NOW(), NOW())"; + + await db.sequelize.query(insertQuery, { + replacements: { gameId: gameId, teamId: teamId }, + type: db.sequelize.QueryTypes.INSERT, + }); +}; diff --git a/src/routes/games.route.ts b/src/routes/games.route.ts index 15cfd09..969565d 100644 --- a/src/routes/games.route.ts +++ b/src/routes/games.route.ts @@ -9,6 +9,7 @@ import { fetchGameDetail, addGame, modifyGame, + applyGame, } from "../controllers/games.controller"; import { createGameSchema, updateGameSchema } from "../schemas/game.schema"; import { validate } from "../middlewares/validate.middleware"; @@ -26,6 +27,9 @@ gamesRouter.get("/by-region", asyncHandler(fetchGamesByRegion)); // 연습경기 신청 가능 팀 목록 조회 gamesRouter.get("/apply-avail", asyncHandler(fetchTeamsAvailById)); +// 연습경기 신청 +gamesRouter.post("/:gameId/application", asyncHandler(applyGame)); + // 연습경기 모집글 상세 조회 gamesRouter.get("/:gameId", asyncHandler(fetchGameDetail)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 8096b0b..f86b8d0 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -81,3 +81,5 @@ export const gameTimeField = { }; export const descriptionFieldInGame = { description: z.string() }; + +export const teamIdField = { teamId: z.number().int() }; diff --git a/src/schemas/game-apply.schema.ts b/src/schemas/game-apply.schema.ts new file mode 100644 index 0000000..d7b4d00 --- /dev/null +++ b/src/schemas/game-apply.schema.ts @@ -0,0 +1,16 @@ +import { TypeOf, object, z } from "zod"; +import { teamIdField } from "./fields"; + +// export const applyGame = object({ +// teamIdField, +// }); + +const gameApplyBody = object({ + ...teamIdField, +}); + +export const applyGameSchema = object({ + body: gameApplyBody, +}); + +export type ApplyGameBody = TypeOf; diff --git a/src/services/games.service.ts b/src/services/games.service.ts index c4a5594..b7d3d99 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -8,6 +8,7 @@ import { getGameDetail, insertGame, setGame, + insertGameApplication, } from "../daos/games.dao"; import { getTeamDetailforGuesting, @@ -20,6 +21,7 @@ import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; import { getGameById } from "../daos/games.dao"; import { readGameResponseDTO, readGameDetailResponseDTO } from "../dtos/games.dto"; import { CreateGameBody, UpdateGameBody } from "../schemas/game.schema"; +import { ApplyGameBody } from "../schemas/game-apply.schema"; export const readGamesByDate = async (query) => { const games = await findGamesByDate(query.date, query.category); @@ -84,3 +86,10 @@ export const updateGame = async (userId, params, body: UpdateGameBody) => { await setGame(game, body); return; }; + +export const addGameApplication = async (params, body: ApplyGameBody) => { + const gameId = params.gameId; + + await insertGameApplication(gameId, body); + return; +}; From a970a554f319ec28064c6749cd1e0f44d75ccd25 Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Fri, 2 Feb 2024 02:02:27 +0900 Subject: [PATCH 055/147] =?UTF-8?q?[FEAT]=20=ED=98=B8=EC=8A=A4=ED=8C=85=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=ED=8C=80=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #68 * Feat: 컨트롤러 함수 추가 #68 * Feat: 서비스 함수 추가 #68 * Feat: 라우트 추가 #68 * Feat: 컨트롤러 함수 추가 #68 * Feat: 서비스 함수 추가 #68 * Feat: DAO 함수 추가 #68 --- src/controllers/matchings.controller.ts | 7 ++++++- src/daos/matching.dao.ts | 26 ++++++++++++++++++++++++- src/daos/team.dao.ts | 11 +++++++++++ src/routes/matchings.route.ts | 9 ++++++++- src/services/matchings.service.ts | 7 +++++++ 5 files changed, 57 insertions(+), 3 deletions(-) diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts index d75eb70..189d3c5 100644 --- a/src/controllers/matchings.controller.ts +++ b/src/controllers/matchings.controller.ts @@ -1,6 +1,6 @@ import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readMatchingGuesting, readMatchingHosting } from "../services/matchings.service"; +import { readMatchingGuesting, readMatchingHosting, readHostingApplicantsList } from "../services/matchings.service"; export const matchingGuestingPreview = async (req, res, next) => { return res.send(response(status.SUCCESS, await readMatchingGuesting(2, req.query))); @@ -11,3 +11,8 @@ export const matchingHostingPreview = async (req, res, next) => { return res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); // return res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); }; + +export const fetchHostingApplicantsTeamList = async (req, res, next) => { + // return res.send(response(status.SUCCESS, await readHostingApplicantsList(req.user.id, req.query))); + return res.send(response(status.SUCCESS, await readHostingApplicantsList(1, req.params))); +}; diff --git a/src/daos/matching.dao.ts b/src/daos/matching.dao.ts index 73308bb..aafe0c9 100644 --- a/src/daos/matching.dao.ts +++ b/src/daos/matching.dao.ts @@ -1,6 +1,7 @@ import { Sequelize } from "sequelize"; import db from "../models"; -import { getTeamIdByLeaderId } from "./team.dao"; +import { getTeamIdByLeaderId, getTeamNameByTeamId } from "./team.dao"; +import { getMemberCountByTeamId } from "./member.dao"; const { Op } = require("sequelize"); @@ -77,3 +78,26 @@ export const findGuestsOfMatchingHosting = async (userId, date) => { // attributes: ["gameTime", "status"], // order: [["created_at", "DESC"]], // }); + +export const getHostingApplicantsTeamList = async (gameId: number) => { + const selectQuery = "SELECT team_id FROM game_apply WHERE game_id = :gameId;"; + + const teamIdsResult = await db.sequelize.query(selectQuery, { + type: db.sequelize.QueryTypes.SELECT, + replacements: { gameId: gameId }, + }); + + const teamsWithNames = await Promise.all( + teamIdsResult.map(async (team) => { + const name = await getTeamNameByTeamId(team.team_id); + const memberCount = await getMemberCountByTeamId(team.team_id); + return { + team_id: team.team_id, + name: name, + memberCount: memberCount, + }; + }), + ); + + return teamsWithNames; +}; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index f517796..590ef0a 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -118,6 +118,17 @@ export const getTeamCategoryByLeaderId = async (userId: number) => { return team?.category; }; +export const getTeamNameByTeamId = async (teamId: number) => { + const team = await db.Team.findOne({ + raw: true, + where: { + id: teamId, + }, + attributes: ["name"], + }); + return team?.name; +}; + export const setTeam = async (team, body: UpdateTeamBodyWithoutMemberIdsToDelete) => { Object.keys(body).forEach((field) => { team[field] = body[field]; diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts index c7f115e..64eb8b1 100644 --- a/src/routes/matchings.route.ts +++ b/src/routes/matchings.route.ts @@ -1,9 +1,16 @@ import express from "express"; import asyncHandler from "express-async-handler"; -import { matchingGuestingPreview, matchingHostingPreview } from "../controllers/matchings.controller"; +import { + matchingGuestingPreview, + matchingHostingPreview, + fetchHostingApplicantsTeamList, +} from "../controllers/matchings.controller"; export const matchingsRouter = express.Router({ mergeParams: true }); matchingsRouter.get("/guesting", asyncHandler(matchingGuestingPreview)); matchingsRouter.get("/hosting", asyncHandler(matchingHostingPreview)); + +// 호스팅 내역 > 신청팀 목록 조회 +matchingsRouter.get("/hosting/:gameId", asyncHandler(fetchHostingApplicantsTeamList)); diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index 2ddb451..7bb6af9 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -1,5 +1,6 @@ import { findGuestsOfMatchingGuesting, findGuestsOfMatchingHosting } from "../daos/matching.dao"; import { getMemberCountByTeamId } from "../daos/member.dao"; +import { getHostingApplicantsTeamList } from "../daos/matching.dao"; import { readMatchingGuestingResponseDTO } from "../dtos/matchings.dto"; export const readMatchingGuesting = async (userId, query) => { @@ -29,3 +30,9 @@ export const readMatchingHosting = async (userId, query) => { return readMatchingGuestingResponseDTO(matchingHostings); }; + +export const readHostingApplicantsList = async (userId, params) => { + const gameId = params.gameId; + + return await getHostingApplicantsTeamList(gameId); +}; From 1aaddf0ef0d374fb00c62e53fb177cb53e9735bf Mon Sep 17 00:00:00 2001 From: ByeonYujin124 <52813483+ByeonYujin124@users.noreply.github.com> Date: Fri, 2 Feb 2024 14:16:06 +0900 Subject: [PATCH 056/147] =?UTF-8?q?=EB=A7=A4=EC=B9=AD=EB=82=B4=EC=97=AD=20?= =?UTF-8?q?API=20=EA=B5=AC=ED=98=84=20(#78)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FEAT) 게스트 모집글 상세조회 API 구현 #37 * FEAT) 게스트 모집글 작성 API 구현 #38 * FEAT) 게스트 모집글 작성 API 수정 #38 * FEAT) 게스트 모집글 수정 API 구현 #39 * FEAT) 게스트 신청 API 구현 #40 * Fix: where parameter "id" has invalid "undefined" value 에러 해결 #37 * Fix: Incorrect integer value: '[object Promise]' for column 'team_id' 에러 해결 #38 * fix: GUEST_NOT_FOUND 발생 해결 #39 * fix) 게스트 모집글 작성 API TEAM 유무 확인 추가 #38 * Fix) 게스트 신청 API 승인여부 컬럼 추가 * [FEAT] 날짜별 호스팅 내역 조회 API 구현 #58 * Fix) 날짜별 호스팅 내역 조회 API 연습게임 조회 추가 #58 * [FEAT] 호스팅 신청자 목록 조회 #72 * [FEAT] 게스트 신청자 승인 #75 * Fix) 날짜별 게스팅 내역 조회 API 연습게임 조회 추가 #41 * Fix) varifyUser 추가 #41 --------- Co-authored-by: 김민정 --- src/config/response.status.ts | 7 ++ src/controllers/matchings.controller.ts | 44 ++++---- src/daos/matchings.dao.ts | 131 ++++++++++++++++++++++++ src/dtos/matchings.dto.ts | 38 ++++--- src/routes/guests.route.ts | 10 +- src/routes/matchings.route.ts | 37 ++++--- src/services/matchings.service.ts | 104 ++++++++++++------- 7 files changed, 279 insertions(+), 92 deletions(-) create mode 100644 src/daos/matchings.dao.ts diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 5cd0c27..f60a0fb 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -113,6 +113,13 @@ export const status: { [key: string]: Status } = { message: "게스팅을 찾을 수 없습니다.", }, + GUESTUSER_NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "GUESTER001", + message: "해당 게스트 신청 유저를 찾을 수 없습니다.", + }, + // game error GAME_NOT_FOUND: { status: StatusCodes.NOT_FOUND, diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts index 189d3c5..77ca49c 100644 --- a/src/controllers/matchings.controller.ts +++ b/src/controllers/matchings.controller.ts @@ -1,18 +1,26 @@ -import { response } from "../config/response"; -import { status } from "../config/response.status"; -import { readMatchingGuesting, readMatchingHosting, readHostingApplicantsList } from "../services/matchings.service"; - -export const matchingGuestingPreview = async (req, res, next) => { - return res.send(response(status.SUCCESS, await readMatchingGuesting(2, req.query))); - // return res.send(response(status.SUCCESS, await readMatchingGuesting(req.user.id, req.query))); -}; - -export const matchingHostingPreview = async (req, res, next) => { - return res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); - // return res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); -}; - -export const fetchHostingApplicantsTeamList = async (req, res, next) => { - // return res.send(response(status.SUCCESS, await readHostingApplicantsList(req.user.id, req.query))); - return res.send(response(status.SUCCESS, await readHostingApplicantsList(1, req.params))); -}; +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { + readApplyGuestingUser, + readMatchingGuesting, + readMatchingHosting, + updateGuestStatus, +} from "../services/matchings.service"; + +export const matchingGuestingPreview = async (req, res, next) => { + // return res.send(response(status.SUCCESS, await readMatchingGuesting(1, req.query))); + return res.send(response(status.SUCCESS, await readMatchingGuesting(req.user.id, req.query))); +}; + +export const matchingHostingPreview = async (req, res, next) => { + // return res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); + return res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); +}; + +export const ApplyGuestingUserPreview = async (req, res, next) => { + return res.send(response(status.SUCCESS, await readApplyGuestingUser(req.params))); +}; + +export const modifyGuestStatus = async (req, res, next) => { + return res.send(response(status.SUCCESS, await updateGuestStatus(req.params))); +}; diff --git a/src/daos/matchings.dao.ts b/src/daos/matchings.dao.ts new file mode 100644 index 0000000..6d5eff9 --- /dev/null +++ b/src/daos/matchings.dao.ts @@ -0,0 +1,131 @@ +import { Sequelize } from "sequelize"; +import db from "../models"; +import { getTeamIdByLeaderId } from "./team.dao"; + +const { Op } = require("sequelize"); + +export const findGuestsOfMatchingGuesting = async (userId, date) => { + const guestUserResults = await db.GuestUser.findAll({ + raw: true, + where: { + userId: userId, + status: 1, + }, + attributes: ["guestId"], + }); + + const guestIds = guestUserResults.map((guestUser) => guestUser.guestId); + + const guestResults = await db.Guest.findAll({ + raw: true, + where: { + id: { + [Op.in]: guestIds, + }, + [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + }, + include: [ + { + model: db.Team, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["gameTime"], + }); + + return guestResults; +}; + +export const findGamesOfMatchingGuesting = async (userId, date) => { + const team_id = await getTeamIdByLeaderId(userId); + const gameApplyResults = await db.sequelize.query("SELECT game_id FROM game_apply WHERE team_id = :team_id", { + type: db.sequelize.QueryTypes.SELECT, + replacements: { team_id: team_id }, + }); + + const gameIds = gameApplyResults.map((gameApply) => gameApply.game_id); + const games = await db.Game.findAll({ + raw: true, + where: { + id: { + [Op.in]: gameIds, + }, + opposing_team_id: team_id, + [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + }, + include: [ + { + model: db.Team, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["gameTime"], + }); + + return games; +}; + +export const findGuestsOfMatchingHosting = async (userId, date) => { + const teamId = await getTeamIdByLeaderId(userId); + return await db.Guest.findAll({ + raw: true, + where: { + teamId: teamId, + [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + }, + include: [ + { + model: db.Team, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["gameTime"], + }); +}; + +export const findGamesOfMatchingHosting = async (userId, date) => { + const teamId = await getTeamIdByLeaderId(userId); + return await db.Game.findAll({ + raw: true, + where: { + hostTeamId: teamId, + [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + }, + include: [ + { + model: db.Team, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["gameTime"], + }); +}; + +export const getApplyGuestingUser = async (guestingId) => { + return await db.GuestUser.findAll({ + raw: true, + where: { + guestId: guestingId, + }, + include: [ + { + model: db.User, + attributes: ["nickname", "height"], + }, + ], + attributes: ["status"], + }); +}; + +export const setGuestStatus = async (guestUser) => { + guestUser["status"] = 1; + await guestUser.save(); +}; + +export const getGuestUserById = async (guestUserId) => { + return await db.GuestUser.findOne({ + where: { + id: guestUserId, + }, + }); +}; diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index a867a0e..909afff 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -1,15 +1,23 @@ -import { getAgeGroup } from "../constants/age-group.constant"; -import { getGender } from "../constants/gender.constant"; -import { getLevelById } from "../constants/level.constant"; - -export const readMatchingGuestingResponseDTO = (matchings) => { - return matchings.map((matching) => ({ - gameTime: matching.gameTime, - teamName: matching["Team.name"], - teamRegion: matching["Team.region"], - teamGender: getGender(matching["Team.gender"]), - memberCount: matching.memberCount, - teamAgeGroup: getAgeGroup(matching["Team.ageGroup"]), - teamSkillLevel: getLevelById(matching["Team.skillLevel"]), - })); -}; +import { getAgeGroup } from "../constants/age-group.constant"; +import { getTeamGender } from "../constants/gender.constant"; +import { getLevelById } from "../constants/level.constant"; + +export const readMatchingResponseDTO = (matchings) => { + return matchings.map((matching) => ({ + gameTime: matching.gameTime, + teamName: matching["Team.name"], + teamRegion: matching["Team.region"], + teamGender: getTeamGender(matching["Team.gender"]), + memberCount: matching.memberCount, + teamAgeGroup: getAgeGroup(matching["Team.ageGroup"]), + teamSkillLevel: getLevelById(matching["Team.skillLevel"]), + })); +}; + +export const readApplyGuestingUserResponseDTO = (guestingUsers) => { + return guestingUsers.map((guestingUsers) => ({ + nickname: guestingUsers["User.nickname"], + height: guestingUsers["User.height"], + status: guestingUsers.status, + })); +}; diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index c336dcd..7132131 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -1,5 +1,8 @@ import express from "express"; import asyncHandler from "express-async-handler"; +import { validateBody } from "../middlewares/validate.middleware"; +import { verifyUser } from "../middlewares/auth.middleware"; +import { createGuesting, updateGuesting } from "../schemas/guest.schema"; import { GuestingPreview, GuestingPreviewByLevel, @@ -10,17 +13,14 @@ import { modifyGuesting, applicationGuesting, } from "../controllers/guest.controller"; -import { createGuesting, updateGuesting } from "../schemas/guest.schema"; -import { validateBody } from "../middlewares/validate.middleware"; export const guestsRouter = express.Router({ mergeParams: true }); +guestsRouter.use(verifyUser); + guestsRouter.post("/", validateBody(createGuesting), asyncHandler(addGuesting)); -// guestsRouter.post("/", asyncHandler(addGuesting)); guestsRouter.put("/:guestingId", validateBody(updateGuesting), asyncHandler(modifyGuesting)); -// guestsRouter.put("/:guestingId", asyncHandler(modifyGuesting)); - guestsRouter.get("/", asyncHandler(GuestingPreview)); diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts index 64eb8b1..0d2ea39 100644 --- a/src/routes/matchings.route.ts +++ b/src/routes/matchings.route.ts @@ -1,16 +1,21 @@ -import express from "express"; -import asyncHandler from "express-async-handler"; -import { - matchingGuestingPreview, - matchingHostingPreview, - fetchHostingApplicantsTeamList, -} from "../controllers/matchings.controller"; - -export const matchingsRouter = express.Router({ mergeParams: true }); - -matchingsRouter.get("/guesting", asyncHandler(matchingGuestingPreview)); - -matchingsRouter.get("/hosting", asyncHandler(matchingHostingPreview)); - -// 호스팅 내역 > 신청팀 목록 조회 -matchingsRouter.get("/hosting/:gameId", asyncHandler(fetchHostingApplicantsTeamList)); +import express from "express"; +import asyncHandler from "express-async-handler"; +import { verifyUser } from "../middlewares/auth.middleware"; +import { + ApplyGuestingUserPreview, + matchingGuestingPreview, + matchingHostingPreview, + modifyGuestStatus, +} from "../controllers/matchings.controller"; + +export const matchingsRouter = express.Router({ mergeParams: true }); + +matchingsRouter.use(verifyUser); + +matchingsRouter.get("/guesting", asyncHandler(matchingGuestingPreview)); + +matchingsRouter.get("/hosting", asyncHandler(matchingHostingPreview)); + +matchingsRouter.get("/hosting/:guestingId", asyncHandler(ApplyGuestingUserPreview)); + +matchingsRouter.put("/confirmGuest/:guestUserId", asyncHandler(modifyGuestStatus)); diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index 7bb6af9..64f26fd 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -1,38 +1,66 @@ -import { findGuestsOfMatchingGuesting, findGuestsOfMatchingHosting } from "../daos/matching.dao"; -import { getMemberCountByTeamId } from "../daos/member.dao"; -import { getHostingApplicantsTeamList } from "../daos/matching.dao"; -import { readMatchingGuestingResponseDTO } from "../dtos/matchings.dto"; - -export const readMatchingGuesting = async (userId, query) => { - const matchingGuestings = await findGuestsOfMatchingGuesting(userId, query.date); - for (const matchingGuesting of matchingGuestings) { - matchingGuestings.memberCount = (await getMemberCountByTeamId(matchingGuesting["Team.id"])) + 1; - } - - // const matchingGames = await findGamesOfMatchingGuesting(userId, query.date); - // for (const matchingGame of matchingGames) { - // matchingGames.memberCount = (await getMemberCountByTeamId(matchingGame["HostTeam.id"])) + 1; - // } - - return readMatchingGuestingResponseDTO(matchingGuestings); -}; - -export const readMatchingHosting = async (userId, query) => { - const matchingHostings = await findGuestsOfMatchingHosting(userId, query.date); - for (const matchingHosting of matchingHostings) { - matchingHostings.memberCount = (await getMemberCountByTeamId(matchingHosting["Team.id"])) + 1; - } - - // const matchingGames = await findGamesOfMatchingGuesting(userId, query.date); - // for (const matchingGame of matchingGames) { - // matchingGames.memberCount = (await getMemberCountByTeamId(matchingGame["HostTeam.id"])) + 1; - // } - - return readMatchingGuestingResponseDTO(matchingHostings); -}; - -export const readHostingApplicantsList = async (userId, params) => { - const gameId = params.gameId; - - return await getHostingApplicantsTeamList(gameId); -}; +import { BaseError } from "../config/error"; +import { + findGamesOfMatchingGuesting, + findGuestsOfMatchingHosting, + findGuestsOfMatchingGuesting, + findGamesOfMatchingHosting, + getApplyGuestingUser, + setGuestStatus, + getGuestUserById, +} from "../daos/matchings.dao"; +import { getMemberCountByTeamId } from "../daos/member.dao"; +import { readApplyGuestingUserResponseDTO, readMatchingResponseDTO } from "../dtos/matchings.dto"; +import { status } from "../config/response.status"; + +export const readMatchingGuesting = async (userId, query) => { + const matchingGuestings = await findGuestsOfMatchingGuesting(userId, query.date); + for (const matchingGuesting of matchingGuestings) { + matchingGuesting.memberCount = (await getMemberCountByTeamId(matchingGuesting["Team.id"])) + 1; + } + + const matchingGames = await findGamesOfMatchingGuesting(userId, query.date); + for (const matchingGame of matchingGames) { + matchingGame.memberCount = (await getMemberCountByTeamId(matchingGame["Team.id"])) + 1; + } + + const guestingResponseDTO = readMatchingResponseDTO(matchingGuestings); + const gameResponseDTO = readMatchingResponseDTO(matchingGames); + + return { guesting: guestingResponseDTO, game: gameResponseDTO }; +}; + +export const readMatchingHosting = async (userId, query) => { + const matchingGuestings = await findGuestsOfMatchingHosting(userId, query.date); + for (const matchingGuesting of matchingGuestings) { + matchingGuesting.memberCount = (await getMemberCountByTeamId(matchingGuesting["Team.id"])) + 1; + } + + const matchingGames = await findGamesOfMatchingHosting(userId, query.date); + for (const matchingGame of matchingGames) { + matchingGame.memberCount = (await getMemberCountByTeamId(matchingGame["Team.id"])) + 1; + } + + const guestingResponseDTO = readMatchingResponseDTO(matchingGuestings); + const gameResponseDTO = readMatchingResponseDTO(matchingGames); + + return { guesting: guestingResponseDTO, game: gameResponseDTO }; +}; + +export const readApplyGuestingUser = async (params) => { + const guestingId = params.guestingId; + const applyGuestingUser = await getApplyGuestingUser(guestingId); + + return readApplyGuestingUserResponseDTO(applyGuestingUser); +}; + +export const updateGuestStatus = async (params) => { + const guestUserId = params.guestUserId; + const guestUser = await getGuestUserById(guestUserId); + if (!guestUser) { + throw new BaseError(status.GUESTUSER_NOT_FOUND); + } + + await setGuestStatus(guestUser); + return; +}; + From f59b58f9179c2523a71bb3984629866d3c3594aa Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Sat, 3 Feb 2024 00:46:01 +0900 Subject: [PATCH 057/147] =?UTF-8?q?[FEAT]=20=EC=8B=A0=EC=B2=AD=ED=8C=80=20?= =?UTF-8?q?=EC=8A=B9=EC=9D=B8=20API=20=EA=B5=AC=ED=98=84=20(#80)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: 라우트 추가 #77 * Feat: 컨트롤러 함수 추가 #77 * Feat: 서비스 함수 추가 #77 * Feat: DAO 함수 추가 #77 --- src/controllers/matchings.controller.ts | 63 +++++++++++++++---------- src/daos/games.dao.ts | 4 ++ src/routes/matchings.route.ts | 48 ++++++++++--------- src/services/teams.service.ts | 9 ++++ 4 files changed, 77 insertions(+), 47 deletions(-) diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts index 77ca49c..6fb787c 100644 --- a/src/controllers/matchings.controller.ts +++ b/src/controllers/matchings.controller.ts @@ -1,26 +1,37 @@ -import { response } from "../config/response"; -import { status } from "../config/response.status"; -import { - readApplyGuestingUser, - readMatchingGuesting, - readMatchingHosting, - updateGuestStatus, -} from "../services/matchings.service"; - -export const matchingGuestingPreview = async (req, res, next) => { - // return res.send(response(status.SUCCESS, await readMatchingGuesting(1, req.query))); - return res.send(response(status.SUCCESS, await readMatchingGuesting(req.user.id, req.query))); -}; - -export const matchingHostingPreview = async (req, res, next) => { - // return res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); - return res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); -}; - -export const ApplyGuestingUserPreview = async (req, res, next) => { - return res.send(response(status.SUCCESS, await readApplyGuestingUser(req.params))); -}; - -export const modifyGuestStatus = async (req, res, next) => { - return res.send(response(status.SUCCESS, await updateGuestStatus(req.params))); -}; +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { + readApplyGuestingUser, + readMatchingGuesting, + readMatchingHosting, + updateGuestStatus, +} from "../services/matchings.service"; +import { addOpposingTeam } from "../services/teams.service"; + +export const matchingGuestingPreview = async (req, res, next) => { + // return res.send(response(status.SUCCESS, await readMatchingGuesting(1, req.query))); + return res.send(response(status.SUCCESS, await readMatchingGuesting(req.user.id, req.query))); +}; + +export const matchingHostingPreview = async (req, res, next) => { + // return res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); + return res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); +}; + +export const ApplyGuestingUserPreview = async (req, res, next) => { + return res.send(response(status.SUCCESS, await readApplyGuestingUser(req.params))); +}; + +export const modifyGuestStatus = async (req, res, next) => { + return res.send(response(status.SUCCESS, await updateGuestStatus(req.params))); +}; + +export const fetchHostingApplicantsTeamList = async (req, res, next) => { + // return res.send(response(status.SUCCESS, await readHostingApplicantsList(req.user.id, req.query))); + return res.send(response(status.SUCCESS, await readHostingApplicantsList(1, req.params))); +}; + +export const gameApplicationApproval = async (req, res, next) => { + return res.send(response(status.SUCCESS, await readHostingApplicantsList(req.user.id, req.query))); + // return res.send(response(status.SUCCESS, await addOpposingTeam(1, req.params, req.body))); // for testing +}; \ No newline at end of file diff --git a/src/daos/games.dao.ts b/src/daos/games.dao.ts index 22b9526..75f48c0 100644 --- a/src/daos/games.dao.ts +++ b/src/daos/games.dao.ts @@ -151,3 +151,7 @@ export const insertGameApplication = async (gameId: number, body: ApplyGameBody) type: db.sequelize.QueryTypes.INSERT, }); }; + +export const updateOpposingTeam = async (gameId: number, opposingTeamId: number) => { + await db.Game.update({ opposingTeamId: opposingTeamId, status: 1 }, { where: { id: gameId } }); +}; diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts index 0d2ea39..044a8f1 100644 --- a/src/routes/matchings.route.ts +++ b/src/routes/matchings.route.ts @@ -1,21 +1,27 @@ -import express from "express"; -import asyncHandler from "express-async-handler"; -import { verifyUser } from "../middlewares/auth.middleware"; -import { - ApplyGuestingUserPreview, - matchingGuestingPreview, - matchingHostingPreview, - modifyGuestStatus, -} from "../controllers/matchings.controller"; - -export const matchingsRouter = express.Router({ mergeParams: true }); - -matchingsRouter.use(verifyUser); - -matchingsRouter.get("/guesting", asyncHandler(matchingGuestingPreview)); - -matchingsRouter.get("/hosting", asyncHandler(matchingHostingPreview)); - -matchingsRouter.get("/hosting/:guestingId", asyncHandler(ApplyGuestingUserPreview)); - -matchingsRouter.put("/confirmGuest/:guestUserId", asyncHandler(modifyGuestStatus)); +import express from "express"; +import asyncHandler from "express-async-handler"; +import { verifyUser } from "../middlewares/auth.middleware"; +import { + ApplyGuestingUserPreview, + matchingGuestingPreview, + matchingHostingPreview, + modifyGuestStatus, +} from "../controllers/matchings.controller"; + +export const matchingsRouter = express.Router({ mergeParams: true }); + +matchingsRouter.use(verifyUser); + +matchingsRouter.get("/guesting", asyncHandler(matchingGuestingPreview)); + +matchingsRouter.get("/hosting", asyncHandler(matchingHostingPreview)); + +matchingsRouter.get("/hosting/:guestingId", asyncHandler(ApplyGuestingUserPreview)); + +matchingsRouter.put("/confirmGuest/:guestUserId", asyncHandler(modifyGuestStatus)); + +// 호스팅 내역 > 신청팀 목록 조회 +matchingsRouter.get("/hosting/:gameId", asyncHandler(fetchHostingApplicantsTeamList)); + +// 호스팅 내역 > 신청 승인 +matchingsRouter.post("/hosting/:gameId", asyncHandler(gameApplicationApproval)); \ No newline at end of file diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index 66d78a9..340f64c 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -13,6 +13,7 @@ import { } from "../daos/team.dao"; import { deleteMembers, findMemberToDelete } from "../daos/member.dao"; import { getUserInfoByCategory } from "../daos/user.dao"; +import { updateOpposingTeam } from "../daos/games.dao"; import { readTeamDetailResponseDTO } from "../dtos/teams.dto"; export const readTeamPreviews = async (userId: number, query) => { @@ -67,3 +68,11 @@ const readMembersInfo = async (details, category: Category) => { export const readTeamAvailPreviewById = async (userId, query) => { return await findTeamPreviewByCategoryForLeader(userId, query.category); }; + +export const addOpposingTeam = async (userId: number, params, body) => { + const gameId = params.gameId; + const opposingTeamId = body.teamId; + + await updateOpposingTeam(gameId, opposingTeamId); + return; +}; From f4dcc08ac57e0d4e6f7dc2c799381f1547ff0a5d Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Sat, 3 Feb 2024 01:00:24 +0900 Subject: [PATCH 058/147] =?UTF-8?q?[FEAT]=20=ED=8C=80=20=EB=A6=AC=EB=B7=B0?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1=20API=20=EA=B5=AC=ED=98=84=20(#81)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: team_review 테이블 생성 #61 * Feat: schema 생성 #61 * Feat: 라우트 추가 #61 * Feat: 컨트롤러 함수 추가 #61 * Feat: schema에 team_match_id 필드 추가 #61 * Style: schema 필드명 수정 #61 * Feat: games DAO 함수 작성, getGameById 함수 이름 getGameByUserId으로 변경 #61 * Feat: DAO 함수 추가 #61 * Feat: 서비스 함수 추가, 응답 에러 추가 #61 --- src/app.ts | 2 + src/config/response.status.ts | 26 +++++++++++++ src/controllers/reviews.controller.ts | 8 ++++ src/daos/games.dao.ts | 12 +++++- src/daos/guest.dao.ts | 22 ++++++++++- src/daos/team-review.dao.ts | 28 ++++++++++++++ src/daos/team.dao.ts | 11 ++++++ src/models/game.model.ts | 1 + src/models/guest.model.ts | 1 + src/models/team-review.ts | 52 ++++++++++++++++++++++++++ src/models/team.model.ts | 1 + src/models/user.model.ts | 1 + src/routes/reviews.route.ts | 12 ++++++ src/schemas/fields.ts | 4 ++ src/schemas/team-review.schema.ts | 15 ++++++++ src/services/games.service.ts | 4 +- src/services/reviews.service.ts | 53 +++++++++++++++++++++++++++ 17 files changed, 249 insertions(+), 4 deletions(-) create mode 100644 src/controllers/reviews.controller.ts create mode 100644 src/daos/team-review.dao.ts create mode 100644 src/models/team-review.ts create mode 100644 src/routes/reviews.route.ts create mode 100644 src/schemas/team-review.schema.ts create mode 100644 src/services/reviews.service.ts diff --git a/src/app.ts b/src/app.ts index 605ed53..579e5ad 100644 --- a/src/app.ts +++ b/src/app.ts @@ -17,6 +17,7 @@ import { guestsRouter } from "./routes/guests.route"; import { postsRouter } from "./routes/posts.route"; import { usersRouter } from "./routes/users.route"; import { matchingsRouter } from "./routes/matchings.route"; +import { reviewsRouter } from "./routes/reviews.route"; const app = express(); @@ -42,6 +43,7 @@ app.use("/guests", guestsRouter); app.use("/posts", postsRouter); app.use("/users", usersRouter); app.use("/matchings", matchingsRouter); +app.use("/reviews", reviewsRouter); app.use((req: Request, res: Response, next: NextFunction) => { const err = new BaseError(status.NOT_FOUND); diff --git a/src/config/response.status.ts b/src/config/response.status.ts index f60a0fb..09ec0d3 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -143,4 +143,30 @@ export const status: { [key: string]: Status } = { code: "USER001", message: "요청한 유저를 찾을 수 없습니다.", }, + + //review err + MATCH_ID_REQUIRED: { + status: StatusCodes.BAD_REQUEST, + isSuccess: false, + code: "REVIEW001", + message: "TeamMatchId 또는 GuestMatchId 중 하나가 필요합니다.", + }, + NO_REVIEW_TARGET: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "REVIEW002", + message: "리뷰할 대상이 없습니다.", + }, + REVIEW_NOT_CURRENTLY_WRITABLE: { + status: StatusCodes.BAD_REQUEST, + isSuccess: false, + code: "REVIEW003", + message: "리뷰 작성 가능한 시간이 아닙니다.", + }, + REVIEW_ALREADY_WRITTEN: { + status: StatusCodes.CONFLICT, + isSuccess: false, + code: "REVIEW004", + message: "이미 리뷰를 작성했습니다.", + }, }; diff --git a/src/controllers/reviews.controller.ts b/src/controllers/reviews.controller.ts new file mode 100644 index 0000000..872b20e --- /dev/null +++ b/src/controllers/reviews.controller.ts @@ -0,0 +1,8 @@ +import { Response } from "express"; +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { createTeamReview } from "../services/reviews.service"; + +export const addTeamReview = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await createTeamReview(req.user.id, req.body))); +}; diff --git a/src/daos/games.dao.ts b/src/daos/games.dao.ts index 75f48c0..1ab7227 100644 --- a/src/daos/games.dao.ts +++ b/src/daos/games.dao.ts @@ -130,7 +130,7 @@ export const setGame = async (game, body) => { await game.save(); }; -export const getGameById = async (gameId, userId) => { +export const getGameByUserId = async (gameId, userId) => { const hostTeamId = await getTeamIdByLeaderId(userId); return await db.Game.findOne({ where: { @@ -152,6 +152,16 @@ export const insertGameApplication = async (gameId: number, body: ApplyGameBody) }); }; +export const getGame = async (gameId: number) => { + return await db.Game.findOne({ + raw: true, + where: { + id: gameId, + }, + attributes: ["hostTeamId", "opposingTeamId", "gameTime"], + }); +}; + export const updateOpposingTeam = async (gameId: number, opposingTeamId: number) => { await db.Game.update({ opposingTeamId: opposingTeamId, status: 1 }, { where: { id: gameId } }); }; diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index 70c2a6c..be9252e 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -1,5 +1,5 @@ -import { Sequelize } from "sequelize"; import db from "../models"; +import { Sequelize } from "sequelize"; import { CreateGuestingSchema } from "../schemas/guest.schema"; import { getTeamIdByLeaderId } from "./team.dao"; @@ -117,3 +117,23 @@ export const getGuestingById = async (guestingId, userId) => { }, }); }; + +export const getGuestingByAcceptedUserId = async (guestingId: number, userId: number) => { + return await db.Guest.findOne({ + raw: true, + where: { + id: guestingId, + }, + include: [ + { + model: db.GuestUser, + where: { + userId, + status: 1, + }, + attributes: [], + }, + ], + attributes: ["teamId", "gameTime"], + }); +}; diff --git a/src/daos/team-review.dao.ts b/src/daos/team-review.dao.ts new file mode 100644 index 0000000..37405ec --- /dev/null +++ b/src/daos/team-review.dao.ts @@ -0,0 +1,28 @@ +import db from "../models"; +import { CreateTeamReviewBody } from "../schemas/team-review.schema"; + +export const getExistingTeamReview = async ( + userId: number, + reviewedTeamId: number, + teamMatchId?: number, + guestMatchId?: number, +) => { + return await db.TeamReview.findOne({ + raw: true, + where: { + reviewerId: userId, + reviewedTeamId, + teamMatchId: teamMatchId ?? null, + guestMatchId: guestMatchId ?? null, + }, + attributes: ["id"], + }); +}; + +export const insertTeamReview = async (userId: number, reviewedTeamId: number, body: CreateTeamReviewBody) => { + await db.TeamReview.create({ + reviewerId: userId, + reviewedTeamId, + ...body, + }); +}; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index 590ef0a..ddab9d8 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -2,6 +2,7 @@ import db from "../models"; import { defaultLevel } from "../constants/level.constant"; import { CreateTeamBody, UpdateTeamBodyWithoutMemberIdsToDelete } from "../schemas/team.schema"; import { Category } from "../types/category.enum"; +import { Op } from "sequelize"; export const findTeamPreviewByCategory = async (userId: number, category: Category) => { const teamsAsLeader = await db.Team.findAll({ @@ -146,3 +147,13 @@ export const findTeamPreviewByCategoryForLeader = async (userId: number, categor attributes: ["name"], }); }; + +export const findLeaderId = async (hostingTeamId: number, opposingTeamId: number) => { + return await db.Team.findAll({ + raw: true, + where: { + [Op.or]: [{ id: hostingTeamId }, { id: opposingTeamId }], + }, + attributes: ["id", "leaderId"], + }); +}; diff --git a/src/models/game.model.ts b/src/models/game.model.ts index 98e1a13..dc8f2c4 100644 --- a/src/models/game.model.ts +++ b/src/models/game.model.ts @@ -52,6 +52,7 @@ class Game extends Model, InferCreationAttributes> { db.Game.belongsToMany(db.Team, { through: "game_apply", }); + db.Game.hasMany(db.TeamReview, { foreignKey: "team_match_id" }); } } diff --git a/src/models/guest.model.ts b/src/models/guest.model.ts index e118ae2..afd4614 100644 --- a/src/models/guest.model.ts +++ b/src/models/guest.model.ts @@ -42,6 +42,7 @@ class Guest extends Model, InferCreationAttributes static associate(db) { db.Guest.hasMany(db.GuestUser, { foreignKey: "guest_id" }); db.Guest.belongsTo(db.Team, { foreignKey: "team_id" }); + db.Guest.hasMany(db.TeamReview, { foreignKey: "guest_match_id" }); } } diff --git a/src/models/team-review.ts b/src/models/team-review.ts new file mode 100644 index 0000000..6a2ed36 --- /dev/null +++ b/src/models/team-review.ts @@ -0,0 +1,52 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class TeamReview extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + TeamReview.init( + { + reviewerId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + reviewedTeamId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + teamMatchId: { + type: new DataTypes.INTEGER(), + allowNull: true, + }, + guestMatchId: { + type: new DataTypes.INTEGER(), + allowNull: true, + }, + skillScore: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + mannerScore: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "TeamReview", + tableName: "team_review", + paranoid: false, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.TeamReview.belongsTo(db.User, { foreignKey: "reviewer_id" }); + db.TeamReview.belongsTo(db.Team, { foreignKey: "reviewed_team_id" }); + db.TeamReview.belongsTo(db.Game, { foreignKey: "team_match_id" }); + db.TeamReview.belongsTo(db.Guest, { foreignKey: "guest_match_id" }); + } +} + +module.exports = TeamReview; diff --git a/src/models/team.model.ts b/src/models/team.model.ts index ca2d2f3..d358523 100644 --- a/src/models/team.model.ts +++ b/src/models/team.model.ts @@ -79,6 +79,7 @@ class Team extends Model, InferCreationAttributes> { through: "game_apply", }); db.Team.hasMany(db.Guest, { foreignKey: "team_id" }); + db.Team.hasMany(db.TeamReview, { foreignKey: "reviewed_team_id" }); } } diff --git a/src/models/user.model.ts b/src/models/user.model.ts index b02af96..498799f 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -53,6 +53,7 @@ class User extends Model, InferCreationAttributes> { db.User.hasMany(db.Post, { foreignKey: "author_id" }); db.User.hasMany(db.Bookmark, { foreignKey: "user_id" }); db.User.hasMany(db.Comment, { foreignKey: "author_id" }); + db.User.hasMany(db.TeamReview, { foreignKey: "reviewer_id" }); } } diff --git a/src/routes/reviews.route.ts b/src/routes/reviews.route.ts new file mode 100644 index 0000000..d31b3e5 --- /dev/null +++ b/src/routes/reviews.route.ts @@ -0,0 +1,12 @@ +import express from "express"; +import asyncHandler from "express-async-handler"; +import { verifyUser } from "../middlewares/auth.middleware"; +import { validate } from "../middlewares/validate.middleware"; +import { createTeamReviewSchema } from "../schemas/team-review.schema"; +import { addTeamReview } from "../controllers/reviews.controller"; + +export const reviewsRouter = express.Router(); + +reviewsRouter.use(verifyUser); + +reviewsRouter.post("/team", validate(createTeamReviewSchema), asyncHandler(addTeamReview)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index f86b8d0..5356605 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -83,3 +83,7 @@ export const gameTimeField = { export const descriptionFieldInGame = { description: z.string() }; export const teamIdField = { teamId: z.number().int() }; + +export const skillScoreField = { skillScore: z.number().int().min(1).max(5) }; + +export const mannerScoreField = { mannerScore: z.number().int().min(1).max(5) }; diff --git a/src/schemas/team-review.schema.ts b/src/schemas/team-review.schema.ts new file mode 100644 index 0000000..00836c6 --- /dev/null +++ b/src/schemas/team-review.schema.ts @@ -0,0 +1,15 @@ +import { TypeOf, object, z } from "zod"; +import { mannerScoreField, skillScoreField } from "./fields"; + +const createTeamReviewBody = object({ + teamMatchId: z.optional(z.number().int()), + guestMatchId: z.optional(z.number().int()), + ...skillScoreField, + ...mannerScoreField, +}); + +export const createTeamReviewSchema = object({ + body: createTeamReviewBody, +}); + +export type CreateTeamReviewBody = TypeOf; diff --git a/src/services/games.service.ts b/src/services/games.service.ts index b7d3d99..c9ad651 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -18,7 +18,7 @@ import { } from "../daos/team.dao"; import { findMemberInfoWithoutLeaderByTeamId, getMemberCountByTeamId } from "../daos/member.dao"; import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; -import { getGameById } from "../daos/games.dao"; +import { getGameByUserId } from "../daos/games.dao"; import { readGameResponseDTO, readGameDetailResponseDTO } from "../dtos/games.dto"; import { CreateGameBody, UpdateGameBody } from "../schemas/game.schema"; import { ApplyGameBody } from "../schemas/game-apply.schema"; @@ -79,7 +79,7 @@ export const createGame = async (userId, body: CreateGameBody) => { export const updateGame = async (userId, params, body: UpdateGameBody) => { const gameId = params.gameId; - const game = await getGameById(gameId, userId); + const game = await getGameByUserId(gameId, userId); if (!game) { throw new BaseError(status.GAME_NOT_FOUND); } diff --git a/src/services/reviews.service.ts b/src/services/reviews.service.ts new file mode 100644 index 0000000..918a8d8 --- /dev/null +++ b/src/services/reviews.service.ts @@ -0,0 +1,53 @@ +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; +import { CreateTeamReviewBody } from "../schemas/team-review.schema"; +import { getGame } from "../daos/games.dao"; +import { findLeaderId } from "../daos/team.dao"; +import { getGuestingByAcceptedUserId } from "../daos/guest.dao"; +import { getExistingTeamReview, insertTeamReview } from "../daos/team-review.dao"; + +export const createTeamReview = async (userId: number, body: CreateTeamReviewBody) => { + const { teamMatchId, guestMatchId } = body; + if (!(teamMatchId || guestMatchId) || (teamMatchId && guestMatchId)) { + throw new BaseError(status.MATCH_ID_REQUIRED); + } + + const result = await retrieveReviewedTeamIdAndGameTime(userId, teamMatchId, guestMatchId); + + //validate user write permission + if (!result?.reviewedTeamId) { + throw new BaseError(status.NO_REVIEW_TARGET); + } + const currentTime = getCurrentTime(); + console.log(result.gameTime, currentTime); + if (result.gameTime > currentTime) { + throw new BaseError(status.REVIEW_NOT_CURRENTLY_WRITABLE); + } + const review = await getExistingTeamReview(userId, result.reviewedTeamId, teamMatchId, guestMatchId); + if (review) { + throw new BaseError(status.REVIEW_ALREADY_WRITTEN); + } + + await insertTeamReview(userId, result.reviewedTeamId, body); + return; +}; + +const retrieveReviewedTeamIdAndGameTime = async (userId: number, teamMatchId?: number, guestMatchId?: number) => { + if (teamMatchId) { + const teamMatch = await getGame(teamMatchId); + const teams = await findLeaderId(teamMatch.hostTeamId, teamMatch.opposingTeamId); + for (const team of teams) { + if (team.leaderId == userId) { + return { reviewedTeamId: team.id, gameTime: teamMatch.gameTime }; + } + } + } + if (guestMatchId) { + const guestMatch = await getGuestingByAcceptedUserId(guestMatchId, userId); + return { reviewedTeamId: guestMatch?.teamId, gameTime: guestMatch?.gameTime }; + } +}; + +const getCurrentTime = (): Date => { + return new Date(); +}; From 497dee53f1938fa9692bf115e686b5e2fe7f5a6a Mon Sep 17 00:00:00 2001 From: Doeun Kim <102356909+doeunyy@users.noreply.github.com> Date: Sat, 3 Feb 2024 01:01:13 +0900 Subject: [PATCH 059/147] =?UTF-8?q?Feat:=20=EB=82=A0=EC=95=84=EA=B0=84=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EB=B3=B5=EA=B5=AC=20#73=20(#82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/matchings.controller.ts | 5 +++-- src/daos/matchings.dao.ts | 26 ++++++++++++++++++++++++- src/routes/matchings.route.ts | 4 +++- src/services/matchings.service.ts | 6 ++++++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts index 6fb787c..de1f5aa 100644 --- a/src/controllers/matchings.controller.ts +++ b/src/controllers/matchings.controller.ts @@ -5,6 +5,7 @@ import { readMatchingGuesting, readMatchingHosting, updateGuestStatus, + readHostingApplicantsList, } from "../services/matchings.service"; import { addOpposingTeam } from "../services/teams.service"; @@ -32,6 +33,6 @@ export const fetchHostingApplicantsTeamList = async (req, res, next) => { }; export const gameApplicationApproval = async (req, res, next) => { - return res.send(response(status.SUCCESS, await readHostingApplicantsList(req.user.id, req.query))); + return res.send(response(status.SUCCESS, await addOpposingTeam(req.user.id, req.params, req.body))); // return res.send(response(status.SUCCESS, await addOpposingTeam(1, req.params, req.body))); // for testing -}; \ No newline at end of file +}; diff --git a/src/daos/matchings.dao.ts b/src/daos/matchings.dao.ts index 6d5eff9..1ca0cdb 100644 --- a/src/daos/matchings.dao.ts +++ b/src/daos/matchings.dao.ts @@ -1,6 +1,7 @@ import { Sequelize } from "sequelize"; import db from "../models"; -import { getTeamIdByLeaderId } from "./team.dao"; +import { getTeamIdByLeaderId, getTeamNameByTeamId } from "./team.dao"; +import { getMemberCountByTeamId } from "./member.dao"; const { Op } = require("sequelize"); @@ -129,3 +130,26 @@ export const getGuestUserById = async (guestUserId) => { }, }); }; + +export const getHostingApplicantsTeamList = async (gameId: number) => { + const selectQuery = "SELECT team_id FROM game_apply WHERE game_id = :gameId;"; + + const teamIdsResult = await db.sequelize.query(selectQuery, { + type: db.sequelize.QueryTypes.SELECT, + replacements: { gameId: gameId }, + }); + + const teamsWithNames = await Promise.all( + teamIdsResult.map(async (team) => { + const name = await getTeamNameByTeamId(team.team_id); + const memberCount = await getMemberCountByTeamId(team.team_id); + return { + team_id: team.team_id, + name: name, + memberCount: memberCount, + }; + }), + ); + + return teamsWithNames; +}; diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts index 044a8f1..29be608 100644 --- a/src/routes/matchings.route.ts +++ b/src/routes/matchings.route.ts @@ -6,6 +6,8 @@ import { matchingGuestingPreview, matchingHostingPreview, modifyGuestStatus, + fetchHostingApplicantsTeamList, + gameApplicationApproval, } from "../controllers/matchings.controller"; export const matchingsRouter = express.Router({ mergeParams: true }); @@ -24,4 +26,4 @@ matchingsRouter.put("/confirmGuest/:guestUserId", asyncHandler(modifyGuestStatus matchingsRouter.get("/hosting/:gameId", asyncHandler(fetchHostingApplicantsTeamList)); // 호스팅 내역 > 신청 승인 -matchingsRouter.post("/hosting/:gameId", asyncHandler(gameApplicationApproval)); \ No newline at end of file +matchingsRouter.post("/hosting/:gameId", asyncHandler(gameApplicationApproval)); diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index 64f26fd..6c7a969 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -7,6 +7,7 @@ import { getApplyGuestingUser, setGuestStatus, getGuestUserById, + getHostingApplicantsTeamList, } from "../daos/matchings.dao"; import { getMemberCountByTeamId } from "../daos/member.dao"; import { readApplyGuestingUserResponseDTO, readMatchingResponseDTO } from "../dtos/matchings.dto"; @@ -64,3 +65,8 @@ export const updateGuestStatus = async (params) => { return; }; +export const readHostingApplicantsList = async (userId, params) => { + const gameId = params.gameId; + + return await getHostingApplicantsTeamList(gameId); +}; From 33a4b629198bd771a6c952fdc695d57e5d08ce46 Mon Sep 17 00:00:00 2001 From: mzeong <70924556+mzeong@users.noreply.github.com> Date: Sat, 3 Feb 2024 15:28:43 +0900 Subject: [PATCH 060/147] =?UTF-8?q?[FEAT]=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EC=9E=91=EC=84=B1=20API=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#83)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Feat: schema 추가 #62 * Feat: 라우트 함수 추가 #62 * Feat: 컨트롤러 함수 추가 #62 * Fix: schema 필드명 guest_match_id에서 guestMatchId로 수정 #62 * Feat: schema에 revieweeId 필드 추가 #62 * Feat: user_review 테이블 생성 #62 * Style: model 파일 이름 일관성 있게 수정 #62 * Feat: DAO 함수 추가 #62 * Feat: 서비스 함수 추가 #62 * Fix: model 파일의 model name, table name 수정 #62 * Fix: getLeaderId 함수가 leaderId를 반환하도록 수정 #62 --- src/controllers/reviews.controller.ts | 6 ++- src/daos/team.dao.ts | 11 +++++ src/daos/user-review.dao.ts | 21 +++++++++ src/models/{bookmark.ts => bookmark.model.ts} | 0 src/models/{comment.ts => comment.model.ts} | 0 src/models/guest.model.ts | 1 + src/models/{image.ts => image.model.ts} | 0 .../{team-review.ts => team-review.model.ts} | 0 src/models/user-review.model.ts | 47 +++++++++++++++++++ src/models/user.model.ts | 2 + src/routes/reviews.route.ts | 4 +- src/schemas/fields.ts | 8 ++++ src/schemas/team-review.schema.ts | 8 ++-- src/schemas/user-review.schema.ts | 15 ++++++ src/services/reviews.service.ts | 46 +++++++++++++++--- 15 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 src/daos/user-review.dao.ts rename src/models/{bookmark.ts => bookmark.model.ts} (100%) rename src/models/{comment.ts => comment.model.ts} (100%) rename src/models/{image.ts => image.model.ts} (100%) rename src/models/{team-review.ts => team-review.model.ts} (100%) create mode 100644 src/models/user-review.model.ts create mode 100644 src/schemas/user-review.schema.ts diff --git a/src/controllers/reviews.controller.ts b/src/controllers/reviews.controller.ts index 872b20e..bc9dbc7 100644 --- a/src/controllers/reviews.controller.ts +++ b/src/controllers/reviews.controller.ts @@ -1,8 +1,12 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { createTeamReview } from "../services/reviews.service"; +import { createTeamReview, createUserReview } from "../services/reviews.service"; export const addTeamReview = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await createTeamReview(req.user.id, req.body))); }; + +export const addUserReview = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await createUserReview(req.user.id, req.body))); +}; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index ddab9d8..7bc9441 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -157,3 +157,14 @@ export const findLeaderId = async (hostingTeamId: number, opposingTeamId: number attributes: ["id", "leaderId"], }); }; + +export const getLeaderId = async (teamId: number) => { + const team = await db.Team.findOne({ + raw: true, + where: { + id: teamId, + }, + attributes: ["leaderId"], + }); + return team.leaderId; +}; diff --git a/src/daos/user-review.dao.ts b/src/daos/user-review.dao.ts new file mode 100644 index 0000000..e532f66 --- /dev/null +++ b/src/daos/user-review.dao.ts @@ -0,0 +1,21 @@ +import db from "../models"; +import { CreateUserReviewBody } from "../schemas/user-review.schema"; + +export const getExistingUserReview = async (userId: number, revieweeId: number, guestMatchId: number) => { + return await db.UserReview.findOne({ + rew: true, + where: { + reviewerId: userId, + revieweeId, + guestMatchId, + }, + attributes: ["id"], + }); +}; + +export const insertUserReview = async (userId: number, body: CreateUserReviewBody) => { + await db.UserReview.create({ + reviewerId: userId, + ...body, + }); +}; diff --git a/src/models/bookmark.ts b/src/models/bookmark.model.ts similarity index 100% rename from src/models/bookmark.ts rename to src/models/bookmark.model.ts diff --git a/src/models/comment.ts b/src/models/comment.model.ts similarity index 100% rename from src/models/comment.ts rename to src/models/comment.model.ts diff --git a/src/models/guest.model.ts b/src/models/guest.model.ts index afd4614..5246d9f 100644 --- a/src/models/guest.model.ts +++ b/src/models/guest.model.ts @@ -43,6 +43,7 @@ class Guest extends Model, InferCreationAttributes db.Guest.hasMany(db.GuestUser, { foreignKey: "guest_id" }); db.Guest.belongsTo(db.Team, { foreignKey: "team_id" }); db.Guest.hasMany(db.TeamReview, { foreignKey: "guest_match_id" }); + db.Guest.hasMany(db.UserReview, { foreignKey: "guest_match_id" }); } } diff --git a/src/models/image.ts b/src/models/image.model.ts similarity index 100% rename from src/models/image.ts rename to src/models/image.model.ts diff --git a/src/models/team-review.ts b/src/models/team-review.model.ts similarity index 100% rename from src/models/team-review.ts rename to src/models/team-review.model.ts diff --git a/src/models/user-review.model.ts b/src/models/user-review.model.ts new file mode 100644 index 0000000..daf4458 --- /dev/null +++ b/src/models/user-review.model.ts @@ -0,0 +1,47 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class UserReview extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + UserReview.init( + { + reviewerId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + revieweeId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + guestMatchId: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + skillScore: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + mannerScore: { + type: new DataTypes.INTEGER(), + allowNull: false, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "UserReview", + tableName: "user_review", + paranoid: false, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.UserReview.belongsTo(db.User, { foreignKey: "reviewer_id" }); + db.UserReview.belongsTo(db.User, { foreignKey: "reviewee_id" }); + db.UserReview.belongsTo(db.Guest, { foreignKey: "guest_match_id" }); + } +} + +module.exports = UserReview; diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 498799f..88f7071 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -54,6 +54,8 @@ class User extends Model, InferCreationAttributes> { db.User.hasMany(db.Bookmark, { foreignKey: "user_id" }); db.User.hasMany(db.Comment, { foreignKey: "author_id" }); db.User.hasMany(db.TeamReview, { foreignKey: "reviewer_id" }); + db.User.hasMany(db.UserReview, { foreignKey: "reviewer_id" }); + db.User.hasMany(db.UserReview, { foreignKey: "reviewee_id" }); } } diff --git a/src/routes/reviews.route.ts b/src/routes/reviews.route.ts index d31b3e5..082a4b9 100644 --- a/src/routes/reviews.route.ts +++ b/src/routes/reviews.route.ts @@ -3,10 +3,12 @@ import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; import { validate } from "../middlewares/validate.middleware"; import { createTeamReviewSchema } from "../schemas/team-review.schema"; -import { addTeamReview } from "../controllers/reviews.controller"; +import { addTeamReview, addUserReview } from "../controllers/reviews.controller"; export const reviewsRouter = express.Router(); reviewsRouter.use(verifyUser); reviewsRouter.post("/team", validate(createTeamReviewSchema), asyncHandler(addTeamReview)); + +reviewsRouter.post("/user", validate(createTeamReviewSchema), asyncHandler(addUserReview)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 5356605..e93b0ac 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -87,3 +87,11 @@ export const teamIdField = { teamId: z.number().int() }; export const skillScoreField = { skillScore: z.number().int().min(1).max(5) }; export const mannerScoreField = { mannerScore: z.number().int().min(1).max(5) }; + +export const guestMatchIdFieldInUserReview = { guestMatchId: z.number().int() }; + +export const guestMatchIdFieldInTeamReview = { guestMatchId: z.optional(z.number().int()) }; + +export const teamMatchIdField = { teamMatchId: z.optional(z.number().int()) }; + +export const revieweeIdField = { revieweeId: z.number().int() }; diff --git a/src/schemas/team-review.schema.ts b/src/schemas/team-review.schema.ts index 00836c6..007cca7 100644 --- a/src/schemas/team-review.schema.ts +++ b/src/schemas/team-review.schema.ts @@ -1,9 +1,9 @@ -import { TypeOf, object, z } from "zod"; -import { mannerScoreField, skillScoreField } from "./fields"; +import { TypeOf, object } from "zod"; +import { guestMatchIdFieldInTeamReview, mannerScoreField, skillScoreField, teamMatchIdField } from "./fields"; const createTeamReviewBody = object({ - teamMatchId: z.optional(z.number().int()), - guestMatchId: z.optional(z.number().int()), + ...teamMatchIdField, + ...guestMatchIdFieldInTeamReview, ...skillScoreField, ...mannerScoreField, }); diff --git a/src/schemas/user-review.schema.ts b/src/schemas/user-review.schema.ts new file mode 100644 index 0000000..71f2e71 --- /dev/null +++ b/src/schemas/user-review.schema.ts @@ -0,0 +1,15 @@ +import { TypeOf, object } from "zod"; +import { guestMatchIdFieldInUserReview, mannerScoreField, revieweeIdField, skillScoreField } from "./fields"; + +const createUserReviewBody = object({ + ...revieweeIdField, + ...guestMatchIdFieldInUserReview, + ...skillScoreField, + ...mannerScoreField, +}); + +export const createTeamReviewSchema = object({ + body: createUserReviewBody, +}); + +export type CreateUserReviewBody = TypeOf; diff --git a/src/services/reviews.service.ts b/src/services/reviews.service.ts index 918a8d8..13b3cd8 100644 --- a/src/services/reviews.service.ts +++ b/src/services/reviews.service.ts @@ -2,9 +2,11 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { CreateTeamReviewBody } from "../schemas/team-review.schema"; import { getGame } from "../daos/games.dao"; -import { findLeaderId } from "../daos/team.dao"; +import { findLeaderId, getLeaderId } from "../daos/team.dao"; import { getGuestingByAcceptedUserId } from "../daos/guest.dao"; import { getExistingTeamReview, insertTeamReview } from "../daos/team-review.dao"; +import { CreateUserReviewBody } from "../schemas/user-review.schema"; +import { getExistingUserReview, insertUserReview } from "../daos/user-review.dao"; export const createTeamReview = async (userId: number, body: CreateTeamReviewBody) => { const { teamMatchId, guestMatchId } = body; @@ -18,11 +20,7 @@ export const createTeamReview = async (userId: number, body: CreateTeamReviewBod if (!result?.reviewedTeamId) { throw new BaseError(status.NO_REVIEW_TARGET); } - const currentTime = getCurrentTime(); - console.log(result.gameTime, currentTime); - if (result.gameTime > currentTime) { - throw new BaseError(status.REVIEW_NOT_CURRENTLY_WRITABLE); - } + validateReviewableTime(result.gameTime, getCurrentTime()); const review = await getExistingTeamReview(userId, result.reviewedTeamId, teamMatchId, guestMatchId); if (review) { throw new BaseError(status.REVIEW_ALREADY_WRITTEN); @@ -48,6 +46,42 @@ const retrieveReviewedTeamIdAndGameTime = async (userId: number, teamMatchId?: n } }; +export const createUserReview = async (userId: number, body: CreateUserReviewBody) => { + const { guestMatchId, revieweeId } = body; + const result = await retrieveRevieweeIdAndGameTime(userId, guestMatchId, revieweeId); + + //validate user write permission + if (!result.revieweeId) { + throw new BaseError(status.NO_REVIEW_TARGET); + } + validateReviewableTime(result.gameTime, getCurrentTime()); + const review = await getExistingUserReview(userId, result.revieweeId, guestMatchId); + if (review) { + throw new BaseError(status.REVIEW_ALREADY_WRITTEN); + } + + await insertUserReview(userId, body); + return; +}; + +const retrieveRevieweeIdAndGameTime = async (userId: number, guestMatchId: number, revieweeId: number) => { + const guestMatch = await getGuestingByAcceptedUserId(guestMatchId, revieweeId); + if (!guestMatch) { + return { revieweeId: null, gameTime: null }; + } + const leaderId = await getLeaderId(guestMatch.teamId); + return { + revieweeId: userId === leaderId ? revieweeId : null, + gameTime: guestMatch.gameTime, + }; +}; + +const validateReviewableTime = (gameTime: Date, currentTime: Date) => { + if (gameTime > currentTime) { + throw new BaseError(status.REVIEW_NOT_CURRENTLY_WRITABLE); + } +}; + const getCurrentTime = (): Date => { return new Date(); }; From ccda50620ee59e8ecc5b955345f4ae1f4642f3b2 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 <52813483+ByeonYujin124@users.noreply.github.com> Date: Sat, 3 Feb 2024 18:17:00 +0900 Subject: [PATCH 061/147] =?UTF-8?q?Refactor:=20guest,=20matching=20API=20?= =?UTF-8?q?=EC=A4=91=20=EB=B3=80=EA=B2=BD=EB=90=9C=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EB=B0=8F=20=ED=83=80=EC=9E=85=20=EC=88=98=EC=A0=95=20(#85)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/guest.controller.ts | 45 +++++++---- src/controllers/matchings.controller.ts | 75 +++++++++-------- src/daos/guest.dao.ts | 22 ++--- src/daos/matching.dao.ts | 103 ------------------------ src/daos/matchings.dao.ts | 22 ++--- src/daos/team.dao.ts | 19 ++++- src/dtos/guests.dto.ts | 79 ++++++++++++++---- src/dtos/matchings.dto.ts | 34 ++++++-- src/routes/guests.route.ts | 39 ++++++--- src/routes/matchings.route.ts | 60 +++++++------- src/schemas/fields.ts | 36 ++++++++- src/schemas/guest-user.schema.ts | 18 +++++ src/schemas/guest.schema.ts | 33 +++++--- src/services/guest.service.ts | 25 +++--- src/services/teams.service.ts | 2 +- 15 files changed, 344 insertions(+), 268 deletions(-) delete mode 100644 src/daos/matching.dao.ts create mode 100644 src/schemas/guest-user.schema.ts diff --git a/src/controllers/guest.controller.ts b/src/controllers/guest.controller.ts index 94ed276..34730c9 100644 --- a/src/controllers/guest.controller.ts +++ b/src/controllers/guest.controller.ts @@ -1,3 +1,4 @@ +import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; import { @@ -10,35 +11,47 @@ import { addGuestUser, updateGuesting, } from "../services/guest.service"; +import { createTeam } from "../services/teams.service"; +import { getTeamIdByLeaderId } from "../daos/team.dao"; +import { updateUserProfile } from "../services/users.service"; +import { getUser } from "../daos/user.dao"; -export const addGuesting = async (req, res, next) => { - return res.send(response(status.SUCCESS, await createGuesting(req.user.id, req.body))); +export const addGuesting = async (req, res: Response, next) => { + const teamId = await getTeamIdByLeaderId(req.user.id); + if (!teamId) { + res.send(response(status.SUCCESS, await createTeam(req.user.id, req.body))); + } + res.send(response(status.SUCCESS, await createGuesting(req.user.id, req.body))); }; -export const modifyGuesting = async (req, res, next) => { - return res.send(response(status.SUCCESS, await updateGuesting(req.user.id, req.params, req.body))); +export const modifyGuesting = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await updateGuesting(req.user.id, req.params, req.body))); }; -export const GuestingPreview = async (req, res, next) => { - return res.send(response(status.SUCCESS, await readGuesting(req.query))); +export const GuestingPreview = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readGuesting(req.query))); }; -export const GuestingPreviewByLevel = async (req, res, next) => { - return res.send(response(status.SUCCESS, await readGuestingByLevel(req.query))); +export const GuestingPreviewByLevel = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readGuestingByLevel(req.query))); }; -export const GuestingPreviewByGender = async (req, res, next) => { - return res.send(response(status.SUCCESS, await readGuestingByGender(req.query))); +export const GuestingPreviewByGender = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readGuestingByGender(req.query))); }; -export const GuestingPreviewByRegion = async (req, res, next) => { - return res.send(response(status.SUCCESS, await readGuestingByRegion(req.query))); +export const GuestingPreviewByRegion = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readGuestingByRegion(req.query))); }; -export const DetailedGuestingPreview = async (req, res, next) => { - return res.send(response(status.SUCCESS, await readDetailedGuesting(req.params))); +export const DetailedGuestingPreview = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readDetailedGuesting(req.params))); }; -export const applicationGuesting = async (req, res, next) => { - return res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.params))); +export const applicationGuesting = async (req, res: Response, next) => { + const user = await getUser(req.user.id); + if (!user) { + res.send(response(status.SUCCESS, await updateUserProfile(req.user.id, req.params, req.body))); + } + res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.params))); }; diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts index de1f5aa..5038c7c 100644 --- a/src/controllers/matchings.controller.ts +++ b/src/controllers/matchings.controller.ts @@ -1,38 +1,37 @@ -import { response } from "../config/response"; -import { status } from "../config/response.status"; -import { - readApplyGuestingUser, - readMatchingGuesting, - readMatchingHosting, - updateGuestStatus, - readHostingApplicantsList, -} from "../services/matchings.service"; -import { addOpposingTeam } from "../services/teams.service"; - -export const matchingGuestingPreview = async (req, res, next) => { - // return res.send(response(status.SUCCESS, await readMatchingGuesting(1, req.query))); - return res.send(response(status.SUCCESS, await readMatchingGuesting(req.user.id, req.query))); -}; - -export const matchingHostingPreview = async (req, res, next) => { - // return res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); - return res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); -}; - -export const ApplyGuestingUserPreview = async (req, res, next) => { - return res.send(response(status.SUCCESS, await readApplyGuestingUser(req.params))); -}; - -export const modifyGuestStatus = async (req, res, next) => { - return res.send(response(status.SUCCESS, await updateGuestStatus(req.params))); -}; - -export const fetchHostingApplicantsTeamList = async (req, res, next) => { - // return res.send(response(status.SUCCESS, await readHostingApplicantsList(req.user.id, req.query))); - return res.send(response(status.SUCCESS, await readHostingApplicantsList(1, req.params))); -}; - -export const gameApplicationApproval = async (req, res, next) => { - return res.send(response(status.SUCCESS, await addOpposingTeam(req.user.id, req.params, req.body))); - // return res.send(response(status.SUCCESS, await addOpposingTeam(1, req.params, req.body))); // for testing -}; +import { Response } from "express"; +import { response } from "../config/response"; +import { status } from "../config/response.status"; +import { + readApplyGuestingUser, + readMatchingGuesting, + readMatchingHosting, + updateGuestStatus, + readHostingApplicantsList, +} from "../services/matchings.service"; +import { addOpposingTeam } from "../services/teams.service"; + +export const matchingGuestingPreview = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readMatchingGuesting(req.user.id, req.query))); +}; + +export const matchingHostingPreview = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); +}; + +export const ApplyGuestingUserPreview = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await readApplyGuestingUser(req.params))); +}; + +export const modifyGuestStatus = async (req, res: Response, next) => { + res.send(response(status.SUCCESS, await updateGuestStatus(req.params))); +}; + +export const fetchHostingApplicantsTeamList = async (req, res, next) => { + // return res.send(response(status.SUCCESS, await readHostingApplicantsList(req.user.id, req.query))); + return res.send(response(status.SUCCESS, await readHostingApplicantsList(1, req.params))); +}; + +export const gameApplicationApproval = async (req, res, next) => { + return res.send(response(status.SUCCESS, await addOpposingTeam(req.user.id, req.params, req.body))); + // return res.send(response(status.SUCCESS, await addOpposingTeam(1, req.params, req.body))); // for testing +}; diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index be9252e..dc06ba8 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -1,9 +1,11 @@ import db from "../models"; +import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; import { Sequelize } from "sequelize"; -import { CreateGuestingSchema } from "../schemas/guest.schema"; import { getTeamIdByLeaderId } from "./team.dao"; +import { Category } from "../types/category.enum"; +import { Gender } from "../types/gender.enum"; -export const insertGuesting = async (teamId, data: CreateGuestingSchema) => { +export const insertGuesting = async (teamId: number, data: CreateGuestingBody) => { await db.Guest.create({ teamId: teamId, gameTime: data.gameTime, @@ -12,14 +14,14 @@ export const insertGuesting = async (teamId, data: CreateGuestingSchema) => { }); }; -export const setGuesting = async (guesting, body) => { +export const setGuesting = async (guesting, body: UpdateGuestingBody) => { Object.keys(body).forEach((field) => { guesting[field] = body[field]; }); await guesting.save(); }; -export const findGuesting = async (date, category) => { +export const findGuesting = async (date: string, category: Category) => { return await db.Guest.findAll({ raw: true, where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), @@ -36,7 +38,7 @@ export const findGuesting = async (date, category) => { }); }; -export const findGuestingByGender = async (date, category, gender) => { +export const findGuestingByGender = async (date: string, category: Category, gender: Gender) => { return await db.Guest.findAll({ raw: true, where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), @@ -54,7 +56,7 @@ export const findGuestingByGender = async (date, category, gender) => { }); }; -export const findGuestingByLevel = async (date, category, skillLevel) => { +export const findGuestingByLevel = async (date: string, category: Category, skillLevel: number) => { return await db.Guest.findAll({ raw: true, where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), @@ -72,7 +74,7 @@ export const findGuestingByLevel = async (date, category, skillLevel) => { }); }; -export const findGuestingByRegion = async (date, category, region) => { +export const findGuestingByRegion = async (date: string, category: Category, region: number) => { return await db.Guest.findAll({ raw: true, where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), @@ -90,7 +92,7 @@ export const findGuestingByRegion = async (date, category, region) => { }); }; -export const getDetailedGuesting = async (guestingId) => { +export const getDetailedGuesting = async (guestingId: number) => { return await db.Guest.findOne({ raw: true, where: { @@ -100,7 +102,7 @@ export const getDetailedGuesting = async (guestingId) => { }); }; -export const InsertGuestUser = async (guestingId, userId) => { +export const InsertGuestUser = async (guestingId: number, userId: number) => { await db.GuestUser.create({ guestId: guestingId, userId: userId, @@ -108,7 +110,7 @@ export const InsertGuestUser = async (guestingId, userId) => { }); }; -export const getGuestingById = async (guestingId, userId) => { +export const getGuestingById = async (guestingId: number, userId: number) => { const teamId = await getTeamIdByLeaderId(userId); return await db.Guest.findOne({ where: { diff --git a/src/daos/matching.dao.ts b/src/daos/matching.dao.ts deleted file mode 100644 index aafe0c9..0000000 --- a/src/daos/matching.dao.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { Sequelize } from "sequelize"; -import db from "../models"; -import { getTeamIdByLeaderId, getTeamNameByTeamId } from "./team.dao"; -import { getMemberCountByTeamId } from "./member.dao"; - -const { Op } = require("sequelize"); - -export const findGuestsOfMatchingGuesting = async (userId, date) => { - const guestUserResults = await db.GuestUser.findAll({ - raw: true, - where: { - userId: userId, - status: 1, - }, - attributes: ["guestId"], - }); - - const guestIds = guestUserResults.map((guestUser) => guestUser.guestId); - - const guestResults = await db.Guest.findAll({ - raw: true, - where: { - id: { - [Op.in]: guestIds, - }, - [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - }, - include: [ - { - model: db.Team, - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - }, - ], - attributes: ["gameTime"], - }); - - return guestResults; -}; - -export const findGuestsOfMatchingHosting = async (userId, date) => { - const teamId = await getTeamIdByLeaderId(userId); - return await db.Guest.findAll({ - raw: true, - where: { - teamId: teamId, - [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - }, - include: [ - { - model: db.Team, - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - }, - ], - attributes: ["gameTime"], - }); -}; - -// export const findGamesOfMatchingGuesting = async (userId, date) => { -// const guestUserResults = await db.game.findAll({ -// raw: true, -// where: { -// userId: userId, -// }, -// attributes: ["guestId"], -// }); - -// const guestIds = guestUserResults.map((guestUser) => guestUser.guestId); -// const games = await db.Game.findAll({ -// raw: true, -// where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), -// include: [ -// { -// model: db.Team, -// as: "HostTeam", -// attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], -// }, -// ], -// attributes: ["gameTime", "status"], -// order: [["created_at", "DESC"]], -// }); - -export const getHostingApplicantsTeamList = async (gameId: number) => { - const selectQuery = "SELECT team_id FROM game_apply WHERE game_id = :gameId;"; - - const teamIdsResult = await db.sequelize.query(selectQuery, { - type: db.sequelize.QueryTypes.SELECT, - replacements: { gameId: gameId }, - }); - - const teamsWithNames = await Promise.all( - teamIdsResult.map(async (team) => { - const name = await getTeamNameByTeamId(team.team_id); - const memberCount = await getMemberCountByTeamId(team.team_id); - return { - team_id: team.team_id, - name: name, - memberCount: memberCount, - }; - }), - ); - - return teamsWithNames; -}; diff --git a/src/daos/matchings.dao.ts b/src/daos/matchings.dao.ts index 1ca0cdb..a4a8acd 100644 --- a/src/daos/matchings.dao.ts +++ b/src/daos/matchings.dao.ts @@ -1,11 +1,12 @@ import { Sequelize } from "sequelize"; import db from "../models"; +import { UpdateGuestUserBody } from "../schemas/guest-user.schema"; import { getTeamIdByLeaderId, getTeamNameByTeamId } from "./team.dao"; import { getMemberCountByTeamId } from "./member.dao"; const { Op } = require("sequelize"); -export const findGuestsOfMatchingGuesting = async (userId, date) => { +export const findGuestsOfMatchingGuesting = async (userId: number, date: string) => { const guestUserResults = await db.GuestUser.findAll({ raw: true, where: { @@ -37,8 +38,8 @@ export const findGuestsOfMatchingGuesting = async (userId, date) => { return guestResults; }; -export const findGamesOfMatchingGuesting = async (userId, date) => { - const team_id = await getTeamIdByLeaderId(userId); +export const findGamesOfMatchingGuesting = async (userId: number, date: string) => { + const team_id = await NaIdByLeaderId(userId); const gameApplyResults = await db.sequelize.query("SELECT game_id FROM game_apply WHERE team_id = :team_id", { type: db.sequelize.QueryTypes.SELECT, replacements: { team_id: team_id }, @@ -66,7 +67,7 @@ export const findGamesOfMatchingGuesting = async (userId, date) => { return games; }; -export const findGuestsOfMatchingHosting = async (userId, date) => { +export const findGuestsOfMatchingHosting = async (userId: number, date: string) => { const teamId = await getTeamIdByLeaderId(userId); return await db.Guest.findAll({ raw: true, @@ -84,7 +85,7 @@ export const findGuestsOfMatchingHosting = async (userId, date) => { }); }; -export const findGamesOfMatchingHosting = async (userId, date) => { +export const findGamesOfMatchingHosting = async (userId: number, date: string) => { const teamId = await getTeamIdByLeaderId(userId); return await db.Game.findAll({ raw: true, @@ -102,7 +103,7 @@ export const findGamesOfMatchingHosting = async (userId, date) => { }); }; -export const getApplyGuestingUser = async (guestingId) => { +export const getApplyGuestingUser = async (guestingId: number) => { return await db.GuestUser.findAll({ raw: true, where: { @@ -118,12 +119,13 @@ export const getApplyGuestingUser = async (guestingId) => { }); }; -export const setGuestStatus = async (guestUser) => { - guestUser["status"] = 1; - await guestUser.save(); +export const setGuestStatus = async (guestUser: UpdateGuestUserBody) => { + const guestUserInstance = await db.GuestUser.findByPk(guestUser.guestId); + guestUserInstance.status = 1; + await guestUserInstance.save(); }; -export const getGuestUserById = async (guestUserId) => { +export const getGuestUserById = async (guestUserId: number) => { return await db.GuestUser.findOne({ where: { id: guestUserId, diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index 7bc9441..ff3a888 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -73,7 +73,24 @@ export const getTeamDetailforGuesting = async (teamId: number) => { where: { id: teamId, }, - attributes: ["name", "description", "gender", "ageGroup", "gymName", "skillLevel", "mannerLevel", "leaderId"], + include: [ + { + model: db.Member, + attributes: ["userId"], + required: false, + }, + ], + attributes: [ + "name", + "description", + "gender", + "ageGroup", + "gymName", + "skillLevel", + "mannerLevel", + "leaderId", + "category", + ], }); }; diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index acee494..d37afd2 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -1,26 +1,71 @@ +import { getAgeGroup } from "../constants/age-group.constant"; +import { getTeamGender } from "../constants/gender.constant"; import { getLevelById } from "../constants/level.constant"; +import { AgeGroup } from "../types/age-group.enum"; +import { Gender } from "../types/gender.enum"; -//TODO: getGender, getAgeGroup 함수 사용 +interface ReadGuest { + gameTime: string; + memberCount: number | null; + recruitCount: number | null; + Team: { + name: string; + region: string | null; + gender: Gender; + ageGroup: AgeGroup; + skillLevel: number; + }; +} + +interface GuestDetail { + gameTime: string; + description: string | null; + memberCount: number | null; + recruitCount: number | null; +} + +interface TeamDetail { + name: string; + logo: string | null; + mannerLevel: number | null; + description: string | null; + gender: Gender; + ageGroup: AgeGroup; + skillLevel: number; + gymName: string | null; +} + +interface UserInfo { + nickname: string; + height: number | null; + Profiles: { + position: string | null; + }; +} -export const readGuestingResponseDTO = (guestings) => { - return guestings.map((guesting) => ({ +export const readGuestingResponseDTO = (guesting: ReadGuest) => { + return { gameTime: guesting.gameTime, teamName: guesting["Team.name"], teamRegion: guesting["Team.region"], - teamGender: guesting["Team.gender"], + teamGender: getTeamGender(guesting["Team.gender"]), memberCount: guesting.memberCount, - teamAgeGroup: guesting["Team.ageGroup"], + teamAgeGroup: getAgeGroup(guesting["Team.ageGroup"]), teamSkillLevel: getLevelById(guesting["Team.skillLevel"]), recruitCount: guesting.recruitCount, - })); + }; }; -export const readGuestingDetailResponseDTO = (guestingDetail, TeamDetail, leaderInfo, memberInfo) => { - const member = memberInfo.map((info) => ({ - nickname: info["User.nickname"], - height: null, - weight: null, - position: null, +export const readGuestingDetailResponseDTO = ( + guestingDetail: GuestDetail, + TeamDetail: TeamDetail, + leaderInfo: UserInfo, + memberInfo: UserInfo[], +) => { + const member = memberInfo.map((info: UserInfo) => ({ + nickname: info.nickname, + height: info.height, + position: info["Profiles.position"], })); return { name: TeamDetail.name, @@ -29,13 +74,17 @@ export const readGuestingDetailResponseDTO = (guestingDetail, TeamDetail, leader description: TeamDetail.description, gusting_info: { gameTime: guestingDetail.gameTime, - gender: TeamDetail.gender, - ageGroup: TeamDetail.ageGroup, + gender: getTeamGender(TeamDetail.gender), + ageGroup: getAgeGroup(TeamDetail.ageGroup), gymName: TeamDetail.gymName, skillLevel: getLevelById(TeamDetail.skillLevel), }, member_info: { - leader: leaderInfo, + leader: { + nickname: leaderInfo.nickname, + height: leaderInfo.height, + position: leaderInfo["Profiles.position"], + }, member, }, }; diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index 909afff..6fceed6 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -1,9 +1,31 @@ import { getAgeGroup } from "../constants/age-group.constant"; import { getTeamGender } from "../constants/gender.constant"; import { getLevelById } from "../constants/level.constant"; +import { AgeGroup } from "../types/age-group.enum"; +import { Gender } from "../types/gender.enum"; -export const readMatchingResponseDTO = (matchings) => { - return matchings.map((matching) => ({ +interface ReadMatching { + gameTime: string; + memberCount: number | null; + Team: { + name: string; + region: string | null; + gender: Gender; + ageGroup: AgeGroup; + skillLevel: number; + }; +} + +interface ReadGuestingUser { + status: number; + User: { + nickname: string; + height: number | null; + }; +} + +export const readMatchingResponseDTO = (matching: ReadMatching) => { + return { gameTime: matching.gameTime, teamName: matching["Team.name"], teamRegion: matching["Team.region"], @@ -11,13 +33,13 @@ export const readMatchingResponseDTO = (matchings) => { memberCount: matching.memberCount, teamAgeGroup: getAgeGroup(matching["Team.ageGroup"]), teamSkillLevel: getLevelById(matching["Team.skillLevel"]), - })); + }; }; -export const readApplyGuestingUserResponseDTO = (guestingUsers) => { - return guestingUsers.map((guestingUsers) => ({ +export const readApplyGuestingUserResponseDTO = (guestingUsers: ReadGuestingUser) => { + return { nickname: guestingUsers["User.nickname"], height: guestingUsers["User.height"], status: guestingUsers.status, - })); + }; }; diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index 7132131..ecaf25f 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -1,8 +1,9 @@ import express from "express"; import asyncHandler from "express-async-handler"; -import { validateBody } from "../middlewares/validate.middleware"; import { verifyUser } from "../middlewares/auth.middleware"; -import { createGuesting, updateGuesting } from "../schemas/guest.schema"; +import { validate } from "../middlewares/validate.middleware"; +import { categoryParam, dateParam, genderParam, levelParam, regionParam } from "../schemas/fields"; +import { createGuestingSchema, updateGuestingSchema } from "../schemas/guest.schema"; import { GuestingPreview, GuestingPreviewByLevel, @@ -14,22 +15,40 @@ import { applicationGuesting, } from "../controllers/guest.controller"; -export const guestsRouter = express.Router({ mergeParams: true }); +export const guestsRouter = express.Router(); guestsRouter.use(verifyUser); -guestsRouter.post("/", validateBody(createGuesting), asyncHandler(addGuesting)); +guestsRouter.post("/", verifyUser, validate(createGuestingSchema), asyncHandler(addGuesting)); -guestsRouter.put("/:guestingId", validateBody(updateGuesting), asyncHandler(modifyGuesting)); +guestsRouter.put("/:guestingId", verifyUser, validate(updateGuestingSchema), asyncHandler(modifyGuesting)); -guestsRouter.get("/", asyncHandler(GuestingPreview)); +guestsRouter.get("/", validate(categoryParam), validate(dateParam), asyncHandler(GuestingPreview)); -guestsRouter.get("/level", asyncHandler(GuestingPreviewByLevel)); +guestsRouter.get( + "/level", + validate(categoryParam), + validate(dateParam), + validate(levelParam), + asyncHandler(GuestingPreviewByLevel), +); -guestsRouter.get("/gender", asyncHandler(GuestingPreviewByGender)); +guestsRouter.get( + "/gender", + validate(categoryParam), + validate(dateParam), + validate(genderParam), + asyncHandler(GuestingPreviewByGender), +); -guestsRouter.get("/region", asyncHandler(GuestingPreviewByRegion)); +guestsRouter.get( + "/region", + validate(categoryParam), + validate(dateParam), + validate(regionParam), + asyncHandler(GuestingPreviewByRegion), +); guestsRouter.get("/:guestingId", asyncHandler(DetailedGuestingPreview)); -guestsRouter.post("/:guestingId/application", asyncHandler(applicationGuesting)); +guestsRouter.post("/application/:category/:guestingId", verifyUser, asyncHandler(applicationGuesting)); diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts index 29be608..a64eadc 100644 --- a/src/routes/matchings.route.ts +++ b/src/routes/matchings.route.ts @@ -1,29 +1,31 @@ -import express from "express"; -import asyncHandler from "express-async-handler"; -import { verifyUser } from "../middlewares/auth.middleware"; -import { - ApplyGuestingUserPreview, - matchingGuestingPreview, - matchingHostingPreview, - modifyGuestStatus, - fetchHostingApplicantsTeamList, - gameApplicationApproval, -} from "../controllers/matchings.controller"; - -export const matchingsRouter = express.Router({ mergeParams: true }); - -matchingsRouter.use(verifyUser); - -matchingsRouter.get("/guesting", asyncHandler(matchingGuestingPreview)); - -matchingsRouter.get("/hosting", asyncHandler(matchingHostingPreview)); - -matchingsRouter.get("/hosting/:guestingId", asyncHandler(ApplyGuestingUserPreview)); - -matchingsRouter.put("/confirmGuest/:guestUserId", asyncHandler(modifyGuestStatus)); - -// 호스팅 내역 > 신청팀 목록 조회 -matchingsRouter.get("/hosting/:gameId", asyncHandler(fetchHostingApplicantsTeamList)); - -// 호스팅 내역 > 신청 승인 -matchingsRouter.post("/hosting/:gameId", asyncHandler(gameApplicationApproval)); +import express from "express"; +import asyncHandler from "express-async-handler"; +import { verifyUser } from "../middlewares/auth.middleware"; +import { validate } from "../middlewares/validate.middleware"; +import { dateParam } from "../schemas/fields"; +import { + matchingGuestingPreview, + matchingHostingPreview, + ApplyGuestingUserPreview, + modifyGuestStatus, + fetchHostingApplicantsTeamList, + gameApplicationApproval, +} from "../controllers/matchings.controller"; + +export const matchingsRouter = express.Router(); + +matchingsRouter.use(verifyUser); + +matchingsRouter.get("/guesting", verifyUser, validate(dateParam), asyncHandler(matchingGuestingPreview)); + +matchingsRouter.get("/hosting", verifyUser, validate(dateParam), asyncHandler(matchingHostingPreview)); + +matchingsRouter.get("/hosting/:guestingId", asyncHandler(ApplyGuestingUserPreview)); + +matchingsRouter.put("/confirmGuest/:guestUserId", asyncHandler(modifyGuestStatus)); + +// 호스팅 내역 > 신청팀 목록 조회 +matchingsRouter.get("/hosting/:gameId", asyncHandler(fetchHostingApplicantsTeamList)); + +// 호스팅 내역 > 신청 승인 +matchingsRouter.post("/hosting/:gameId", asyncHandler(gameApplicationApproval)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index e93b0ac..03c93d2 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -84,6 +84,40 @@ export const descriptionFieldInGame = { description: z.string() }; export const teamIdField = { teamId: z.number().int() }; +export const guestIdField = { guestId: z.number().int() }; + +export const userIdField = { userId: z.number().int() }; + +export const statusField = { status: z.number().int() }; + +export const recruitCountField = { recruitCount: z.number().int() }; + +export const levelField = { level: z.number().int().optional() }; + +export const dateParam = object({ + params: object({ + ...gameTimeField, + }), +}); + +export const levelParam = object({ + params: object({ + ...levelField, + }), +}); + +export const genderParam = object({ + params: object({ + ...genderFieldInTeam, + }), +}); + +export const regionParam = object({ + params: object({ + ...regionFieldInTeam, + }), +}); + export const skillScoreField = { skillScore: z.number().int().min(1).max(5) }; export const mannerScoreField = { mannerScore: z.number().int().min(1).max(5) }; @@ -94,4 +128,4 @@ export const guestMatchIdFieldInTeamReview = { guestMatchId: z.optional(z.number export const teamMatchIdField = { teamMatchId: z.optional(z.number().int()) }; -export const revieweeIdField = { revieweeId: z.number().int() }; +export const revieweeIdField = { revieweeId: z.number().int() }; \ No newline at end of file diff --git a/src/schemas/guest-user.schema.ts b/src/schemas/guest-user.schema.ts new file mode 100644 index 0000000..a17ada0 --- /dev/null +++ b/src/schemas/guest-user.schema.ts @@ -0,0 +1,18 @@ +import { TypeOf, object } from "zod"; +import { guestIdField, userIdField, statusField } from "./fields"; + +const body = { + ...guestIdField, + ...userIdField, + ...statusField, +}; + +const updateGuestUserBody = object({ + ...body, +}); + +export const updateGuestUserSchema = object({ + body: updateGuestUserBody, +}); + +export type UpdateGuestUserBody = TypeOf; diff --git a/src/schemas/guest.schema.ts b/src/schemas/guest.schema.ts index 8496df9..16327c0 100644 --- a/src/schemas/guest.schema.ts +++ b/src/schemas/guest.schema.ts @@ -1,19 +1,28 @@ -import { TypeOf, object, z } from "zod"; +import { TypeOf, object } from "zod"; +import { teamIdField, gameTimeField, descriptionField, recruitCountField } from "./fields"; -const fields = { - teamId: z.number().int(), - gameTime: z.date(), - description: z.optional(z.string()), - recruitCount: z.number().int().min(1), +const body = { + ...teamIdField, + ...gameTimeField, + ...descriptionField, + ...recruitCountField, }; -export const createGuesting = object({ - ...fields, +const createGuestBody = object({ + ...body, }); -export const updateGuesting = object({ - ...fields, +const updateGuestBody = object({ + ...body, }); -export type CreateGuestingSchema = TypeOf; -export type UpdateGuestingSchema = TypeOf; +export const createGuestingSchema = object({ + body: createGuestBody, +}); + +export const updateGuestingSchema = object({ + body: updateGuestBody, +}); + +export type CreateGuestingBody = TypeOf; +export type UpdateGuestingBody = TypeOf; diff --git a/src/services/guest.service.ts b/src/services/guest.service.ts index a6430ad..a63941c 100644 --- a/src/services/guest.service.ts +++ b/src/services/guest.service.ts @@ -11,23 +11,20 @@ import { insertGuesting, setGuesting, } from "../daos/guest.dao"; -import { findMemberInfoByTeamId, getMemberCountByTeamId } from "../daos/member.dao"; -import { getTeamByLeaderId, getTeamDetailforGuesting, getTeamIdByLeaderId } from "../daos/team.dao"; -import { getUser, getUserInfoById, userInfoAttributes } from "../daos/user.dao"; +import { readMembersInfo } from "../services/teams.service"; +import { getMemberCountByTeamId } from "../daos/member.dao"; +import { getTeamDetailforGuesting, getTeamIdByLeaderId } from "../daos/team.dao"; +import { getUserInfoByCategory } from "../daos/user.dao"; import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; -import { CreateGuestingSchema, UpdateGuestingSchema } from "../schemas/guest.schema"; +import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; -export const createGuesting = async (userId, body: CreateGuestingSchema) => { +export const createGuesting = async (userId, body: CreateGuestingBody) => { const teamId = await getTeamIdByLeaderId(userId); - const team = await getTeamByLeaderId(teamId, userId); - if (!team) { - // team 메뉴로 이동 - } await insertGuesting(teamId, body); return; }; -export const updateGuesting = async (userId, params, body: UpdateGuestingSchema) => { +export const updateGuesting = async (userId, params, body: UpdateGuestingBody) => { const guestingId = params.guestingId; const guesting = await getGuestingById(guestingId, userId); if (!guesting) { @@ -73,17 +70,13 @@ export const readDetailedGuesting = async (params) => { const guestingId = params.guestingId; const guestingDetail = await getDetailedGuesting(guestingId); const TeamDetail = await getTeamDetailforGuesting(guestingDetail.teamId); - const leaderInfo = await getUserInfoById(TeamDetail.leaderId); - const memberInfo = await findMemberInfoByTeamId(guestingDetail.teamId, userInfoAttributes); + const leaderInfo = await getUserInfoByCategory(TeamDetail.leaderId, TeamDetail.category); + const memberInfo = await readMembersInfo(TeamDetail, TeamDetail.category); return readGuestingDetailResponseDTO(guestingDetail, TeamDetail, leaderInfo, memberInfo); }; export const addGuestUser = async (userId, params) => { const guestingId = params.guestingId; - const user = await getUser(userId); - if (!user) { - // user 정보 수정 API 연결 - } await InsertGuestUser(guestingId, userId); return; }; diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index 340f64c..ad8e5f0 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -56,7 +56,7 @@ export const readTeamDetail = async (userId: number, params) => { return readTeamDetailResponseDTO(detail, leaderInfo, membersInfo, userId == detail.leaderId); }; -const readMembersInfo = async (details, category: Category) => { +export const readMembersInfo = async (details, category: Category) => { const membersInfoPromises = details .filter((detail) => detail["Members.userId"] !== null) .map(async (detail) => { From 1eac00adca3ec8b0bce7a39029082bd59c71a791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Mon, 5 Feb 2024 21:33:01 +0900 Subject: [PATCH 062/147] =?UTF-8?q?Fix:=20=EC=B9=B4=EC=B9=B4=EC=98=A4=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20KOE010=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0=20#91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/auth.service.ts | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/services/auth.service.ts b/src/services/auth.service.ts index d9f1717..ac8fb14 100644 --- a/src/services/auth.service.ts +++ b/src/services/auth.service.ts @@ -53,15 +53,7 @@ export const kakaoLogin = async (body) => { const getKakaoAccessToken = async (code: string) => { const url = "https://kauth.kakao.com/oauth/token"; const response = await redaxios.post( - url, - { - params: { - grant_type: "authorization_code", - client_id: process.env.KAKAO_CLIENT_ID, - redirect_uri: process.env.KAKAO_REDIRECT_URI, - code: code, - }, - }, + `${url}?grant_type=authorization_code&client_id=${process.env.KAKAO_CLIENT_ID}&redirect_uri=${process.env.KAKAO_REDIRECT_URI}&code=${code}`, { headers: { "Content-type": "application/x-www-form-urlencoded;charset=utf-8", From 39a5b0c12fee26ab6224bfbd2da838441896cca6 Mon Sep 17 00:00:00 2001 From: Doeun Date: Tue, 6 Feb 2024 19:09:19 +0900 Subject: [PATCH 063/147] =?UTF-8?q?Feat:=20game-DTO=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/status.constant.ts | 2 +- src/controllers/games.controller.ts | 2 -- src/daos/games.dao.ts | 30 +++++--------------------- src/daos/member.dao.ts | 33 +++++++++++++++++++++++++++++ src/dtos/games.dto.ts | 16 ++++++++------ src/services/games.service.ts | 12 +++++++---- 6 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/constants/status.constant.ts b/src/constants/status.constant.ts index 2bc8f82..7f80d62 100644 --- a/src/constants/status.constant.ts +++ b/src/constants/status.constant.ts @@ -9,6 +9,6 @@ const Statuss: { [key: number]: Status } = { export const defaultStatus = 0; -export const getStatusById = (id: number): string | undefined => { +export const getStatus = (id: number): string | undefined => { return Statuss[id].name; }; diff --git a/src/controllers/games.controller.ts b/src/controllers/games.controller.ts index 4f04ed5..eabe272 100644 --- a/src/controllers/games.controller.ts +++ b/src/controllers/games.controller.ts @@ -1,5 +1,3 @@ -import { application } from "express"; -import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; import { diff --git a/src/daos/games.dao.ts b/src/daos/games.dao.ts index 1ab7227..0a34605 100644 --- a/src/daos/games.dao.ts +++ b/src/daos/games.dao.ts @@ -1,12 +1,12 @@ import db from "../models"; import { Sequelize } from "sequelize"; -import { getStatusById } from "../constants/status.constant"; +import { getStatus } from "../constants/status.constant"; import { CreateGameBody } from "../schemas/game.schema"; import { ApplyGameBody } from "../schemas/game-apply.schema"; import { getTeamIdByLeaderId } from "../daos/team.dao"; export const findGamesByDate = async (date, category) => { - const games = await db.Game.findAll({ + return await db.Game.findAll({ raw: true, where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), include: [ @@ -22,15 +22,10 @@ export const findGamesByDate = async (date, category) => { attributes: ["gameTime", "status"], order: [["created_at", "DESC"]], }); - - return games.map((game) => ({ - ...game, - status: getStatusById(game.status), - })); }; export const findGamesByGender = async (date, category, gender) => { - const games = await db.Game.findAll({ + return await db.Game.findAll({ raw: true, where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), include: [ @@ -47,15 +42,10 @@ export const findGamesByGender = async (date, category, gender) => { attributes: ["gameTime", "status"], order: [["created_at", "DESC"]], }); - - return games.map((game) => ({ - ...game, - status: getStatusById(game.status), - })); }; export const findGamesByLevel = async (date, category, skillLevel) => { - const games = await db.Game.findAll({ + return await db.Game.findAll({ raw: true, where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), include: [ @@ -72,15 +62,10 @@ export const findGamesByLevel = async (date, category, skillLevel) => { attributes: ["gameTime", "status"], order: [["created_at", "DESC"]], }); - - return games.map((game) => ({ - ...game, - status: getStatusById(game.status), - })); }; export const findGamesByRegion = async (date, category, region) => { - const games = await db.Game.findAll({ + return await db.Game.findAll({ raw: true, where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), include: [ @@ -97,11 +82,6 @@ export const findGamesByRegion = async (date, category, region) => { attributes: ["gameTime", "status"], order: [["created_at", "DESC"]], }); - - return games.map((game) => ({ - ...game, - status: getStatusById(game.status), - })); }; export const getGameDetail = async (gameId) => { diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts index 729ff65..1f0a6a7 100644 --- a/src/daos/member.dao.ts +++ b/src/daos/member.dao.ts @@ -1,5 +1,7 @@ import { Op } from "sequelize"; import db from "../models"; +import { Category } from "../types/category.enum"; +import { userInfoAttributes } from "../daos/user.dao"; export const findMemberInfoByTeamId = async (teamId, userInfoAttributes) => { throw new Error("더 이상 사용하지 않는 함수"); @@ -18,6 +20,37 @@ export const findMemberInfoByTeamId = async (teamId, userInfoAttributes) => { }); }; +export const getMemberInfoByCategory = async (teamId: number, category: Category) => { + const team = await db.Team.findByPk(teamId); + if (!team) { + throw new Error("Team not found"); + } + + return await db.Member.findAll({ + raw: true, + where: { + teamId: teamId, + }, + include: [ + { + model: db.User, + attributes: userInfoAttributes(), + include: [ + { + model: db.Profile, + where: { + category: category, + }, + required: false, + attributes: ["position"], + }, + ], + }, + ], + attributes: [], + }); +}; + export const findMemberInfoWithoutLeaderByTeamId = async (teamId, userInfoAttributes) => { const team = await db.Team.findByPk(teamId); if (!team) { diff --git a/src/dtos/games.dto.ts b/src/dtos/games.dto.ts index b6b5b22..39ced7d 100644 --- a/src/dtos/games.dto.ts +++ b/src/dtos/games.dto.ts @@ -1,4 +1,7 @@ import { getLevelById } from "../constants/level.constant"; +import { getGender } from "../constants/gender.constant"; +import { getAgeGroup } from "../constants/age-group.constant"; +import { getStatus } from "../constants/status.constant"; //TODO: getGender, getAgeGroup 함수 사용 @@ -7,20 +10,20 @@ export const readGameResponseDTO = (games) => { gameTime: game.gameTime, teamName: game["HostTeam.name"], teamRegion: game["HostTeam.region"], - teamGender: game["HostTeam.gender"], + teamGender: getGender(game["HostTeam.gender"]), memberCount: game.memberCount, - teamAgeGroup: game["HostTeam.ageGroup"], + teamAgeGroup: getAgeGroup(game["HostTeam.ageGroup"]), teamSkillLevel: getLevelById(game["HostTeam.skillLevel"]), - status: game.status, + status: getStatus(game.status), })); }; export const readGameDetailResponseDTO = (gameDetail, teamDetail, leaderInfo, memberInfo) => { + console.log(memberInfo); const member = memberInfo.map((info) => ({ nickname: info["User.nickname"], - // height: null, - // weight: null, - // position: null, + height: info["User.height"], + position: info["User.Profiles.position"], })); return { name: teamDetail.name, @@ -32,7 +35,6 @@ export const readGameDetailResponseDTO = (gameDetail, teamDetail, leaderInfo, me gameTime: gameDetail.gameTime, gender: teamDetail.gender, ageGroup: teamDetail.ageGroup, - skillLevel: teamDetail.skillLevel, }, member_info: { leader: leaderInfo, diff --git a/src/services/games.service.ts b/src/services/games.service.ts index c9ad651..a9ff437 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -16,8 +16,12 @@ import { getTeamCategoryByLeaderId, getTeamByLeaderId, } from "../daos/team.dao"; -import { findMemberInfoWithoutLeaderByTeamId, getMemberCountByTeamId } from "../daos/member.dao"; -import { getUserInfoById, userInfoAttributes } from "../daos/user.dao"; +import { + findMemberInfoWithoutLeaderByTeamId, + getMemberInfoByCategory, + getMemberCountByTeamId, +} from "../daos/member.dao"; +import { getUserInfoByCategory, userInfoAttributes } from "../daos/user.dao"; import { getGameByUserId } from "../daos/games.dao"; import { readGameResponseDTO, readGameDetailResponseDTO } from "../dtos/games.dto"; import { CreateGameBody, UpdateGameBody } from "../schemas/game.schema"; @@ -59,8 +63,8 @@ export const readGameDetail = async (params) => { const gameId = params.gameId; const gameDetail = await getGameDetail(gameId); const teamDetail = await getTeamDetailforGuesting(gameDetail.hostTeamId); - const leaderInfo = await getUserInfoById(teamDetail.leaderId); - const memberInfo = await findMemberInfoWithoutLeaderByTeamId(gameDetail.hostTeamId, userInfoAttributes); + const leaderInfo = await getUserInfoByCategory(teamDetail.leaderId, teamDetail.category); + const memberInfo = await getMemberInfoByCategory(gameDetail.hostTeamId, teamDetail.category); return readGameDetailResponseDTO(gameDetail, teamDetail, leaderInfo, memberInfo); }; From 8db9737d65f0c69d073685cc46f182a0b8ee947d Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Tue, 6 Feb 2024 19:29:47 +0900 Subject: [PATCH 064/147] =?UTF-8?q?Fix)=20guest=20schema=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20validate=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/guests.route.ts | 51 +++++++++++++++------------------ src/schemas/fields.ts | 57 ++++++++++--------------------------- src/schemas/guest.schema.ts | 56 +++++++++++++++++++++++++++++++++++- 3 files changed, 93 insertions(+), 71 deletions(-) diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index ecaf25f..d5958a7 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -2,8 +2,15 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; import { validate } from "../middlewares/validate.middleware"; -import { categoryParam, dateParam, genderParam, levelParam, regionParam } from "../schemas/fields"; -import { createGuestingSchema, updateGuestingSchema } from "../schemas/guest.schema"; +import { + addGuestUserSchema, + createGuestingSchema, + readGuestFilterGenderSchema, + readGuestFilterLevelSchema, + readGuestFilterRegionSchema, + readGuestSchema, + updateGuestingSchema, +} from "../schemas/guest.schema"; import { GuestingPreview, GuestingPreviewByLevel, @@ -13,42 +20,30 @@ import { addGuesting, modifyGuesting, applicationGuesting, -} from "../controllers/guest.controller"; +} from "../controllers/guests.controller"; export const guestsRouter = express.Router(); -guestsRouter.use(verifyUser); - guestsRouter.post("/", verifyUser, validate(createGuestingSchema), asyncHandler(addGuesting)); +// guestsRouter.post("/", validate(createGuestingSchema), asyncHandler(addGuesting)); guestsRouter.put("/:guestingId", verifyUser, validate(updateGuestingSchema), asyncHandler(modifyGuesting)); +// guestsRouter.put("/:guestingId", validate(updateGuestingSchema), asyncHandler(modifyGuesting)); -guestsRouter.get("/", validate(categoryParam), validate(dateParam), asyncHandler(GuestingPreview)); +guestsRouter.get("/", validate(readGuestSchema), asyncHandler(GuestingPreview)); -guestsRouter.get( - "/level", - validate(categoryParam), - validate(dateParam), - validate(levelParam), - asyncHandler(GuestingPreviewByLevel), -); +guestsRouter.get("/level", validate(readGuestFilterLevelSchema), asyncHandler(GuestingPreviewByLevel)); -guestsRouter.get( - "/gender", - validate(categoryParam), - validate(dateParam), - validate(genderParam), - asyncHandler(GuestingPreviewByGender), -); +guestsRouter.get("/gender", validate(readGuestFilterGenderSchema), asyncHandler(GuestingPreviewByGender)); -guestsRouter.get( - "/region", - validate(categoryParam), - validate(dateParam), - validate(regionParam), - asyncHandler(GuestingPreviewByRegion), -); +guestsRouter.get("/region", validate(readGuestFilterRegionSchema), asyncHandler(GuestingPreviewByRegion)); guestsRouter.get("/:guestingId", asyncHandler(DetailedGuestingPreview)); -guestsRouter.post("/application/:category/:guestingId", verifyUser, asyncHandler(applicationGuesting)); +guestsRouter.post( + "/:guestingId/application", + verifyUser, + validate(addGuestUserSchema), + asyncHandler(applicationGuesting), +); +// guestsRouter.put("/:guestingId", validate(updateGuestingSchema), asyncHandler(applicationGuesting)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 03c93d2..7bfc9ab 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -80,52 +80,25 @@ export const gameTimeField = { }, z.date()), }; -export const descriptionFieldInGame = { description: z.string() }; - -export const teamIdField = { teamId: z.number().int() }; - -export const guestIdField = { guestId: z.number().int() }; - -export const userIdField = { userId: z.number().int() }; - -export const statusField = { status: z.number().int() }; - -export const recruitCountField = { recruitCount: z.number().int() }; - -export const levelField = { level: z.number().int().optional() }; - -export const dateParam = object({ - params: object({ - ...gameTimeField, - }), -}); - -export const levelParam = object({ - params: object({ - ...levelField, - }), -}); - -export const genderParam = object({ - params: object({ - ...genderFieldInTeam, - }), -}); +export const dateField = { + date: z.preprocess((arg) => { + if (typeof arg == "string") { + return new Date(arg); + } + return arg; + }, z.date()), +}; -export const regionParam = object({ - params: object({ - ...regionFieldInTeam, +export const dateQuery = object({ + query: object({ + ...dateField, }), }); -export const skillScoreField = { skillScore: z.number().int().min(1).max(5) }; - -export const mannerScoreField = { mannerScore: z.number().int().min(1).max(5) }; - -export const guestMatchIdFieldInUserReview = { guestMatchId: z.number().int() }; +export const descriptionFieldInGame = { description: z.string() }; -export const guestMatchIdFieldInTeamReview = { guestMatchId: z.optional(z.number().int()) }; +export const teamIdField = { teamId: z.number().int() }; -export const teamMatchIdField = { teamMatchId: z.optional(z.number().int()) }; +export const recruitCountField = { recruitCount: z.number().int() }; -export const revieweeIdField = { revieweeId: z.number().int() }; \ No newline at end of file +export const levelField = { level: z.number().int().optional() }; diff --git a/src/schemas/guest.schema.ts b/src/schemas/guest.schema.ts index 16327c0..2a47ba6 100644 --- a/src/schemas/guest.schema.ts +++ b/src/schemas/guest.schema.ts @@ -1,5 +1,15 @@ import { TypeOf, object } from "zod"; -import { teamIdField, gameTimeField, descriptionField, recruitCountField } from "./fields"; +import { + teamIdField, + gameTimeField, + descriptionField, + recruitCountField, + categoryField, + dateField, + levelField, + regionFieldInTeam, + genderFieldInTeam, +} from "./fields"; const body = { ...teamIdField, @@ -16,13 +26,57 @@ const updateGuestBody = object({ ...body, }); +const createGuestQuery = object({ + ...categoryField, +}); + export const createGuestingSchema = object({ body: createGuestBody, + query: createGuestQuery, }); export const updateGuestingSchema = object({ body: updateGuestBody, }); +const readGuest = { + ...categoryField, + ...dateField, +}; + +export const readGuestSchema = object({ + query: object({ + ...readGuest, + ...dateField, + }), +}); + +export const readGuestFilterGenderSchema = object({ + query: object({ + ...readGuest, + ...genderFieldInTeam, + }), +}); + +export const readGuestFilterLevelSchema = object({ + query: object({ + ...readGuest, + ...levelField, + }), +}); + +export const readGuestFilterRegionSchema = object({ + query: object({ + ...readGuest, + ...regionFieldInTeam, + }), +}); + +export const addGuestUserSchema = object({ + query: object({ + ...categoryField, + }), +}); + export type CreateGuestingBody = TypeOf; export type UpdateGuestingBody = TypeOf; From 4837d95d90792f3c80c878de53b55ba06a263b0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 6 Feb 2024 19:33:31 +0900 Subject: [PATCH 065/147] =?UTF-8?q?Fix:=20=EC=A2=85=EB=AA=A9=EB=B3=84=20?= =?UTF-8?q?=EB=82=98=EC=9D=98=20=ED=8C=80=20=EC=A1=B0=ED=9A=8C=20API=20Sch?= =?UTF-8?q?ema=20=EB=B3=80=EA=B2=BD=20#96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/teams.route.ts | 5 ++--- src/schemas/fields.ts | 2 +- src/schemas/team.schema.ts | 8 +++++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/routes/teams.route.ts b/src/routes/teams.route.ts index 4631abd..f8d2edf 100644 --- a/src/routes/teams.route.ts +++ b/src/routes/teams.route.ts @@ -2,9 +2,8 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; import { validate } from "../middlewares/validate.middleware"; -import { createTeamSchema, updateTeamSchema } from "../schemas/team.schema"; +import { createTeamSchema, readTeamPreviewsSchema, updateTeamSchema } from "../schemas/team.schema"; import { fetchTeamPreviews, fetchTeamDetail, addTeam, modifyTeam } from "../controllers/teams.controller"; -import { categoryParam } from "../schemas/fields"; export const teamsRouter = express.Router(); @@ -12,7 +11,7 @@ teamsRouter.use(verifyUser); teamsRouter.post("/", validate(createTeamSchema), asyncHandler(addTeam)); -teamsRouter.get("/", validate(categoryParam), asyncHandler(fetchTeamPreviews)); +teamsRouter.get("/", validate(readTeamPreviewsSchema), asyncHandler(fetchTeamPreviews)); teamsRouter.put("/:teamId", validate(updateTeamSchema), asyncHandler(modifyTeam)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 03c93d2..cb1b498 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -128,4 +128,4 @@ export const guestMatchIdFieldInTeamReview = { guestMatchId: z.optional(z.number export const teamMatchIdField = { teamMatchId: z.optional(z.number().int()) }; -export const revieweeIdField = { revieweeId: z.number().int() }; \ No newline at end of file +export const revieweeIdField = { revieweeId: z.number().int() }; diff --git a/src/schemas/team.schema.ts b/src/schemas/team.schema.ts index 2ed4782..fa753c8 100644 --- a/src/schemas/team.schema.ts +++ b/src/schemas/team.schema.ts @@ -1,4 +1,4 @@ -import { TypeOf, object, z } from "zod"; +import { TypeOf, object } from "zod"; import { ageGroupFieldInTeam, categoryField, @@ -52,6 +52,12 @@ export const createMemberSchema = object({ body: createMemberBody, }); +export const readTeamPreviewsSchema = object({ + query: object({ + ...categoryField, + }), +}); + export type CreateTeamBody = TypeOf; export type UpdateTeamBody = TypeOf; export type UpdateTeamBodyWithoutMemberIdsToDelete = TypeOf; From 7884d15d7776884a17cc2977e73d17b94644fb9a Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Tue, 6 Feb 2024 19:29:47 +0900 Subject: [PATCH 066/147] =?UTF-8?q?Fix)=20guest=20schema=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20validate=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20#97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/guests.route.ts | 51 +++++++++++++++------------------ src/schemas/fields.ts | 57 ++++++++++--------------------------- src/schemas/guest.schema.ts | 56 +++++++++++++++++++++++++++++++++++- 3 files changed, 93 insertions(+), 71 deletions(-) diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index ecaf25f..d5958a7 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -2,8 +2,15 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; import { validate } from "../middlewares/validate.middleware"; -import { categoryParam, dateParam, genderParam, levelParam, regionParam } from "../schemas/fields"; -import { createGuestingSchema, updateGuestingSchema } from "../schemas/guest.schema"; +import { + addGuestUserSchema, + createGuestingSchema, + readGuestFilterGenderSchema, + readGuestFilterLevelSchema, + readGuestFilterRegionSchema, + readGuestSchema, + updateGuestingSchema, +} from "../schemas/guest.schema"; import { GuestingPreview, GuestingPreviewByLevel, @@ -13,42 +20,30 @@ import { addGuesting, modifyGuesting, applicationGuesting, -} from "../controllers/guest.controller"; +} from "../controllers/guests.controller"; export const guestsRouter = express.Router(); -guestsRouter.use(verifyUser); - guestsRouter.post("/", verifyUser, validate(createGuestingSchema), asyncHandler(addGuesting)); +// guestsRouter.post("/", validate(createGuestingSchema), asyncHandler(addGuesting)); guestsRouter.put("/:guestingId", verifyUser, validate(updateGuestingSchema), asyncHandler(modifyGuesting)); +// guestsRouter.put("/:guestingId", validate(updateGuestingSchema), asyncHandler(modifyGuesting)); -guestsRouter.get("/", validate(categoryParam), validate(dateParam), asyncHandler(GuestingPreview)); +guestsRouter.get("/", validate(readGuestSchema), asyncHandler(GuestingPreview)); -guestsRouter.get( - "/level", - validate(categoryParam), - validate(dateParam), - validate(levelParam), - asyncHandler(GuestingPreviewByLevel), -); +guestsRouter.get("/level", validate(readGuestFilterLevelSchema), asyncHandler(GuestingPreviewByLevel)); -guestsRouter.get( - "/gender", - validate(categoryParam), - validate(dateParam), - validate(genderParam), - asyncHandler(GuestingPreviewByGender), -); +guestsRouter.get("/gender", validate(readGuestFilterGenderSchema), asyncHandler(GuestingPreviewByGender)); -guestsRouter.get( - "/region", - validate(categoryParam), - validate(dateParam), - validate(regionParam), - asyncHandler(GuestingPreviewByRegion), -); +guestsRouter.get("/region", validate(readGuestFilterRegionSchema), asyncHandler(GuestingPreviewByRegion)); guestsRouter.get("/:guestingId", asyncHandler(DetailedGuestingPreview)); -guestsRouter.post("/application/:category/:guestingId", verifyUser, asyncHandler(applicationGuesting)); +guestsRouter.post( + "/:guestingId/application", + verifyUser, + validate(addGuestUserSchema), + asyncHandler(applicationGuesting), +); +// guestsRouter.put("/:guestingId", validate(updateGuestingSchema), asyncHandler(applicationGuesting)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 03c93d2..7bfc9ab 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -80,52 +80,25 @@ export const gameTimeField = { }, z.date()), }; -export const descriptionFieldInGame = { description: z.string() }; - -export const teamIdField = { teamId: z.number().int() }; - -export const guestIdField = { guestId: z.number().int() }; - -export const userIdField = { userId: z.number().int() }; - -export const statusField = { status: z.number().int() }; - -export const recruitCountField = { recruitCount: z.number().int() }; - -export const levelField = { level: z.number().int().optional() }; - -export const dateParam = object({ - params: object({ - ...gameTimeField, - }), -}); - -export const levelParam = object({ - params: object({ - ...levelField, - }), -}); - -export const genderParam = object({ - params: object({ - ...genderFieldInTeam, - }), -}); +export const dateField = { + date: z.preprocess((arg) => { + if (typeof arg == "string") { + return new Date(arg); + } + return arg; + }, z.date()), +}; -export const regionParam = object({ - params: object({ - ...regionFieldInTeam, +export const dateQuery = object({ + query: object({ + ...dateField, }), }); -export const skillScoreField = { skillScore: z.number().int().min(1).max(5) }; - -export const mannerScoreField = { mannerScore: z.number().int().min(1).max(5) }; - -export const guestMatchIdFieldInUserReview = { guestMatchId: z.number().int() }; +export const descriptionFieldInGame = { description: z.string() }; -export const guestMatchIdFieldInTeamReview = { guestMatchId: z.optional(z.number().int()) }; +export const teamIdField = { teamId: z.number().int() }; -export const teamMatchIdField = { teamMatchId: z.optional(z.number().int()) }; +export const recruitCountField = { recruitCount: z.number().int() }; -export const revieweeIdField = { revieweeId: z.number().int() }; \ No newline at end of file +export const levelField = { level: z.number().int().optional() }; diff --git a/src/schemas/guest.schema.ts b/src/schemas/guest.schema.ts index 16327c0..2a47ba6 100644 --- a/src/schemas/guest.schema.ts +++ b/src/schemas/guest.schema.ts @@ -1,5 +1,15 @@ import { TypeOf, object } from "zod"; -import { teamIdField, gameTimeField, descriptionField, recruitCountField } from "./fields"; +import { + teamIdField, + gameTimeField, + descriptionField, + recruitCountField, + categoryField, + dateField, + levelField, + regionFieldInTeam, + genderFieldInTeam, +} from "./fields"; const body = { ...teamIdField, @@ -16,13 +26,57 @@ const updateGuestBody = object({ ...body, }); +const createGuestQuery = object({ + ...categoryField, +}); + export const createGuestingSchema = object({ body: createGuestBody, + query: createGuestQuery, }); export const updateGuestingSchema = object({ body: updateGuestBody, }); +const readGuest = { + ...categoryField, + ...dateField, +}; + +export const readGuestSchema = object({ + query: object({ + ...readGuest, + ...dateField, + }), +}); + +export const readGuestFilterGenderSchema = object({ + query: object({ + ...readGuest, + ...genderFieldInTeam, + }), +}); + +export const readGuestFilterLevelSchema = object({ + query: object({ + ...readGuest, + ...levelField, + }), +}); + +export const readGuestFilterRegionSchema = object({ + query: object({ + ...readGuest, + ...regionFieldInTeam, + }), +}); + +export const addGuestUserSchema = object({ + query: object({ + ...categoryField, + }), +}); + export type CreateGuestingBody = TypeOf; export type UpdateGuestingBody = TypeOf; From 4bc98a900f696f22efceac960dc1a84947def76f Mon Sep 17 00:00:00 2001 From: Doeun Date: Tue, 6 Feb 2024 19:57:46 +0900 Subject: [PATCH 067/147] =?UTF-8?q?Refactor:=20game=20=EB=AA=A8=EB=8D=B8?= =?UTF-8?q?=20applyTeamId=20=ED=95=84=EB=93=9C=20=EC=82=AD=EC=A0=9C=20#102?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/game.model.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/models/game.model.ts b/src/models/game.model.ts index dc8f2c4..61bea68 100644 --- a/src/models/game.model.ts +++ b/src/models/game.model.ts @@ -8,10 +8,6 @@ class Game extends Model, InferCreationAttributes> { type: new DataTypes.INTEGER(), allowNull: false, }, - applyTeamId: { - type: new DataTypes.INTEGER(), - allowNull: true, - }, opposingTeamId: { type: new DataTypes.INTEGER(), allowNull: true, From 56dff12e6394f3b01d2521eeef28d4e078ec80d6 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Tue, 6 Feb 2024 20:15:23 +0900 Subject: [PATCH 068/147] =?UTF-8?q?[BUGFIX]=20=EA=B2=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=9E=91=EC=84=B1=20=EB=B0=8F?= =?UTF-8?q?=20=EC=8B=A0=EC=B2=AD=20API=20=EC=88=98=EC=A0=95=20#103?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/response.status.ts | 16 +++++++++++++- ...est.controller.ts => guests.controller.ts} | 16 ++------------ src/models/guest.model.ts | 2 +- src/schemas/guest.schema.ts | 7 +------ .../{guest.service.ts => guests.service.ts} | 21 ++++++++++++++----- 5 files changed, 35 insertions(+), 27 deletions(-) rename src/controllers/{guest.controller.ts => guests.controller.ts} (69%) rename src/services/{guest.service.ts => guests.service.ts} (76%) diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 09ec0d3..208671f 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -116,10 +116,24 @@ export const status: { [key: string]: Status } = { GUESTUSER_NOT_FOUND: { status: StatusCodes.NOT_FOUND, isSuccess: false, - code: "GUESTER001", + code: "GUESTER002", message: "해당 게스트 신청 유저를 찾을 수 없습니다.", }, + TEAM_LEADER_NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "GUESTER003", + message: "요청한 유저가 팀장인 팀을 찾을 수 없습니다.", + }, + + TEAM_INFO_NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "GUESTER004", + message: "팀 정보 입력이 필요합니다.", + }, + // game error GAME_NOT_FOUND: { status: StatusCodes.NOT_FOUND, diff --git a/src/controllers/guest.controller.ts b/src/controllers/guests.controller.ts similarity index 69% rename from src/controllers/guest.controller.ts rename to src/controllers/guests.controller.ts index 34730c9..6e781a5 100644 --- a/src/controllers/guest.controller.ts +++ b/src/controllers/guests.controller.ts @@ -10,17 +10,9 @@ import { createGuesting, addGuestUser, updateGuesting, -} from "../services/guest.service"; -import { createTeam } from "../services/teams.service"; -import { getTeamIdByLeaderId } from "../daos/team.dao"; -import { updateUserProfile } from "../services/users.service"; -import { getUser } from "../daos/user.dao"; +} from "../services/guests.service"; export const addGuesting = async (req, res: Response, next) => { - const teamId = await getTeamIdByLeaderId(req.user.id); - if (!teamId) { - res.send(response(status.SUCCESS, await createTeam(req.user.id, req.body))); - } res.send(response(status.SUCCESS, await createGuesting(req.user.id, req.body))); }; @@ -49,9 +41,5 @@ export const DetailedGuestingPreview = async (req, res: Response, next) => { }; export const applicationGuesting = async (req, res: Response, next) => { - const user = await getUser(req.user.id); - if (!user) { - res.send(response(status.SUCCESS, await updateUserProfile(req.user.id, req.params, req.body))); - } - res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.params))); + res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.query, req.params))); }; diff --git a/src/models/guest.model.ts b/src/models/guest.model.ts index 5246d9f..fbbf7db 100644 --- a/src/models/guest.model.ts +++ b/src/models/guest.model.ts @@ -8,7 +8,7 @@ class Guest extends Model, InferCreationAttributes type: new DataTypes.INTEGER(), allowNull: false, primaryKey: true, - autoIncrement: true, // 이 부분을 추가 + autoIncrement: true, }, teamId: { type: new DataTypes.INTEGER(), diff --git a/src/schemas/guest.schema.ts b/src/schemas/guest.schema.ts index 2a47ba6..89685b2 100644 --- a/src/schemas/guest.schema.ts +++ b/src/schemas/guest.schema.ts @@ -12,13 +12,13 @@ import { } from "./fields"; const body = { - ...teamIdField, ...gameTimeField, ...descriptionField, ...recruitCountField, }; const createGuestBody = object({ + ...teamIdField, ...body, }); @@ -26,13 +26,8 @@ const updateGuestBody = object({ ...body, }); -const createGuestQuery = object({ - ...categoryField, -}); - export const createGuestingSchema = object({ body: createGuestBody, - query: createGuestQuery, }); export const updateGuestingSchema = object({ diff --git a/src/services/guest.service.ts b/src/services/guests.service.ts similarity index 76% rename from src/services/guest.service.ts rename to src/services/guests.service.ts index a63941c..965672c 100644 --- a/src/services/guest.service.ts +++ b/src/services/guests.service.ts @@ -11,15 +11,22 @@ import { insertGuesting, setGuesting, } from "../daos/guest.dao"; -import { readMembersInfo } from "../services/teams.service"; +import { readMembersInfo } from "./teams.service"; import { getMemberCountByTeamId } from "../daos/member.dao"; -import { getTeamDetailforGuesting, getTeamIdByLeaderId } from "../daos/team.dao"; -import { getUserInfoByCategory } from "../daos/user.dao"; +import { findTeamPreviewByCategoryForLeader, getTeamByLeaderId, getTeamDetailforGuesting } from "../daos/team.dao"; +import { getUserInfoByCategory, getUserProfileByCategory } from "../daos/user.dao"; import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; export const createGuesting = async (userId, body: CreateGuestingBody) => { - const teamId = await getTeamIdByLeaderId(userId); + const teamId = body.teamId; + const team = await getTeamByLeaderId(teamId, userId); + const teamInfo = await getTeamDetailforGuesting(teamId); + if (!team) { + throw new BaseError(status.TEAM_LEADER_NOT_FOUND); + } else if (!teamInfo) { + throw new BaseError(status.TEAM_INFO_NOT_FOUND); + } await insertGuesting(teamId, body); return; }; @@ -75,8 +82,12 @@ export const readDetailedGuesting = async (params) => { return readGuestingDetailResponseDTO(guestingDetail, TeamDetail, leaderInfo, memberInfo); }; -export const addGuestUser = async (userId, params) => { +export const addGuestUser = async (userId, query, params) => { const guestingId = params.guestingId; + const userProfile = await getUserProfileByCategory(userId, query.category); + if (!userProfile) { + throw new BaseError(status.GUESTUSER_NOT_FOUND); + } await InsertGuestUser(guestingId, userId); return; }; From 52463698ef2eb57f1aaaa2664f39ad9cb611687b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 6 Feb 2024 20:23:14 +0900 Subject: [PATCH 069/147] =?UTF-8?q?Refactor:=20Record=EB=A5=BC=20=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=ED=95=9C=20Statuses=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/status.constant.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/constants/status.constant.ts b/src/constants/status.constant.ts index 7f80d62..cf32418 100644 --- a/src/constants/status.constant.ts +++ b/src/constants/status.constant.ts @@ -1,14 +1,10 @@ -type Status = { - name: string; -}; - -const Statuss: { [key: number]: Status } = { - 0: { name: "모집 중" }, - 1: { name: "모집 완료" }, +const Statuses: Record = { + 0: "모집 중", + 1: "모집 완료", }; export const defaultStatus = 0; -export const getStatus = (id: number): string | undefined => { - return Statuss[id].name; +export const getStatus = (key: number): string | undefined => { + return Statuses[key]; }; From 02103c264dc858f727cca277df5841f01c4e4c28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 6 Feb 2024 20:26:04 +0900 Subject: [PATCH 070/147] =?UTF-8?q?Style:=20games.dao.ts=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=AA=85=20game.dao.ts=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/{games.dao.ts => game.dao.ts} | 2 +- src/services/games.service.ts | 4 ++-- src/services/reviews.service.ts | 2 +- src/services/teams.service.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) rename src/daos/{games.dao.ts => game.dao.ts} (98%) diff --git a/src/daos/games.dao.ts b/src/daos/game.dao.ts similarity index 98% rename from src/daos/games.dao.ts rename to src/daos/game.dao.ts index 0a34605..2959a31 100644 --- a/src/daos/games.dao.ts +++ b/src/daos/game.dao.ts @@ -3,7 +3,7 @@ import { Sequelize } from "sequelize"; import { getStatus } from "../constants/status.constant"; import { CreateGameBody } from "../schemas/game.schema"; import { ApplyGameBody } from "../schemas/game-apply.schema"; -import { getTeamIdByLeaderId } from "../daos/team.dao"; +import { getTeamIdByLeaderId } from "./team.dao"; export const findGamesByDate = async (date, category) => { return await db.Game.findAll({ diff --git a/src/services/games.service.ts b/src/services/games.service.ts index a9ff437..5b7264b 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -9,7 +9,7 @@ import { insertGame, setGame, insertGameApplication, -} from "../daos/games.dao"; +} from "../daos/game.dao"; import { getTeamDetailforGuesting, getTeamIdByLeaderId, @@ -22,7 +22,7 @@ import { getMemberCountByTeamId, } from "../daos/member.dao"; import { getUserInfoByCategory, userInfoAttributes } from "../daos/user.dao"; -import { getGameByUserId } from "../daos/games.dao"; +import { getGameByUserId } from "../daos/game.dao"; import { readGameResponseDTO, readGameDetailResponseDTO } from "../dtos/games.dto"; import { CreateGameBody, UpdateGameBody } from "../schemas/game.schema"; import { ApplyGameBody } from "../schemas/game-apply.schema"; diff --git a/src/services/reviews.service.ts b/src/services/reviews.service.ts index 13b3cd8..a5c4f18 100644 --- a/src/services/reviews.service.ts +++ b/src/services/reviews.service.ts @@ -1,7 +1,7 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { CreateTeamReviewBody } from "../schemas/team-review.schema"; -import { getGame } from "../daos/games.dao"; +import { getGame } from "../daos/game.dao"; import { findLeaderId, getLeaderId } from "../daos/team.dao"; import { getGuestingByAcceptedUserId } from "../daos/guest.dao"; import { getExistingTeamReview, insertTeamReview } from "../daos/team-review.dao"; diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index ad8e5f0..3cd8770 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -13,7 +13,7 @@ import { } from "../daos/team.dao"; import { deleteMembers, findMemberToDelete } from "../daos/member.dao"; import { getUserInfoByCategory } from "../daos/user.dao"; -import { updateOpposingTeam } from "../daos/games.dao"; +import { updateOpposingTeam } from "../daos/game.dao"; import { readTeamDetailResponseDTO } from "../dtos/teams.dto"; export const readTeamPreviews = async (userId: number, query) => { From 3b571b606b05e9d63da1d5863b183d52b7b2c74d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 6 Feb 2024 20:28:43 +0900 Subject: [PATCH 071/147] =?UTF-8?q?Refactor:=20=EC=9D=98=EB=AF=B8=20?= =?UTF-8?q?=EC=97=86=EB=8A=94=20=EC=A3=BC=EC=84=9D=EA=B3=BC=20console.log?= =?UTF-8?q?=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/games.dto.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/dtos/games.dto.ts b/src/dtos/games.dto.ts index 39ced7d..b8e43e2 100644 --- a/src/dtos/games.dto.ts +++ b/src/dtos/games.dto.ts @@ -3,8 +3,6 @@ import { getGender } from "../constants/gender.constant"; import { getAgeGroup } from "../constants/age-group.constant"; import { getStatus } from "../constants/status.constant"; -//TODO: getGender, getAgeGroup 함수 사용 - export const readGameResponseDTO = (games) => { return games.map((game) => ({ gameTime: game.gameTime, @@ -19,7 +17,6 @@ export const readGameResponseDTO = (games) => { }; export const readGameDetailResponseDTO = (gameDetail, teamDetail, leaderInfo, memberInfo) => { - console.log(memberInfo); const member = memberInfo.map((info) => ({ nickname: info["User.nickname"], height: info["User.height"], From 05868b9a951512dfa9d9d35bc45a3285c742a653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 6 Feb 2024 22:50:01 +0900 Subject: [PATCH 072/147] =?UTF-8?q?Fix:=20gameId=20=EC=9C=A0=ED=9A=A8?= =?UTF-8?q?=EC=84=B1=20=EA=B2=80=EC=A6=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/member.dao.ts | 11 +++-------- src/services/games.service.ts | 12 ++++++------ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts index 1f0a6a7..d630b77 100644 --- a/src/daos/member.dao.ts +++ b/src/daos/member.dao.ts @@ -20,21 +20,16 @@ export const findMemberInfoByTeamId = async (teamId, userInfoAttributes) => { }); }; -export const getMemberInfoByCategory = async (teamId: number, category: Category) => { - const team = await db.Team.findByPk(teamId); - if (!team) { - throw new Error("Team not found"); - } - +export const findMemberInfoByCategory = async (teamId: number, category: Category) => { return await db.Member.findAll({ raw: true, where: { - teamId: teamId, + teamId, }, include: [ { model: db.User, - attributes: userInfoAttributes(), + attributes: ["nickname", "height"], include: [ { model: db.Profile, diff --git a/src/services/games.service.ts b/src/services/games.service.ts index 5b7264b..8ca31fb 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -16,11 +16,7 @@ import { getTeamCategoryByLeaderId, getTeamByLeaderId, } from "../daos/team.dao"; -import { - findMemberInfoWithoutLeaderByTeamId, - getMemberInfoByCategory, - getMemberCountByTeamId, -} from "../daos/member.dao"; +import { getMemberCountByTeamId, findMemberInfoByCategory } from "../daos/member.dao"; import { getUserInfoByCategory, userInfoAttributes } from "../daos/user.dao"; import { getGameByUserId } from "../daos/game.dao"; import { readGameResponseDTO, readGameDetailResponseDTO } from "../dtos/games.dto"; @@ -62,9 +58,13 @@ export const readGamesByRegion = async (query) => { export const readGameDetail = async (params) => { const gameId = params.gameId; const gameDetail = await getGameDetail(gameId); + if (!gameDetail) { + throw new BaseError(status.GAME_NOT_FOUND); + } + const teamDetail = await getTeamDetailforGuesting(gameDetail.hostTeamId); const leaderInfo = await getUserInfoByCategory(teamDetail.leaderId, teamDetail.category); - const memberInfo = await getMemberInfoByCategory(gameDetail.hostTeamId, teamDetail.category); + const memberInfo = await findMemberInfoByCategory(gameDetail.hostTeamId, teamDetail.category); return readGameDetailResponseDTO(gameDetail, teamDetail, leaderInfo, memberInfo); }; From 3d2600041aea0bfb91676ea71554273c04252571 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Tue, 6 Feb 2024 22:54:18 +0900 Subject: [PATCH 073/147] =?UTF-8?q?Feat:=20readGameDetailResponseDTO?= =?UTF-8?q?=EC=97=90=20getGender,=20getAgeGroup=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/games.dto.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dtos/games.dto.ts b/src/dtos/games.dto.ts index b8e43e2..6af7458 100644 --- a/src/dtos/games.dto.ts +++ b/src/dtos/games.dto.ts @@ -30,8 +30,8 @@ export const readGameDetailResponseDTO = (gameDetail, teamDetail, leaderInfo, me game_info: { gymName: teamDetail.gymName, gameTime: gameDetail.gameTime, - gender: teamDetail.gender, - ageGroup: teamDetail.ageGroup, + gender: getGender(teamDetail.gender), + ageGroup: getAgeGroup(teamDetail.ageGroup), }, member_info: { leader: leaderInfo, From 6689393cf9d310774df825b7460a873ef25226d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Wed, 7 Feb 2024 01:40:11 +0900 Subject: [PATCH 074/147] =?UTF-8?q?Fix:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=9E=91=EC=84=B1=20service=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=88=98=EC=A0=95=20#107?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/guests.service.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index 965672c..3f7cc3f 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -13,7 +13,7 @@ import { } from "../daos/guest.dao"; import { readMembersInfo } from "./teams.service"; import { getMemberCountByTeamId } from "../daos/member.dao"; -import { findTeamPreviewByCategoryForLeader, getTeamByLeaderId, getTeamDetailforGuesting } from "../daos/team.dao"; +import { getTeamByLeaderId, getTeamDetailforGuesting } from "../daos/team.dao"; import { getUserInfoByCategory, getUserProfileByCategory } from "../daos/user.dao"; import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; @@ -21,11 +21,8 @@ import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema" export const createGuesting = async (userId, body: CreateGuestingBody) => { const teamId = body.teamId; const team = await getTeamByLeaderId(teamId, userId); - const teamInfo = await getTeamDetailforGuesting(teamId); if (!team) { throw new BaseError(status.TEAM_LEADER_NOT_FOUND); - } else if (!teamInfo) { - throw new BaseError(status.TEAM_INFO_NOT_FOUND); } await insertGuesting(teamId, body); return; From 832dfb04ec1ae2a3c3b1dfa993adf4291b3bc605 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Wed, 7 Feb 2024 02:31:14 +0900 Subject: [PATCH 075/147] =?UTF-8?q?[BUGFIX]=20=EA=B2=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=8B=A0=EC=B2=AD=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?#107?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/response.status.ts | 34 +++++++++++++++--------- src/daos/guest-user.dao.ts | 48 ++++++++++++++++++++++++++++++++++ src/schemas/guest.schema.ts | 1 - src/services/guests.service.ts | 14 ++++++++++ 4 files changed, 84 insertions(+), 13 deletions(-) create mode 100644 src/daos/guest-user.dao.ts diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 208671f..d4e391c 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -90,6 +90,18 @@ export const status: { [key: string]: Status } = { code: "TEAM002", message: "요청한 팀을 찾을 수 없습니다.", }, + TEAM_LEADER_NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "TEAM003", + message: "요청한 유저가 팀장인 팀을 찾을 수 없습니다.", + }, + TEAM_INFO_NOT_FOUND: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "TEAM004", + message: "팀 정보 입력이 필요합니다.", + }, //member err ALREADY_JOINED: { @@ -112,26 +124,17 @@ export const status: { [key: string]: Status } = { code: "GUESTER001", message: "게스팅을 찾을 수 없습니다.", }, - GUESTUSER_NOT_FOUND: { status: StatusCodes.NOT_FOUND, isSuccess: false, code: "GUESTER002", message: "해당 게스트 신청 유저를 찾을 수 없습니다.", }, - - TEAM_LEADER_NOT_FOUND: { + GUESTUSER_ALREADY_EXIST: { status: StatusCodes.NOT_FOUND, isSuccess: false, - code: "GUESTER003", - message: "요청한 유저가 팀장인 팀을 찾을 수 없습니다.", - }, - - TEAM_INFO_NOT_FOUND: { - status: StatusCodes.NOT_FOUND, - isSuccess: false, - code: "GUESTER004", - message: "팀 정보 입력이 필요합니다.", + code: "GUESTER002", + message: "이미 신청한 게스트 모집글입니다.", }, // game error @@ -158,6 +161,13 @@ export const status: { [key: string]: Status } = { message: "요청한 유저를 찾을 수 없습니다.", }, + NOT_FILL_USER_PROFILE: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "USER002", + message: "유저 정보가 필요합니다.", + }, + //review err MATCH_ID_REQUIRED: { status: StatusCodes.BAD_REQUEST, diff --git a/src/daos/guest-user.dao.ts b/src/daos/guest-user.dao.ts new file mode 100644 index 0000000..d62005f --- /dev/null +++ b/src/daos/guest-user.dao.ts @@ -0,0 +1,48 @@ +import db from "../models"; + +export const InsertGuestUser = async (guestingId: number, userId: number) => { + await db.GuestUser.create({ + guestId: guestingId, + userId: userId, + status: 0, + }); +}; + +export const getApplyGuestingUser = async (guestingId: number) => { + return await db.GuestUser.findAll({ + raw: true, + where: { + guestId: guestingId, + }, + include: [ + { + model: db.User, + attributes: ["nickname", "height"], + }, + ], + attributes: ["status"], + }); +}; + +export const getGuestUserById = async (guestUserId: number) => { + return await db.GuestUser.findOne({ + where: { + id: guestUserId, + }, + }); +}; + +export const setGuestUserStatus = async (guestUser) => { + guestUser.status = 1; + await guestUser.save(); +}; + +export const checkForDuplicateGuestUser = async (userId, guestId) => { + return await db.GuestUser.findOne({ + raw: true, + where: { + userId, + guestId, + }, + }); +}; diff --git a/src/schemas/guest.schema.ts b/src/schemas/guest.schema.ts index 89685b2..05dca65 100644 --- a/src/schemas/guest.schema.ts +++ b/src/schemas/guest.schema.ts @@ -42,7 +42,6 @@ const readGuest = { export const readGuestSchema = object({ query: object({ ...readGuest, - ...dateField, }), }); diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index 3f7cc3f..d237b3b 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -17,6 +17,7 @@ import { getTeamByLeaderId, getTeamDetailforGuesting } from "../daos/team.dao"; import { getUserInfoByCategory, getUserProfileByCategory } from "../daos/user.dao"; import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; +import { checkForDuplicateGuestUser } from "../daos/guest-user.dao"; export const createGuesting = async (userId, body: CreateGuestingBody) => { const teamId = body.teamId; @@ -82,9 +83,22 @@ export const readDetailedGuesting = async (params) => { export const addGuestUser = async (userId, query, params) => { const guestingId = params.guestingId; const userProfile = await getUserProfileByCategory(userId, query.category); + const userProfileVerify: boolean = + userProfile.gender || + userProfile.ageGroup || + userProfile["Profile.region"] || + userProfile["Profile.height"] || + userProfile["Profile.position"] || + userProfile["Profile.description"]; + const checkGuestUser = await checkForDuplicateGuestUser(userId, guestingId); if (!userProfile) { throw new BaseError(status.GUESTUSER_NOT_FOUND); + } else if (!userProfileVerify) { + throw new BaseError(status.NOT_FILL_USER_PROFILE); + } else if (checkGuestUser) { + throw new BaseError(status.GUESTUSER_ALREADY_EXIST); } + await InsertGuestUser(guestingId, userId); return; }; From 622dfa78be1a906ec0275267cece41f89bd0ce45 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Wed, 7 Feb 2024 02:42:20 +0900 Subject: [PATCH 076/147] =?UTF-8?q?[BUGFIX]=20guest,=20guest-user=20dao?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EA=B5=AC=EB=B6=84=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/guest-user.dao.ts | 48 ++++++++++++++++++++++++++++++++++++++ src/daos/guest.dao.ts | 8 ------- 2 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 src/daos/guest-user.dao.ts diff --git a/src/daos/guest-user.dao.ts b/src/daos/guest-user.dao.ts new file mode 100644 index 0000000..d62005f --- /dev/null +++ b/src/daos/guest-user.dao.ts @@ -0,0 +1,48 @@ +import db from "../models"; + +export const InsertGuestUser = async (guestingId: number, userId: number) => { + await db.GuestUser.create({ + guestId: guestingId, + userId: userId, + status: 0, + }); +}; + +export const getApplyGuestingUser = async (guestingId: number) => { + return await db.GuestUser.findAll({ + raw: true, + where: { + guestId: guestingId, + }, + include: [ + { + model: db.User, + attributes: ["nickname", "height"], + }, + ], + attributes: ["status"], + }); +}; + +export const getGuestUserById = async (guestUserId: number) => { + return await db.GuestUser.findOne({ + where: { + id: guestUserId, + }, + }); +}; + +export const setGuestUserStatus = async (guestUser) => { + guestUser.status = 1; + await guestUser.save(); +}; + +export const checkForDuplicateGuestUser = async (userId, guestId) => { + return await db.GuestUser.findOne({ + raw: true, + where: { + userId, + guestId, + }, + }); +}; diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index dc06ba8..d4efe28 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -102,14 +102,6 @@ export const getDetailedGuesting = async (guestingId: number) => { }); }; -export const InsertGuestUser = async (guestingId: number, userId: number) => { - await db.GuestUser.create({ - guestId: guestingId, - userId: userId, - status: 0, - }); -}; - export const getGuestingById = async (guestingId: number, userId: number) => { const teamId = await getTeamIdByLeaderId(userId); return await db.Guest.findOne({ From ef1f9b2de8d146106665ae32bc5062b92b3f91c9 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Wed, 7 Feb 2024 02:46:59 +0900 Subject: [PATCH 077/147] =?UTF-8?q?[BUGFIX]=20=EB=A7=A4=EC=B9=AD=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20=ED=83=80=EC=9E=85=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{matchings.dao.ts => matching.dao.ts} | 60 +++++-------------- src/dtos/matchings.dto.ts | 5 +- 2 files changed, 20 insertions(+), 45 deletions(-) rename src/daos/{matchings.dao.ts => matching.dao.ts} (67%) diff --git a/src/daos/matchings.dao.ts b/src/daos/matching.dao.ts similarity index 67% rename from src/daos/matchings.dao.ts rename to src/daos/matching.dao.ts index a4a8acd..c288ce1 100644 --- a/src/daos/matchings.dao.ts +++ b/src/daos/matching.dao.ts @@ -1,11 +1,8 @@ -import { Sequelize } from "sequelize"; +import { Sequelize, Op } from "sequelize"; import db from "../models"; -import { UpdateGuestUserBody } from "../schemas/guest-user.schema"; -import { getTeamIdByLeaderId, getTeamNameByTeamId } from "./team.dao"; +import { getTeamNameByTeamId } from "./team.dao"; import { getMemberCountByTeamId } from "./member.dao"; -const { Op } = require("sequelize"); - export const findGuestsOfMatchingGuesting = async (userId: number, date: string) => { const guestUserResults = await db.GuestUser.findAll({ raw: true, @@ -34,19 +31,19 @@ export const findGuestsOfMatchingGuesting = async (userId: number, date: string) ], attributes: ["gameTime"], }); + guestResults.type = "guest"; return guestResults; }; -export const findGamesOfMatchingGuesting = async (userId: number, date: string) => { - const team_id = await NaIdByLeaderId(userId); +export const findGamesOfMatchingGuesting = async (team_id: number, date: string) => { const gameApplyResults = await db.sequelize.query("SELECT game_id FROM game_apply WHERE team_id = :team_id", { type: db.sequelize.QueryTypes.SELECT, replacements: { team_id: team_id }, }); const gameIds = gameApplyResults.map((gameApply) => gameApply.game_id); - const games = await db.Game.findAll({ + const gameResults = await db.Game.findAll({ raw: true, where: { id: { @@ -63,13 +60,13 @@ export const findGamesOfMatchingGuesting = async (userId: number, date: string) ], attributes: ["gameTime"], }); + gameResults.type = "game"; - return games; + return gameResults; }; -export const findGuestsOfMatchingHosting = async (userId: number, date: string) => { - const teamId = await getTeamIdByLeaderId(userId); - return await db.Guest.findAll({ +export const findGuestsOfMatchingHosting = async (teamId: number, date: string) => { + const guestResults = await db.Guest.findAll({ raw: true, where: { teamId: teamId, @@ -83,11 +80,13 @@ export const findGuestsOfMatchingHosting = async (userId: number, date: string) ], attributes: ["gameTime"], }); + guestResults.type = "guest"; + + return guestResults; }; -export const findGamesOfMatchingHosting = async (userId: number, date: string) => { - const teamId = await getTeamIdByLeaderId(userId); - return await db.Game.findAll({ +export const findGamesOfMatchingHosting = async (teamId: number, date: string) => { + const gameResults = await db.Game.findAll({ raw: true, where: { hostTeamId: teamId, @@ -101,36 +100,9 @@ export const findGamesOfMatchingHosting = async (userId: number, date: string) = ], attributes: ["gameTime"], }); -}; - -export const getApplyGuestingUser = async (guestingId: number) => { - return await db.GuestUser.findAll({ - raw: true, - where: { - guestId: guestingId, - }, - include: [ - { - model: db.User, - attributes: ["nickname", "height"], - }, - ], - attributes: ["status"], - }); -}; + gameResults.type = "guest"; -export const setGuestStatus = async (guestUser: UpdateGuestUserBody) => { - const guestUserInstance = await db.GuestUser.findByPk(guestUser.guestId); - guestUserInstance.status = 1; - await guestUserInstance.save(); -}; - -export const getGuestUserById = async (guestUserId: number) => { - return await db.GuestUser.findOne({ - where: { - id: guestUserId, - }, - }); + return gameResults; }; export const getHostingApplicantsTeamList = async (gameId: number) => { diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index 6fceed6..a5f5e7e 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -1,5 +1,6 @@ import { getAgeGroup } from "../constants/age-group.constant"; import { getTeamGender } from "../constants/gender.constant"; +import { getGuestStatus } from "../constants/guest-status.constant"; import { getLevelById } from "../constants/level.constant"; import { AgeGroup } from "../types/age-group.enum"; import { Gender } from "../types/gender.enum"; @@ -7,6 +8,7 @@ import { Gender } from "../types/gender.enum"; interface ReadMatching { gameTime: string; memberCount: number | null; + type: string; Team: { name: string; region: string | null; @@ -33,6 +35,7 @@ export const readMatchingResponseDTO = (matching: ReadMatching) => { memberCount: matching.memberCount, teamAgeGroup: getAgeGroup(matching["Team.ageGroup"]), teamSkillLevel: getLevelById(matching["Team.skillLevel"]), + type: matching.type, }; }; @@ -40,6 +43,6 @@ export const readApplyGuestingUserResponseDTO = (guestingUsers: ReadGuestingUser return { nickname: guestingUsers["User.nickname"], height: guestingUsers["User.height"], - status: guestingUsers.status, + status: getGuestStatus(guestingUsers.status), }; }; From 48a6cc097cea5012016bb995897f9ed38382133f Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Wed, 7 Feb 2024 02:47:45 +0900 Subject: [PATCH 078/147] =?UTF-8?q?[BUGFIX]=20guestUser=20status=20constan?= =?UTF-8?q?t=20=EC=B6=94=EA=B0=80=20=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/guest-status.constant.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 src/constants/guest-status.constant.ts diff --git a/src/constants/guest-status.constant.ts b/src/constants/guest-status.constant.ts new file mode 100644 index 0000000..99be765 --- /dev/null +++ b/src/constants/guest-status.constant.ts @@ -0,0 +1,10 @@ +const guestStatus: Record = { + 0: "미승인", + 1: "승인", +}; + +export const defaultStatus = 0; + +export const getGuestStatus = (key: number): string | undefined => { + return guestStatus[key]; +}; From a0f40f94f67036d7614c9daf00e19d18c071ecd8 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Wed, 7 Feb 2024 02:49:06 +0900 Subject: [PATCH 079/147] =?UTF-8?q?[BUGFIX]=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EC=B9=B4=EC=9A=B4=ED=8A=B8=20=ED=95=A8=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/guest.controller.ts | 2 +- src/schemas/guest-user.schema.ts | 18 ----------------- .../{guest.service.ts => guests.service.ts} | 20 ++++++------------- 3 files changed, 7 insertions(+), 33 deletions(-) delete mode 100644 src/schemas/guest-user.schema.ts rename src/services/{guest.service.ts => guests.service.ts} (79%) diff --git a/src/controllers/guest.controller.ts b/src/controllers/guest.controller.ts index 34730c9..9a7c05a 100644 --- a/src/controllers/guest.controller.ts +++ b/src/controllers/guest.controller.ts @@ -10,7 +10,7 @@ import { createGuesting, addGuestUser, updateGuesting, -} from "../services/guest.service"; +} from "../services/guests.service"; import { createTeam } from "../services/teams.service"; import { getTeamIdByLeaderId } from "../daos/team.dao"; import { updateUserProfile } from "../services/users.service"; diff --git a/src/schemas/guest-user.schema.ts b/src/schemas/guest-user.schema.ts deleted file mode 100644 index a17ada0..0000000 --- a/src/schemas/guest-user.schema.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { TypeOf, object } from "zod"; -import { guestIdField, userIdField, statusField } from "./fields"; - -const body = { - ...guestIdField, - ...userIdField, - ...statusField, -}; - -const updateGuestUserBody = object({ - ...body, -}); - -export const updateGuestUserSchema = object({ - body: updateGuestUserBody, -}); - -export type UpdateGuestUserBody = TypeOf; diff --git a/src/services/guest.service.ts b/src/services/guests.service.ts similarity index 79% rename from src/services/guest.service.ts rename to src/services/guests.service.ts index a63941c..395758c 100644 --- a/src/services/guest.service.ts +++ b/src/services/guests.service.ts @@ -1,7 +1,6 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { - InsertGuestUser, findGuesting, findGuestingByGender, findGuestingByLevel, @@ -12,11 +11,12 @@ import { setGuesting, } from "../daos/guest.dao"; import { readMembersInfo } from "../services/teams.service"; -import { getMemberCountByTeamId } from "../daos/member.dao"; +import { addMemberCount } from "../daos/member.dao"; import { getTeamDetailforGuesting, getTeamIdByLeaderId } from "../daos/team.dao"; import { getUserInfoByCategory } from "../daos/user.dao"; import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; +import { InsertGuestUser } from "../daos/guest-user.dao"; export const createGuesting = async (userId, body: CreateGuestingBody) => { const teamId = await getTeamIdByLeaderId(userId); @@ -36,33 +36,25 @@ export const updateGuesting = async (userId, params, body: UpdateGuestingBody) = export const readGuesting = async (query) => { const guestings = await findGuesting(query.date, query.category); - for (const guesting of guestings) { - guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; - } + await addMemberCount(guestings); return readGuestingResponseDTO(guestings); }; export const readGuestingByGender = async (query) => { const guestings = await findGuestingByGender(query.date, query.category, query.gender); - for (const guesting of guestings) { - guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; - } + await addMemberCount(guestings); return readGuestingResponseDTO(guestings); }; export const readGuestingByLevel = async (query) => { const guestings = await findGuestingByLevel(query.date, query.category, query.skillLevel); - for (const guesting of guestings) { - guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; - } + await addMemberCount(guestings); return readGuestingResponseDTO(guestings); }; export const readGuestingByRegion = async (query) => { const guestings = await findGuestingByRegion(query.date, query.category, query.region); - for (const guesting of guestings) { - guesting.memberCount = (await getMemberCountByTeamId(guesting["Team.id"])) + 1; - } + await addMemberCount(guestings); return readGuestingResponseDTO(guestings); }; From c3693128aa3279d95336f9ebc671001ac3d1c481 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Wed, 7 Feb 2024 02:50:23 +0900 Subject: [PATCH 080/147] =?UTF-8?q?[BUGFIX]=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EB=A7=A4=EC=B9=AD=EB=82=B4=EC=97=AD=20API?= =?UTF-8?q?=EC=97=90=20=EC=A0=81=EC=9A=A9=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/matchings.controller.ts | 4 +-- src/daos/member.dao.ts | 6 ++++ src/routes/matchings.route.ts | 14 ++++---- src/services/matchings.service.ts | 46 +++++++++++-------------- 4 files changed, 36 insertions(+), 34 deletions(-) diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts index 5038c7c..e6dda3d 100644 --- a/src/controllers/matchings.controller.ts +++ b/src/controllers/matchings.controller.ts @@ -11,11 +11,11 @@ import { import { addOpposingTeam } from "../services/teams.service"; export const matchingGuestingPreview = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readMatchingGuesting(req.user.id, req.query))); + res.send(response(status.SUCCESS, await readMatchingGuesting(1, req.query))); }; export const matchingHostingPreview = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); + res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); }; export const ApplyGuestingUserPreview = async (req, res: Response, next) => { diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts index d630b77..2aec27e 100644 --- a/src/daos/member.dao.ts +++ b/src/daos/member.dao.ts @@ -114,3 +114,9 @@ export const getMemberCountByTeamId = async (teamId) => { }); return count; }; + +export const addMemberCount = async (lists) => { + for (const list of lists) { + list.memberCount = (await getMemberCountByTeamId(list["Team.id"])) + 1; + } +}; diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts index a64eadc..6a17e76 100644 --- a/src/routes/matchings.route.ts +++ b/src/routes/matchings.route.ts @@ -2,7 +2,6 @@ import express from "express"; import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; import { validate } from "../middlewares/validate.middleware"; -import { dateParam } from "../schemas/fields"; import { matchingGuestingPreview, matchingHostingPreview, @@ -11,18 +10,19 @@ import { fetchHostingApplicantsTeamList, gameApplicationApproval, } from "../controllers/matchings.controller"; +import { dateQuery } from "../schemas/fields"; export const matchingsRouter = express.Router(); -matchingsRouter.use(verifyUser); +matchingsRouter.get("/guesting", verifyUser, validate(dateQuery), asyncHandler(matchingGuestingPreview)); +// matchingsRouter.get("/guesting", asyncHandler(matchingGuestingPreview)); -matchingsRouter.get("/guesting", verifyUser, validate(dateParam), asyncHandler(matchingGuestingPreview)); +matchingsRouter.get("/hosting", verifyUser, validate(dateQuery), asyncHandler(matchingHostingPreview)); +// matchingsRouter.get("/hosting", asyncHandler(matchingHostingPreview)); -matchingsRouter.get("/hosting", verifyUser, validate(dateParam), asyncHandler(matchingHostingPreview)); +matchingsRouter.get("/hosting/user/:guestingId", asyncHandler(ApplyGuestingUserPreview)); -matchingsRouter.get("/hosting/:guestingId", asyncHandler(ApplyGuestingUserPreview)); - -matchingsRouter.put("/confirmGuest/:guestUserId", asyncHandler(modifyGuestStatus)); +matchingsRouter.patch("/:guestUserId/confirm", asyncHandler(modifyGuestStatus)); // 호스팅 내역 > 신청팀 목록 조회 matchingsRouter.get("/hosting/:gameId", asyncHandler(fetchHostingApplicantsTeamList)); diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index 6c7a969..29e3e72 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -1,50 +1,47 @@ import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; import { findGamesOfMatchingGuesting, findGuestsOfMatchingHosting, findGuestsOfMatchingGuesting, findGamesOfMatchingHosting, - getApplyGuestingUser, - setGuestStatus, - getGuestUserById, getHostingApplicantsTeamList, -} from "../daos/matchings.dao"; -import { getMemberCountByTeamId } from "../daos/member.dao"; +} from "../daos/matching.dao"; +import { addMemberCount } from "../daos/member.dao"; import { readApplyGuestingUserResponseDTO, readMatchingResponseDTO } from "../dtos/matchings.dto"; -import { status } from "../config/response.status"; +import { getApplyGuestingUser, getGuestUserById, setGuestUserStatus } from "../daos/guest-user.dao"; +import { getTeamIdByLeaderId } from "../daos/team.dao"; export const readMatchingGuesting = async (userId, query) => { - const matchingGuestings = await findGuestsOfMatchingGuesting(userId, query.date); - for (const matchingGuesting of matchingGuestings) { - matchingGuesting.memberCount = (await getMemberCountByTeamId(matchingGuesting["Team.id"])) + 1; - } + const teamId = await getTeamIdByLeaderId(userId); + const matchingGuestings = await findGuestsOfMatchingGuesting(teamId, query.date); + await addMemberCount(matchingGuestings); const matchingGames = await findGamesOfMatchingGuesting(userId, query.date); - for (const matchingGame of matchingGames) { - matchingGame.memberCount = (await getMemberCountByTeamId(matchingGame["Team.id"])) + 1; - } + await addMemberCount(matchingGames); const guestingResponseDTO = readMatchingResponseDTO(matchingGuestings); const gameResponseDTO = readMatchingResponseDTO(matchingGames); - return { guesting: guestingResponseDTO, game: gameResponseDTO }; + const matchingGuesting = [guestingResponseDTO, gameResponseDTO].sort((a, b) => + a.gameTime.localeCompare(b.gameTime), + ); + return matchingGuesting; }; export const readMatchingHosting = async (userId, query) => { - const matchingGuestings = await findGuestsOfMatchingHosting(userId, query.date); - for (const matchingGuesting of matchingGuestings) { - matchingGuesting.memberCount = (await getMemberCountByTeamId(matchingGuesting["Team.id"])) + 1; - } + const teamId = await getTeamIdByLeaderId(userId); + const matchingGuestings = await findGuestsOfMatchingHosting(teamId, query.date); + await addMemberCount(matchingGuestings); - const matchingGames = await findGamesOfMatchingHosting(userId, query.date); - for (const matchingGame of matchingGames) { - matchingGame.memberCount = (await getMemberCountByTeamId(matchingGame["Team.id"])) + 1; - } + const matchingGames = await findGamesOfMatchingHosting(teamId, query.date); + await addMemberCount(matchingGames); const guestingResponseDTO = readMatchingResponseDTO(matchingGuestings); const gameResponseDTO = readMatchingResponseDTO(matchingGames); - return { guesting: guestingResponseDTO, game: gameResponseDTO }; + const matchingHosting = [guestingResponseDTO, gameResponseDTO].sort((a, b) => a.gameTime.localeCompare(b.gameTime)); + return matchingHosting; }; export const readApplyGuestingUser = async (params) => { @@ -60,8 +57,7 @@ export const updateGuestStatus = async (params) => { if (!guestUser) { throw new BaseError(status.GUESTUSER_NOT_FOUND); } - - await setGuestStatus(guestUser); + await setGuestUserStatus(guestUser); return; }; From 7341fe57015294e4f9ad0f3b8486bab9b6ec8e62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Wed, 7 Feb 2024 11:21:54 +0900 Subject: [PATCH 081/147] =?UTF-8?q?Fix:=20fields.ts=20=ED=8C=8C=EC=9D=BC?= =?UTF-8?q?=20=EB=82=B4=20=EB=82=A0=EC=95=84=EA=B0=84=20=EC=BD=94=EB=93=9C?= =?UTF-8?q?=20=EB=B3=B5=EA=B5=AC=20#107?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/schemas/fields.ts | 64 +++++++++++++++++++++++++++++++++---------- 1 file changed, 49 insertions(+), 15 deletions(-) diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index ccb274c..407e241 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -80,27 +80,61 @@ export const gameTimeField = { }, z.date()), }; -export const dateField = { - date: z.preprocess((arg) => { - if (typeof arg == "string") { - return new Date(arg); - } - return arg; - }, z.date()), -}; - -export const dateQuery = object({ - query: object({ - ...dateField, - }), -}); - export const descriptionFieldInGame = { description: z.string() }; export const teamIdField = { teamId: z.number().int() }; +export const guestIdField = { guestId: z.number().int() }; + +export const userIdField = { userId: z.number().int() }; + +export const statusField = { status: z.number().int() }; + export const recruitCountField = { recruitCount: z.number().int() }; export const levelField = { level: z.number().int().optional() }; +export const dateParam = object({ + params: object({ + ...gameTimeField, + }), +}); + +export const levelParam = object({ + params: object({ + ...levelField, + }), +}); + +export const genderParam = object({ + params: object({ + ...genderFieldInTeam, + }), +}); + +export const regionParam = object({ + params: object({ + ...regionFieldInTeam, + }), +}); + +export const skillScoreField = { skillScore: z.number().int().min(1).max(5) }; + +export const mannerScoreField = { mannerScore: z.number().int().min(1).max(5) }; + +export const guestMatchIdFieldInUserReview = { guestMatchId: z.number().int() }; + +export const guestMatchIdFieldInTeamReview = { guestMatchId: z.optional(z.number().int()) }; + +export const teamMatchIdField = { teamMatchId: z.optional(z.number().int()) }; + export const revieweeIdField = { revieweeId: z.number().int() }; + +export const dateField = { + date: z.preprocess((arg) => { + if (typeof arg == "string") { + return new Date(arg); + } + return arg; + }, z.date()), +}; From 05d3a230b2696c1ae74f0e8aad23e14779897f6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Wed, 7 Feb 2024 11:23:30 +0900 Subject: [PATCH 082/147] =?UTF-8?q?Fix:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=20=EC=A0=84=20=EC=84=A0=EC=88=98=20=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EB=AF=B8=EC=9E=85=EB=A0=A5=EC=9D=B8=EC=A7=80=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20#107?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/guests.service.ts | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index d237b3b..6691acd 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -80,25 +80,31 @@ export const readDetailedGuesting = async (params) => { return readGuestingDetailResponseDTO(guestingDetail, TeamDetail, leaderInfo, memberInfo); }; -export const addGuestUser = async (userId, query, params) => { +export const addGuestUser = async (userId: number, query, params) => { const guestingId = params.guestingId; + + const existingGuestUser = await checkForDuplicateGuestUser(userId, guestingId); + if (existingGuestUser) { + throw new BaseError(status.GUESTUSER_ALREADY_EXIST); + } + const userProfile = await getUserProfileByCategory(userId, query.category); - const userProfileVerify: boolean = - userProfile.gender || - userProfile.ageGroup || - userProfile["Profile.region"] || - userProfile["Profile.height"] || - userProfile["Profile.position"] || - userProfile["Profile.description"]; - const checkGuestUser = await checkForDuplicateGuestUser(userId, guestingId); - if (!userProfile) { - throw new BaseError(status.GUESTUSER_NOT_FOUND); - } else if (!userProfileVerify) { + if (!isUserProfileValid(userProfile)) { throw new BaseError(status.NOT_FILL_USER_PROFILE); - } else if (checkGuestUser) { - throw new BaseError(status.GUESTUSER_ALREADY_EXIST); } await InsertGuestUser(guestingId, userId); return; }; + +const isUserProfileValid = (userProfile): boolean => { + return ( + userProfile && + // userProfile["Profiles.description"] && + userProfile.gender && + userProfile.ageGroup && + userProfile["Profiles.region"] + // userProfile.height && + // userProfile["Profiles.position"] + ); +}; From 779b6d4963b38df81e65470c954092b458b3c87e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Wed, 7 Feb 2024 11:50:08 +0900 Subject: [PATCH 083/147] =?UTF-8?q?Fix:=20request=20query=EA=B0=80=20?= =?UTF-8?q?=EC=95=84=EB=8B=8C=20DB=20=EC=A1=B0=ED=9A=8C=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=9C=20category=20=EC=82=AC=EC=9A=A9=20#107?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/guests.controller.ts | 2 +- src/daos/guest.dao.ts | 22 ++++++++++++++++++++++ src/routes/guests.route.ts | 8 +------- src/schemas/guest.schema.ts | 6 ------ src/services/guests.service.ts | 11 +++++------ 5 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/controllers/guests.controller.ts b/src/controllers/guests.controller.ts index 6e781a5..b936fd2 100644 --- a/src/controllers/guests.controller.ts +++ b/src/controllers/guests.controller.ts @@ -41,5 +41,5 @@ export const DetailedGuestingPreview = async (req, res: Response, next) => { }; export const applicationGuesting = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.query, req.params))); + res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.params))); }; diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index dc06ba8..cc40167 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -4,6 +4,8 @@ import { Sequelize } from "sequelize"; import { getTeamIdByLeaderId } from "./team.dao"; import { Category } from "../types/category.enum"; import { Gender } from "../types/gender.enum"; +import { BaseError } from "../config/error"; +import { status } from "../config/response.status"; export const insertGuesting = async (teamId: number, data: CreateGuestingBody) => { await db.Guest.create({ @@ -139,3 +141,23 @@ export const getGuestingByAcceptedUserId = async (guestingId: number, userId: nu attributes: ["teamId", "gameTime"], }); }; + +export const getCategoryThroughTeamJoin = async (guestingId: number) => { + const guest = await db.Guest.findOne({ + raw: true, + where: { + id: guestingId, + }, + include: [ + { + model: db.Team, + attributes: ["category"], + }, + ], + attributes: [], + }); + if (!guest) { + throw new BaseError(status.GUEST_NOT_FOUND); + } + return guest["Team.category"]; +}; diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index d5958a7..28fb354 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -3,7 +3,6 @@ import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; import { validate } from "../middlewares/validate.middleware"; import { - addGuestUserSchema, createGuestingSchema, readGuestFilterGenderSchema, readGuestFilterLevelSchema, @@ -40,10 +39,5 @@ guestsRouter.get("/region", validate(readGuestFilterRegionSchema), asyncHandler( guestsRouter.get("/:guestingId", asyncHandler(DetailedGuestingPreview)); -guestsRouter.post( - "/:guestingId/application", - verifyUser, - validate(addGuestUserSchema), - asyncHandler(applicationGuesting), -); +guestsRouter.post("/:guestingId/application", verifyUser, asyncHandler(applicationGuesting)); // guestsRouter.put("/:guestingId", validate(updateGuestingSchema), asyncHandler(applicationGuesting)); diff --git a/src/schemas/guest.schema.ts b/src/schemas/guest.schema.ts index 05dca65..5ba739a 100644 --- a/src/schemas/guest.schema.ts +++ b/src/schemas/guest.schema.ts @@ -66,11 +66,5 @@ export const readGuestFilterRegionSchema = object({ }), }); -export const addGuestUserSchema = object({ - query: object({ - ...categoryField, - }), -}); - export type CreateGuestingBody = TypeOf; export type UpdateGuestingBody = TypeOf; diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index 6691acd..981a9fe 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -6,6 +6,7 @@ import { findGuestingByGender, findGuestingByLevel, findGuestingByRegion, + getCategoryThroughTeamJoin, getDetailedGuesting, getGuestingById, insertGuesting, @@ -80,15 +81,16 @@ export const readDetailedGuesting = async (params) => { return readGuestingDetailResponseDTO(guestingDetail, TeamDetail, leaderInfo, memberInfo); }; -export const addGuestUser = async (userId: number, query, params) => { +export const addGuestUser = async (userId: number, params) => { const guestingId = params.guestingId; + const category = await getCategoryThroughTeamJoin(guestingId); const existingGuestUser = await checkForDuplicateGuestUser(userId, guestingId); if (existingGuestUser) { throw new BaseError(status.GUESTUSER_ALREADY_EXIST); } - const userProfile = await getUserProfileByCategory(userId, query.category); + const userProfile = await getUserProfileByCategory(userId, category); if (!isUserProfileValid(userProfile)) { throw new BaseError(status.NOT_FILL_USER_PROFILE); } @@ -99,11 +101,8 @@ export const addGuestUser = async (userId: number, query, params) => { const isUserProfileValid = (userProfile): boolean => { return ( - userProfile && // userProfile["Profiles.description"] && - userProfile.gender && - userProfile.ageGroup && - userProfile["Profiles.region"] + userProfile.gender && userProfile.ageGroup && userProfile["Profiles.region"] // userProfile.height && // userProfile["Profiles.position"] ); From 18b9ebbb0e09b855759d9bed4fc8064386fe4b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Wed, 7 Feb 2024 12:14:17 +0900 Subject: [PATCH 084/147] =?UTF-8?q?Fix:=20matchings.dao.ts=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=2042=EB=B2=88=EC=A7=B8=20=EC=A4=84=20=EC=98=A4?= =?UTF-8?q?=ED=83=80=20=EC=88=98=EC=A0=95=20#112?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/matchings.dao.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/daos/matchings.dao.ts b/src/daos/matchings.dao.ts index a4a8acd..48aaa8f 100644 --- a/src/daos/matchings.dao.ts +++ b/src/daos/matchings.dao.ts @@ -39,7 +39,7 @@ export const findGuestsOfMatchingGuesting = async (userId: number, date: string) }; export const findGamesOfMatchingGuesting = async (userId: number, date: string) => { - const team_id = await NaIdByLeaderId(userId); + const team_id = await getTeamIdByLeaderId(userId); const gameApplyResults = await db.sequelize.query("SELECT game_id FROM game_apply WHERE team_id = :team_id", { type: db.sequelize.QueryTypes.SELECT, replacements: { team_id: team_id }, From 4d495bb4a3bc6873f4afcc4562a7953b60d5b93d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Wed, 7 Feb 2024 12:35:49 +0900 Subject: [PATCH 085/147] =?UTF-8?q?Refactor:=20findTeamPreviewByCategory?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=EC=97=90=EC=84=9C=20id=EB=8F=84=20?= =?UTF-8?q?=ED=95=A8=EA=BB=98=20=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95=20#109?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/team.dao.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index ff3a888..b1445e6 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -11,7 +11,7 @@ export const findTeamPreviewByCategory = async (userId: number, category: Catego category, leaderId: userId, }, - attributes: ["name", "logo"], + attributes: teamPreviewAttributes(), }); const teamsAsMember = await db.Team.findAll({ raw: true, @@ -27,12 +27,16 @@ export const findTeamPreviewByCategory = async (userId: number, category: Catego attributes: [], }, ], - attributes: ["name", "logo"], + attributes: teamPreviewAttributes(), }); const previews = [...teamsAsLeader, ...teamsAsMember].sort((a, b) => a.name.localeCompare(b.name)); return previews; }; +const teamPreviewAttributes = () => { + return ["id", "name", "logo"]; +}; + export const insertTeam = async (data: CreateTeamBody, userId: number, inviteCode: string) => { await db.Team.create({ logo: data.logo, From 67ec8644344648faa9a4d497841bce54d4c1954d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Wed, 7 Feb 2024 15:28:27 +0900 Subject: [PATCH 086/147] =?UTF-8?q?Feat:=20=EB=A6=AC=EB=B7=B0=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1=20=EA=B0=80=EB=8A=A5=ED=95=9C=20=EA=B8=B0=ED=95=9C?= =?UTF-8?q?=EC=9D=84=20=EC=8B=9C=ED=95=A9=20=EC=8B=9C=EA=B0=84=20=EC=9D=B4?= =?UTF-8?q?=ED=9B=84=EB=B6=80=ED=84=B0=20=ED=95=9C=20=EB=8B=AC=20=EB=8F=99?= =?UTF-8?q?=EC=95=88=EC=9C=BC=EB=A1=9C=20=EC=A0=9C=ED=95=9C=20#88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/reviews.service.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/services/reviews.service.ts b/src/services/reviews.service.ts index a5c4f18..4ce022a 100644 --- a/src/services/reviews.service.ts +++ b/src/services/reviews.service.ts @@ -76,8 +76,10 @@ const retrieveRevieweeIdAndGameTime = async (userId: number, guestMatchId: numbe }; }; -const validateReviewableTime = (gameTime: Date, currentTime: Date) => { - if (gameTime > currentTime) { +export const validateReviewableTime = (gameTime: Date, currentTime: Date) => { + const timeDifference = currentTime.getTime() - gameTime.getTime(); + const oneMonthInMilliseconds = 30 * 24 * 60 * 60 * 1000; + if (timeDifference < 0 && timeDifference > oneMonthInMilliseconds) { throw new BaseError(status.REVIEW_NOT_CURRENTLY_WRITABLE); } }; From b9c3c8469b731a65ffb6026b5dedfe711f0b9abe Mon Sep 17 00:00:00 2001 From: Doeun Date: Wed, 7 Feb 2024 23:25:54 +0900 Subject: [PATCH 087/147] =?UTF-8?q?Bugfix:=20gameApply=20=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20ORM=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9=20#105?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/matchings.controller.ts | 5 ++-- src/daos/matchings.dao.ts | 32 ++++++++--------------- src/dtos/matchings.dto.ts | 9 +++++++ src/models/game-apply.model.ts | 34 +++++++++++++++++++++++++ src/models/game.model.ts | 4 +-- src/models/team.model.ts | 9 +------ src/routes/matchings.route.ts | 4 +-- src/services/matchings.service.ts | 16 +++++++++--- 8 files changed, 72 insertions(+), 41 deletions(-) create mode 100644 src/models/game-apply.model.ts diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts index 5038c7c..4f97243 100644 --- a/src/controllers/matchings.controller.ts +++ b/src/controllers/matchings.controller.ts @@ -6,7 +6,7 @@ import { readMatchingGuesting, readMatchingHosting, updateGuestStatus, - readHostingApplicantsList, + readHostingApplicantsTeamList, } from "../services/matchings.service"; import { addOpposingTeam } from "../services/teams.service"; @@ -27,8 +27,7 @@ export const modifyGuestStatus = async (req, res: Response, next) => { }; export const fetchHostingApplicantsTeamList = async (req, res, next) => { - // return res.send(response(status.SUCCESS, await readHostingApplicantsList(req.user.id, req.query))); - return res.send(response(status.SUCCESS, await readHostingApplicantsList(1, req.params))); + return res.send(response(status.SUCCESS, await readHostingApplicantsTeamList(req.user.id, req.query))); }; export const gameApplicationApproval = async (req, res, next) => { diff --git a/src/daos/matchings.dao.ts b/src/daos/matchings.dao.ts index a4a8acd..5242f6d 100644 --- a/src/daos/matchings.dao.ts +++ b/src/daos/matchings.dao.ts @@ -39,7 +39,7 @@ export const findGuestsOfMatchingGuesting = async (userId: number, date: string) }; export const findGamesOfMatchingGuesting = async (userId: number, date: string) => { - const team_id = await NaIdByLeaderId(userId); + const team_id = await getTeamNameByTeamId(userId); const gameApplyResults = await db.sequelize.query("SELECT game_id FROM game_apply WHERE team_id = :team_id", { type: db.sequelize.QueryTypes.SELECT, replacements: { team_id: team_id }, @@ -133,25 +133,15 @@ export const getGuestUserById = async (guestUserId: number) => { }); }; -export const getHostingApplicantsTeamList = async (gameId: number) => { - const selectQuery = "SELECT team_id FROM game_apply WHERE game_id = :gameId;"; - - const teamIdsResult = await db.sequelize.query(selectQuery, { - type: db.sequelize.QueryTypes.SELECT, - replacements: { gameId: gameId }, +export const getTeamsAppliedById = async (gameId: number) => { + return await db.GameApply.findAll({ + raw: true, + where: { gameId: gameId }, + include: [ + { + model: db.Team, + attributes: ["id", "name", "logo"], + }, + ], }); - - const teamsWithNames = await Promise.all( - teamIdsResult.map(async (team) => { - const name = await getTeamNameByTeamId(team.team_id); - const memberCount = await getMemberCountByTeamId(team.team_id); - return { - team_id: team.team_id, - name: name, - memberCount: memberCount, - }; - }), - ); - - return teamsWithNames; }; diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index 6fceed6..7e7ae6a 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -43,3 +43,12 @@ export const readApplyGuestingUserResponseDTO = (guestingUsers: ReadGuestingUser status: guestingUsers.status, }; }; + +export const readHostingApplicantsTeamResponseDTO = (teams) => { + return teams.map((team) => ({ + teamId: team["Team.id"], + teamLogo: team["Team.logo"], + teamName: team["Team.name"], + memberCount: team.memberCount, + })); +}; diff --git a/src/models/game-apply.model.ts b/src/models/game-apply.model.ts new file mode 100644 index 0000000..a23c740 --- /dev/null +++ b/src/models/game-apply.model.ts @@ -0,0 +1,34 @@ +import { Model, InferAttributes, InferCreationAttributes, DataTypes, Sequelize } from "sequelize"; + +class GameApply extends Model, InferCreationAttributes> { + static initiate(sequelize: Sequelize) { + GameApply.init( + { + gameId: { + type: new DataTypes.INTEGER(), + allowNull: true, + }, + teamId: { + type: new DataTypes.INTEGER(), + allowNull: true, + }, + }, + { + sequelize, + timestamps: true, + underscored: true, + modelName: "GameApply", + tableName: "game_apply", + paranoid: false, + charset: "utf8", + collate: "utf8_general_ci", + }, + ); + } + static associate(db) { + db.GameApply.belongsTo(db.Game, { foreignKey: "game_id" }); + db.GameApply.belongsTo(db.Team, { foreignKey: "team_id" }); + } +} + +module.exports = GameApply; diff --git a/src/models/game.model.ts b/src/models/game.model.ts index dc8f2c4..a5f08b0 100644 --- a/src/models/game.model.ts +++ b/src/models/game.model.ts @@ -49,9 +49,7 @@ class Game extends Model, InferCreationAttributes> { static associate(db) { db.Game.belongsTo(db.Team, { foreignKey: "host_team_id", as: "HostTeam" }); db.Game.belongsTo(db.Team, { foreignKey: "opposing_team_id" }); - db.Game.belongsToMany(db.Team, { - through: "game_apply", - }); + db.Game.hasMany(db.GameApply, { foreignKey: "game_id" }); db.Game.hasMany(db.TeamReview, { foreignKey: "team_match_id" }); } } diff --git a/src/models/team.model.ts b/src/models/team.model.ts index d358523..2489dbc 100644 --- a/src/models/team.model.ts +++ b/src/models/team.model.ts @@ -70,14 +70,7 @@ class Team extends Model, InferCreationAttributes> { db.Team.belongsTo(db.User, { foreignKey: "leader_id" }); db.Team.hasMany(db.Game, { foreignKey: "host_team_id" }); db.Team.hasMany(db.Game, { foreignKey: "opposing_team_id" }); - // db.Team.belongsToMany(db.Game, { - // through: db.GameApply, - // foreignKey: "apply_team_id", - // constraints: false, - // }); - db.Team.belongsToMany(db.Game, { - through: "game_apply", - }); + db.Game.hasMany(db.GameApply, { foreignKey: "team_id" }); db.Team.hasMany(db.Guest, { foreignKey: "team_id" }); db.Team.hasMany(db.TeamReview, { foreignKey: "reviewed_team_id" }); } diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts index a64eadc..29a351c 100644 --- a/src/routes/matchings.route.ts +++ b/src/routes/matchings.route.ts @@ -25,7 +25,7 @@ matchingsRouter.get("/hosting/:guestingId", asyncHandler(ApplyGuestingUserPrevie matchingsRouter.put("/confirmGuest/:guestUserId", asyncHandler(modifyGuestStatus)); // 호스팅 내역 > 신청팀 목록 조회 -matchingsRouter.get("/hosting/:gameId", asyncHandler(fetchHostingApplicantsTeamList)); +matchingsRouter.get("/hosting/team/:gameId", asyncHandler(fetchHostingApplicantsTeamList)); // 호스팅 내역 > 신청 승인 -matchingsRouter.post("/hosting/:gameId", asyncHandler(gameApplicationApproval)); +matchingsRouter.post("/hosting/team/:gameId", asyncHandler(gameApplicationApproval)); diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index 6c7a969..aa2f052 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -7,10 +7,14 @@ import { getApplyGuestingUser, setGuestStatus, getGuestUserById, - getHostingApplicantsTeamList, + getTeamsAppliedById, } from "../daos/matchings.dao"; import { getMemberCountByTeamId } from "../daos/member.dao"; -import { readApplyGuestingUserResponseDTO, readMatchingResponseDTO } from "../dtos/matchings.dto"; +import { + readApplyGuestingUserResponseDTO, + readMatchingResponseDTO, + readHostingApplicantsTeamResponseDTO, +} from "../dtos/matchings.dto"; import { status } from "../config/response.status"; export const readMatchingGuesting = async (userId, query) => { @@ -65,8 +69,12 @@ export const updateGuestStatus = async (params) => { return; }; -export const readHostingApplicantsList = async (userId, params) => { +export const readHostingApplicantsTeamList = async (userId, params) => { const gameId = params.gameId; + const teamsApplied = await getTeamsAppliedById(gameId); + for (const team of teamsApplied) { + team.memberCount = await getMemberCountByTeamId(team); + } - return await getHostingApplicantsTeamList(gameId); + return readHostingApplicantsTeamResponseDTO(teamsApplied); }; From 1b408bee30a5dfdb594e078e1ab91341500ab323 Mon Sep 17 00:00:00 2001 From: Doeun Date: Thu, 8 Feb 2024 01:29:05 +0900 Subject: [PATCH 088/147] =?UTF-8?q?Bugfix:=20=EC=8B=A0=EC=B2=AD=ED=8C=80?= =?UTF-8?q?=20=EC=8A=B9=EC=9D=B8=20API=20=EB=A9=94=EC=84=9C=EB=93=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=20#118?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/matchings.controller.ts | 1 - src/routes/matchings.route.ts | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts index 4f97243..fe14e17 100644 --- a/src/controllers/matchings.controller.ts +++ b/src/controllers/matchings.controller.ts @@ -32,5 +32,4 @@ export const fetchHostingApplicantsTeamList = async (req, res, next) => { export const gameApplicationApproval = async (req, res, next) => { return res.send(response(status.SUCCESS, await addOpposingTeam(req.user.id, req.params, req.body))); - // return res.send(response(status.SUCCESS, await addOpposingTeam(1, req.params, req.body))); // for testing }; diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts index 29a351c..8f9fc0a 100644 --- a/src/routes/matchings.route.ts +++ b/src/routes/matchings.route.ts @@ -28,4 +28,4 @@ matchingsRouter.put("/confirmGuest/:guestUserId", asyncHandler(modifyGuestStatus matchingsRouter.get("/hosting/team/:gameId", asyncHandler(fetchHostingApplicantsTeamList)); // 호스팅 내역 > 신청 승인 -matchingsRouter.post("/hosting/team/:gameId", asyncHandler(gameApplicationApproval)); +matchingsRouter.patch("/hosting/team/:gameId", asyncHandler(gameApplicationApproval)); From 002a6135c717cf7dac22722f64a02db57107f6f6 Mon Sep 17 00:00:00 2001 From: Doeun Date: Fri, 9 Feb 2024 18:49:31 +0900 Subject: [PATCH 089/147] =?UTF-8?q?Bugfix:=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20=EC=8B=A0=EC=B2=AD=20=ED=8C=80=20=EB=A6=AC=EB=8D=94?= =?UTF-8?q?=20=EA=B2=80=EC=A6=9D=20=EB=B0=8F=20ORM=20=EC=A0=81=EC=9A=A9=20?= =?UTF-8?q?#121?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/games.controller.ts | 5 +---- src/daos/game.dao.ts | 11 ++++------- src/routes/games.route.ts | 13 +++++++------ src/services/games.service.ts | 9 +++++++-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/controllers/games.controller.ts b/src/controllers/games.controller.ts index eabe272..65c43c5 100644 --- a/src/controllers/games.controller.ts +++ b/src/controllers/games.controller.ts @@ -30,7 +30,6 @@ export const fetchGamesByRegion = async (req, res, next) => { export const fetchTeamsAvailById = async (req, res, next) => { res.send(response(status.SUCCESS, await readTeamAvailPreviewById(req.user.id, req.query))); - // res.send(response(status.SUCCESS, await readTeamAvailPreviewById(1, req.query))); // for testing }; export const fetchGameDetail = async (req, res, next) => { @@ -39,14 +38,12 @@ export const fetchGameDetail = async (req, res, next) => { export const addGame = async (req, res, next) => { return res.send(response(status.SUCCESS, await createGame(req.user.id, req.body))); - // return res.send(response(status.SUCCESS, await createGame(2, req.body))); //for testing }; export const modifyGame = async (req, res, next) => { return res.send(response(status.SUCCESS, await updateGame(req.user.id, req.params, req.body))); - // return res.send(response(status.SUCCESS, await updateGame(2, req.params, req.body))); //for testing }; export const applyGame = async (req, res, next) => { - return res.send(response(status.SUCCESS, await addGameApplication(req.params, req.body))); + return res.send(response(status.SUCCESS, await addGameApplication(req.user.id, req.params, req.body))); }; diff --git a/src/daos/game.dao.ts b/src/daos/game.dao.ts index 2959a31..289efca 100644 --- a/src/daos/game.dao.ts +++ b/src/daos/game.dao.ts @@ -121,14 +121,11 @@ export const getGameByUserId = async (gameId, userId) => { }; export const insertGameApplication = async (gameId: number, body: ApplyGameBody) => { - const teamId = body.teamId; // ApplyGameBody 내의 teamId를 추출 + const teamId = body.teamId; - const insertQuery = - "INSERT INTO game_apply (game_id, team_id, created_at, updated_at) VALUES (:gameId, :teamId, NOW(), NOW())"; - - await db.sequelize.query(insertQuery, { - replacements: { gameId: gameId, teamId: teamId }, - type: db.sequelize.QueryTypes.INSERT, + await db.GameApply.create({ + gameId: gameId, + teamId: teamId, }); }; diff --git a/src/routes/games.route.ts b/src/routes/games.route.ts index 969565d..ac92234 100644 --- a/src/routes/games.route.ts +++ b/src/routes/games.route.ts @@ -1,5 +1,7 @@ import express from "express"; import asyncHandler from "express-async-handler"; +import { verifyUser } from "../middlewares/auth.middleware"; +import { validate } from "../middlewares/validate.middleware"; import { fetchGamesByDate, fetchGamesByGender, @@ -12,7 +14,6 @@ import { applyGame, } from "../controllers/games.controller"; import { createGameSchema, updateGameSchema } from "../schemas/game.schema"; -import { validate } from "../middlewares/validate.middleware"; export const gamesRouter = express.Router(); @@ -25,14 +26,14 @@ gamesRouter.get("/by-level", asyncHandler(fetchGamesByLevel)); gamesRouter.get("/by-region", asyncHandler(fetchGamesByRegion)); // 연습경기 신청 가능 팀 목록 조회 -gamesRouter.get("/apply-avail", asyncHandler(fetchTeamsAvailById)); +gamesRouter.get("/apply-avail", verifyUser, asyncHandler(fetchTeamsAvailById)); // 연습경기 신청 -gamesRouter.post("/:gameId/application", asyncHandler(applyGame)); +gamesRouter.post("/:gameId/application", verifyUser, asyncHandler(applyGame)); // 연습경기 모집글 상세 조회 -gamesRouter.get("/:gameId", asyncHandler(fetchGameDetail)); +gamesRouter.get("/:gameId", verifyUser, asyncHandler(fetchGameDetail)); // 연습경기 모집글 작성/수정 -gamesRouter.post("/", validate(createGameSchema), asyncHandler(addGame)); -gamesRouter.put("/:gameId", validate(updateGameSchema), asyncHandler(modifyGame)); +gamesRouter.post("/", verifyUser, validate(createGameSchema), asyncHandler(addGame)); +gamesRouter.put("/:gameId", verifyUser, validate(updateGameSchema), asyncHandler(modifyGame)); diff --git a/src/services/games.service.ts b/src/services/games.service.ts index 8ca31fb..4bb2509 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -74,7 +74,7 @@ export const createGame = async (userId, body: CreateGameBody) => { const team = await getTeamByLeaderId(hostTeamId, userId); if (!team) { - // team 메뉴로 이동 + throw new BaseError(status.TEAM_LEADER_NOT_FOUND); } await insertGame(hostTeamId, body, category); @@ -91,8 +91,13 @@ export const updateGame = async (userId, params, body: UpdateGameBody) => { return; }; -export const addGameApplication = async (params, body: ApplyGameBody) => { +export const addGameApplication = async (userId: number, params, body: ApplyGameBody) => { const gameId = params.gameId; + const teamId = await getTeamIdByLeaderId(userId); + const team = await getTeamByLeaderId(teamId, userId); + if (!team) { + throw new BaseError(status.TEAM_LEADER_NOT_FOUND); + } await insertGameApplication(gameId, body); return; From 2ee44533375e874983cb38da7f0f6934a3884ac9 Mon Sep 17 00:00:00 2001 From: Doeun Date: Fri, 9 Feb 2024 19:05:05 +0900 Subject: [PATCH 090/147] =?UTF-8?q?Bugfix:=20games.route.ts=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EC=97=90=20validate=20=ED=95=A8=EC=88=98=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#123?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/games.route.ts | 17 ++++++++++---- src/schemas/game.schema.ts | 48 +++++++++++++++++++++++++++++++++----- 2 files changed, 54 insertions(+), 11 deletions(-) diff --git a/src/routes/games.route.ts b/src/routes/games.route.ts index ac92234..f979778 100644 --- a/src/routes/games.route.ts +++ b/src/routes/games.route.ts @@ -13,17 +13,24 @@ import { modifyGame, applyGame, } from "../controllers/games.controller"; -import { createGameSchema, updateGameSchema } from "../schemas/game.schema"; +import { + readGameSchema, + readGameFilterGenderSchema, + readGameFilterLevelSchema, + readGameFilterRegionSchema, + createGameSchema, + updateGameSchema, +} from "../schemas/game.schema"; export const gamesRouter = express.Router(); // 날짜별 연습경기 모집글 조회 (기본) -gamesRouter.get("/", asyncHandler(fetchGamesByDate)); +gamesRouter.get("/", validate(readGameSchema), asyncHandler(fetchGamesByDate)); // 연습경기 모집글 필터링 - 성별/레벨/지역 -gamesRouter.get("/by-gender", asyncHandler(fetchGamesByGender)); -gamesRouter.get("/by-level", asyncHandler(fetchGamesByLevel)); -gamesRouter.get("/by-region", asyncHandler(fetchGamesByRegion)); +gamesRouter.get("/by-gender", validate(readGameFilterGenderSchema), asyncHandler(fetchGamesByGender)); +gamesRouter.get("/by-level", validate(readGameFilterLevelSchema), asyncHandler(fetchGamesByLevel)); +gamesRouter.get("/by-region", validate(readGameFilterRegionSchema), asyncHandler(fetchGamesByRegion)); // 연습경기 신청 가능 팀 목록 조회 gamesRouter.get("/apply-avail", verifyUser, asyncHandler(fetchTeamsAvailById)); diff --git a/src/schemas/game.schema.ts b/src/schemas/game.schema.ts index fddbdc3..1f67655 100644 --- a/src/schemas/game.schema.ts +++ b/src/schemas/game.schema.ts @@ -1,21 +1,25 @@ import { TypeOf, object, z } from "zod"; -import { hostTeamIdField, gameTimeField, descriptionFieldInGame } from "./fields"; +import { + hostTeamIdField, + gameTimeField, + descriptionFieldInGame, + categoryField, + dateField, + levelField, + regionFieldInTeam, + genderFieldInTeam, +} from "./fields"; const fields = { hostTeamId: z.number().int().optional(), // 본인이 리더인 팀이 하나인 경우는 바로 들어가겠지만, 여러 개이면 선택하여 들어갈 듯..? - // applyTeamId: z.number().int().min(1), - // opposingTeamId: z.number().int().min(1), gameTime: z.preprocess((arg) => { if (typeof arg == "string") { return new Date(arg); } return arg; }, z.date()), - - // category: z.string(), description: z.string(), - // status: z.number().int().min(0), }; const body = object({ @@ -43,3 +47,35 @@ export const updateGameSchema = object({ }); export type UpdateGameBody = TypeOf; + +export const readGame = { + ...categoryField, + ...dateField, +}; + +export const readGameSchema = object({ + query: object({ + ...readGame, + }), +}); + +export const readGameFilterGenderSchema = object({ + query: object({ + ...readGame, + ...genderFieldInTeam, + }), +}); + +export const readGameFilterLevelSchema = object({ + query: object({ + ...readGame, + ...levelField, + }), +}); + +export const readGameFilterRegionSchema = object({ + query: object({ + ...readGame, + ...regionFieldInTeam, + }), +}); From e52ea58f6801d2872a18c15b1ef4c7d8560b8154 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sat, 10 Feb 2024 17:19:21 +0900 Subject: [PATCH 091/147] =?UTF-8?q?[BUGFIX]=20=EA=B2=8C=EC=8A=A4=ED=8C=85?= =?UTF-8?q?=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20=EB=B0=8F=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=20=EC=97=90=EB=9F=AC=20=ED=95=B4=EA=B2=B0=20?= =?UTF-8?q?#103?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/guests.controller.ts | 9 +++++--- src/daos/team.dao.ts | 2 +- src/dtos/guests.dto.ts | 33 ++++++++++------------------ src/routes/guests.route.ts | 12 +++++----- src/schemas/fields.ts | 2 +- src/services/guests.service.ts | 12 +++++++--- 6 files changed, 34 insertions(+), 36 deletions(-) diff --git a/src/controllers/guests.controller.ts b/src/controllers/guests.controller.ts index b936fd2..dc282f9 100644 --- a/src/controllers/guests.controller.ts +++ b/src/controllers/guests.controller.ts @@ -13,11 +13,13 @@ import { } from "../services/guests.service"; export const addGuesting = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await createGuesting(req.user.id, req.body))); + // res.send(response(status.SUCCESS, await createGuesting(req.user.id, req.body))); + res.send(response(status.SUCCESS, await createGuesting(1, req.body))); }; export const modifyGuesting = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await updateGuesting(req.user.id, req.params, req.body))); + // res.send(response(status.SUCCESS, await updateGuesting(req.user.id, req.params, req.body))); + res.send(response(status.SUCCESS, await updateGuesting(1, req.params, req.body))); }; export const GuestingPreview = async (req, res: Response, next) => { @@ -41,5 +43,6 @@ export const DetailedGuestingPreview = async (req, res: Response, next) => { }; export const applicationGuesting = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.params))); + // res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.params))); + res.send(response(status.SUCCESS, await addGuestUser(1, req.params))); }; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index ff3a888..abf6cbc 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -68,7 +68,7 @@ export const getTeamDetail = async (teamId: number) => { }; export const getTeamDetailforGuesting = async (teamId: number) => { - return await db.Team.findOne({ + return await db.Team.findAll({ raw: true, where: { id: teamId, diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index d37afd2..ba6ceff 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -4,19 +4,6 @@ import { getLevelById } from "../constants/level.constant"; import { AgeGroup } from "../types/age-group.enum"; import { Gender } from "../types/gender.enum"; -interface ReadGuest { - gameTime: string; - memberCount: number | null; - recruitCount: number | null; - Team: { - name: string; - region: string | null; - gender: Gender; - ageGroup: AgeGroup; - skillLevel: number; - }; -} - interface GuestDetail { gameTime: string; description: string | null; @@ -43,16 +30,18 @@ interface UserInfo { }; } -export const readGuestingResponseDTO = (guesting: ReadGuest) => { +export const readGuestingResponseDTO = (result) => { return { - gameTime: guesting.gameTime, - teamName: guesting["Team.name"], - teamRegion: guesting["Team.region"], - teamGender: getTeamGender(guesting["Team.gender"]), - memberCount: guesting.memberCount, - teamAgeGroup: getAgeGroup(guesting["Team.ageGroup"]), - teamSkillLevel: getLevelById(guesting["Team.skillLevel"]), - recruitCount: guesting.recruitCount, + guestings: result.map((guesting) => ({ + gameTime: guesting.gameTime, + teamName: guesting["Team.name"], + teamRegion: guesting["Team.region"], + teamGender: getTeamGender(guesting["Team.gender"]), + memberCount: guesting.memberCount, + teamAgeGroup: getAgeGroup(guesting["Team.ageGroup"]), + teamSkillLevel: getLevelById(guesting["Team.skillLevel"]), + recruitCount: guesting.recruitCount, + })), }; }; diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index 28fb354..a257c86 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -23,11 +23,11 @@ import { export const guestsRouter = express.Router(); -guestsRouter.post("/", verifyUser, validate(createGuestingSchema), asyncHandler(addGuesting)); -// guestsRouter.post("/", validate(createGuestingSchema), asyncHandler(addGuesting)); +// guestsRouter.post("/", verifyUser, validate(createGuestingSchema), asyncHandler(addGuesting)); +guestsRouter.post("/", validate(createGuestingSchema), asyncHandler(addGuesting)); -guestsRouter.put("/:guestingId", verifyUser, validate(updateGuestingSchema), asyncHandler(modifyGuesting)); -// guestsRouter.put("/:guestingId", validate(updateGuestingSchema), asyncHandler(modifyGuesting)); +// guestsRouter.put("/:guestingId", verifyUser, validate(updateGuestingSchema), asyncHandler(modifyGuesting)); +guestsRouter.put("/:guestingId", validate(updateGuestingSchema), asyncHandler(modifyGuesting)); guestsRouter.get("/", validate(readGuestSchema), asyncHandler(GuestingPreview)); @@ -39,5 +39,5 @@ guestsRouter.get("/region", validate(readGuestFilterRegionSchema), asyncHandler( guestsRouter.get("/:guestingId", asyncHandler(DetailedGuestingPreview)); -guestsRouter.post("/:guestingId/application", verifyUser, asyncHandler(applicationGuesting)); -// guestsRouter.put("/:guestingId", validate(updateGuestingSchema), asyncHandler(applicationGuesting)); +// guestsRouter.post("/:guestingId/application", verifyUser, asyncHandler(applicationGuesting)); +guestsRouter.post("/:guestingId/application", asyncHandler(applicationGuesting)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index f53c783..381fd95 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -92,7 +92,7 @@ export const statusField = { status: z.number().int() }; export const recruitCountField = { recruitCount: z.number().int() }; -export const levelField = { level: z.number().int().optional() }; +export const levelField = { level: z.string() }; export const skillScoreField = { skillScore: z.number().int().min(1).max(5) }; diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index 77d36a3..775bb47 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -52,6 +52,10 @@ export const readGuestingByGender = async (query) => { }; export const readGuestingByLevel = async (query) => { + const levelAsNumber = parseInt(query.level); + if (isNaN(levelAsNumber)) { + throw new BaseError(status.REQUEST_VALIDATION_ERROR); + } const guestings = await findGuestingByLevel(query.date, query.category, query.level); await addMemberCount(guestings); return readGuestingResponseDTO(guestings); @@ -66,10 +70,12 @@ export const readGuestingByRegion = async (query) => { export const readDetailedGuesting = async (params) => { const guestingId = params.guestingId; const guestingDetail = await getDetailedGuesting(guestingId); - const TeamDetail = await getTeamDetailforGuesting(guestingDetail.teamId); + const TeamDetails = await getTeamDetailforGuesting(guestingDetail.teamId); + const TeamDetail = TeamDetails[0]; const leaderInfo = await getUserInfoByCategory(TeamDetail.leaderId, TeamDetail.category); - const memberInfo = await readMembersInfo(TeamDetail, TeamDetail.category); - return readGuestingDetailResponseDTO(guestingDetail, TeamDetail, leaderInfo, memberInfo); + const membersInfo = await readMembersInfo(TeamDetails, TeamDetail.category); + + return readGuestingDetailResponseDTO(guestingDetail, TeamDetail, leaderInfo, membersInfo); }; export const addGuestUser = async (userId: number, params) => { From e6f1e7d4de32e8ddf77bc5590ee277e1d3c45896 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sat, 10 Feb 2024 18:39:01 +0900 Subject: [PATCH 092/147] =?UTF-8?q?[BUGFIX]=20=EB=A7=A4=EC=B9=AD=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20=EC=97=90=EB=9F=AC=20=ED=95=B4?= =?UTF-8?q?=EA=B2=B0=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/matchings.controller.ts | 6 ++- src/daos/guest-user.dao.ts | 6 +++ src/daos/matching.dao.ts | 31 +++++++++----- src/dtos/matchings.dto.ts | 56 ++++++++----------------- src/routes/matchings.route.ts | 6 +-- src/schemas/fields.ts | 2 +- src/services/matchings.service.ts | 14 ++++--- 7 files changed, 61 insertions(+), 60 deletions(-) diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts index 8e63bb8..7b28987 100644 --- a/src/controllers/matchings.controller.ts +++ b/src/controllers/matchings.controller.ts @@ -11,11 +11,13 @@ import { import { addOpposingTeam } from "../services/teams.service"; export const matchingGuestingPreview = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readMatchingGuesting(1, req.query))); + res.send(response(status.SUCCESS, await readMatchingGuesting(req.user.id, req.query))); + // res.send(response(status.SUCCESS, await readMatchingGuesting(1, req.query))); }; export const matchingHostingPreview = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); + res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); + // res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); }; export const ApplyGuestingUserPreview = async (req, res: Response, next) => { diff --git a/src/daos/guest-user.dao.ts b/src/daos/guest-user.dao.ts index d62005f..29949ca 100644 --- a/src/daos/guest-user.dao.ts +++ b/src/daos/guest-user.dao.ts @@ -17,6 +17,12 @@ export const getApplyGuestingUser = async (guestingId: number) => { include: [ { model: db.User, + include: [ + { + model: db.Profile, + attributes: ["position"], + }, + ], attributes: ["nickname", "height"], }, ], diff --git a/src/daos/matching.dao.ts b/src/daos/matching.dao.ts index b12e7b1..b503443 100644 --- a/src/daos/matching.dao.ts +++ b/src/daos/matching.dao.ts @@ -29,25 +29,30 @@ export const findGuestsOfMatchingGuesting = async (userId: number, date: string) ], attributes: ["gameTime"], }); - guestResults.type = "guest"; + for (const guestResult of guestResults) { + guestResult.type = "guest"; + } return guestResults; }; -export const findGamesOfMatchingGuesting = async (team_id: number, date: string) => { - const gameApplyResults = await db.sequelize.query("SELECT game_id FROM game_apply WHERE team_id = :team_id", { - type: db.sequelize.QueryTypes.SELECT, - replacements: { team_id: team_id }, +export const findGamesOfMatchingGuesting = async (teamId: number, date: string) => { + const gameApplyResults = await db.GameApply.findAll({ + raw: true, + where: { + teamId: teamId, + }, + attributes: ["gameId"], }); - const gameIds = gameApplyResults.map((gameApply) => gameApply.game_id); + const gameIds = gameApplyResults.map((gameApply) => gameApply.gameId); const gameResults = await db.Game.findAll({ raw: true, where: { id: { [Op.in]: gameIds, }, - opposing_team_id: team_id, + opposingTeamId: teamId, [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), }, include: [ @@ -58,7 +63,9 @@ export const findGamesOfMatchingGuesting = async (team_id: number, date: string) ], attributes: ["gameTime"], }); - gameResults.type = "game"; + for (const gameResult of gameResults) { + gameResult.type = "game"; + } return gameResults; }; @@ -78,7 +85,9 @@ export const findGuestsOfMatchingHosting = async (teamId: number, date: string) ], attributes: ["gameTime"], }); - guestResults.type = "guest"; + for (const guestResult of guestResults) { + guestResult.type = "guest"; + } return guestResults; }; @@ -98,7 +107,9 @@ export const findGamesOfMatchingHosting = async (teamId: number, date: string) = ], attributes: ["gameTime"], }); - gameResults.type = "game"; + for (const gameResult of gameResults) { + gameResult.type = "game"; + } return gameResults; }; diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index 6f94d2c..aec10e8 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -2,49 +2,29 @@ import { getAgeGroup } from "../constants/age-group.constant"; import { getTeamGender } from "../constants/gender.constant"; import { getGuestStatus } from "../constants/guest-status.constant"; import { getLevelById } from "../constants/level.constant"; -import { AgeGroup } from "../types/age-group.enum"; -import { Gender } from "../types/gender.enum"; -interface ReadMatching { - gameTime: string; - memberCount: number | null; - type: string; - Team: { - name: string; - region: string | null; - gender: Gender; - ageGroup: AgeGroup; - skillLevel: number; - }; -} - -interface ReadGuestingUser { - status: number; - User: { - nickname: string; - height: number | null; - }; -} - -export const readMatchingResponseDTO = (matching: ReadMatching) => { +export const readMatchingResponseDTO = (result) => { return { - gameTime: matching.gameTime, - teamName: matching["Team.name"], - teamRegion: matching["Team.region"], - teamGender: getTeamGender(matching["Team.gender"]), - memberCount: matching.memberCount, - teamAgeGroup: getAgeGroup(matching["Team.ageGroup"]), - teamSkillLevel: getLevelById(matching["Team.skillLevel"]), - type: matching.type, + matchings: result.map((matching) => ({ + gameTime: matching.gameTime, + teamName: matching["Team.name"], + teamRegion: matching["Team.region"], + teamGender: getTeamGender(matching["Team.gender"]), + memberCount: matching.memberCount, + teamAgeGroup: getAgeGroup(matching["Team.ageGroup"]), + teamSkillLevel: getLevelById(matching["Team.skillLevel"]), + type: matching.type, + })), }; }; -export const readApplyGuestingUserResponseDTO = (guestingUsers: ReadGuestingUser) => { - return { - nickname: guestingUsers["User.nickname"], - height: guestingUsers["User.height"], - status: getGuestStatus(guestingUsers.status), - }; +export const readApplyGuestingUserResponseDTO = (result) => { + return result.map((guestingUser) => ({ + nickname: guestingUser["User.nickname"], + height: guestingUser["User.height"], + position: guestingUser["User.Profiles.position"], + status: getGuestStatus(guestingUser.status), + })); }; export const readHostingApplicantsTeamResponseDTO = (teams) => { diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts index d681ebe..55c83af 100644 --- a/src/routes/matchings.route.ts +++ b/src/routes/matchings.route.ts @@ -15,14 +15,14 @@ import { dateQuery } from "../schemas/fields"; export const matchingsRouter = express.Router(); matchingsRouter.get("/guesting", verifyUser, validate(dateQuery), asyncHandler(matchingGuestingPreview)); -// matchingsRouter.get("/guesting", asyncHandler(matchingGuestingPreview)); +// matchingsRouter.get("/guesting", validate(dateQuery), asyncHandler(matchingGuestingPreview)); matchingsRouter.get("/hosting", verifyUser, validate(dateQuery), asyncHandler(matchingHostingPreview)); -// matchingsRouter.get("/hosting", asyncHandler(matchingHostingPreview)); +// matchingsRouter.get("/hosting", validate(dateQuery), asyncHandler(matchingHostingPreview)); matchingsRouter.get("/hosting/user/:guestingId", asyncHandler(ApplyGuestingUserPreview)); -matchingsRouter.patch("/:guestUserId/confirm", asyncHandler(modifyGuestStatus)); +matchingsRouter.patch("/hosting/guest/:guestUserId", asyncHandler(modifyGuestStatus)); // 호스팅 내역 > 신청팀 목록 조회 matchingsRouter.get("/hosting/team/:gameId", asyncHandler(fetchHostingApplicantsTeamList)); diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 381fd95..a1e06e4 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -116,7 +116,7 @@ export const dateField = { }; export const dateQuery = object({ - params: object({ + query: object({ ...dateField, }), }); diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index 3ee83e3..13afd4f 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -20,16 +20,16 @@ export const readMatchingGuesting = async (userId, query) => { const teamId = await getTeamIdByLeaderId(userId); const matchingGuestings = await findGuestsOfMatchingGuesting(teamId, query.date); await addMemberCount(matchingGuestings); - - const matchingGames = await findGamesOfMatchingGuesting(userId, query.date); + const matchingGames = await findGamesOfMatchingGuesting(teamId, query.date); await addMemberCount(matchingGames); const guestingResponseDTO = readMatchingResponseDTO(matchingGuestings); const gameResponseDTO = readMatchingResponseDTO(matchingGames); - const matchingGuesting = [guestingResponseDTO, gameResponseDTO].sort((a, b) => - a.gameTime.localeCompare(b.gameTime), + const matchingGuesting = [...guestingResponseDTO.matchings, ...gameResponseDTO.matchings].sort( + (a, b) => new Date(a.gameTime).getTime() - new Date(b.gameTime).getTime(), ); + return matchingGuesting; }; @@ -44,14 +44,16 @@ export const readMatchingHosting = async (userId, query) => { const guestingResponseDTO = readMatchingResponseDTO(matchingGuestings); const gameResponseDTO = readMatchingResponseDTO(matchingGames); - const matchingHosting = [guestingResponseDTO, gameResponseDTO].sort((a, b) => a.gameTime.localeCompare(b.gameTime)); + const matchingHosting = [...guestingResponseDTO.matchings, ...gameResponseDTO.matchings].sort( + (a, b) => new Date(a.gameTime).getTime() - new Date(b.gameTime).getTime(), + ); + return matchingHosting; }; export const readApplyGuestingUser = async (params) => { const guestingId = params.guestingId; const applyGuestingUser = await getApplyGuestingUser(guestingId); - return readApplyGuestingUserResponseDTO(applyGuestingUser); }; From e6562238362899717af52c51db90c122b47f3cb0 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sat, 10 Feb 2024 20:36:06 +0900 Subject: [PATCH 093/147] =?UTF-8?q?[REFACTOR]=20=EA=B2=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20API=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=95=20=EC=B6=94=EA=B0=80=20#125?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/guests.controller.ts | 12 ++-- src/daos/guest.dao.ts | 87 ++++++++++------------------ src/dtos/guests.dto.ts | 3 +- src/models/guest.model.ts | 6 -- src/routes/guests.route.ts | 12 ++-- src/services/guests.service.ts | 37 ++++++------ 6 files changed, 65 insertions(+), 92 deletions(-) diff --git a/src/controllers/guests.controller.ts b/src/controllers/guests.controller.ts index dc282f9..f8d168f 100644 --- a/src/controllers/guests.controller.ts +++ b/src/controllers/guests.controller.ts @@ -13,13 +13,13 @@ import { } from "../services/guests.service"; export const addGuesting = async (req, res: Response, next) => { - // res.send(response(status.SUCCESS, await createGuesting(req.user.id, req.body))); - res.send(response(status.SUCCESS, await createGuesting(1, req.body))); + res.send(response(status.SUCCESS, await createGuesting(req.user.id, req.body))); + // res.send(response(status.SUCCESS, await createGuesting(1, req.body))); }; export const modifyGuesting = async (req, res: Response, next) => { - // res.send(response(status.SUCCESS, await updateGuesting(req.user.id, req.params, req.body))); - res.send(response(status.SUCCESS, await updateGuesting(1, req.params, req.body))); + res.send(response(status.SUCCESS, await updateGuesting(req.user.id, req.params, req.body))); + // res.send(response(status.SUCCESS, await updateGuesting(1, req.params, req.body))); }; export const GuestingPreview = async (req, res: Response, next) => { @@ -43,6 +43,6 @@ export const DetailedGuestingPreview = async (req, res: Response, next) => { }; export const applicationGuesting = async (req, res: Response, next) => { - // res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.params))); - res.send(response(status.SUCCESS, await addGuestUser(1, req.params))); + res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.params))); + // res.send(response(status.SUCCESS, await addGuestUser(1, req.params))); }; diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index cfb9c9e..d12d0cb 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -1,11 +1,14 @@ import db from "../models"; import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; -import { Sequelize } from "sequelize"; +import { Op, Sequelize } from "sequelize"; import { getTeamIdByLeaderId } from "./team.dao"; import { Category } from "../types/category.enum"; import { Gender } from "../types/gender.enum"; import { BaseError } from "../config/error"; import { status } from "../config/response.status"; +import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; + +const defaultLimit = 20; export const insertGuesting = async (teamId: number, data: CreateGuestingBody) => { await db.Guest.create({ @@ -23,75 +26,49 @@ export const setGuesting = async (guesting, body: UpdateGuestingBody) => { await guesting.save(); }; -export const findGuesting = async (date: string, category: Category) => { - return await db.Guest.findAll({ - raw: true, - where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - include: [ - { - model: db.Team, - where: { - category, - }, - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - }, - ], - attributes: ["gameTime", "recruitCount"], - }); +export const findGuestAll = (date: string, category: Category, cursorId: number | undefined) => { + const guestsBeforeCursor = generateCursorCondition(cursorId); + const TeamFilter = { category }; + return findGuests(date, category, guestsBeforeCursor, TeamFilter); }; -export const findGuestingByGender = async (date: string, category: Category, gender: Gender) => { - return await db.Guest.findAll({ - raw: true, - where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - include: [ - { - model: db.Team, - where: { - category, - gender, - }, - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - }, - ], - attributes: ["gameTime", "recruitCount"], - }); +export const findGuestByGender = (date: string, category: Category, gender: Gender, cursorId: number | undefined) => { + const guestsBeforeCursor = generateCursorCondition(cursorId); + const TeamFilter = { gender, category }; + return findGuests(date, category, guestsBeforeCursor, TeamFilter); }; -export const findGuestingByLevel = async (date: string, category: Category, skillLevel: number) => { - return await db.Guest.findAll({ - raw: true, - where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - include: [ - { - model: db.Team, - where: { - category, - skillLevel, - }, - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - }, - ], - attributes: ["gameTime", "recruitCount"], - }); +export const findGuestByLevel = (date: string, category: Category, level: number, cursorId: number | undefined) => { + const guestsBeforeCursor = generateCursorCondition(cursorId); + const TeamFilter = { skillLevel: level, category }; + return findGuests(date, category, guestsBeforeCursor, TeamFilter); +}; + +export const findGuestByRegion = (date: string, category: Category, region: string, cursorId: number | undefined) => { + const guestsBeforeCursor = generateCursorCondition(cursorId); + const TeamFilter = { region, category }; + return findGuests(date, category, guestsBeforeCursor, TeamFilter); }; -export const findGuestingByRegion = async (date: string, category: Category, region: number) => { - return await db.Guest.findAll({ +export const findGuests = async (date: string, category: Category, guestFilter: object, TeamFilter: object) => { + const guests = await db.Guest.findAll({ raw: true, - where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + where: { + ...guestFilter, + [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + }, + order: [["gameTime", "DESC"]], + limit: defaultLimit, include: [ { model: db.Team, - where: { - category, - region, - }, + where: TeamFilter, attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], }, ], attributes: ["gameTime", "recruitCount"], }); + return { guests, hasNext: calculateHasNext(guests, defaultLimit) }; }; export const getDetailedGuesting = async (guestingId: number) => { diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index ba6ceff..f64d13d 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -32,7 +32,7 @@ interface UserInfo { export const readGuestingResponseDTO = (result) => { return { - guestings: result.map((guesting) => ({ + guests: result.guests.map((guesting) => ({ gameTime: guesting.gameTime, teamName: guesting["Team.name"], teamRegion: guesting["Team.region"], @@ -42,6 +42,7 @@ export const readGuestingResponseDTO = (result) => { teamSkillLevel: getLevelById(guesting["Team.skillLevel"]), recruitCount: guesting.recruitCount, })), + hasNext: result.hasNext, }; }; diff --git a/src/models/guest.model.ts b/src/models/guest.model.ts index fbbf7db..1ceaf5e 100644 --- a/src/models/guest.model.ts +++ b/src/models/guest.model.ts @@ -4,12 +4,6 @@ class Guest extends Model, InferCreationAttributes static initiate(sequelize: Sequelize) { Guest.init( { - id: { - type: new DataTypes.INTEGER(), - allowNull: false, - primaryKey: true, - autoIncrement: true, - }, teamId: { type: new DataTypes.INTEGER(), allowNull: false, diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index a257c86..24214e8 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -23,11 +23,11 @@ import { export const guestsRouter = express.Router(); -// guestsRouter.post("/", verifyUser, validate(createGuestingSchema), asyncHandler(addGuesting)); -guestsRouter.post("/", validate(createGuestingSchema), asyncHandler(addGuesting)); +guestsRouter.post("/", verifyUser, validate(createGuestingSchema), asyncHandler(addGuesting)); +// guestsRouter.post("/", validate(createGuestingSchema), asyncHandler(addGuesting)); -// guestsRouter.put("/:guestingId", verifyUser, validate(updateGuestingSchema), asyncHandler(modifyGuesting)); -guestsRouter.put("/:guestingId", validate(updateGuestingSchema), asyncHandler(modifyGuesting)); +guestsRouter.put("/:guestingId", verifyUser, validate(updateGuestingSchema), asyncHandler(modifyGuesting)); +// guestsRouter.put("/:guestingId", validate(updateGuestingSchema), asyncHandler(modifyGuesting)); guestsRouter.get("/", validate(readGuestSchema), asyncHandler(GuestingPreview)); @@ -39,5 +39,5 @@ guestsRouter.get("/region", validate(readGuestFilterRegionSchema), asyncHandler( guestsRouter.get("/:guestingId", asyncHandler(DetailedGuestingPreview)); -// guestsRouter.post("/:guestingId/application", verifyUser, asyncHandler(applicationGuesting)); -guestsRouter.post("/:guestingId/application", asyncHandler(applicationGuesting)); +guestsRouter.post("/:guestingId/application", verifyUser, asyncHandler(applicationGuesting)); +// guestsRouter.post("/:guestingId/application", asyncHandler(applicationGuesting)); diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index 775bb47..1a6848f 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -1,10 +1,10 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { - findGuesting, - findGuestingByGender, - findGuestingByLevel, - findGuestingByRegion, + findGuestAll, + findGuestByGender, + findGuestByLevel, + findGuestByRegion, getCategoryThroughTeamJoin, getDetailedGuesting, getGuestingById, @@ -19,7 +19,7 @@ import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/ import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; import { InsertGuestUser, checkForDuplicateGuestUser } from "../daos/guest-user.dao"; -export const createGuesting = async (userId, body: CreateGuestingBody) => { +export const createGuesting = async (userId: number, body: CreateGuestingBody) => { const teamId = body.teamId; const team = await getTeamByLeaderId(teamId, userId); if (!team) { @@ -29,7 +29,7 @@ export const createGuesting = async (userId, body: CreateGuestingBody) => { return; }; -export const updateGuesting = async (userId, params, body: UpdateGuestingBody) => { +export const updateGuesting = async (userId: number, params, body: UpdateGuestingBody) => { const guestingId = params.guestingId; const guesting = await getGuestingById(guestingId, userId); if (!guesting) { @@ -40,30 +40,31 @@ export const updateGuesting = async (userId, params, body: UpdateGuestingBody) = }; export const readGuesting = async (query) => { - const guestings = await findGuesting(query.date, query.category); - await addMemberCount(guestings); + const cursorId = query.cursorId ? parseInt(query.cursorId) : undefined; + const guestings = await findGuestAll(query.date, query.category, cursorId); + await addMemberCount(guestings.guests); return readGuestingResponseDTO(guestings); }; export const readGuestingByGender = async (query) => { - const guestings = await findGuestingByGender(query.date, query.category, query.gender); - await addMemberCount(guestings); + const cursorId = query.cursorId ? parseInt(query.cursorId) : undefined; + const guestings = await findGuestByGender(query.date, query.category, query.gender, cursorId); + await addMemberCount(guestings.guests); return readGuestingResponseDTO(guestings); }; export const readGuestingByLevel = async (query) => { - const levelAsNumber = parseInt(query.level); - if (isNaN(levelAsNumber)) { - throw new BaseError(status.REQUEST_VALIDATION_ERROR); - } - const guestings = await findGuestingByLevel(query.date, query.category, query.level); - await addMemberCount(guestings); + const cursorId = query.cursorId ? parseInt(query.cursorId) : undefined; + const level = parseInt(query.level); + const guestings = await findGuestByLevel(query.date, query.category, level, cursorId); + await addMemberCount(guestings.guests); return readGuestingResponseDTO(guestings); }; export const readGuestingByRegion = async (query) => { - const guestings = await findGuestingByRegion(query.date, query.category, query.region); - await addMemberCount(guestings); + const cursorId = query.cursorId ? parseInt(query.cursorId) : undefined; + const guestings = await findGuestByRegion(query.date, query.category, query.region, cursorId); + await addMemberCount(guestings.guests); return readGuestingResponseDTO(guestings); }; From bfade087d91723ca7aa33839f9cfc01b254e526f Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sat, 10 Feb 2024 20:59:05 +0900 Subject: [PATCH 094/147] =?UTF-8?q?[REFACTOR]=20=EA=B2=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20API=20?= =?UTF-8?q?=EB=B9=A0=EC=A7=84=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?#125?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/guest.dao.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index d12d0cb..f8664b5 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -29,28 +29,28 @@ export const setGuesting = async (guesting, body: UpdateGuestingBody) => { export const findGuestAll = (date: string, category: Category, cursorId: number | undefined) => { const guestsBeforeCursor = generateCursorCondition(cursorId); const TeamFilter = { category }; - return findGuests(date, category, guestsBeforeCursor, TeamFilter); + return findGuests(date, guestsBeforeCursor, TeamFilter); }; export const findGuestByGender = (date: string, category: Category, gender: Gender, cursorId: number | undefined) => { const guestsBeforeCursor = generateCursorCondition(cursorId); const TeamFilter = { gender, category }; - return findGuests(date, category, guestsBeforeCursor, TeamFilter); + return findGuests(date, guestsBeforeCursor, TeamFilter); }; export const findGuestByLevel = (date: string, category: Category, level: number, cursorId: number | undefined) => { const guestsBeforeCursor = generateCursorCondition(cursorId); const TeamFilter = { skillLevel: level, category }; - return findGuests(date, category, guestsBeforeCursor, TeamFilter); + return findGuests(date, guestsBeforeCursor, TeamFilter); }; export const findGuestByRegion = (date: string, category: Category, region: string, cursorId: number | undefined) => { const guestsBeforeCursor = generateCursorCondition(cursorId); const TeamFilter = { region, category }; - return findGuests(date, category, guestsBeforeCursor, TeamFilter); + return findGuests(date, guestsBeforeCursor, TeamFilter); }; -export const findGuests = async (date: string, category: Category, guestFilter: object, TeamFilter: object) => { +export const findGuests = async (date: string, guestFilter: object, TeamFilter: object) => { const guests = await db.Guest.findAll({ raw: true, where: { From dbf8be3764bd6c9f690b6c9eeb047e576ec5b7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sat, 10 Feb 2024 22:48:06 +0900 Subject: [PATCH 095/147] =?UTF-8?q?Fix:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=9E=91=EC=84=B1,=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C,=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=EC=9D=80=20=EB=A1=9C=EA=B7=B8=EC=9D=B8?= =?UTF-8?q?=EB=90=9C=20=EC=9C=A0=EC=A0=80=EB=A7=8C=20=EA=B0=80=EB=8A=A5?= =?UTF-8?q?=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/guests.route.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/routes/guests.route.ts b/src/routes/guests.route.ts index a257c86..3f80eee 100644 --- a/src/routes/guests.route.ts +++ b/src/routes/guests.route.ts @@ -23,11 +23,9 @@ import { export const guestsRouter = express.Router(); -// guestsRouter.post("/", verifyUser, validate(createGuestingSchema), asyncHandler(addGuesting)); -guestsRouter.post("/", validate(createGuestingSchema), asyncHandler(addGuesting)); +guestsRouter.post("/", verifyUser, validate(createGuestingSchema), asyncHandler(addGuesting)); -// guestsRouter.put("/:guestingId", verifyUser, validate(updateGuestingSchema), asyncHandler(modifyGuesting)); -guestsRouter.put("/:guestingId", validate(updateGuestingSchema), asyncHandler(modifyGuesting)); +guestsRouter.put("/:guestingId", verifyUser, validate(updateGuestingSchema), asyncHandler(modifyGuesting)); guestsRouter.get("/", validate(readGuestSchema), asyncHandler(GuestingPreview)); @@ -37,7 +35,6 @@ guestsRouter.get("/gender", validate(readGuestFilterGenderSchema), asyncHandler( guestsRouter.get("/region", validate(readGuestFilterRegionSchema), asyncHandler(GuestingPreviewByRegion)); -guestsRouter.get("/:guestingId", asyncHandler(DetailedGuestingPreview)); +guestsRouter.get("/:guestingId", verifyUser, asyncHandler(DetailedGuestingPreview)); -// guestsRouter.post("/:guestingId/application", verifyUser, asyncHandler(applicationGuesting)); -guestsRouter.post("/:guestingId/application", asyncHandler(applicationGuesting)); +guestsRouter.post("/:guestingId/application", verifyUser, asyncHandler(applicationGuesting)); From 7c3c78a09436d684eed6ed603e568fef7bed5301 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sat, 10 Feb 2024 22:56:09 +0900 Subject: [PATCH 096/147] =?UTF-8?q?Fix:=20=EB=A7=A4=EC=B9=AD=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=82=B4=20=EB=AA=A8?= =?UTF-8?q?=EB=93=A0=20API=EB=8A=94=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=EB=90=9C?= =?UTF-8?q?=20=EC=9C=A0=EC=A0=80=EB=A7=8C=20=EA=B0=80=EB=8A=A5=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/matchings.route.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/routes/matchings.route.ts b/src/routes/matchings.route.ts index 55c83af..a4a13b8 100644 --- a/src/routes/matchings.route.ts +++ b/src/routes/matchings.route.ts @@ -14,11 +14,11 @@ import { dateQuery } from "../schemas/fields"; export const matchingsRouter = express.Router(); -matchingsRouter.get("/guesting", verifyUser, validate(dateQuery), asyncHandler(matchingGuestingPreview)); -// matchingsRouter.get("/guesting", validate(dateQuery), asyncHandler(matchingGuestingPreview)); +matchingsRouter.use(verifyUser); -matchingsRouter.get("/hosting", verifyUser, validate(dateQuery), asyncHandler(matchingHostingPreview)); -// matchingsRouter.get("/hosting", validate(dateQuery), asyncHandler(matchingHostingPreview)); +matchingsRouter.get("/guesting", validate(dateQuery), asyncHandler(matchingGuestingPreview)); + +matchingsRouter.get("/hosting", validate(dateQuery), asyncHandler(matchingHostingPreview)); matchingsRouter.get("/hosting/user/:guestingId", asyncHandler(ApplyGuestingUserPreview)); From 82d12e540c752c9ee24369a445bc8cd10f6fbc15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sat, 10 Feb 2024 23:02:57 +0900 Subject: [PATCH 097/147] =?UTF-8?q?Fix:=20guests.controller.ts=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=88=98=EC=A0=95=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/guests.controller.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/controllers/guests.controller.ts b/src/controllers/guests.controller.ts index dc282f9..b936fd2 100644 --- a/src/controllers/guests.controller.ts +++ b/src/controllers/guests.controller.ts @@ -13,13 +13,11 @@ import { } from "../services/guests.service"; export const addGuesting = async (req, res: Response, next) => { - // res.send(response(status.SUCCESS, await createGuesting(req.user.id, req.body))); - res.send(response(status.SUCCESS, await createGuesting(1, req.body))); + res.send(response(status.SUCCESS, await createGuesting(req.user.id, req.body))); }; export const modifyGuesting = async (req, res: Response, next) => { - // res.send(response(status.SUCCESS, await updateGuesting(req.user.id, req.params, req.body))); - res.send(response(status.SUCCESS, await updateGuesting(1, req.params, req.body))); + res.send(response(status.SUCCESS, await updateGuesting(req.user.id, req.params, req.body))); }; export const GuestingPreview = async (req, res: Response, next) => { @@ -43,6 +41,5 @@ export const DetailedGuestingPreview = async (req, res: Response, next) => { }; export const applicationGuesting = async (req, res: Response, next) => { - // res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.params))); - res.send(response(status.SUCCESS, await addGuestUser(1, req.params))); + res.send(response(status.SUCCESS, await addGuestUser(req.user.id, req.params))); }; From b8d923cd2cb573fb9f805dbb399eea52597e160b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sat, 10 Feb 2024 23:23:47 +0900 Subject: [PATCH 098/147] =?UTF-8?q?Fix:=20getTeamDetailForGuesting=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=88=98=EC=A0=95=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/team.dao.ts | 11 ++--------- src/services/games.service.ts | 4 ++-- src/services/guests.service.ts | 15 +++++++++------ 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index cea7ae6..2907631 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -71,19 +71,12 @@ export const getTeamDetail = async (teamId: number) => { }); }; -export const getTeamDetailforGuesting = async (teamId: number) => { - return await db.Team.findAll({ +export const getTeamDetailForGuesting = async (teamId: number) => { + return await db.Team.findOne({ raw: true, where: { id: teamId, }, - include: [ - { - model: db.Member, - attributes: ["userId"], - required: false, - }, - ], attributes: [ "name", "description", diff --git a/src/services/games.service.ts b/src/services/games.service.ts index 8ca31fb..e84aa2e 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -11,7 +11,7 @@ import { insertGameApplication, } from "../daos/game.dao"; import { - getTeamDetailforGuesting, + getTeamDetailForGuesting, getTeamIdByLeaderId, getTeamCategoryByLeaderId, getTeamByLeaderId, @@ -62,7 +62,7 @@ export const readGameDetail = async (params) => { throw new BaseError(status.GAME_NOT_FOUND); } - const teamDetail = await getTeamDetailforGuesting(gameDetail.hostTeamId); + const teamDetail = await getTeamDetailForGuesting(gameDetail.hostTeamId); const leaderInfo = await getUserInfoByCategory(teamDetail.leaderId, teamDetail.category); const memberInfo = await findMemberInfoByCategory(gameDetail.hostTeamId, teamDetail.category); return readGameDetailResponseDTO(gameDetail, teamDetail, leaderInfo, memberInfo); diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index 775bb47..5ddcd43 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -13,7 +13,7 @@ import { } from "../daos/guest.dao"; import { readMembersInfo } from "../services/teams.service"; import { addMemberCount } from "../daos/member.dao"; -import { getTeamByLeaderId, getTeamDetailforGuesting } from "../daos/team.dao"; +import { getTeamByLeaderId, getTeamDetailForGuesting } from "../daos/team.dao"; import { getUserInfoByCategory, getUserProfileByCategory } from "../daos/user.dao"; import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; @@ -70,12 +70,15 @@ export const readGuestingByRegion = async (query) => { export const readDetailedGuesting = async (params) => { const guestingId = params.guestingId; const guestingDetail = await getDetailedGuesting(guestingId); - const TeamDetails = await getTeamDetailforGuesting(guestingDetail.teamId); - const TeamDetail = TeamDetails[0]; - const leaderInfo = await getUserInfoByCategory(TeamDetail.leaderId, TeamDetail.category); - const membersInfo = await readMembersInfo(TeamDetails, TeamDetail.category); + if (!guestingDetail) { + throw new BaseError(status.GUEST_NOT_FOUND); + } + + const teamDetails = await getTeamDetailForGuesting(guestingDetail.teamId); + // const leaderInfo = await getUserInfoByCategory(TeamDetail.leaderId, TeamDetail.category); + // const membersInfo = await readMembersInfo(TeamDetails, TeamDetail.category); - return readGuestingDetailResponseDTO(guestingDetail, TeamDetail, leaderInfo, membersInfo); + // return readGuestingDetailResponseDTO(guestingDetail, TeamDetail, leaderInfo, membersInfo); }; export const addGuestUser = async (userId: number, params) => { From 6c23194368d57b199213d1548bfa4e102a6e5f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sat, 10 Feb 2024 23:28:24 +0900 Subject: [PATCH 099/147] =?UTF-8?q?Fix:=20findMemberInfoByCategory=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=EB=A5=BC=20=ED=86=B5=ED=95=9C=20=EB=A9=A4?= =?UTF-8?q?=EB=B2=84=20=ED=94=84=EB=A1=9C=ED=95=84=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/guests.service.ts | 12 +++++------- src/services/teams.service.ts | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index 5ddcd43..a5764a4 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -11,8 +11,7 @@ import { insertGuesting, setGuesting, } from "../daos/guest.dao"; -import { readMembersInfo } from "../services/teams.service"; -import { addMemberCount } from "../daos/member.dao"; +import { addMemberCount, findMemberInfoByCategory } from "../daos/member.dao"; import { getTeamByLeaderId, getTeamDetailForGuesting } from "../daos/team.dao"; import { getUserInfoByCategory, getUserProfileByCategory } from "../daos/user.dao"; import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; @@ -74,11 +73,10 @@ export const readDetailedGuesting = async (params) => { throw new BaseError(status.GUEST_NOT_FOUND); } - const teamDetails = await getTeamDetailForGuesting(guestingDetail.teamId); - // const leaderInfo = await getUserInfoByCategory(TeamDetail.leaderId, TeamDetail.category); - // const membersInfo = await readMembersInfo(TeamDetails, TeamDetail.category); - - // return readGuestingDetailResponseDTO(guestingDetail, TeamDetail, leaderInfo, membersInfo); + const teamDetail = await getTeamDetailForGuesting(guestingDetail.teamId); + const leaderInfo = await getUserInfoByCategory(teamDetail.leaderId, teamDetail.category); + const memberInfo = await findMemberInfoByCategory(guestingDetail.teamId, teamDetail.category); + return readGuestingDetailResponseDTO(guestingDetail, teamDetail, leaderInfo, memberInfo); }; export const addGuestUser = async (userId: number, params) => { diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index 3cd8770..ebe2568 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -56,7 +56,7 @@ export const readTeamDetail = async (userId: number, params) => { return readTeamDetailResponseDTO(detail, leaderInfo, membersInfo, userId == detail.leaderId); }; -export const readMembersInfo = async (details, category: Category) => { +const readMembersInfo = async (details, category: Category) => { const membersInfoPromises = details .filter((detail) => detail["Members.userId"] !== null) .map(async (detail) => { From 5ca85e92970d9569979b6acc56e47ddb4c4d3f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sat, 10 Feb 2024 23:33:37 +0900 Subject: [PATCH 100/147] =?UTF-8?q?Style:=20insertGuestUser=EB=A1=9C=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD=20?= =?UTF-8?q?#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/guest-user.dao.ts | 2 +- src/services/guests.service.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/daos/guest-user.dao.ts b/src/daos/guest-user.dao.ts index 29949ca..c42ee7d 100644 --- a/src/daos/guest-user.dao.ts +++ b/src/daos/guest-user.dao.ts @@ -1,6 +1,6 @@ import db from "../models"; -export const InsertGuestUser = async (guestingId: number, userId: number) => { +export const insertGuestUser = async (guestingId: number, userId: number) => { await db.GuestUser.create({ guestId: guestingId, userId: userId, diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index a5764a4..657c92f 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -16,7 +16,7 @@ import { getTeamByLeaderId, getTeamDetailForGuesting } from "../daos/team.dao"; import { getUserInfoByCategory, getUserProfileByCategory } from "../daos/user.dao"; import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; -import { InsertGuestUser, checkForDuplicateGuestUser } from "../daos/guest-user.dao"; +import { insertGuestUser, checkForDuplicateGuestUser } from "../daos/guest-user.dao"; export const createGuesting = async (userId, body: CreateGuestingBody) => { const teamId = body.teamId; @@ -93,7 +93,7 @@ export const addGuestUser = async (userId: number, params) => { throw new BaseError(status.NOT_FILL_USER_PROFILE); } - await InsertGuestUser(guestingId, userId); + await insertGuestUser(guestingId, userId); return; }; From 32394d6a729fcf27d8f4903359f13da7165eb112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sat, 10 Feb 2024 23:36:48 +0900 Subject: [PATCH 101/147] =?UTF-8?q?Style:=20=EB=8D=94=20=EC=9D=B4=EC=83=81?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?findMemberInfoByTeamId=20=ED=95=A8=EC=88=98=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/member.dao.ts | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts index 2aec27e..b941678 100644 --- a/src/daos/member.dao.ts +++ b/src/daos/member.dao.ts @@ -1,24 +1,6 @@ -import { Op } from "sequelize"; import db from "../models"; +import { Op } from "sequelize"; import { Category } from "../types/category.enum"; -import { userInfoAttributes } from "../daos/user.dao"; - -export const findMemberInfoByTeamId = async (teamId, userInfoAttributes) => { - throw new Error("더 이상 사용하지 않는 함수"); - return await db.Member.findAll({ - raw: true, - where: { - teamId, - }, - include: [ - { - model: db.User, - attributes: userInfoAttributes(), - }, - ], - attributes: [], - }); -}; export const findMemberInfoByCategory = async (teamId: number, category: Category) => { return await db.Member.findAll({ From 8d93c838d41deb138d1c6e874bdeb5c28791f4c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Sat, 10 Feb 2024 23:38:53 +0900 Subject: [PATCH 102/147] =?UTF-8?q?Comment:=20addMemberCount=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EC=97=90=20=EA=B2=BD=EA=B3=A0=20=EB=A9=94=EC=84=B8?= =?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/member.dao.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts index b941678..b432d92 100644 --- a/src/daos/member.dao.ts +++ b/src/daos/member.dao.ts @@ -98,6 +98,7 @@ export const getMemberCountByTeamId = async (teamId) => { }; export const addMemberCount = async (lists) => { + console.log("addMemberCount 함수는 제거될 함수입니다"); for (const list of lists) { list.memberCount = (await getMemberCountByTeamId(list["Team.id"])) + 1; } From 428f81434b623fc6d7cac4889a555cdd2c13ac28 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sun, 11 Feb 2024 01:07:11 +0900 Subject: [PATCH 103/147] =?UTF-8?q?Fix:=20=EC=9C=A0=EC=A0=80(=ED=8C=80?= =?UTF-8?q?=EC=9E=A5)=EC=9D=98=20=EB=AA=A8=EB=93=A0=20=EC=B9=B4=ED=85=8C?= =?UTF-8?q?=EA=B3=A0=EB=A6=AC=20=EA=B2=8C=EC=8A=A4=ED=8C=85,=20=ED=98=B8?= =?UTF-8?q?=EC=8A=A4=ED=8C=85=20=EB=82=B4=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/matching.dao.ts | 24 ++++++------------------ src/daos/team.dao.ts | 10 ++++++++++ src/services/matchings.service.ts | 18 +++++++++++------- 3 files changed, 27 insertions(+), 25 deletions(-) diff --git a/src/daos/matching.dao.ts b/src/daos/matching.dao.ts index b503443..578f75b 100644 --- a/src/daos/matching.dao.ts +++ b/src/daos/matching.dao.ts @@ -36,23 +36,11 @@ export const findGuestsOfMatchingGuesting = async (userId: number, date: string) return guestResults; }; -export const findGamesOfMatchingGuesting = async (teamId: number, date: string) => { - const gameApplyResults = await db.GameApply.findAll({ - raw: true, - where: { - teamId: teamId, - }, - attributes: ["gameId"], - }); - - const gameIds = gameApplyResults.map((gameApply) => gameApply.gameId); +export const findGamesOfMatchingGuesting = async (teamIds, date: string) => { const gameResults = await db.Game.findAll({ raw: true, where: { - id: { - [Op.in]: gameIds, - }, - opposingTeamId: teamId, + opposingTeamId: { [Op.in]: teamIds }, [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), }, include: [ @@ -70,11 +58,11 @@ export const findGamesOfMatchingGuesting = async (teamId: number, date: string) return gameResults; }; -export const findGuestsOfMatchingHosting = async (teamId: number, date: string) => { +export const findGuestsOfMatchingHosting = async (teamIds, date: string) => { const guestResults = await db.Guest.findAll({ raw: true, where: { - teamId: teamId, + teamId: { [Op.in]: teamIds }, [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), }, include: [ @@ -92,11 +80,11 @@ export const findGuestsOfMatchingHosting = async (teamId: number, date: string) return guestResults; }; -export const findGamesOfMatchingHosting = async (teamId: number, date: string) => { +export const findGamesOfMatchingHosting = async (teamIds, date: string) => { const gameResults = await db.Game.findAll({ raw: true, where: { - hostTeamId: teamId, + hostTeamId: { [Op.in]: teamIds }, [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), }, include: [ diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index 2907631..43b64a9 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -122,6 +122,16 @@ export const getTeamIdByLeaderId = async (userId: number) => { return team?.id; }; +export const getTeamIdsByLeaderId = async (userId: number) => { + return await db.Team.findAll({ + raw: true, + where: { + leaderId: userId, + }, + attributes: ["id"], + }); +}; + export const getTeamCategoryByLeaderId = async (userId: number) => { const team = await db.Team.findOne({ raw: true, diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index 13afd4f..6f29cfc 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -9,7 +9,7 @@ import { getTeamsAppliedById, } from "../daos/matching.dao"; import { getMemberCountByTeamId, addMemberCount } from "../daos/member.dao"; -import { getTeamIdByLeaderId } from "../daos/team.dao"; +import { getTeamIdsByLeaderId } from "../daos/team.dao"; import { readApplyGuestingUserResponseDTO, readMatchingResponseDTO, @@ -17,10 +17,12 @@ import { } from "../dtos/matchings.dto"; export const readMatchingGuesting = async (userId, query) => { - const teamId = await getTeamIdByLeaderId(userId); - const matchingGuestings = await findGuestsOfMatchingGuesting(teamId, query.date); + const teamIdResults = await getTeamIdsByLeaderId(userId); + const teamIds = teamIdResults.map((team) => team.id); + + const matchingGuestings = await findGuestsOfMatchingGuesting(userId, query.date); await addMemberCount(matchingGuestings); - const matchingGames = await findGamesOfMatchingGuesting(teamId, query.date); + const matchingGames = await findGamesOfMatchingGuesting(teamIds, query.date); await addMemberCount(matchingGames); const guestingResponseDTO = readMatchingResponseDTO(matchingGuestings); @@ -34,11 +36,13 @@ export const readMatchingGuesting = async (userId, query) => { }; export const readMatchingHosting = async (userId, query) => { - const teamId = await getTeamIdByLeaderId(userId); - const matchingGuestings = await findGuestsOfMatchingHosting(teamId, query.date); + const teamIdResults = await getTeamIdsByLeaderId(userId); + const teamIds = teamIdResults.map((team) => team.id); + + const matchingGuestings = await findGuestsOfMatchingHosting(teamIds, query.date); await addMemberCount(matchingGuestings); - const matchingGames = await findGamesOfMatchingHosting(teamId, query.date); + const matchingGames = await findGamesOfMatchingHosting(teamIds, query.date); await addMemberCount(matchingGames); const guestingResponseDTO = readMatchingResponseDTO(matchingGuestings); From 367ed2e61110181b12df68213d4802b77ff1485c Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sun, 11 Feb 2024 01:44:27 +0900 Subject: [PATCH 104/147] =?UTF-8?q?Fix:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=88=98=EC=A0=95=20API=EC=97=90?= =?UTF-8?q?=EC=84=9C=20teamId=20=EA=B0=80=EC=A0=B8=EC=98=A4=EB=8A=94=20?= =?UTF-8?q?=EB=B6=80=EB=B6=84=20=EC=88=98=EC=A0=95=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/response.status.ts | 8 +++++++- src/daos/guest.dao.ts | 20 +++++++++++++++++--- src/services/guests.service.ts | 4 +++- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/src/config/response.status.ts b/src/config/response.status.ts index d4e391c..77a7f15 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -133,9 +133,15 @@ export const status: { [key: string]: Status } = { GUESTUSER_ALREADY_EXIST: { status: StatusCodes.NOT_FOUND, isSuccess: false, - code: "GUESTER002", + code: "GUESTER003", message: "이미 신청한 게스트 모집글입니다.", }, + ACCESS_DENIED_FOR_GUEST: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "GUESTER004", + message: "게스트 수정 권한이 없습니다.", + }, // game error GAME_NOT_FOUND: { diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index cfb9c9e..2139e9a 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -1,7 +1,6 @@ import db from "../models"; import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; import { Sequelize } from "sequelize"; -import { getTeamIdByLeaderId } from "./team.dao"; import { Category } from "../types/category.enum"; import { Gender } from "../types/gender.enum"; import { BaseError } from "../config/error"; @@ -104,8 +103,23 @@ export const getDetailedGuesting = async (guestingId: number) => { }); }; -export const getGuestingById = async (guestingId: number, userId: number) => { - const teamId = await getTeamIdByLeaderId(userId); +export const getTeamByGuestingId = async (guestingId: number, userId: number) => { + return await db.Guest.findOne({ + where: { + id: guestingId, + }, + include: { + model: db.Team, + where: { + leaderId: userId, + }, + attributes: [], + }, + attributes: ["teamId"], + }); +}; + +export const getGuestingById = async (guestingId: number, teamId: number) => { return await db.Guest.findOne({ where: { id: guestingId, diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index 657c92f..6cb28a9 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -8,6 +8,7 @@ import { getCategoryThroughTeamJoin, getDetailedGuesting, getGuestingById, + getTeamByGuestingId, insertGuesting, setGuesting, } from "../daos/guest.dao"; @@ -30,7 +31,8 @@ export const createGuesting = async (userId, body: CreateGuestingBody) => { export const updateGuesting = async (userId, params, body: UpdateGuestingBody) => { const guestingId = params.guestingId; - const guesting = await getGuestingById(guestingId, userId); + const teamId = await getTeamByGuestingId(guestingId, userId); + const guesting = await getGuestingById(guestingId, teamId.teamId); if (!guesting) { throw new BaseError(status.GUEST_NOT_FOUND); } From a7fa7590b0b34bff5a8d978b956a48158619eec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Mon, 12 Feb 2024 15:20:30 +0900 Subject: [PATCH 105/147] =?UTF-8?q?Fix:=20=EB=82=A0=EC=A7=9C=EB=B3=84=20?= =?UTF-8?q?=ED=98=B8=EC=8A=A4=ED=8C=85=20=EB=82=B4=EC=97=AD=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EC=88=98=EC=A0=95=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 유저가 팀장인 팀이 게스트를 모집한 경기, 상대팀을 모집한 경기를 조회하는 API - 유저가 팀장인 팀을 모두 조회하도록 수정 - findGamesOfMatchingHosting 함수에서 game, team 테이블 간 조회가 안 되는 문제 해결 - findGuestsOfMatchingHosting, findGamesOfMatchingHosting 함수 이름 변경 및 파일 이동 - readMatchingHostingResponseDTO 내에서 경기시간 순 정렬 - 경기 타입을 이넘으로 관리 --- src/daos/game.dao.ts | 28 ++++++++++++++++++-- src/daos/guest.dao.ts | 26 +++++++++++++++++- src/daos/matching.dao.ts | 44 ------------------------------- src/daos/team.dao.ts | 11 ++++++++ src/dtos/matchings.dto.ts | 29 ++++++++++++++++++++ src/services/matchings.service.ts | 34 ++++++++---------------- src/types/match-type.enum.ts | 4 +++ 7 files changed, 106 insertions(+), 70 deletions(-) create mode 100644 src/types/match-type.enum.ts diff --git a/src/daos/game.dao.ts b/src/daos/game.dao.ts index 2959a31..3b68220 100644 --- a/src/daos/game.dao.ts +++ b/src/daos/game.dao.ts @@ -1,9 +1,9 @@ import db from "../models"; -import { Sequelize } from "sequelize"; -import { getStatus } from "../constants/status.constant"; +import { Op, Sequelize } from "sequelize"; import { CreateGameBody } from "../schemas/game.schema"; import { ApplyGameBody } from "../schemas/game-apply.schema"; import { getTeamIdByLeaderId } from "./team.dao"; +import { MatchType } from "../types/match-type.enum"; export const findGamesByDate = async (date, category) => { return await db.Game.findAll({ @@ -145,3 +145,27 @@ export const getGame = async (gameId: number) => { export const updateOpposingTeam = async (gameId: number, opposingTeamId: number) => { await db.Game.update({ opposingTeamId: opposingTeamId, status: 1 }, { where: { id: gameId } }); }; + +export const findGameByTeamsAndGameTime = async (teamIds: number[], gameTime: string) => { + const gameResults = await db.Game.findAll({ + raw: true, + where: { + hostTeamId: { + [Op.in]: teamIds, + }, + [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${gameTime}', '%Y-%m-%d')`), + }, + include: [ + { + model: db.Team, + as: "HostTeam", + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["id", "gameTime"], + }); + for (const gameResult of gameResults) { + gameResult.type = MatchType.game; + } + return gameResults; +}; diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index cfb9c9e..bb469fb 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -1,11 +1,12 @@ import db from "../models"; import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; -import { Sequelize } from "sequelize"; +import { Op, Sequelize } from "sequelize"; import { getTeamIdByLeaderId } from "./team.dao"; import { Category } from "../types/category.enum"; import { Gender } from "../types/gender.enum"; import { BaseError } from "../config/error"; import { status } from "../config/response.status"; +import { MatchType } from "../types/match-type.enum"; export const insertGuesting = async (teamId: number, data: CreateGuestingBody) => { await db.Guest.create({ @@ -153,3 +154,26 @@ export const getCategoryThroughTeamJoin = async (guestingId: number) => { } return guest["Team.category"]; }; + +export const findGuestingByTeamsAndGameTime = async (teamIds: number[], gameTime: string) => { + const guestResults = await db.Guest.findAll({ + raw: true, + where: { + teamId: { + [Op.in]: teamIds, + }, + [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${gameTime}', '%Y-%m-%d')`), + }, + include: [ + { + model: db.Team, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + ], + attributes: ["id", "gameTime"], + }); + for (const guestResult of guestResults) { + guestResult.type = MatchType.guest; + } + return guestResults; +}; diff --git a/src/daos/matching.dao.ts b/src/daos/matching.dao.ts index b503443..270cbb8 100644 --- a/src/daos/matching.dao.ts +++ b/src/daos/matching.dao.ts @@ -70,50 +70,6 @@ export const findGamesOfMatchingGuesting = async (teamId: number, date: string) return gameResults; }; -export const findGuestsOfMatchingHosting = async (teamId: number, date: string) => { - const guestResults = await db.Guest.findAll({ - raw: true, - where: { - teamId: teamId, - [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - }, - include: [ - { - model: db.Team, - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - }, - ], - attributes: ["gameTime"], - }); - for (const guestResult of guestResults) { - guestResult.type = "guest"; - } - - return guestResults; -}; - -export const findGamesOfMatchingHosting = async (teamId: number, date: string) => { - const gameResults = await db.Game.findAll({ - raw: true, - where: { - hostTeamId: teamId, - [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - }, - include: [ - { - model: db.Team, - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - }, - ], - attributes: ["gameTime"], - }); - for (const gameResult of gameResults) { - gameResult.type = "game"; - } - - return gameResults; -}; - export const getTeamsAppliedById = async (gameId: number) => { return await db.GameApply.findAll({ raw: true, diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index 2907631..5b0f386 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -122,6 +122,17 @@ export const getTeamIdByLeaderId = async (userId: number) => { return team?.id; }; +export const findTeamIdByLeaderId = async (userId: number) => { + const teams = await db.Team.findAll({ + raw: true, + where: { + leaderId: userId, + }, + attributes: ["id"], + }); + return teams.map((team) => team.id); +}; + export const getTeamCategoryByLeaderId = async (userId: number) => { const team = await db.Team.findOne({ raw: true, diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index aec10e8..81cf2f9 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -35,3 +35,32 @@ export const readHostingApplicantsTeamResponseDTO = (teams) => { memberCount: team.memberCount, })); }; + +export const readMatchingHostingResponseDTO = (guestings, games) => { + const sortedMatch = [...guestings, ...games].sort((a, b) => a.gameTime.getTime() - b.gameTime.getTime()); + return sortedMatch.map((match) => { + if (match.type === "guest") { + return { + type: match.type, + matchId: match.id, + gameTime: match.gameTime, + name: match["Team.name"], + region: match["Team.region"], + gender: getTeamGender(match["Team.gender"]), + ageGroup: getAgeGroup(match["Team.ageGroup"]), + skillLevel: getLevelById(match["Team.skillLevel"]), + }; + } else { + return { + type: match.type, + matchId: match.id, + gameTime: match.gameTime, + name: match["HostTeam.name"], + region: match["HostTeam.region"], + gender: getTeamGender(match["HostTeam.gender"]), + ageGroup: getAgeGroup(match["HostTeam.ageGroup"]), + skillLevel: getLevelById(match["HostTeam.skillLevel"]), + }; + } + }); +}; diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index 13afd4f..a10b506 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -1,19 +1,16 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; +import { findGameByTeamsAndGameTime } from "../daos/game.dao"; import { getApplyGuestingUser, getGuestUserById, setGuestUserStatus } from "../daos/guest-user.dao"; -import { - findGamesOfMatchingGuesting, - findGuestsOfMatchingHosting, - findGuestsOfMatchingGuesting, - findGamesOfMatchingHosting, - getTeamsAppliedById, -} from "../daos/matching.dao"; +import { findGuestingByTeamsAndGameTime } from "../daos/guest.dao"; +import { findGamesOfMatchingGuesting, findGuestsOfMatchingGuesting, getTeamsAppliedById } from "../daos/matching.dao"; import { getMemberCountByTeamId, addMemberCount } from "../daos/member.dao"; -import { getTeamIdByLeaderId } from "../daos/team.dao"; +import { findTeamIdByLeaderId, getTeamIdByLeaderId } from "../daos/team.dao"; import { readApplyGuestingUserResponseDTO, readMatchingResponseDTO, readHostingApplicantsTeamResponseDTO, + readMatchingHostingResponseDTO, } from "../dtos/matchings.dto"; export const readMatchingGuesting = async (userId, query) => { @@ -33,22 +30,13 @@ export const readMatchingGuesting = async (userId, query) => { return matchingGuesting; }; -export const readMatchingHosting = async (userId, query) => { - const teamId = await getTeamIdByLeaderId(userId); - const matchingGuestings = await findGuestsOfMatchingHosting(teamId, query.date); - await addMemberCount(matchingGuestings); - - const matchingGames = await findGamesOfMatchingHosting(teamId, query.date); - await addMemberCount(matchingGames); - - const guestingResponseDTO = readMatchingResponseDTO(matchingGuestings); - const gameResponseDTO = readMatchingResponseDTO(matchingGames); - - const matchingHosting = [...guestingResponseDTO.matchings, ...gameResponseDTO.matchings].sort( - (a, b) => new Date(a.gameTime).getTime() - new Date(b.gameTime).getTime(), - ); +export const readMatchingHosting = async (userId: number, query) => { + const gameTime = query.date; + const teamIds = await findTeamIdByLeaderId(userId); - return matchingHosting; + const matchingGuestings = await findGuestingByTeamsAndGameTime(teamIds, gameTime); + const matchingGames = await findGameByTeamsAndGameTime(teamIds, gameTime); + return readMatchingHostingResponseDTO(matchingGuestings, matchingGames); }; export const readApplyGuestingUser = async (params) => { diff --git a/src/types/match-type.enum.ts b/src/types/match-type.enum.ts new file mode 100644 index 0000000..25e5aac --- /dev/null +++ b/src/types/match-type.enum.ts @@ -0,0 +1,4 @@ +export enum MatchType { + guest = "guest", + game = "game", +} From b4ebdce0f324564069bb02ab94bc2bc1028c34c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EB=AF=BC=EC=A0=95?= Date: Mon, 12 Feb 2024 16:14:29 +0900 Subject: [PATCH 106/147] =?UTF-8?q?Fix:=20=EB=82=A0=EC=A7=9C=EB=B3=84=20?= =?UTF-8?q?=EA=B2=8C=EC=8A=A4=ED=8C=85=20=EB=82=B4=EC=97=AD=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EC=88=98=EC=A0=95=20#110?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 유저가 게스트로 신청한 경기, 유저가 팀장인 팀이 상대팀으로 신청한 경기를 조회하는 API --- src/daos/game.dao.ts | 24 ++++++++++--- src/daos/guest.dao.ts | 28 +++++++++++++++ src/daos/matching.dao.ts | 58 ------------------------------- src/dtos/matchings.dto.ts | 17 +-------- src/services/matchings.service.ts | 35 +++++++------------ 5 files changed, 61 insertions(+), 101 deletions(-) diff --git a/src/daos/game.dao.ts b/src/daos/game.dao.ts index 3b68220..8819415 100644 --- a/src/daos/game.dao.ts +++ b/src/daos/game.dao.ts @@ -146,13 +146,29 @@ export const updateOpposingTeam = async (gameId: number, opposingTeamId: number) await db.Game.update({ opposingTeamId: opposingTeamId, status: 1 }, { where: { id: gameId } }); }; -export const findGameByTeamsAndGameTime = async (teamIds: number[], gameTime: string) => { +export const findGameByHostTeamsAndGameTime = async (teamIds: number[], gameTime) => { + const hostTeamFilter = { + hostTeamId: { + [Op.in]: teamIds, + }, + }; + return findGameByTeamsAndGameTime(hostTeamFilter, gameTime); +}; + +export const findGameByOpposingTeamsAndGameTime = async (teamIds: number[], gameTime) => { + const opposingTeamFilter = { + opposingTeamId: { + [Op.in]: teamIds, + }, + }; + return findGameByTeamsAndGameTime(opposingTeamFilter, gameTime); +}; + +const findGameByTeamsAndGameTime = async (teamFilter, gameTime: string) => { const gameResults = await db.Game.findAll({ raw: true, where: { - hostTeamId: { - [Op.in]: teamIds, - }, + ...teamFilter, [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${gameTime}', '%Y-%m-%d')`), }, include: [ diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index 7d6e48b..2a91f98 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -192,3 +192,31 @@ export const findGuestingByTeamsAndGameTime = async (teamIds: number[], gameTime } return guestResults; }; + +export const findGuestingByUserAndGameTime = async (userId: number, date: string) => { + const guestResults = await db.Guest.findAll({ + raw: true, + where: { + [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + }, + include: [ + { + model: db.Team, + attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], + }, + { + model: db.GuestUser, + where: { + userId, + status: 1, + }, + attributes: [], + }, + ], + attributes: ["id", "gameTime"], + }); + for (const guestResult of guestResults) { + guestResult.type = MatchType.guest; + } + return guestResults; +}; diff --git a/src/daos/matching.dao.ts b/src/daos/matching.dao.ts index e2d89e0..755190e 100644 --- a/src/daos/matching.dao.ts +++ b/src/daos/matching.dao.ts @@ -1,63 +1,5 @@ -import { Sequelize, Op } from "sequelize"; import db from "../models"; -export const findGuestsOfMatchingGuesting = async (userId: number, date: string) => { - const guestUserResults = await db.GuestUser.findAll({ - raw: true, - where: { - userId: userId, - status: 1, - }, - attributes: ["guestId"], - }); - - const guestIds = guestUserResults.map((guestUser) => guestUser.guestId); - - const guestResults = await db.Guest.findAll({ - raw: true, - where: { - id: { - [Op.in]: guestIds, - }, - [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - }, - include: [ - { - model: db.Team, - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - }, - ], - attributes: ["gameTime"], - }); - for (const guestResult of guestResults) { - guestResult.type = "guest"; - } - - return guestResults; -}; - -export const findGamesOfMatchingGuesting = async (teamIds, date: string) => { - const gameResults = await db.Game.findAll({ - raw: true, - where: { - opposingTeamId: { [Op.in]: teamIds }, - [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - }, - include: [ - { - model: db.Team, - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - }, - ], - attributes: ["gameTime"], - }); - for (const gameResult of gameResults) { - gameResult.type = "game"; - } - - return gameResults; -}; - export const getTeamsAppliedById = async (gameId: number) => { return await db.GameApply.findAll({ raw: true, diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index 3094b78..907f199 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -3,21 +3,6 @@ import { getTeamGender } from "../constants/gender.constant"; import { getGuestStatus } from "../constants/guest-status.constant"; import { getLevelById } from "../constants/level.constant"; -export const readMatchingResponseDTO = (result) => { - return { - matchings: result.map((matching) => ({ - gameTime: matching.gameTime, - teamName: matching["Team.name"], - teamRegion: matching["Team.region"], - teamGender: getTeamGender(matching["Team.gender"]), - memberCount: matching.memberCount, - teamAgeGroup: getAgeGroup(matching["Team.ageGroup"]), - teamSkillLevel: getLevelById(matching["Team.skillLevel"]), - type: matching.type, - })), - }; -}; - export const readApplyGuestingUserResponseDTO = (result) => { return result.map((guestingUser) => ({ nickname: guestingUser["User.nickname"], @@ -36,7 +21,7 @@ export const readHostingApplicantsTeamResponseDTO = (teams) => { })); }; -export const readMatchingHostingResponseDTO = (guestings, games) => { +export const readMatchingResponseDTO = (guestings, games) => { const sortedMatch = [...guestings, ...games].sort((a, b) => a.gameTime.getTime() - b.gameTime.getTime()); return sortedMatch.map((match) => { if (match.type === "guest") { diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index 4baee0a..f7130f0 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -1,46 +1,35 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; -import { findGameByTeamsAndGameTime } from "../daos/game.dao"; +import { findGameByHostTeamsAndGameTime, findGameByOpposingTeamsAndGameTime } from "../daos/game.dao"; import { getApplyGuestingUser, getGuestUserById, setGuestUserStatus } from "../daos/guest-user.dao"; -import { findGuestingByTeamsAndGameTime } from "../daos/guest.dao"; -import { findGamesOfMatchingGuesting, findGuestsOfMatchingGuesting, getTeamsAppliedById } from "../daos/matching.dao"; +import { findGuestingByTeamsAndGameTime, findGuestingByUserAndGameTime } from "../daos/guest.dao"; +import { getTeamsAppliedById } from "../daos/matching.dao"; import { getMemberCountByTeamId, addMemberCount } from "../daos/member.dao"; -import { findTeamIdByLeaderId, getTeamIdsByLeaderId } from "../daos/team.dao"; +import { findTeamIdByLeaderId } from "../daos/team.dao"; import { readApplyGuestingUserResponseDTO, - readMatchingResponseDTO, readHostingApplicantsTeamResponseDTO, - readMatchingHostingResponseDTO, + readMatchingResponseDTO, } from "../dtos/matchings.dto"; export const readMatchingGuesting = async (userId, query) => { - const teamIdResults = await getTeamIdsByLeaderId(userId); - const teamIds = teamIdResults.map((team) => team.id); - - const matchingGuestings = await findGuestsOfMatchingGuesting(userId, query.date); + const gameTime = query.date; + const matchingGuestings = await findGuestingByUserAndGameTime(userId, query.date); + const teamIds = await findTeamIdByLeaderId(userId); + const matchingGames = await findGameByOpposingTeamsAndGameTime(teamIds, gameTime); await addMemberCount(matchingGuestings); - const matchingGames = await findGamesOfMatchingGuesting(teamIds, query.date); await addMemberCount(matchingGames); - - const guestingResponseDTO = readMatchingResponseDTO(matchingGuestings); - const gameResponseDTO = readMatchingResponseDTO(matchingGames); - - const matchingGuesting = [...guestingResponseDTO.matchings, ...gameResponseDTO.matchings].sort( - (a, b) => new Date(a.gameTime).getTime() - new Date(b.gameTime).getTime(), - ); - - return matchingGuesting; + return readMatchingResponseDTO(matchingGuestings, matchingGames); }; export const readMatchingHosting = async (userId: number, query) => { const gameTime = query.date; const teamIds = await findTeamIdByLeaderId(userId); - const matchingGuestings = await findGuestingByTeamsAndGameTime(teamIds, gameTime); - const matchingGames = await findGameByTeamsAndGameTime(teamIds, gameTime); + const matchingGames = await findGameByHostTeamsAndGameTime(teamIds, gameTime); await addMemberCount(matchingGuestings); await addMemberCount(matchingGames); - return readMatchingHostingResponseDTO(matchingGuestings, matchingGames); + return readMatchingResponseDTO(matchingGuestings, matchingGames); }; export const readApplyGuestingUser = async (params) => { From 991609db3b4b093b6184e78636ad5e2b16c957ac Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Mon, 12 Feb 2024 17:08:23 +0900 Subject: [PATCH 107/147] =?UTF-8?q?Fix:=20level=20type=20string->int=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20#125?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/schemas/fields.ts | 2 +- src/services/guests.service.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index a1e06e4..3b6f3a0 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -92,7 +92,7 @@ export const statusField = { status: z.number().int() }; export const recruitCountField = { recruitCount: z.number().int() }; -export const levelField = { level: z.string() }; +export const levelField = { level: z.number().int() }; export const skillScoreField = { skillScore: z.number().int().min(1).max(5) }; diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index de1d6fe..1a89733 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -56,8 +56,7 @@ export const readGuestingByGender = async (query) => { export const readGuestingByLevel = async (query) => { const cursorId = query.cursorId ? parseInt(query.cursorId) : undefined; - const level = parseInt(query.level); - const guestings = await findGuestByLevel(query.date, query.category, level, cursorId); + const guestings = await findGuestByLevel(query.date, query.category, query.level, cursorId); await addMemberCount(guestings.guests); return readGuestingResponseDTO(guestings); }; From 8d22d9844905578b543d8a83e2f88d44681f57fb Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Mon, 12 Feb 2024 17:29:57 +0900 Subject: [PATCH 108/147] =?UTF-8?q?Refactor:=20getGuestingById=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EB=B3=80=EA=B2=BD=20#128?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/guest.dao.ts | 3 +-- src/services/guests.service.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index 2a91f98..e3c3536 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -121,11 +121,10 @@ export const getTeamByGuestingId = async (guestingId: number, userId: number) => }); }; -export const getGuestingById = async (guestingId: number, teamId: number) => { +export const getGuestingById = async (guestingId: number) => { return await db.Guest.findOne({ where: { id: guestingId, - teamId: teamId, }, }); }; diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index 6cb28a9..2d68237 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -32,8 +32,8 @@ export const createGuesting = async (userId, body: CreateGuestingBody) => { export const updateGuesting = async (userId, params, body: UpdateGuestingBody) => { const guestingId = params.guestingId; const teamId = await getTeamByGuestingId(guestingId, userId); - const guesting = await getGuestingById(guestingId, teamId.teamId); - if (!guesting) { + const guesting = await getGuestingById(guestingId); + if (!guesting || !teamId) { throw new BaseError(status.GUEST_NOT_FOUND); } await setGuesting(guesting, body); From 6e344bbc41a5ca59b307f331f52d585c02d4bf38 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Mon, 12 Feb 2024 17:31:57 +0900 Subject: [PATCH 109/147] =?UTF-8?q?[FEAT]=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20status=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80=20#128?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/guest-user.model.ts | 1 + src/models/guest.model.ts | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/src/models/guest-user.model.ts b/src/models/guest-user.model.ts index cd0bbf9..0c78024 100644 --- a/src/models/guest-user.model.ts +++ b/src/models/guest-user.model.ts @@ -15,6 +15,7 @@ class GuestUser extends Model, InferCreationAttribute status: { type: new DataTypes.INTEGER(), allowNull: false, + defaultValue: 0, }, }, { diff --git a/src/models/guest.model.ts b/src/models/guest.model.ts index fbbf7db..d326624 100644 --- a/src/models/guest.model.ts +++ b/src/models/guest.model.ts @@ -26,6 +26,11 @@ class Guest extends Model, InferCreationAttributes type: new DataTypes.INTEGER(), allowNull: false, }, + status: { + type: new DataTypes.INTEGER(), + allowNull: false, + defaultValue: 0, + }, }, { sequelize, From 0abada278e00e1e6cdbf97e10278351a8802b352 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Mon, 12 Feb 2024 17:33:19 +0900 Subject: [PATCH 110/147] =?UTF-8?q?[FEAT]=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=8B=A0=EC=B2=AD=20=EC=9C=A0=EC=A0=80=EC=88=98=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20Guest.status=20=EA=B0=92=20=ED=8C=90?= =?UTF-8?q?=EB=B3=84=20#128?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/response.status.ts | 8 +++++++- src/daos/guest-user.dao.ts | 33 ++++++++++++++++++++++++++++++- src/services/matchings.service.ts | 13 +++++++++--- 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 77a7f15..937ef72 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -128,7 +128,7 @@ export const status: { [key: string]: Status } = { status: StatusCodes.NOT_FOUND, isSuccess: false, code: "GUESTER002", - message: "해당 게스트 신청 유저를 찾을 수 없습니다.", + message: "게스트 신청 유저를 찾을 수 없습니다.", }, GUESTUSER_ALREADY_EXIST: { status: StatusCodes.NOT_FOUND, @@ -142,6 +142,12 @@ export const status: { [key: string]: Status } = { code: "GUESTER004", message: "게스트 수정 권한이 없습니다.", }, + CLOSED_GUEST: { + status: StatusCodes.NOT_FOUND, + isSuccess: false, + code: "GUESTER005", + message: "마감된 게스트 모집글입니다.", + }, // game error GAME_NOT_FOUND: { diff --git a/src/daos/guest-user.dao.ts b/src/daos/guest-user.dao.ts index c42ee7d..cdc3c8e 100644 --- a/src/daos/guest-user.dao.ts +++ b/src/daos/guest-user.dao.ts @@ -38,8 +38,39 @@ export const getGuestUserById = async (guestUserId: number) => { }); }; -export const setGuestUserStatus = async (guestUser) => { +export const getGuestIdById = async (guestUserId: number) => { + const guest = await db.GuestUser.findOne({ + where: { + id: guestUserId, + }, + attributes: ["guestId"], + }); + return guest?.guestId; +}; + +export const checkClosedGuest = async (guestId: number) => { + const count = await db.GuestUser.count({ + where: { + guestId: guestId, + status: 1, + }, + }); + const recruitCount = await db.Guest.findOne({ + where: { + id: guestId, + }, + attributes: ["recruitCount"], + }); + return count >= recruitCount.recruitCount; +}; + +export const setGuestUserStatus = async (guestUser, guest) => { guestUser.status = 1; + const check: boolean = await checkClosedGuest(guest.id); + if (check) { + guest.status = 1; + await guest.save(); + } await guestUser.save(); }; diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index f7130f0..33fe434 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -1,8 +1,8 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { findGameByHostTeamsAndGameTime, findGameByOpposingTeamsAndGameTime } from "../daos/game.dao"; -import { getApplyGuestingUser, getGuestUserById, setGuestUserStatus } from "../daos/guest-user.dao"; -import { findGuestingByTeamsAndGameTime, findGuestingByUserAndGameTime } from "../daos/guest.dao"; +import { getApplyGuestingUser, getGuestIdById, getGuestUserById, setGuestUserStatus } from "../daos/guest-user.dao"; +import { findGuestingByTeamsAndGameTime, findGuestingByUserAndGameTime, getGuestingById } from "../daos/guest.dao"; import { getTeamsAppliedById } from "../daos/matching.dao"; import { getMemberCountByTeamId, addMemberCount } from "../daos/member.dao"; import { findTeamIdByLeaderId } from "../daos/team.dao"; @@ -44,7 +44,14 @@ export const updateGuestStatus = async (params) => { if (!guestUser) { throw new BaseError(status.GUESTUSER_NOT_FOUND); } - await setGuestUserStatus(guestUser); + + const guestId = await getGuestIdById(guestUserId); + const guest = await getGuestingById(guestId); + if (guest.status) { + throw new BaseError(status.CLOSED_GUEST); + } + + await setGuestUserStatus(guestUser, guest); return; }; From 009233875d7489cdaa9345a324e80e1b6ab64baf Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Mon, 12 Feb 2024 17:38:58 +0900 Subject: [PATCH 111/147] =?UTF-8?q?Refactor:=20updateGuestUserStatus=20?= =?UTF-8?q?=ED=95=A8=EC=88=98=20matchings.service=20->=20guests.service=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99=20#128?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/matchings.controller.ts | 6 ++---- src/services/guests.service.ts | 25 ++++++++++++++++++++++++- src/services/matchings.service.ts | 23 ++--------------------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/controllers/matchings.controller.ts b/src/controllers/matchings.controller.ts index ae53f15..e9e4a29 100644 --- a/src/controllers/matchings.controller.ts +++ b/src/controllers/matchings.controller.ts @@ -5,19 +5,17 @@ import { readApplyGuestingUser, readMatchingGuesting, readMatchingHosting, - updateGuestStatus, readHostingApplicantsTeamList, } from "../services/matchings.service"; import { addOpposingTeam } from "../services/teams.service"; +import { updateGuestUserStatus } from "../services/guests.service"; export const matchingGuestingPreview = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readMatchingGuesting(req.user.id, req.query))); - // res.send(response(status.SUCCESS, await readMatchingGuesting(1, req.query))); }; export const matchingHostingPreview = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readMatchingHosting(req.user.id, req.query))); - // res.send(response(status.SUCCESS, await readMatchingHosting(1, req.query))); }; export const ApplyGuestingUserPreview = async (req, res: Response, next) => { @@ -25,7 +23,7 @@ export const ApplyGuestingUserPreview = async (req, res: Response, next) => { }; export const modifyGuestStatus = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await updateGuestStatus(req.params))); + res.send(response(status.SUCCESS, await updateGuestUserStatus(req.params))); }; export const fetchHostingApplicantsTeamList = async (req, res, next) => { diff --git a/src/services/guests.service.ts b/src/services/guests.service.ts index 2d68237..8dabff3 100644 --- a/src/services/guests.service.ts +++ b/src/services/guests.service.ts @@ -17,7 +17,13 @@ import { getTeamByLeaderId, getTeamDetailForGuesting } from "../daos/team.dao"; import { getUserInfoByCategory, getUserProfileByCategory } from "../daos/user.dao"; import { readGuestingDetailResponseDTO, readGuestingResponseDTO } from "../dtos/guests.dto"; import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; -import { insertGuestUser, checkForDuplicateGuestUser } from "../daos/guest-user.dao"; +import { + insertGuestUser, + checkForDuplicateGuestUser, + getGuestUserById, + getGuestIdById, + setGuestUserStatus, +} from "../daos/guest-user.dao"; export const createGuesting = async (userId, body: CreateGuestingBody) => { const teamId = body.teamId; @@ -107,3 +113,20 @@ const isUserProfileValid = (userProfile): boolean => { // userProfile["Profiles.position"] ); }; + +export const updateGuestUserStatus = async (params) => { + const guestUserId = params.guestUserId; + const guestUser = await getGuestUserById(guestUserId); + if (!guestUser) { + throw new BaseError(status.GUESTUSER_NOT_FOUND); + } + + const guestId = await getGuestIdById(guestUserId); + const guest = await getGuestingById(guestId); + if (guest.status) { + throw new BaseError(status.CLOSED_GUEST); + } + + await setGuestUserStatus(guestUser, guest); + return; +}; diff --git a/src/services/matchings.service.ts b/src/services/matchings.service.ts index 33fe434..fcfedd3 100644 --- a/src/services/matchings.service.ts +++ b/src/services/matchings.service.ts @@ -1,8 +1,6 @@ -import { BaseError } from "../config/error"; -import { status } from "../config/response.status"; import { findGameByHostTeamsAndGameTime, findGameByOpposingTeamsAndGameTime } from "../daos/game.dao"; -import { getApplyGuestingUser, getGuestIdById, getGuestUserById, setGuestUserStatus } from "../daos/guest-user.dao"; -import { findGuestingByTeamsAndGameTime, findGuestingByUserAndGameTime, getGuestingById } from "../daos/guest.dao"; +import { getApplyGuestingUser } from "../daos/guest-user.dao"; +import { findGuestingByTeamsAndGameTime, findGuestingByUserAndGameTime } from "../daos/guest.dao"; import { getTeamsAppliedById } from "../daos/matching.dao"; import { getMemberCountByTeamId, addMemberCount } from "../daos/member.dao"; import { findTeamIdByLeaderId } from "../daos/team.dao"; @@ -38,23 +36,6 @@ export const readApplyGuestingUser = async (params) => { return readApplyGuestingUserResponseDTO(applyGuestingUser); }; -export const updateGuestStatus = async (params) => { - const guestUserId = params.guestUserId; - const guestUser = await getGuestUserById(guestUserId); - if (!guestUser) { - throw new BaseError(status.GUESTUSER_NOT_FOUND); - } - - const guestId = await getGuestIdById(guestUserId); - const guest = await getGuestingById(guestId); - if (guest.status) { - throw new BaseError(status.CLOSED_GUEST); - } - - await setGuestUserStatus(guestUser, guest); - return; -}; - export const readHostingApplicantsTeamList = async (userId, params) => { const gameId = params.gameId; const teamsApplied = await getTeamsAppliedById(gameId); From 7892bd2c550d117758f7dcaeeeb504806236a3b5 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Mon, 12 Feb 2024 17:53:08 +0900 Subject: [PATCH 112/147] =?UTF-8?q?Refactor:=20Guest.status=20dto=EC=97=90?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=20#128?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/guest-status.constant.ts | 2 +- src/daos/guest.dao.ts | 12 ++++++------ src/dtos/guests.dto.ts | 4 ++++ src/dtos/matchings.dto.ts | 4 ++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/constants/guest-status.constant.ts b/src/constants/guest-status.constant.ts index 99be765..3653e5b 100644 --- a/src/constants/guest-status.constant.ts +++ b/src/constants/guest-status.constant.ts @@ -5,6 +5,6 @@ const guestStatus: Record = { export const defaultStatus = 0; -export const getGuestStatus = (key: number): string | undefined => { +export const getGuestUserStatus = (key: number): string | undefined => { return guestStatus[key]; }; diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index e3c3536..8c813ff 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -1,7 +1,6 @@ import db from "../models"; import { CreateGuestingBody, UpdateGuestingBody } from "../schemas/guest.schema"; import { Op, Sequelize } from "sequelize"; -import { getTeamIdByLeaderId } from "./team.dao"; import { Category } from "../types/category.enum"; import { Gender } from "../types/gender.enum"; import { BaseError } from "../config/error"; @@ -14,6 +13,7 @@ export const insertGuesting = async (teamId: number, data: CreateGuestingBody) = gameTime: data.gameTime, description: data.description, recruitCount: data.recruitCount, + status: 0, }); }; @@ -37,7 +37,7 @@ export const findGuesting = async (date: string, category: Category) => { attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], }, ], - attributes: ["gameTime", "recruitCount"], + attributes: ["gameTime", "recruitCount", "status"], }); }; @@ -55,7 +55,7 @@ export const findGuestingByGender = async (date: string, category: Category, gen attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], }, ], - attributes: ["gameTime", "recruitCount"], + attributes: ["gameTime", "recruitCount", "status"], }); }; @@ -73,7 +73,7 @@ export const findGuestingByLevel = async (date: string, category: Category, skil attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], }, ], - attributes: ["gameTime", "recruitCount"], + attributes: ["gameTime", "recruitCount", "status"], }); }; @@ -91,7 +91,7 @@ export const findGuestingByRegion = async (date: string, category: Category, reg attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], }, ], - attributes: ["gameTime", "recruitCount"], + attributes: ["gameTime", "recruitCount", "status"], }); }; @@ -101,7 +101,7 @@ export const getDetailedGuesting = async (guestingId: number) => { where: { id: guestingId, }, - attributes: ["teamId", "gameTime", "description", "recruitCount"], + attributes: ["teamId", "gameTime", "description", "recruitCount", "status"], }); }; diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index ba6ceff..501e561 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -1,6 +1,7 @@ import { getAgeGroup } from "../constants/age-group.constant"; import { getTeamGender } from "../constants/gender.constant"; import { getLevelById } from "../constants/level.constant"; +import { getStatus } from "../constants/status.constant"; import { AgeGroup } from "../types/age-group.enum"; import { Gender } from "../types/gender.enum"; @@ -9,6 +10,7 @@ interface GuestDetail { description: string | null; memberCount: number | null; recruitCount: number | null; + status: number; } interface TeamDetail { @@ -41,6 +43,7 @@ export const readGuestingResponseDTO = (result) => { teamAgeGroup: getAgeGroup(guesting["Team.ageGroup"]), teamSkillLevel: getLevelById(guesting["Team.skillLevel"]), recruitCount: guesting.recruitCount, + status: getStatus(guesting.status), })), }; }; @@ -61,6 +64,7 @@ export const readGuestingDetailResponseDTO = ( skillLevel: TeamDetail.skillLevel, mannerLevel: TeamDetail.mannerLevel, description: TeamDetail.description, + status: getStatus(guestingDetail.status), gusting_info: { gameTime: guestingDetail.gameTime, gender: getTeamGender(TeamDetail.gender), diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index 907f199..3d2b687 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -1,6 +1,6 @@ import { getAgeGroup } from "../constants/age-group.constant"; import { getTeamGender } from "../constants/gender.constant"; -import { getGuestStatus } from "../constants/guest-status.constant"; +import { getGuestUserStatus } from "../constants/guest-status.constant"; import { getLevelById } from "../constants/level.constant"; export const readApplyGuestingUserResponseDTO = (result) => { @@ -8,7 +8,7 @@ export const readApplyGuestingUserResponseDTO = (result) => { nickname: guestingUser["User.nickname"], height: guestingUser["User.height"], position: guestingUser["User.Profiles.position"], - status: getGuestStatus(guestingUser.status), + status: getGuestUserStatus(guestingUser.status), })); }; From 710e08f60a5ddcc3088f732379cddd281e0fd4ae Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Mon, 12 Feb 2024 18:06:34 +0900 Subject: [PATCH 113/147] =?UTF-8?q?Fix:=20Guest.status=20=EA=B0=92=20?= =?UTF-8?q?=ED=8C=90=EB=B3=84=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?#128?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/guest-user.dao.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/daos/guest-user.dao.ts b/src/daos/guest-user.dao.ts index cdc3c8e..ebad23c 100644 --- a/src/daos/guest-user.dao.ts +++ b/src/daos/guest-user.dao.ts @@ -61,17 +61,18 @@ export const checkClosedGuest = async (guestId: number) => { }, attributes: ["recruitCount"], }); + console.log(count, recruitCount.recruitCount, count >= recruitCount.recruitCount); return count >= recruitCount.recruitCount; }; export const setGuestUserStatus = async (guestUser, guest) => { guestUser.status = 1; + await guestUser.save(); const check: boolean = await checkClosedGuest(guest.id); if (check) { guest.status = 1; await guest.save(); } - await guestUser.save(); }; export const checkForDuplicateGuestUser = async (userId, guestId) => { From c53e9bf270bc8577ae05c84ef1a18e38a1f465af Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Wed, 14 Feb 2024 22:42:39 +0900 Subject: [PATCH 114/147] =?UTF-8?q?Refactor:=20=EA=B2=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=A1=B0=ED=9A=8C=20=EB=A0=88?= =?UTF-8?q?=EB=B2=A8=20=ED=95=84=ED=84=B0=EB=A7=81=20API=20=20level=20?= =?UTF-8?q?=EB=B2=94=EC=9C=84=20=EC=88=98=EC=A0=95=20#132?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/guest.dao.ts | 5 +++-- src/dtos/guests.dto.ts | 5 ++--- src/schemas/fields.ts | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index 5649b1a..fdf4df3 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -39,9 +39,10 @@ export const findGuestByGender = (date: string, category: Category, gender: Gend return findGuests(date, guestsBeforeCursor, TeamFilter); }; -export const findGuestByLevel = (date: string, category: Category, level: number, cursorId: number | undefined) => { +export const findGuestByLevel = (date: string, category: Category, level: string, cursorId: number | undefined) => { const guestsBeforeCursor = generateCursorCondition(cursorId); - const TeamFilter = { skillLevel: level, category }; + const minLevel = Math.floor(parseInt(level) / 10) * 10; + const TeamFilter = { skillLevel: { [Op.between]: [minLevel, minLevel + 9] }, category }; return findGuests(date, guestsBeforeCursor, TeamFilter); }; diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index 486c448..f1a8bd2 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -1,6 +1,5 @@ import { getAgeGroup } from "../constants/age-group.constant"; import { getTeamGender } from "../constants/gender.constant"; -import { getLevelById } from "../constants/level.constant"; import { getStatus } from "../constants/status.constant"; import { AgeGroup } from "../types/age-group.enum"; import { Gender } from "../types/gender.enum"; @@ -41,7 +40,7 @@ export const readGuestingResponseDTO = (result) => { teamGender: getTeamGender(guesting["Team.gender"]), memberCount: guesting.memberCount, teamAgeGroup: getAgeGroup(guesting["Team.ageGroup"]), - teamSkillLevel: getLevelById(guesting["Team.skillLevel"]), + teamSkillLevel: guesting["Team.skillLevel"], recruitCount: guesting.recruitCount, status: getStatus(guesting.status), })), @@ -71,7 +70,7 @@ export const readGuestingDetailResponseDTO = ( gender: getTeamGender(TeamDetail.gender), ageGroup: getAgeGroup(TeamDetail.ageGroup), gymName: TeamDetail.gymName, - skillLevel: getLevelById(TeamDetail.skillLevel), + skillLevel: TeamDetail.skillLevel, }, member_info: { leader: { diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 3b6f3a0..092c8b1 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -92,7 +92,7 @@ export const statusField = { status: z.number().int() }; export const recruitCountField = { recruitCount: z.number().int() }; -export const levelField = { level: z.number().int() }; +export const levelField = { level: z.string().max(5) }; export const skillScoreField = { skillScore: z.number().int().min(1).max(5) }; From 6a2356098c3d33fc9102dd2533af0c2bc60d006b Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Wed, 14 Feb 2024 22:43:17 +0900 Subject: [PATCH 115/147] =?UTF-8?q?Refactor:=20=EC=97=B0=EC=8A=B5=EA=B2=8C?= =?UTF-8?q?=EC=9E=84=20=EC=A1=B0=ED=9A=8C=20=EB=A0=88=EB=B2=A8=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=EB=A7=81=20API=20level=20=EB=B2=94=EC=9C=84=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20#132?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/game.dao.ts | 4 +++- src/dtos/games.dto.ts | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/daos/game.dao.ts b/src/daos/game.dao.ts index 8819415..f2d1b20 100644 --- a/src/daos/game.dao.ts +++ b/src/daos/game.dao.ts @@ -55,7 +55,9 @@ export const findGamesByLevel = async (date, category, skillLevel) => { attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], where: { category, - skillLevel, + skillLevel: { + [Op.between]: [Math.floor(skillLevel / 10) * 10, Math.floor(skillLevel / 10) * 10 + 9], + }, }, }, ], diff --git a/src/dtos/games.dto.ts b/src/dtos/games.dto.ts index 6af7458..43d52b3 100644 --- a/src/dtos/games.dto.ts +++ b/src/dtos/games.dto.ts @@ -1,4 +1,3 @@ -import { getLevelById } from "../constants/level.constant"; import { getGender } from "../constants/gender.constant"; import { getAgeGroup } from "../constants/age-group.constant"; import { getStatus } from "../constants/status.constant"; @@ -11,7 +10,7 @@ export const readGameResponseDTO = (games) => { teamGender: getGender(game["HostTeam.gender"]), memberCount: game.memberCount, teamAgeGroup: getAgeGroup(game["HostTeam.ageGroup"]), - teamSkillLevel: getLevelById(game["HostTeam.skillLevel"]), + teamSkillLevel: game["HostTeam.skillLevel"], status: getStatus(game.status), })); }; From 61631003811208a32b29031659412d9ebf76421c Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Wed, 14 Feb 2024 22:45:19 +0900 Subject: [PATCH 116/147] =?UTF-8?q?Refactor:=20getLevelById=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=20=EC=82=AC=EC=9A=A9=ED=95=98=EB=8A=94=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=20=EC=82=AD=EC=A0=9C=20#132?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/matchings.dto.ts | 5 ++--- src/dtos/users.dto.ts | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index 3d2b687..446b630 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -1,7 +1,6 @@ import { getAgeGroup } from "../constants/age-group.constant"; import { getTeamGender } from "../constants/gender.constant"; import { getGuestUserStatus } from "../constants/guest-status.constant"; -import { getLevelById } from "../constants/level.constant"; export const readApplyGuestingUserResponseDTO = (result) => { return result.map((guestingUser) => ({ @@ -34,7 +33,7 @@ export const readMatchingResponseDTO = (guestings, games) => { gender: getTeamGender(match["Team.gender"]), memberCount: match.memberCount, ageGroup: getAgeGroup(match["Team.ageGroup"]), - skillLevel: getLevelById(match["Team.skillLevel"]), + skillLevel: match["Team.skillLevel"], }; } else { return { @@ -46,7 +45,7 @@ export const readMatchingResponseDTO = (guestings, games) => { gender: getTeamGender(match["HostTeam.gender"]), memberCount: match.memberCount, ageGroup: getAgeGroup(match["HostTeam.ageGroup"]), - skillLevel: getLevelById(match["HostTeam.skillLevel"]), + skillLevel: match["HostTeam.skillLevel"], }; } }); diff --git a/src/dtos/users.dto.ts b/src/dtos/users.dto.ts index f5a95a2..a8e987b 100644 --- a/src/dtos/users.dto.ts +++ b/src/dtos/users.dto.ts @@ -1,6 +1,5 @@ import { getAgeGroup } from "../constants/age-group.constant"; import { getGender } from "../constants/gender.constant"; -import { getLevelById } from "../constants/level.constant"; import { AgeGroup } from "../types/age-group.enum"; import { Gender } from "../types/gender.enum"; @@ -20,8 +19,8 @@ interface ReadUserProfile { export const readUserProfileResponseDTO = (profile: ReadUserProfile) => { return { nickname: profile.nickname, - skillLevel: !profile["Profiles.skillLevel"] ? null : getLevelById(profile["Profiles.skillLevel"]), - mannerLevel: !profile["Profiles.mannerLevel"] ? null : getLevelById(profile["Profiles.mannerLevel"]), + skillLevel: !profile["Profiles.skillLevel"] ? null : profile["Profiles.skillLevel"], + mannerLevel: !profile["Profiles.mannerLevel"] ? null : profile["Profiles.mannerLevel"], gender: !profile.gender ? null : getGender(profile.gender), ageGroup: !profile.ageGroup ? null : getAgeGroup(profile.ageGroup), region: profile["Profiles.region"], From b542107e76f718060095ccebc57533ad1127c3af Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Wed, 14 Feb 2024 22:46:32 +0900 Subject: [PATCH 117/147] =?UTF-8?q?Refactor:=20defaultLevel=20=EC=82=AC?= =?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8A=94=20=EB=B6=80=EB=B6=84=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20#132?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/profile.dao.ts | 3 ++- src/daos/team.dao.ts | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/daos/profile.dao.ts b/src/daos/profile.dao.ts index 8789b15..d08eae0 100644 --- a/src/daos/profile.dao.ts +++ b/src/daos/profile.dao.ts @@ -1,8 +1,9 @@ import db from "../models"; -import { defaultLevel } from "../constants/level.constant"; import { CategoryProfile } from "../schemas/user-profile.schema"; import { Category } from "../types/category.enum"; +const defaultLevel = 0; + export const getUserProfile = async (userId: number, category: Category) => { return await db.Profile.findOne({ raw: true, diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index b9caa0d..bf4cf01 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -1,9 +1,10 @@ import db from "../models"; -import { defaultLevel } from "../constants/level.constant"; import { CreateTeamBody, UpdateTeamBodyWithoutMemberIdsToDelete } from "../schemas/team.schema"; import { Category } from "../types/category.enum"; import { Op } from "sequelize"; +const defaultLevel = 0; + export const findTeamPreviewByCategory = async (userId: number, category: Category) => { const teamsAsLeader = await db.Team.findAll({ raw: true, From 568c70a973d41df2181a8de62f42e1ecda40abf1 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Wed, 14 Feb 2024 22:47:00 +0900 Subject: [PATCH 118/147] =?UTF-8?q?Refactor:=20level.constant.ts=20?= =?UTF-8?q?=EC=82=AD=EC=A0=9C=20#132?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/level.constant.ts | 16 ---------------- 1 file changed, 16 deletions(-) delete mode 100644 src/constants/level.constant.ts diff --git a/src/constants/level.constant.ts b/src/constants/level.constant.ts deleted file mode 100644 index 49849e9..0000000 --- a/src/constants/level.constant.ts +++ /dev/null @@ -1,16 +0,0 @@ -type Level = { - name: string; -}; - -const Levels: { [key: number]: Level } = { - 1: { name: "Level 1" }, - 2: { name: "Level 2" }, - 3: { name: "Level 3" }, - 4: { name: "Level 4" }, -}; - -export const defaultLevel = 1; - -export const getLevelById = (id: number): string | undefined => { - return Levels[id]?.name; -}; From ff3c446d8c4a55290ac67cc9679f23c712ba5558 Mon Sep 17 00:00:00 2001 From: Doeun Date: Wed, 14 Feb 2024 23:14:33 +0900 Subject: [PATCH 119/147] =?UTF-8?q?Feat:=20=EB=8C=80=EA=B4=80=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=EC=97=90=20=EB=8C=80=EA=B4=80=EC=9D=BC=EC=9E=90/?= =?UTF-8?q?=EB=8C=80=EA=B4=80=EC=9E=A5=EC=86=8C/=EB=8C=80=EA=B4=80?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80=20#13?= =?UTF-8?q?1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/posts.controller.ts | 1 - src/daos/post.dao.ts | 37 +++++++++++++++++++++++++++-- src/models/post.model.ts | 12 ++++++++++ src/schemas/fields.ts | 15 ++++++++++++ src/schemas/post.schema.ts | 4 +++- src/services/posts.service.ts | 4 ++-- 6 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/controllers/posts.controller.ts b/src/controllers/posts.controller.ts index c9c0eee..bc13a99 100644 --- a/src/controllers/posts.controller.ts +++ b/src/controllers/posts.controller.ts @@ -48,7 +48,6 @@ export const fetchComments = async (req: Request, res: Response, next) => { export const addRentPost = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await createRentPost(req.user.id, req.body))); - // res.send(response(status.SUCCESS, await createRentPost(2, req.body))); }; export const fetchRentPosts = async (req, res: Response, next) => { diff --git a/src/daos/post.dao.ts b/src/daos/post.dao.ts index 7864619..1c8fcfb 100644 --- a/src/daos/post.dao.ts +++ b/src/daos/post.dao.ts @@ -1,13 +1,24 @@ import db from "../models"; +import { Op, Sequelize } from "sequelize"; import { CreatePostBody } from "../schemas/post.schema"; import { PostType } from "../types/post-type.enum"; import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; const defaultLimit = 20; -export const findPostByType = async (userId: number | undefined, cursorId: number | undefined, type: PostType) => { +export const findPostByType = async ( + userId: number | undefined, + date: string | undefined, + cursorId: number | undefined, + type: PostType, +) => { const postsBeforeCursorByType = { type, ...generateCursorCondition(cursorId) }; - return findPostByFilter(userId, postsBeforeCursorByType); + + if (type === PostType.RentalInfo) { + return findRentPostByDate(userId, date, postsBeforeCursorByType); + } else { + return findPostByFilter(userId, postsBeforeCursorByType); + } }; export const findPostByAuthorId = async (userId: number, cursorId?: number) => { @@ -54,6 +65,25 @@ const findPostByFilter = async (userId: number | undefined, postFilter: object) return findPost(postFilter, includeAllPosts); }; +const findRentPostByDate = async (userId: number | undefined, date, postFilter: object) => { + console.log(postFilter); + + const posts = await db.Post.findAll({ + raw: true, + where: { + [Op.and]: [ + postFilter, + Sequelize.literal(`DATE_FORMAT(rent_date, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + ], + }, + + order: [["createdAt", "DESC"]], + limit: defaultLimit, + attributes: ["id", "title", "content", "rentStatus"], + }); + return { posts, hasNext: calculateHasNext(posts, defaultLimit) }; +}; + const findPost = async (postFilter: object, bookmarkInclude: Array) => { const posts = await db.Post.findAll({ raw: true, @@ -72,6 +102,9 @@ export const insertPost = async (userId: number, data: CreatePostBody, type: Pos content: data.content, link: data.link, authorId: userId, + rentDate: data.rentDate, + rentPlace: data.rentPlace, + rentStatus: 0, type, }); }; diff --git a/src/models/post.model.ts b/src/models/post.model.ts index 97ccff0..fee9020 100644 --- a/src/models/post.model.ts +++ b/src/models/post.model.ts @@ -24,6 +24,18 @@ class Post extends Model, InferCreationAttributes> { type: new DataTypes.INTEGER(), allowNull: false, }, + rentDate: { + type: new DataTypes.DATE(), + allowNull: true, + }, + rentPlace: { + type: new DataTypes.STRING(100), + allowNull: true, + }, + rentStatus: { + type: new DataTypes.INTEGER(), + allowNull: true, + }, }, { sequelize, diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 3b6f3a0..1b50c6e 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -120,3 +120,18 @@ export const dateQuery = object({ ...dateField, }), }); + +export const rentDateField = { + rentDate: z.optional( + z.preprocess((arg) => { + if (typeof arg == "string") { + return new Date(arg); + } + return arg; + }, z.date()), + ), +}; + +export const rentPlaceField = { rentPlace: z.optional(z.string().max(100)) }; + +export const rentStatusField = { status: z.optional(z.number().int()) }; diff --git a/src/schemas/post.schema.ts b/src/schemas/post.schema.ts index 116f700..548b2aa 100644 --- a/src/schemas/post.schema.ts +++ b/src/schemas/post.schema.ts @@ -1,10 +1,12 @@ import { TypeOf, object } from "zod"; -import { contentFieldInPost, linkField, titleField } from "./fields"; +import { contentFieldInPost, linkField, titleField, rentDateField, rentPlaceField } from "./fields"; const body = object({ ...titleField, ...contentFieldInPost, ...linkField, + ...rentDateField, + ...rentPlaceField, }); export const createPostSchema = object({ diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index 0e59973..c7833a4 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -10,7 +10,7 @@ import { findPostByType, findPostByAuthorId, findBookmarkedPost, getPost, insert import { readCommentsResonseDTO, readPostResponseDTO, readPostsResponseDTO } from "../dtos/posts.dto"; export const readCommunityPosts = async (userId: number | undefined, query) => { - const result = await findPostByType(userId, query.cursorId, PostType.Community); + const result = await findPostByType(userId, query.date, query.cursorId, PostType.Community); return readPostsResponseDTO(result); }; @@ -80,6 +80,6 @@ export const createRentPost = async (userId: number, body: CreatePostBody) => { }; export const readRentPosts = async (userId: number | undefined, query) => { - const result = await findPostByType(userId, query.cursorId, PostType.RentalInfo); + const result = await findPostByType(userId, query.date, query.cursorId, PostType.RentalInfo); return readPostsResponseDTO(result); }; From e2d6db6cb6bbe7e1ec141e01335804e9935595d4 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Thu, 15 Feb 2024 01:10:21 +0900 Subject: [PATCH 120/147] =?UTF-8?q?[FEAT]=20Guest=20=EB=AA=A8=EB=8D=B8=20g?= =?UTF-8?q?ameDuration=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80=20#133?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/guest.model.ts | 4 ++++ src/schemas/fields.ts | 2 ++ src/schemas/guest.schema.ts | 2 ++ 3 files changed, 8 insertions(+) diff --git a/src/models/guest.model.ts b/src/models/guest.model.ts index b6cbe98..bbae61e 100644 --- a/src/models/guest.model.ts +++ b/src/models/guest.model.ts @@ -20,6 +20,10 @@ class Guest extends Model, InferCreationAttributes type: new DataTypes.INTEGER(), allowNull: false, }, + gameDuration: { + type: new DataTypes.TIME(), + allowNull: false, + }, status: { type: new DataTypes.INTEGER(), allowNull: false, diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 3b6f3a0..38abdf7 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -120,3 +120,5 @@ export const dateQuery = object({ ...dateField, }), }); + +export const gameDurationField = { gameDuration: z.string() }; diff --git a/src/schemas/guest.schema.ts b/src/schemas/guest.schema.ts index 5ba739a..0bf028b 100644 --- a/src/schemas/guest.schema.ts +++ b/src/schemas/guest.schema.ts @@ -9,12 +9,14 @@ import { levelField, regionFieldInTeam, genderFieldInTeam, + gameDurationField, } from "./fields"; const body = { ...gameTimeField, ...descriptionField, ...recruitCountField, + ...gameDurationField, }; const createGuestBody = object({ From 1893afe8b41519cbda5b7e81b6ca48dc192a70d0 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Thu, 15 Feb 2024 01:13:10 +0900 Subject: [PATCH 121/147] =?UTF-8?q?[FEAT]=20guest=20=EC=A1=B0=ED=9A=8C,=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1=20API=EC=97=90=20gameDuration=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#133?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/guest.dao.ts | 9 +++++---- src/dtos/guests.dto.ts | 3 +++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index 5649b1a..d6415f3 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -16,6 +16,7 @@ export const insertGuesting = async (teamId: number, data: CreateGuestingBody) = gameTime: data.gameTime, description: data.description, recruitCount: data.recruitCount, + gameDuration: data.gameDuration, status: 0, }); }; @@ -67,7 +68,7 @@ export const findGuests = async (date: string, guestFilter: object, TeamFilter: attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], }, ], - attributes: ["gameTime", "recruitCount", "status"], + attributes: ["gameTime", "recruitCount", "gameDuration", "status"], }); return { guests, hasNext: calculateHasNext(guests, defaultLimit) }; }; @@ -78,7 +79,7 @@ export const getDetailedGuesting = async (guestingId: number) => { where: { id: guestingId, }, - attributes: ["teamId", "gameTime", "description", "recruitCount", "status"], + attributes: ["teamId", "gameTime", "description", "recruitCount", "gameDuration", "status"], }); }; @@ -161,7 +162,7 @@ export const findGuestingByTeamsAndGameTime = async (teamIds: number[], gameTime attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], }, ], - attributes: ["id", "gameTime"], + attributes: ["id", "gameTime", "gameDuration"], }); for (const guestResult of guestResults) { guestResult.type = MatchType.guest; @@ -189,7 +190,7 @@ export const findGuestingByUserAndGameTime = async (userId: number, date: string attributes: [], }, ], - attributes: ["id", "gameTime"], + attributes: ["id", "gameTime", "gameDuration"], }); for (const guestResult of guestResults) { guestResult.type = MatchType.guest; diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index 486c448..a177f90 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -10,6 +10,7 @@ interface GuestDetail { description: string | null; memberCount: number | null; recruitCount: number | null; + gameDuration: string; status: number; } @@ -36,6 +37,7 @@ export const readGuestingResponseDTO = (result) => { return { guests: result.guests.map((guesting) => ({ gameTime: guesting.gameTime, + gameDuration: guesting.gameDuration, teamName: guesting["Team.name"], teamRegion: guesting["Team.region"], teamGender: getTeamGender(guesting["Team.gender"]), @@ -68,6 +70,7 @@ export const readGuestingDetailResponseDTO = ( status: getStatus(guestingDetail.status), gusting_info: { gameTime: guestingDetail.gameTime, + gameDuration: guestingDetail.gameDuration, gender: getTeamGender(TeamDetail.gender), ageGroup: getAgeGroup(TeamDetail.ageGroup), gymName: TeamDetail.gymName, From 8e9a4810ca28eb3f72c74da7cdec926a73499cc2 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Thu, 15 Feb 2024 01:13:41 +0900 Subject: [PATCH 122/147] =?UTF-8?q?[FEAT]=20=EB=A7=A4=EC=B9=AD=EB=82=B4?= =?UTF-8?q?=EC=97=AD=20=EC=A1=B0=ED=9A=8C=20API=EC=97=90=20gameDuration=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20#133?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/matchings.dto.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index 3d2b687..77cd733 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -29,6 +29,7 @@ export const readMatchingResponseDTO = (guestings, games) => { type: match.type, matchId: match.id, gameTime: match.gameTime, + gameDuration: match.gameDuration, name: match["Team.name"], region: match["Team.region"], gender: getTeamGender(match["Team.gender"]), @@ -41,6 +42,7 @@ export const readMatchingResponseDTO = (guestings, games) => { type: match.type, matchId: match.id, gameTime: match.gameTime, + gameDuration: match.gameDuration, name: match["HostTeam.name"], region: match["HostTeam.region"], gender: getTeamGender(match["HostTeam.gender"]), From 119306dd8d7cfd9d42f3c17b65f78635a58bfac4 Mon Sep 17 00:00:00 2001 From: mzeong Date: Thu, 15 Feb 2024 12:39:11 +0900 Subject: [PATCH 123/147] =?UTF-8?q?Feat:=20user=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EC=97=90=20avatarUrl=20=ED=95=84=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#116?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/user.model.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 88f7071..552a56a 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -32,6 +32,10 @@ class User extends Model, InferCreationAttributes> { type: new DataTypes.INTEGER(), allowNull: true, }, + avatarUrl: { + type: new DataTypes.STRING(1000), + allowNull: true, + }, }, { sequelize, From 1c74215cc16a5b41be5c251e53c5e6ba2927f6a9 Mon Sep 17 00:00:00 2001 From: mzeong Date: Thu, 15 Feb 2024 12:42:52 +0900 Subject: [PATCH 124/147] =?UTF-8?q?Feat:=20avatarUrl=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EC=88=98=EC=A0=95=20#116?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/models/user.model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/models/user.model.ts b/src/models/user.model.ts index 552a56a..b417bdc 100644 --- a/src/models/user.model.ts +++ b/src/models/user.model.ts @@ -33,7 +33,7 @@ class User extends Model, InferCreationAttributes> { allowNull: true, }, avatarUrl: { - type: new DataTypes.STRING(1000), + type: new DataTypes.STRING(200), allowNull: true, }, }, From 43e09489234f649d8c39f616fe50e3978d2c560a Mon Sep 17 00:00:00 2001 From: mzeong Date: Thu, 15 Feb 2024 12:49:31 +0900 Subject: [PATCH 125/147] =?UTF-8?q?Feat:=20schema=EC=97=90=20avatarUrlFiel?= =?UTF-8?q?d=20=EC=B6=94=EA=B0=80=20#116?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/schemas/fields.ts | 2 ++ src/schemas/user-profile.schema.ts | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index 5b1f273..d52a7b7 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -122,3 +122,5 @@ export const dateQuery = object({ }); export const gameDurationField = { gameDuration: z.string() }; + +export const avatarUrlField = { avatarUrl: z.optional(z.string().max(200)) }; diff --git a/src/schemas/user-profile.schema.ts b/src/schemas/user-profile.schema.ts index 5291989..9677792 100644 --- a/src/schemas/user-profile.schema.ts +++ b/src/schemas/user-profile.schema.ts @@ -8,9 +8,11 @@ import { positionField, regionFieldInProfile, heightField, + avatarUrlField, } from "./fields"; const commonFields = { + ...avatarUrlField, ...nicknameField, ...genderFieldInUser, ...ageGroupFieldInUser, From f1ca9ec86ed71a1375e701a56074d29208d3e521 Mon Sep 17 00:00:00 2001 From: mzeong Date: Thu, 15 Feb 2024 12:56:05 +0900 Subject: [PATCH 126/147] =?UTF-8?q?Feat:=20=EC=A2=85=EB=AA=A9=EB=B3=84=20?= =?UTF-8?q?=EC=84=A0=EC=88=98=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?API=20=EC=9D=91=EB=8B=B5=EC=97=90=20avatarUrl=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20#116?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/user.dao.ts | 2 +- src/dtos/users.dto.ts | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/daos/user.dao.ts b/src/daos/user.dao.ts index 2ae74a1..edd649b 100644 --- a/src/daos/user.dao.ts +++ b/src/daos/user.dao.ts @@ -103,7 +103,7 @@ export const getUserProfileByCategory = async (userId: number, category: Categor attributes: ["skillLevel", "mannerLevel", "region", "position", "description"], }, ], - attributes: ["nickname", "gender", "ageGroup"], + attributes: ["nickname", "gender", "ageGroup", "avatarUrl"], }); }; diff --git a/src/dtos/users.dto.ts b/src/dtos/users.dto.ts index a8e987b..397b403 100644 --- a/src/dtos/users.dto.ts +++ b/src/dtos/users.dto.ts @@ -4,6 +4,7 @@ import { AgeGroup } from "../types/age-group.enum"; import { Gender } from "../types/gender.enum"; interface ReadUserProfile { + avatarUrl: string | null; nickname: string; gender: Exclude | null; ageGroup: AgeGroup | null; @@ -18,6 +19,7 @@ interface ReadUserProfile { export const readUserProfileResponseDTO = (profile: ReadUserProfile) => { return { + avatarUrl: profile.avatarUrl, nickname: profile.nickname, skillLevel: !profile["Profiles.skillLevel"] ? null : profile["Profiles.skillLevel"], mannerLevel: !profile["Profiles.mannerLevel"] ? null : profile["Profiles.mannerLevel"], From f5df7920ce467636140fa041d24d1f8bb45490c4 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Thu, 15 Feb 2024 18:18:13 +0900 Subject: [PATCH 127/147] =?UTF-8?q?Fix:=20post.dao=20=EB=B6=88=ED=95=84?= =?UTF-8?q?=EC=9A=94=ED=95=9C=20=EB=B6=80=EB=B6=84=20=EC=82=AD=EC=A0=9C=20?= =?UTF-8?q?#131?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/post.dao.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/daos/post.dao.ts b/src/daos/post.dao.ts index 1c8fcfb..2b2da0a 100644 --- a/src/daos/post.dao.ts +++ b/src/daos/post.dao.ts @@ -66,8 +66,6 @@ const findPostByFilter = async (userId: number | undefined, postFilter: object) }; const findRentPostByDate = async (userId: number | undefined, date, postFilter: object) => { - console.log(postFilter); - const posts = await db.Post.findAll({ raw: true, where: { From 6d71896a462f29066c6659f59cc287b374cb2b2b Mon Sep 17 00:00:00 2001 From: Doeun Date: Thu, 15 Feb 2024 18:23:21 +0900 Subject: [PATCH 128/147] =?UTF-8?q?Feat:=20=EB=8C=80=EA=B4=80=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=EA=B8=80=20=EB=AA=A9=EB=A1=9D=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EC=8B=9C=20=EA=B8=80=20=EB=82=B4=EC=9A=A9/=EB=AA=A8?= =?UTF-8?q?=EC=A7=91=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80=20#136?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/posts.dto.ts | 12 ++++++++++++ src/services/posts.service.ts | 9 +++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/dtos/posts.dto.ts b/src/dtos/posts.dto.ts index 409f78c..d9b40f7 100644 --- a/src/dtos/posts.dto.ts +++ b/src/dtos/posts.dto.ts @@ -10,6 +10,18 @@ export const readPostsResponseDTO = (result) => { }; }; +export const readRentPostsResponseDTO = (result) => { + return { + posts: result.posts.map((post) => ({ + id: post.id, + title: post.title, + content: post.content, + status: post.rentStatus, + })), + hasNext: result.hasNext, + }; +}; + export const readPostResponseDTO = (post, imageUrls, commentCount, comments, isBookmarked) => { return { post: { diff --git a/src/services/posts.service.ts b/src/services/posts.service.ts index c7833a4..7c9df29 100644 --- a/src/services/posts.service.ts +++ b/src/services/posts.service.ts @@ -7,7 +7,12 @@ import { getBookmark, insertOrDeleteBookmark } from "../daos/bookmark.dao"; import { findComment, getCommentCount, insertComment } from "../daos/comment.dao"; import { findImage } from "../daos/image.dao"; import { findPostByType, findPostByAuthorId, findBookmarkedPost, getPost, insertPost } from "../daos/post.dao"; -import { readCommentsResonseDTO, readPostResponseDTO, readPostsResponseDTO } from "../dtos/posts.dto"; +import { + readCommentsResonseDTO, + readPostResponseDTO, + readPostsResponseDTO, + readRentPostsResponseDTO, +} from "../dtos/posts.dto"; export const readCommunityPosts = async (userId: number | undefined, query) => { const result = await findPostByType(userId, query.date, query.cursorId, PostType.Community); @@ -81,5 +86,5 @@ export const createRentPost = async (userId: number, body: CreatePostBody) => { export const readRentPosts = async (userId: number | undefined, query) => { const result = await findPostByType(userId, query.date, query.cursorId, PostType.RentalInfo); - return readPostsResponseDTO(result); + return readRentPostsResponseDTO(result); }; From bed60cf799551bb95f5b6889774988b3e0fd94dc Mon Sep 17 00:00:00 2001 From: Doeun Date: Sat, 17 Feb 2024 16:45:20 +0900 Subject: [PATCH 129/147] =?UTF-8?q?Bugfix:=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20=EC=8B=A0=EC=B2=AD=20=EC=97=90=EB=9F=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20#144?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/response.status.ts | 8 ++++++++ src/daos/game-apply.dao.ts | 11 +++++++++++ src/daos/team.dao.ts | 10 ++++++++++ src/schemas/game.schema.ts | 14 -------------- src/services/games.service.ts | 21 +++++++++++++++++++-- 5 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 src/daos/game-apply.dao.ts diff --git a/src/config/response.status.ts b/src/config/response.status.ts index 937ef72..9631a6c 100644 --- a/src/config/response.status.ts +++ b/src/config/response.status.ts @@ -205,4 +205,12 @@ export const status: { [key: string]: Status } = { code: "REVIEW004", message: "이미 리뷰를 작성했습니다.", }, + + // game-apply err + GAME_APPLICATION_ALREADY_EXIST: { + status: StatusCodes.CONFLICT, + isSuccess: false, + code: "GAMEAPPLY001", + message: "이미 신청한 연습경기 모집글입니다.", + }, }; diff --git a/src/daos/game-apply.dao.ts b/src/daos/game-apply.dao.ts new file mode 100644 index 0000000..e63e802 --- /dev/null +++ b/src/daos/game-apply.dao.ts @@ -0,0 +1,11 @@ +import db from "../models"; + +export const checkApplicationExisting = async (gameId: number, teamId: number) => { + return await db.GameApply.findOne({ + raw: true, + where: { + gameId, + teamId, + }, + }); +}; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index bf4cf01..4ac1d77 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -55,6 +55,16 @@ export const insertTeam = async (data: CreateTeamBody, userId: number, inviteCod }); }; +export const getTeam = async (teamId: number) => { + return await db.Team.findOne({ + raw: true, + where: { + id: teamId, + }, + attributes: ["id"], + }); +}; + export const getTeamDetail = async (teamId: number) => { return await db.Team.findAll({ raw: true, diff --git a/src/schemas/game.schema.ts b/src/schemas/game.schema.ts index fddbdc3..c3e00b5 100644 --- a/src/schemas/game.schema.ts +++ b/src/schemas/game.schema.ts @@ -3,19 +3,13 @@ import { hostTeamIdField, gameTimeField, descriptionFieldInGame } from "./fields const fields = { hostTeamId: z.number().int().optional(), - // 본인이 리더인 팀이 하나인 경우는 바로 들어가겠지만, 여러 개이면 선택하여 들어갈 듯..? - // applyTeamId: z.number().int().min(1), - // opposingTeamId: z.number().int().min(1), gameTime: z.preprocess((arg) => { if (typeof arg == "string") { return new Date(arg); } return arg; }, z.date()), - - // category: z.string(), description: z.string(), - // status: z.number().int().min(0), }; const body = object({ @@ -24,20 +18,12 @@ const body = object({ ...descriptionFieldInGame, }); -export const createGame = object({ - ...fields, -}); - export const createGameSchema = object({ body: body, }); export type CreateGameBody = TypeOf; -export const updateGame = object({ - ...fields, -}); - export const updateGameSchema = object({ body: body, }); diff --git a/src/services/games.service.ts b/src/services/games.service.ts index 0e890ef..6c875d7 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -11,6 +11,7 @@ import { insertGameApplication, } from "../daos/game.dao"; import { + getTeam, getTeamDetailForGuesting, getTeamIdByLeaderId, getTeamCategoryByLeaderId, @@ -19,6 +20,7 @@ import { import { getMemberCountByTeamId, findMemberInfoByCategory } from "../daos/member.dao"; import { getUserInfoByCategory, userInfoAttributes } from "../daos/user.dao"; import { getGameByUserId } from "../daos/game.dao"; +import { checkApplicationExisting } from "../daos/game-apply.dao"; import { readGameResponseDTO, readGameDetailResponseDTO } from "../dtos/games.dto"; import { CreateGameBody, UpdateGameBody } from "../schemas/game.schema"; import { ApplyGameBody } from "../schemas/game-apply.schema"; @@ -93,12 +95,27 @@ export const updateGame = async (userId, params, body: UpdateGameBody) => { export const addGameApplication = async (userId: number, params, body: ApplyGameBody) => { const gameId = params.gameId; - const teamId = await getTeamIdByLeaderId(userId); - const team = await getTeamByLeaderId(teamId, userId); + const teamId = body.teamId; + + // team does not exist + const team = await getTeam(teamId); + console.log(team); if (!team) { + throw new BaseError(status.TEAM_NOT_FOUND); + } + + // no team of which the user is a leader + const myTeam = await getTeamByLeaderId(teamId, userId); + if (!myTeam) { throw new BaseError(status.TEAM_LEADER_NOT_FOUND); } + // application already exists + const applicationExisting = await checkApplicationExisting(gameId, teamId); + if (applicationExisting) { + throw new BaseError(status.GAME_APPLICATION_ALREADY_EXIST); + } + await insertGameApplication(gameId, body); return; }; From f27a37866a3b9b1bb6c59338daa9b297b360d396 Mon Sep 17 00:00:00 2001 From: Doeun Date: Sat, 17 Feb 2024 17:41:55 +0900 Subject: [PATCH 130/147] =?UTF-8?q?Bugfix:=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20=EC=8B=A0=EC=B2=AD=20=EA=B0=80=EB=8A=A5=20=ED=8C=80?= =?UTF-8?q?=20=EC=A1=B0=ED=9A=8C=20=EC=97=94=EB=93=9C=ED=8F=AC=EC=9D=B8?= =?UTF-8?q?=ED=8A=B8=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20API=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=EC=88=98=EC=A0=95=20#146?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/games.controller.ts | 4 ---- src/controllers/teams.controller.ts | 12 +++++++++++- src/daos/team.dao.ts | 2 +- src/routes/games.route.ts | 4 ---- src/routes/teams.route.ts | 11 ++++++++++- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/src/controllers/games.controller.ts b/src/controllers/games.controller.ts index 65c43c5..18143a9 100644 --- a/src/controllers/games.controller.ts +++ b/src/controllers/games.controller.ts @@ -28,10 +28,6 @@ export const fetchGamesByRegion = async (req, res, next) => { res.send(response(status.SUCCESS, await readGamesByRegion(req.query))); }; -export const fetchTeamsAvailById = async (req, res, next) => { - res.send(response(status.SUCCESS, await readTeamAvailPreviewById(req.user.id, req.query))); -}; - export const fetchGameDetail = async (req, res, next) => { res.send(response(status.SUCCESS, await readGameDetail(req.params))); }; diff --git a/src/controllers/teams.controller.ts b/src/controllers/teams.controller.ts index f3bb0e4..e650a11 100644 --- a/src/controllers/teams.controller.ts +++ b/src/controllers/teams.controller.ts @@ -1,7 +1,13 @@ import { Response } from "express"; import { response } from "../config/response"; import { status } from "../config/response.status"; -import { readTeamPreviews, readTeamDetail, createTeam, updateTeam } from "../services/teams.service"; +import { + readTeamAvailPreviewById, + readTeamPreviews, + readTeamDetail, + createTeam, + updateTeam, +} from "../services/teams.service"; export const fetchTeamPreviews = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readTeamPreviews(req.user.id, req.query))); @@ -18,3 +24,7 @@ export const modifyTeam = async (req, res: Response, next) => { export const fetchTeamDetail = async (req, res: Response, next) => { res.send(response(status.SUCCESS, await readTeamDetail(req.user.id, req.params))); }; + +export const fetchTeamsAvailById = async (req, res, next) => { + res.send(response(status.SUCCESS, await readTeamAvailPreviewById(req.user.id, req.query))); +}; diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index bf4cf01..31c2415 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -180,7 +180,7 @@ export const findTeamPreviewByCategoryForLeader = async (userId: number, categor category, leaderId: userId, }, - attributes: ["name"], + attributes: ["id", "name"], }); }; diff --git a/src/routes/games.route.ts b/src/routes/games.route.ts index f979778..99c5dd8 100644 --- a/src/routes/games.route.ts +++ b/src/routes/games.route.ts @@ -7,7 +7,6 @@ import { fetchGamesByGender, fetchGamesByLevel, fetchGamesByRegion, - fetchTeamsAvailById, fetchGameDetail, addGame, modifyGame, @@ -32,9 +31,6 @@ gamesRouter.get("/by-gender", validate(readGameFilterGenderSchema), asyncHandler gamesRouter.get("/by-level", validate(readGameFilterLevelSchema), asyncHandler(fetchGamesByLevel)); gamesRouter.get("/by-region", validate(readGameFilterRegionSchema), asyncHandler(fetchGamesByRegion)); -// 연습경기 신청 가능 팀 목록 조회 -gamesRouter.get("/apply-avail", verifyUser, asyncHandler(fetchTeamsAvailById)); - // 연습경기 신청 gamesRouter.post("/:gameId/application", verifyUser, asyncHandler(applyGame)); diff --git a/src/routes/teams.route.ts b/src/routes/teams.route.ts index f8d2edf..447eeec 100644 --- a/src/routes/teams.route.ts +++ b/src/routes/teams.route.ts @@ -3,7 +3,13 @@ import asyncHandler from "express-async-handler"; import { verifyUser } from "../middlewares/auth.middleware"; import { validate } from "../middlewares/validate.middleware"; import { createTeamSchema, readTeamPreviewsSchema, updateTeamSchema } from "../schemas/team.schema"; -import { fetchTeamPreviews, fetchTeamDetail, addTeam, modifyTeam } from "../controllers/teams.controller"; +import { + fetchTeamsAvailById, + fetchTeamPreviews, + fetchTeamDetail, + addTeam, + modifyTeam, +} from "../controllers/teams.controller"; export const teamsRouter = express.Router(); @@ -16,3 +22,6 @@ teamsRouter.get("/", validate(readTeamPreviewsSchema), asyncHandler(fetchTeamPre teamsRouter.put("/:teamId", validate(updateTeamSchema), asyncHandler(modifyTeam)); teamsRouter.get("/:teamId", asyncHandler(fetchTeamDetail)); + +// 연습경기 신청 가능 팀 목록 조회 +teamsRouter.get("/apply-avail", asyncHandler(fetchTeamsAvailById)); From 9174172b9e350f0bf14de37a2c0279013894be77 Mon Sep 17 00:00:00 2001 From: mzeong Date: Sat, 17 Feb 2024 18:28:40 +0900 Subject: [PATCH 131/147] =?UTF-8?q?Refactor:=20=EC=84=9C=EB=B9=84=EC=8A=A4?= =?UTF-8?q?=20=ED=95=A8=EC=88=98=20=EC=88=98=EC=A0=95=20#141?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/teams.service.ts | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index ebe2568..e0c62b5 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -11,7 +11,7 @@ import { setTeam, findTeamPreviewByCategoryForLeader, } from "../daos/team.dao"; -import { deleteMembers, findMemberToDelete } from "../daos/member.dao"; +import { deleteMembers, findMemberInfoByCategory, findMemberToDelete } from "../daos/member.dao"; import { getUserInfoByCategory } from "../daos/user.dao"; import { updateOpposingTeam } from "../daos/game.dao"; import { readTeamDetailResponseDTO } from "../dtos/teams.dto"; @@ -46,24 +46,23 @@ export const updateTeam = async (userId: number, params, body: UpdateTeamBody) = export const readTeamDetail = async (userId: number, params) => { const teamId = params.teamId; - const details = await getTeamDetail(teamId); - const detail = details[0]; + const detail = await getTeamDetail(teamId); if (!detail) { throw new BaseError(status.TEAM_NOT_FOUND); } const leaderInfo = await getUserInfoByCategory(detail.leaderId, detail.category); - const membersInfo = await readMembersInfo(details, detail.category); + const membersInfo = await findMemberInfoByCategory(teamId, detail.category); return readTeamDetailResponseDTO(detail, leaderInfo, membersInfo, userId == detail.leaderId); }; -const readMembersInfo = async (details, category: Category) => { - const membersInfoPromises = details - .filter((detail) => detail["Members.userId"] !== null) - .map(async (detail) => { - return await getUserInfoByCategory(detail["Members.userId"], category); - }); - return await Promise.all(membersInfoPromises); -}; +// const readMembersInfo = async (details, category: Category) => { +// const membersInfoPromises = details +// .filter((detail) => detail["Members.userId"] !== null) +// .map(async (detail) => { +// return await getUserInfoByCategory(detail["Members.userId"], category); +// }); +// return await Promise.all(membersInfoPromises); +// }; export const readTeamAvailPreviewById = async (userId, query) => { return await findTeamPreviewByCategoryForLeader(userId, query.category); From 8485b1208bc72a767e2e570dc0cbdd5e60ac8407 Mon Sep 17 00:00:00 2001 From: mzeong Date: Sat, 17 Feb 2024 18:29:34 +0900 Subject: [PATCH 132/147] =?UTF-8?q?Refactor:=20=EC=9D=91=EB=8B=B5=EC=97=90?= =?UTF-8?q?=20=EC=9C=A0=EC=A0=80=20=EC=95=84=EC=9D=B4=EB=94=94,=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=ED=95=84=20=EC=82=AC=EC=A7=84=20=ED=8F=AC?= =?UTF-8?q?=ED=95=A8=20#141?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/member.dao.ts | 2 +- src/daos/user.dao.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/daos/member.dao.ts b/src/daos/member.dao.ts index 7bb195d..003da5a 100644 --- a/src/daos/member.dao.ts +++ b/src/daos/member.dao.ts @@ -11,7 +11,7 @@ export const findMemberInfoByCategory = async (teamId: number, category: Categor include: [ { model: db.User, - attributes: ["nickname", "height"], + attributes: ["id", "nickname", "height", "avatarUrl"], include: [ { model: db.Profile, diff --git a/src/daos/user.dao.ts b/src/daos/user.dao.ts index edd649b..5031a05 100644 --- a/src/daos/user.dao.ts +++ b/src/daos/user.dao.ts @@ -58,7 +58,7 @@ export const getUserInfoByCategory = async (userId: number, category: Category) where: { id: userId, }, - attributes: ["nickname", "height"], + attributes: ["id", "nickname", "height", "avatarUrl"], include: [ { model: db.Profile, From 9bfd14c5e05ceea1dd43407f84184e56053db7ff Mon Sep 17 00:00:00 2001 From: mzeong Date: Sat, 17 Feb 2024 18:30:22 +0900 Subject: [PATCH 133/147] =?UTF-8?q?Refactor:=20getTeamDetail=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EC=97=90=EC=84=9C=20Member=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=20=EC=A1=B0=EC=9D=B8=20=EC=A0=9C=EA=B1=B0=20#141?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/team.dao.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index bf4cf01..5c73ee0 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -56,18 +56,11 @@ export const insertTeam = async (data: CreateTeamBody, userId: number, inviteCod }; export const getTeamDetail = async (teamId: number) => { - return await db.Team.findAll({ + return await db.Team.findOne({ raw: true, where: { id: teamId, }, - include: [ - { - model: db.Member, - attributes: ["userId"], - required: false, - }, - ], attributes: ["name", "logo", "skillLevel", "mannerLevel", "description", "leaderId", "category"], }); }; From c5b0994055e577b73d846e09b110fa51b0135e33 Mon Sep 17 00:00:00 2001 From: mzeong Date: Sat, 17 Feb 2024 18:31:37 +0900 Subject: [PATCH 134/147] =?UTF-8?q?Refactor:=20DTO=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?#141?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/teams.dto.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/dtos/teams.dto.ts b/src/dtos/teams.dto.ts index e1e196b..abd1565 100644 --- a/src/dtos/teams.dto.ts +++ b/src/dtos/teams.dto.ts @@ -7,8 +7,10 @@ interface TeamDetail { } interface UserInfo { + id: number; nickname: string; height: number | null; + avatarUrl: string | null; Profiles: { position: string | null; }; @@ -17,10 +19,12 @@ interface UserInfo { export const readTeamDetailResponseDTO = ( detail: TeamDetail, leaderInfo: UserInfo, - membersInfo: UserInfo[], + membersInfo, isTeamLeader: boolean, ) => { - const member = membersInfo.map((memberInfo: UserInfo) => ({ + const member = membersInfo.map((memberInfo) => ({ + id: memberInfo.id, + avatarUrl: memberInfo.avatarUrl, nickname: memberInfo.nickname, height: memberInfo.height, position: memberInfo["Profiles.position"], @@ -33,6 +37,8 @@ export const readTeamDetailResponseDTO = ( description: detail.description, participants: { leader: { + id: leaderInfo.id, + avatarUrl: leaderInfo.avatarUrl, nickname: leaderInfo.nickname, height: leaderInfo.height, position: leaderInfo["Profiles.position"], From 2f9728f14e45bce0076216ca0f7ef709e47f3055 Mon Sep 17 00:00:00 2001 From: mzeong Date: Sat, 17 Feb 2024 19:12:50 +0900 Subject: [PATCH 135/147] =?UTF-8?q?Feat:=20=EB=8D=94=20=EC=9D=B4=EC=83=81?= =?UTF-8?q?=20=EC=82=AC=EC=9A=A9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?readMembersInfo=20=ED=95=A8=EC=88=98=20=EC=A0=9C=EA=B1=B0=20#14?= =?UTF-8?q?1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/services/teams.service.ts | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/src/services/teams.service.ts b/src/services/teams.service.ts index e0c62b5..d9452c5 100644 --- a/src/services/teams.service.ts +++ b/src/services/teams.service.ts @@ -1,7 +1,6 @@ import { BaseError } from "../config/error"; import { status } from "../config/response.status"; import { v4 as uuidv4 } from "uuid"; -import { Category } from "../types/category.enum"; import { CreateTeamBody, UpdateTeamBody } from "../schemas/team.schema"; import { findTeamPreviewByCategory, @@ -55,15 +54,6 @@ export const readTeamDetail = async (userId: number, params) => { return readTeamDetailResponseDTO(detail, leaderInfo, membersInfo, userId == detail.leaderId); }; -// const readMembersInfo = async (details, category: Category) => { -// const membersInfoPromises = details -// .filter((detail) => detail["Members.userId"] !== null) -// .map(async (detail) => { -// return await getUserInfoByCategory(detail["Members.userId"], category); -// }); -// return await Promise.all(membersInfoPromises); -// }; - export const readTeamAvailPreviewById = async (userId, query) => { return await findTeamPreviewByCategoryForLeader(userId, query.category); }; From 4b85bc442748f3116ebb8511948a2c3489396b04 Mon Sep 17 00:00:00 2001 From: mzeong Date: Sat, 17 Feb 2024 19:13:35 +0900 Subject: [PATCH 136/147] =?UTF-8?q?Feat:=20getTeamDetail=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EC=97=90=EC=84=9C=20=EC=B4=88=EB=8C=80=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C,=20=EC=84=B1=EB=B3=84,=20=EC=97=B0=EB=A0=B9=EB=8C=80,?= =?UTF-8?q?=20=EC=A7=80=EC=97=AD,=20=EC=B2=B4=EC=9C=A1=EA=B4=80=EB=AA=85?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=EB=A1=9C=20=EC=A1=B0=ED=9A=8C=20#141?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/team.dao.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/daos/team.dao.ts b/src/daos/team.dao.ts index 5c73ee0..2319cf7 100644 --- a/src/daos/team.dao.ts +++ b/src/daos/team.dao.ts @@ -61,7 +61,20 @@ export const getTeamDetail = async (teamId: number) => { where: { id: teamId, }, - attributes: ["name", "logo", "skillLevel", "mannerLevel", "description", "leaderId", "category"], + attributes: [ + "name", + "logo", + "skillLevel", + "mannerLevel", + "description", + "inviteCode", + "gender", + "ageGroup", + "region", + "gymName", + "leaderId", + "category", + ], }); }; From 3a8a88b97ab1c6c83cf67037e792b0d0ca0ac8f0 Mon Sep 17 00:00:00 2001 From: mzeong Date: Sat, 17 Feb 2024 19:15:57 +0900 Subject: [PATCH 137/147] =?UTF-8?q?Refactor:=20DTO=20=ED=95=A8=EC=88=98?= =?UTF-8?q?=EC=97=90=20MemberInfo=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=20=EC=B6=94=EA=B0=80=20#141?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/teams.dto.ts | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/src/dtos/teams.dto.ts b/src/dtos/teams.dto.ts index abd1565..bfcac0f 100644 --- a/src/dtos/teams.dto.ts +++ b/src/dtos/teams.dto.ts @@ -1,9 +1,17 @@ +import { AgeGroup } from "../types/age-group.enum"; +import { Gender } from "../types/gender.enum"; + interface TeamDetail { name: string; logo: string | null; skillLevel: number | null; mannerLevel: number | null; description: string | null; + inviteCode: string; + gender: Gender; + ageGroup: AgeGroup; + region: string; + gymName: string; } interface UserInfo { @@ -16,18 +24,23 @@ interface UserInfo { }; } +interface MemberInfo { + User: UserInfo; +} + export const readTeamDetailResponseDTO = ( detail: TeamDetail, leaderInfo: UserInfo, - membersInfo, + membersInfo: MemberInfo[], isTeamLeader: boolean, ) => { - const member = membersInfo.map((memberInfo) => ({ - id: memberInfo.id, - avatarUrl: memberInfo.avatarUrl, - nickname: memberInfo.nickname, - height: memberInfo.height, - position: memberInfo["Profiles.position"], + console.log(membersInfo); + const member = membersInfo.map((memberInfo: MemberInfo) => ({ + id: memberInfo["User.id"], + avatarUrl: memberInfo["User.avatarUrl"], + nickname: memberInfo["User.nickname"], + height: memberInfo["User.height"], + position: memberInfo["User.Profiles.position"], })); return { name: detail.name, @@ -35,6 +48,11 @@ export const readTeamDetailResponseDTO = ( skillLevel: detail.skillLevel, mannerLevel: detail.mannerLevel, description: detail.description, + inviteCode: isTeamLeader ? detail.inviteCode : null, + gender: detail.gender, + ageGroup: detail.ageGroup, + region: detail.region, + gymName: detail.gymName, participants: { leader: { id: leaderInfo.id, From 7fc3d5fc58b70cc0d8bb33d2c303db3eaa4e628b Mon Sep 17 00:00:00 2001 From: mzeong Date: Sat, 17 Feb 2024 19:16:22 +0900 Subject: [PATCH 138/147] =?UTF-8?q?Refactor:=20DTO=20=EB=82=B4=EC=97=90=20?= =?UTF-8?q?console.log=20=EC=A0=9C=EA=B1=B0=20#141?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/teams.dto.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/dtos/teams.dto.ts b/src/dtos/teams.dto.ts index bfcac0f..fd93e96 100644 --- a/src/dtos/teams.dto.ts +++ b/src/dtos/teams.dto.ts @@ -34,7 +34,6 @@ export const readTeamDetailResponseDTO = ( membersInfo: MemberInfo[], isTeamLeader: boolean, ) => { - console.log(membersInfo); const member = membersInfo.map((memberInfo: MemberInfo) => ({ id: memberInfo["User.id"], avatarUrl: memberInfo["User.avatarUrl"], From 057921957a1c5feb0fa50384d49510dcc6706d8e Mon Sep 17 00:00:00 2001 From: mzeong Date: Sat, 17 Feb 2024 19:28:04 +0900 Subject: [PATCH 139/147] =?UTF-8?q?Feat:=20DAO,=20DTO=EC=97=90=20id,=20hei?= =?UTF-8?q?ght=20=EC=B6=94=EA=B0=80=20#140?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/user.dao.ts | 2 +- src/dtos/users.dto.ts | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/daos/user.dao.ts b/src/daos/user.dao.ts index edd649b..01d3755 100644 --- a/src/daos/user.dao.ts +++ b/src/daos/user.dao.ts @@ -103,7 +103,7 @@ export const getUserProfileByCategory = async (userId: number, category: Categor attributes: ["skillLevel", "mannerLevel", "region", "position", "description"], }, ], - attributes: ["nickname", "gender", "ageGroup", "avatarUrl"], + attributes: ["id", "nickname", "gender", "ageGroup", "height", "avatarUrl"], }); }; diff --git a/src/dtos/users.dto.ts b/src/dtos/users.dto.ts index 397b403..fb1f3e5 100644 --- a/src/dtos/users.dto.ts +++ b/src/dtos/users.dto.ts @@ -4,10 +4,12 @@ import { AgeGroup } from "../types/age-group.enum"; import { Gender } from "../types/gender.enum"; interface ReadUserProfile { + id: number; avatarUrl: string | null; nickname: string; gender: Exclude | null; ageGroup: AgeGroup | null; + height: number | null; Profiles: { skillLevel: number; mannerLevel: number; @@ -19,13 +21,15 @@ interface ReadUserProfile { export const readUserProfileResponseDTO = (profile: ReadUserProfile) => { return { + id: profile.id, avatarUrl: profile.avatarUrl, nickname: profile.nickname, - skillLevel: !profile["Profiles.skillLevel"] ? null : profile["Profiles.skillLevel"], - mannerLevel: !profile["Profiles.mannerLevel"] ? null : profile["Profiles.mannerLevel"], - gender: !profile.gender ? null : getGender(profile.gender), - ageGroup: !profile.ageGroup ? null : getAgeGroup(profile.ageGroup), + skillLevel: profile["Profiles.skillLevel"], + mannerLevel: profile["Profiles.mannerLevel"], + gender: profile.gender, + ageGroup: profile.ageGroup, region: profile["Profiles.region"], + height: profile.height, position: profile["Profiles.position"], description: profile["Profiles.description"], }; From 589deb446e132afcfffa2b2b69cb66cb1f8c6cb6 Mon Sep 17 00:00:00 2001 From: mzeong Date: Sat, 17 Feb 2024 20:02:39 +0900 Subject: [PATCH 140/147] =?UTF-8?q?Refactor:=20userId=20path=20variable=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=20#150?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controllers/users.controller.ts | 2 +- src/routes/users.route.ts | 2 +- src/services/users.service.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/controllers/users.controller.ts b/src/controllers/users.controller.ts index 3d63587..44cce66 100644 --- a/src/controllers/users.controller.ts +++ b/src/controllers/users.controller.ts @@ -4,7 +4,7 @@ import { status } from "../config/response.status"; import { readUserProfile, updateUserProfile } from "../services/users.service"; export const fetchUserProfile = async (req, res: Response, next) => { - res.send(response(status.SUCCESS, await readUserProfile(req.user.id, req.params))); + res.send(response(status.SUCCESS, await readUserProfile(req.params))); }; export const modifyUserProfile = async (req, res: Response, next) => { diff --git a/src/routes/users.route.ts b/src/routes/users.route.ts index 29a90b6..72befea 100644 --- a/src/routes/users.route.ts +++ b/src/routes/users.route.ts @@ -10,6 +10,6 @@ export const usersRouter = express.Router(); usersRouter.use(verifyUser); -usersRouter.get("/profiles/:category", validate(categoryParam), asyncHandler(fetchUserProfile)); +usersRouter.get(":userId/profiles/:category", validate(categoryParam), asyncHandler(fetchUserProfile)); usersRouter.put("/profiles/:category", validate(updateUserProfileSchema), asyncHandler(modifyUserProfile)); diff --git a/src/services/users.service.ts b/src/services/users.service.ts index 78c969f..8b9082c 100644 --- a/src/services/users.service.ts +++ b/src/services/users.service.ts @@ -27,8 +27,8 @@ export const updateRefreshToken = async (refreshToken: string, userId: number) = await setRefreshToken(refreshToken, userId); }; -export const readUserProfile = async (userId: number, params) => { - const profile = await getUserProfileByCategory(userId, params.category); +export const readUserProfile = async (params) => { + const profile = await getUserProfileByCategory(params.userId, params.category); if (!profile) { throw new BaseError(status.USER_NOT_FOUND); } From ad75fb97d2d83b55eefd3af7afc0b1aeec44d78b Mon Sep 17 00:00:00 2001 From: mzeong Date: Sun, 18 Feb 2024 00:08:25 +0900 Subject: [PATCH 141/147] =?UTF-8?q?Fix:=20=EC=97=94=EB=93=9C=ED=8F=AC?= =?UTF-8?q?=EC=9D=B8=ED=8A=B8=20=EC=98=A4=ED=83=80=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?#152?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/routes/users.route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/users.route.ts b/src/routes/users.route.ts index 72befea..c52ec35 100644 --- a/src/routes/users.route.ts +++ b/src/routes/users.route.ts @@ -10,6 +10,6 @@ export const usersRouter = express.Router(); usersRouter.use(verifyUser); -usersRouter.get(":userId/profiles/:category", validate(categoryParam), asyncHandler(fetchUserProfile)); +usersRouter.get("/:userId/profiles/:category", validate(categoryParam), asyncHandler(fetchUserProfile)); usersRouter.put("/profiles/:category", validate(updateUserProfileSchema), asyncHandler(modifyUserProfile)); From 10b7554bf4e47d1a6d8813438fb87c9f91c0ac93 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sun, 18 Feb 2024 00:50:59 +0900 Subject: [PATCH 142/147] =?UTF-8?q?[FEAT]=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=A1=B0=ED=9A=8C=20API=20guest.?= =?UTF-8?q?id=20=EC=B6=94=EA=B0=80=20#155?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/guest.dao.ts | 2 +- src/dtos/guests.dto.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/daos/guest.dao.ts b/src/daos/guest.dao.ts index b880d40..6c884ae 100644 --- a/src/daos/guest.dao.ts +++ b/src/daos/guest.dao.ts @@ -69,7 +69,7 @@ export const findGuests = async (date: string, guestFilter: object, TeamFilter: attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], }, ], - attributes: ["gameTime", "recruitCount", "gameDuration", "status"], + attributes: ["id", "gameTime", "recruitCount", "gameDuration", "status"], }); return { guests, hasNext: calculateHasNext(guests, defaultLimit) }; }; diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index 7d8138a..c95a95d 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -35,6 +35,7 @@ interface UserInfo { export const readGuestingResponseDTO = (result) => { return { guests: result.guests.map((guesting) => ({ + guestId: guesting.id, gameTime: guesting.gameTime, gameDuration: guesting.gameDuration, teamName: guesting["Team.name"], From f3aea882b24003cc1f81cd136d8c82a7cb5625a5 Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sun, 18 Feb 2024 00:56:48 +0900 Subject: [PATCH 143/147] =?UTF-8?q?[FEAT]=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=20=EC=A1=B0=ED=9A=8C=20API=20guest.descripti?= =?UTF-8?q?on=20=EC=B6=94=EA=B0=80=20#155?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/guests.dto.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index c95a95d..50f111d 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -66,9 +66,10 @@ export const readGuestingDetailResponseDTO = ( name: TeamDetail.name, skillLevel: TeamDetail.skillLevel, mannerLevel: TeamDetail.mannerLevel, - description: TeamDetail.description, + teamDescription: TeamDetail.description, status: getStatus(guestingDetail.status), gusting_info: { + guestDescription: guestingDetail.description, gameTime: guestingDetail.gameTime, gameDuration: guestingDetail.gameDuration, gender: getTeamGender(TeamDetail.gender), From 32e082c465ffb9c07460a6679288ba2fc879f054 Mon Sep 17 00:00:00 2001 From: Doeun Date: Sun, 18 Feb 2024 03:41:14 +0900 Subject: [PATCH 144/147] =?UTF-8?q?Feat:=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20API=20=EA=B2=8C=EC=9E=84=20=EC=86=8C=EC=9A=94?= =?UTF-8?q?=EC=8B=9C=EA=B0=84=20=ED=95=84=EB=93=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EB=B0=8F=20=EC=97=B0=EC=8A=B5=EA=B2=BD=EA=B8=B0=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20id=20=EC=B6=94=EA=B0=80=20#154?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/game.dao.ts | 18 ++++++++++-------- src/dtos/games.dto.ts | 3 +++ src/models/game.model.ts | 4 ++++ src/schemas/fields.ts | 6 ++++++ src/schemas/game.schema.ts | 17 ++++------------- 5 files changed, 27 insertions(+), 21 deletions(-) diff --git a/src/daos/game.dao.ts b/src/daos/game.dao.ts index bd569a7..b68b7c9 100644 --- a/src/daos/game.dao.ts +++ b/src/daos/game.dao.ts @@ -19,7 +19,7 @@ export const findGamesByDate = async (date, category) => { }, }, ], - attributes: ["gameTime", "status"], + attributes: ["id", "gameTime", "status"], order: [["created_at", "DESC"]], }); }; @@ -39,7 +39,7 @@ export const findGamesByGender = async (date, category, gender) => { }, }, ], - attributes: ["gameTime", "status"], + attributes: ["id", "gameTime", "status"], order: [["created_at", "DESC"]], }); }; @@ -55,13 +55,14 @@ export const findGamesByLevel = async (date, category, skillLevel) => { attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], where: { category, - skillLevel: { - [Op.between]: [Math.floor(skillLevel / 10) * 10, Math.floor(skillLevel / 10) * 10 + 9], - }, + skillLevel, + // skillLevel: { + // [Op.between]: [Math.floor(skillLevel / 10) * 10, Math.floor(skillLevel / 10) * 10 + 9], + // }, }, }, ], - attributes: ["gameTime", "status"], + attributes: ["id", "gameTime", "status"], order: [["created_at", "DESC"]], }); }; @@ -81,7 +82,7 @@ export const findGamesByRegion = async (date, category, region) => { }, }, ], - attributes: ["gameTime", "status"], + attributes: ["id", "gameTime", "status"], order: [["created_at", "DESC"]], }); }; @@ -92,7 +93,7 @@ export const getGameDetail = async (gameId) => { where: { id: gameId, }, - attributes: ["hostTeamId", "gameTime", "description"], + attributes: ["hostTeamId", "gameTime", "gameDuration", "description"], }); }; @@ -100,6 +101,7 @@ export const insertGame = async (hostTeamId, data: CreateGameBody, category) => await db.Game.create({ hostTeamId: hostTeamId, gameTime: data.gameTime, + gameDuration: data.gameDuration, category: category, description: data.description, }); diff --git a/src/dtos/games.dto.ts b/src/dtos/games.dto.ts index 43d52b3..40cf169 100644 --- a/src/dtos/games.dto.ts +++ b/src/dtos/games.dto.ts @@ -4,6 +4,7 @@ import { getStatus } from "../constants/status.constant"; export const readGameResponseDTO = (games) => { return games.map((game) => ({ + gameId: game.id, gameTime: game.gameTime, teamName: game["HostTeam.name"], teamRegion: game["HostTeam.region"], @@ -22,6 +23,7 @@ export const readGameDetailResponseDTO = (gameDetail, teamDetail, leaderInfo, me position: info["User.Profiles.position"], })); return { + id: gameDetail.id, name: teamDetail.name, skillLevel: teamDetail.skillLevel, mannerLevel: teamDetail.mannerLevel, @@ -29,6 +31,7 @@ export const readGameDetailResponseDTO = (gameDetail, teamDetail, leaderInfo, me game_info: { gymName: teamDetail.gymName, gameTime: gameDetail.gameTime, + gameDuration: gameDetail.gameDuration, gender: getGender(teamDetail.gender), ageGroup: getAgeGroup(teamDetail.ageGroup), }, diff --git a/src/models/game.model.ts b/src/models/game.model.ts index f7e5e7e..dec6222 100644 --- a/src/models/game.model.ts +++ b/src/models/game.model.ts @@ -16,6 +16,10 @@ class Game extends Model, InferCreationAttributes> { type: new DataTypes.DATE(), allowNull: false, }, + gameDuration: { + type: new DataTypes.TIME(), + allowNull: false, + }, category: { type: new DataTypes.STRING(15), allowNull: false, diff --git a/src/schemas/fields.ts b/src/schemas/fields.ts index f608ee4..19142d3 100644 --- a/src/schemas/fields.ts +++ b/src/schemas/fields.ts @@ -94,6 +94,12 @@ export const recruitCountField = { recruitCount: z.number().int() }; export const levelField = { level: z.string().max(5) }; +export const levelFieldInTeam = { + skillLevel: z.string().refine((val) => !Number.isNaN(parseInt(val, 10)), { + message: "Expected number, received a string", + }), +}; + export const skillScoreField = { skillScore: z.number().int().min(1).max(5) }; export const mannerScoreField = { mannerScore: z.number().int().min(1).max(5) }; diff --git a/src/schemas/game.schema.ts b/src/schemas/game.schema.ts index 95a1c56..c6d0b1e 100644 --- a/src/schemas/game.schema.ts +++ b/src/schemas/game.schema.ts @@ -5,25 +5,16 @@ import { descriptionFieldInGame, categoryField, dateField, - levelField, + levelFieldInTeam, regionFieldInTeam, genderFieldInTeam, + gameDurationField, } from "./fields"; -const fields = { - hostTeamId: z.number().int().optional(), - gameTime: z.preprocess((arg) => { - if (typeof arg == "string") { - return new Date(arg); - } - return arg; - }, z.date()), - description: z.string(), -}; - const body = object({ ...hostTeamIdField, ...gameTimeField, + ...gameDurationField, ...descriptionFieldInGame, }); @@ -60,7 +51,7 @@ export const readGameFilterGenderSchema = object({ export const readGameFilterLevelSchema = object({ query: object({ ...readGame, - ...levelField, + ...levelFieldInTeam, }), }); From 4264a0aaedcf23981e9274b246883cd50f751097 Mon Sep 17 00:00:00 2001 From: Doeun Date: Sun, 18 Feb 2024 03:56:53 +0900 Subject: [PATCH 145/147] =?UTF-8?q?Refactor:=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20=EC=9D=91=EB=8B=B5=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20#160?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dtos/games.dto.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dtos/games.dto.ts b/src/dtos/games.dto.ts index 43d52b3..576f8ff 100644 --- a/src/dtos/games.dto.ts +++ b/src/dtos/games.dto.ts @@ -17,6 +17,7 @@ export const readGameResponseDTO = (games) => { export const readGameDetailResponseDTO = (gameDetail, teamDetail, leaderInfo, memberInfo) => { const member = memberInfo.map((info) => ({ + avatarUrl: info["User.avatarUrl"], nickname: info["User.nickname"], height: info["User.height"], position: info["User.Profiles.position"], From 0dbdc2eddecb32602760b75a62d023c9feb56ede Mon Sep 17 00:00:00 2001 From: Doeun Date: Sun, 18 Feb 2024 04:38:51 +0900 Subject: [PATCH 146/147] =?UTF-8?q?Refactor:=20=EC=97=B0=EC=8A=B5=EA=B2=BD?= =?UTF-8?q?=EA=B8=B0=20=EB=AA=A8=EC=A7=91=EA=B8=80=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=88=98=EC=A0=95=20#162?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/game.dao.ts | 100 +++++++++++++--------------------- src/dtos/games.dto.ts | 27 +++++---- src/services/games.service.ts | 30 +++++----- 3 files changed, 65 insertions(+), 92 deletions(-) diff --git a/src/daos/game.dao.ts b/src/daos/game.dao.ts index b68b7c9..ebe3999 100644 --- a/src/daos/game.dao.ts +++ b/src/daos/game.dao.ts @@ -4,87 +4,61 @@ import { CreateGameBody } from "../schemas/game.schema"; import { ApplyGameBody } from "../schemas/game-apply.schema"; import { getTeamIdByLeaderId } from "./team.dao"; import { MatchType } from "../types/match-type.enum"; +import { Category } from "../types/category.enum"; +import { Gender } from "../types/gender.enum"; +import { calculateHasNext, generateCursorCondition } from "../utils/paging.util"; -export const findGamesByDate = async (date, category) => { - return await db.Game.findAll({ - raw: true, - where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - include: [ - { - model: db.Team, - as: "HostTeam", - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - where: { - category, - }, - }, - ], - attributes: ["id", "gameTime", "status"], - order: [["created_at", "DESC"]], - }); +const defaultLimit = 20; + +export const findGamesByDate = async (date: string, category: Category, cursorId: number | undefined) => { + const gamesBeforeCursor = generateCursorCondition(cursorId); + const teamFilter = { category }; + return findGames(date, gamesBeforeCursor, teamFilter); }; -export const findGamesByGender = async (date, category, gender) => { - return await db.Game.findAll({ - raw: true, - where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - include: [ - { - model: db.Team, - as: "HostTeam", - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - where: { - category, - gender, - }, - }, - ], - attributes: ["id", "gameTime", "status"], - order: [["created_at", "DESC"]], - }); +export const findGamesByGender = async ( + date: string, + category: Category, + gender: Gender, + cursorId: number | undefined, +) => { + const gamesBeforeCursor = generateCursorCondition(cursorId); + const teamFilter = { category, gender }; + return findGames(date, gamesBeforeCursor, teamFilter); }; -export const findGamesByLevel = async (date, category, skillLevel) => { - return await db.Game.findAll({ - raw: true, - where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), - include: [ - { - model: db.Team, - as: "HostTeam", - attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - where: { - category, - skillLevel, - // skillLevel: { - // [Op.between]: [Math.floor(skillLevel / 10) * 10, Math.floor(skillLevel / 10) * 10 + 9], - // }, - }, - }, - ], - attributes: ["id", "gameTime", "status"], - order: [["created_at", "DESC"]], - }); +export const findGamesByLevel = async (date: string, category: Category, skillLevel, cursorId: number | undefined) => { + const gamesBeforeCursor = generateCursorCondition(cursorId); + const teamFilter = { category, skillLevel }; + return findGames(date, gamesBeforeCursor, teamFilter); }; -export const findGamesByRegion = async (date, category, region) => { - return await db.Game.findAll({ +export const findGamesByRegion = async (date: string, category: Category, region, cursorId: number | undefined) => { + const gamesBeforeCursor = generateCursorCondition(cursorId); + const teamFilter = { category, region }; + return findGames(date, gamesBeforeCursor, teamFilter); +}; + +export const findGames = async (date: string, gameFilter: object, teamFilter: object) => { + const games = await db.Game.findAll({ raw: true, - where: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + where: { + ...gameFilter, + [Op.and]: Sequelize.literal(`DATE_FORMAT(game_time, '%Y-%m-%d') = DATE_FORMAT('${date}', '%Y-%m-%d')`), + }, include: [ { model: db.Team, as: "HostTeam", attributes: ["id", "name", "region", "gender", "ageGroup", "skillLevel"], - where: { - category, - region, - }, + where: teamFilter, }, ], attributes: ["id", "gameTime", "status"], order: [["created_at", "DESC"]], + limit: defaultLimit, }); + return { games, hasNext: calculateHasNext(games, defaultLimit) }; }; export const getGameDetail = async (gameId) => { diff --git a/src/dtos/games.dto.ts b/src/dtos/games.dto.ts index feac91e..9668b3f 100644 --- a/src/dtos/games.dto.ts +++ b/src/dtos/games.dto.ts @@ -2,18 +2,21 @@ import { getGender } from "../constants/gender.constant"; import { getAgeGroup } from "../constants/age-group.constant"; import { getStatus } from "../constants/status.constant"; -export const readGameResponseDTO = (games) => { - return games.map((game) => ({ - gameId: game.id, - gameTime: game.gameTime, - teamName: game["HostTeam.name"], - teamRegion: game["HostTeam.region"], - teamGender: getGender(game["HostTeam.gender"]), - memberCount: game.memberCount, - teamAgeGroup: getAgeGroup(game["HostTeam.ageGroup"]), - teamSkillLevel: game["HostTeam.skillLevel"], - status: getStatus(game.status), - })); +export const readGameResponseDTO = (result) => { + return { + games: result.games.map((game) => ({ + gameId: game.id, + gameTime: game.gameTime, + teamName: game["HostTeam.name"], + teamRegion: game["HostTeam.region"], + teamGender: getGender(game["HostTeam.gender"]), + memberCount: game.memberCount, + teamAgeGroup: getAgeGroup(game["HostTeam.ageGroup"]), + teamSkillLevel: game["HostTeam.skillLevel"], + status: getStatus(game.status), + })), + hasNext: result.hasNext, + }; }; export const readGameDetailResponseDTO = (gameDetail, teamDetail, leaderInfo, memberInfo) => { diff --git a/src/services/games.service.ts b/src/services/games.service.ts index 6c875d7..93b2b51 100644 --- a/src/services/games.service.ts +++ b/src/services/games.service.ts @@ -17,7 +17,7 @@ import { getTeamCategoryByLeaderId, getTeamByLeaderId, } from "../daos/team.dao"; -import { getMemberCountByTeamId, findMemberInfoByCategory } from "../daos/member.dao"; +import { addMemberCount, findMemberInfoByCategory } from "../daos/member.dao"; import { getUserInfoByCategory, userInfoAttributes } from "../daos/user.dao"; import { getGameByUserId } from "../daos/game.dao"; import { checkApplicationExisting } from "../daos/game-apply.dao"; @@ -26,34 +26,30 @@ import { CreateGameBody, UpdateGameBody } from "../schemas/game.schema"; import { ApplyGameBody } from "../schemas/game-apply.schema"; export const readGamesByDate = async (query) => { - const games = await findGamesByDate(query.date, query.category); - for (const game of games) { - game.memberCount = await getMemberCountByTeamId(game["HostTeam.id"]); - } + const cursorId = query.cursorId ? parseInt(query.cursorId) : undefined; + const games = await findGamesByDate(query.date, query.category, cursorId); + await addMemberCount(games.games); return readGameResponseDTO(games); }; export const readGamesByGender = async (query) => { - const games = await findGamesByGender(query.date, query.category, query.gender); - for (const game of games) { - game.memberCount = await getMemberCountByTeamId(game["HostTeam.id"]); - } + const cursorId = query.cursorId ? parseInt(query.cursorId) : undefined; + const games = await findGamesByGender(query.date, query.category, query.gender, cursorId); + await addMemberCount(games.games); return readGameResponseDTO(games); }; export const readGamesByLevel = async (query) => { - const games = await findGamesByLevel(query.date, query.category, query.skillLevel); - for (const game of games) { - game.memberCount = await getMemberCountByTeamId(game["HostTeam.id"]); - } + const cursorId = query.cursorId ? parseInt(query.cursorId) : undefined; + const games = await findGamesByLevel(query.date, query.category, query.skillLevel, cursorId); + await addMemberCount(games.games); return readGameResponseDTO(games); }; export const readGamesByRegion = async (query) => { - const games = await findGamesByRegion(query.date, query.category, query.region); - for (const game of games) { - game.memberCount = await getMemberCountByTeamId(game["HostTeam.id"]); - } + const cursorId = query.cursorId ? parseInt(query.cursorId) : undefined; + const games = await findGamesByRegion(query.date, query.category, query.region, cursorId); + await addMemberCount(games.games); return readGameResponseDTO(games); }; From f0959e486b8879feaefe976e3675a04c9ad6e68d Mon Sep 17 00:00:00 2001 From: ByeonYujin124 Date: Sun, 18 Feb 2024 13:30:59 +0900 Subject: [PATCH 147/147] =?UTF-8?q?[FEAT]=20=EA=B2=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C,=20=ED=98=B8=EC=8A=A4?= =?UTF-8?q?=ED=8C=85=20=EC=8B=A0=EC=B2=AD=EC=9E=90=20=EC=A1=B0=ED=9A=8C=20?= =?UTF-8?q?avatarUrl=20=EC=B6=94=EA=B0=80=20#143?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/daos/guest-user.dao.ts | 2 +- src/dtos/guests.dto.ts | 3 +++ src/dtos/matchings.dto.ts | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/daos/guest-user.dao.ts b/src/daos/guest-user.dao.ts index ebad23c..abea246 100644 --- a/src/daos/guest-user.dao.ts +++ b/src/daos/guest-user.dao.ts @@ -23,7 +23,7 @@ export const getApplyGuestingUser = async (guestingId: number) => { attributes: ["position"], }, ], - attributes: ["nickname", "height"], + attributes: ["nickname", "height", "avatarUrl"], }, ], attributes: ["status"], diff --git a/src/dtos/guests.dto.ts b/src/dtos/guests.dto.ts index 7d8138a..8be1637 100644 --- a/src/dtos/guests.dto.ts +++ b/src/dtos/guests.dto.ts @@ -27,6 +27,7 @@ interface TeamDetail { interface UserInfo { nickname: string; height: number | null; + avatarUrl: string | null; Profiles: { position: string | null; }; @@ -59,6 +60,7 @@ export const readGuestingDetailResponseDTO = ( const member = memberInfo.map((info: UserInfo) => ({ nickname: info.nickname, height: info.height, + avatarUrl: info.avatarUrl, position: info["Profiles.position"], })); return { @@ -79,6 +81,7 @@ export const readGuestingDetailResponseDTO = ( leader: { nickname: leaderInfo.nickname, height: leaderInfo.height, + avatarUrl: leaderInfo.avatarUrl, position: leaderInfo["Profiles.position"], }, member, diff --git a/src/dtos/matchings.dto.ts b/src/dtos/matchings.dto.ts index 6ce571c..bb43133 100644 --- a/src/dtos/matchings.dto.ts +++ b/src/dtos/matchings.dto.ts @@ -6,6 +6,7 @@ export const readApplyGuestingUserResponseDTO = (result) => { return result.map((guestingUser) => ({ nickname: guestingUser["User.nickname"], height: guestingUser["User.height"], + avatarUrl: guestingUser["User.avatarUrl"], position: guestingUser["User.Profiles.position"], status: getGuestUserStatus(guestingUser.status), }));