From c1d1948ef914abed6078e8155cae61b3f04bbaf0 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Wed, 13 Sep 2023 20:45:46 +0900 Subject: [PATCH 01/67] Add: instagram event handler --- src/lottery/index.js | 1 + src/lottery/routes/docs/events.js | 0 src/lottery/routes/events.js | 9 +++++++++ src/lottery/services/events.js | 28 ++++++++++++++++++++++++++++ 4 files changed, 38 insertions(+) create mode 100644 src/lottery/routes/docs/events.js create mode 100644 src/lottery/routes/events.js create mode 100644 src/lottery/services/events.js diff --git a/src/lottery/index.js b/src/lottery/index.js index 9ca0f3eb..98ab5a36 100644 --- a/src/lottery/index.js +++ b/src/lottery/index.js @@ -22,6 +22,7 @@ const lotteryRouter = express.Router(); lotteryRouter.use(require("../middlewares/originValidator")); // [Router] APIs +lotteryRouter.use("/events", require("./routes/events")); lotteryRouter.use("/global-state", require("./routes/globalState")); lotteryRouter.use("/transactions", require("./routes/transactions")); lotteryRouter.use("/items", require("./routes/items")); diff --git a/src/lottery/routes/docs/events.js b/src/lottery/routes/docs/events.js new file mode 100644 index 00000000..e69de29b diff --git a/src/lottery/routes/events.js b/src/lottery/routes/events.js new file mode 100644 index 00000000..9f2e6b18 --- /dev/null +++ b/src/lottery/routes/events.js @@ -0,0 +1,9 @@ +const express = require("express"); +const router = express.Router(); +const events = require("../services/events"); + +router.use(require("../../middlewares/auth")); +router.post("/instagram/share-event", events.instagramEventShareHandler); +router.post("/instagram/share-purchase", events.instagramPurchaseShareHandler); + +module.exports = router; diff --git a/src/lottery/services/events.js b/src/lottery/services/events.js new file mode 100644 index 00000000..a9da434f --- /dev/null +++ b/src/lottery/services/events.js @@ -0,0 +1,28 @@ +const logger = require("../../modules/logger"); +const eventHandler = require("../modules/events"); +// 인스타그램 스토리에 이벤트를 공유했을 때. +const instagramEventShareHandler = async (req, res) => { + try { + const userId = req.userOid; + // const eventId = + } catch (err) { + logger.err(err); + res.status(500).json({ error: "Events/Insagram/Share-Event" }); + } +}; + +// 인스타그램 스토리에 아이템 구매 내역을 공유했을 때. +const instagramPurchaseShareHandler = async (req, res) => { + try { + const userId = req.userOid; + // const eventId = + } catch (err) { + logger.err(err); + res.status(500).json({ error: "Events/Insagram/Share-Purchase" }); + } +}; + +module.exports = { + instagramEventShareHandler, + instagramPurchaseShareHandler, +}; From f454b62a736412bcf433ae2d262bf273e5d8a486 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Thu, 14 Sep 2023 23:03:34 +0900 Subject: [PATCH 02/67] ADD: docs/events.js event documentation. --- src/lottery/routes/docs/events.js | 29 ++++++++++++++++++++++++++ src/lottery/routes/docs/swaggerDocs.js | 6 ++++++ 2 files changed, 35 insertions(+) diff --git a/src/lottery/routes/docs/events.js b/src/lottery/routes/docs/events.js index e69de29b..937ad1ee 100644 --- a/src/lottery/routes/docs/events.js +++ b/src/lottery/routes/docs/events.js @@ -0,0 +1,29 @@ +const { eventMode } = require("../../../../loadenv"); +const apiPrefix = `/events/${eventMode}/instagram`; + +const eventsDocs = {}; +eventsDocs[`${apiPrefix}/share-event`] = { + post: { + tags: [`${apiPrefix}`], + summary: "이벤트 공유시 보상 반환", + description: "인스타그램 스토리에 이벤트를 공유하면 보상 반환", + responses: { + 200: {}, + 500: {}, + }, + }, +}; + +eventsDocs[`${apiPrefix}/share-purchase`] = { + post: { + tags: [`${apiPrefix}`], + summary: "이벤트 공유시 보상 반환", + description: "인스타그램 스토리에 구매내역을 공유하면 보상 반환", + responses: { + 200: {}, + 500: {}, + }, + }, +}; + +module.exports = eventsDocs; diff --git a/src/lottery/routes/docs/swaggerDocs.js b/src/lottery/routes/docs/swaggerDocs.js index 654bcc43..85af25cd 100644 --- a/src/lottery/routes/docs/swaggerDocs.js +++ b/src/lottery/routes/docs/swaggerDocs.js @@ -2,6 +2,7 @@ const { eventMode } = require("../../../../loadenv"); const globalStateDocs = require("./globalState"); const itemsDocs = require("./items"); const transactionsDocs = require("./transactions"); +const eventsDocs = require("./events"); const apiPrefix = `/events/${eventMode}`; @@ -19,11 +20,16 @@ const eventSwaggerDocs = { name: `${apiPrefix}/transactions`, description: "이벤트 - 입출금 내역 관련 API", }, + { + name: `${apiPrefix}/instagram`, + description: "이벤트 - 인스타그램 이벤트 관련 API", + }, ], paths: { ...globalStateDocs, ...itemsDocs, ...transactionsDocs, + ...eventsDocs, }, components: { schemas: {}, From 12df4bb8e257ba348c4871aaef7581bd15baad5c Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Fri, 15 Sep 2023 04:01:25 +0900 Subject: [PATCH 03/67] =?UTF-8?q?ADD:=20publicNoticePopulationOption=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80,=20=EA=B8=B0=EC=A1=B4=20=EC=98=B5=EC=85=98?= =?UTF-8?q?=20=EB=B6=84=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lottery/index.js | 3 +- src/lottery/modules/populates/publicNotice.js | 14 +++++ src/lottery/routes/docs/events.js | 28 +++++++-- src/lottery/routes/docs/publicNotice.js | 27 ++++++++ src/lottery/routes/docs/swaggerDocs.js | 6 ++ src/lottery/routes/publicNotice.js | 10 +++ src/lottery/services/events.js | 8 ++- src/lottery/services/publicNotice.js | 62 +++++++++++++++++++ 8 files changed, 151 insertions(+), 7 deletions(-) create mode 100644 src/lottery/modules/populates/publicNotice.js create mode 100644 src/lottery/routes/docs/publicNotice.js create mode 100644 src/lottery/routes/publicNotice.js create mode 100644 src/lottery/services/publicNotice.js diff --git a/src/lottery/index.js b/src/lottery/index.js index 98ab5a36..35861589 100644 --- a/src/lottery/index.js +++ b/src/lottery/index.js @@ -24,8 +24,9 @@ lotteryRouter.use(require("../middlewares/originValidator")); // [Router] APIs lotteryRouter.use("/events", require("./routes/events")); lotteryRouter.use("/global-state", require("./routes/globalState")); -lotteryRouter.use("/transactions", require("./routes/transactions")); lotteryRouter.use("/items", require("./routes/items")); +lotteryRouter.use("/transactions", require("./routes/transactions")); +lotteryRouter.use("/public-notice", require("./routes/publicNotice")); const resources = [ eventStatusModel, diff --git a/src/lottery/modules/populates/publicNotice.js b/src/lottery/modules/populates/publicNotice.js new file mode 100644 index 00000000..d0c3e5c5 --- /dev/null +++ b/src/lottery/modules/populates/publicNotice.js @@ -0,0 +1,14 @@ +const publicNoticePopulateOption = [ + { path: "event" }, + { + path: "item", + select: "name imageUrl price description isDisabled stock itemType", + }, + { + path: "", + }, +]; + +module.exports = { + publicNoticePopulateOption, +}; diff --git a/src/lottery/routes/docs/events.js b/src/lottery/routes/docs/events.js index 937ad1ee..9998e4ce 100644 --- a/src/lottery/routes/docs/events.js +++ b/src/lottery/routes/docs/events.js @@ -8,8 +8,18 @@ eventsDocs[`${apiPrefix}/share-event`] = { summary: "이벤트 공유시 보상 반환", description: "인스타그램 스토리에 이벤트를 공유하면 보상 반환", responses: { - 200: {}, - 500: {}, + 200: { + description: "", + content: { + "application/json": { + schema: { + type: "boolean", + example: { result: true }, + }, + }, + }, + }, + 500: { error: "Events/Insagram/Share-Event" }, }, }, }; @@ -20,8 +30,18 @@ eventsDocs[`${apiPrefix}/share-purchase`] = { summary: "이벤트 공유시 보상 반환", description: "인스타그램 스토리에 구매내역을 공유하면 보상 반환", responses: { - 200: {}, - 500: {}, + 200: { + description: "", + content: { + "application/json": { + schema: { + type: "boolean", + example: { result: true }, + }, + }, + }, + }, + 500: { error: "Events/Insagram/Share-Purchase" }, }, }, }; diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js new file mode 100644 index 00000000..de51e82e --- /dev/null +++ b/src/lottery/routes/docs/publicNotice.js @@ -0,0 +1,27 @@ +const { eventMode } = require("../../../../loadenv"); +const apiPrefix = `/events/${eventMode}/public-notice`; + +const publicNoticeDocs = {}; +publicNoticeDocs[`${apiPrefix}/get-recent-transaction`] = { + get: { + tags: [`${apiPrefix}`], + summary: "공지를 띄움", + description: "상품 구매, 뽑기 획득 등등에 대한 공지를 반환", + responses: { + 200: { + content: { + "application/json": { + schema: { + type: "array", + description: + "use 트랜젝션의 정보를 담은 string 배열. 구매자의 익명 이름과 구매 내역을 문자열로 변환 후 이를 배열로 담아 5개 반환", + example: ["AB****님께서 송편을(를) 구매하셨습니다.", "..."], + }, + }, + }, + }, + }, + }, +}; + +module.exports = publicNoticeDocs; diff --git a/src/lottery/routes/docs/swaggerDocs.js b/src/lottery/routes/docs/swaggerDocs.js index 85af25cd..d2a05a43 100644 --- a/src/lottery/routes/docs/swaggerDocs.js +++ b/src/lottery/routes/docs/swaggerDocs.js @@ -3,6 +3,7 @@ const globalStateDocs = require("./globalState"); const itemsDocs = require("./items"); const transactionsDocs = require("./transactions"); const eventsDocs = require("./events"); +const publicNoticeDocs = require("./publicNotice"); const apiPrefix = `/events/${eventMode}`; @@ -24,12 +25,17 @@ const eventSwaggerDocs = { name: `${apiPrefix}/instagram`, description: "이벤트 - 인스타그램 이벤트 관련 API", }, + { + name: `${apiPrefix}/public-notice`, + description: "이벤트 - 아이템 구매, 뽑기, 획득 공지 관련 API", + }, ], paths: { ...globalStateDocs, ...itemsDocs, ...transactionsDocs, ...eventsDocs, + ...publicNoticeDocs, }, components: { schemas: {}, diff --git a/src/lottery/routes/publicNotice.js b/src/lottery/routes/publicNotice.js new file mode 100644 index 00000000..454183a6 --- /dev/null +++ b/src/lottery/routes/publicNotice.js @@ -0,0 +1,10 @@ +const express = require("express"); +const router = express.Router(); +const publicNotice = require("../services/publicNotice"); + +// 상점이용은 로그인을 요구합니다. +router.use(require("../../middlewares/auth")); +router.get("/", publicNotice.test); +router.get("/get-recent-transaction", publicNotice.getRecentTransaction); + +module.exports = router; diff --git a/src/lottery/services/events.js b/src/lottery/services/events.js index a9da434f..8ea9bedc 100644 --- a/src/lottery/services/events.js +++ b/src/lottery/services/events.js @@ -4,7 +4,9 @@ const eventHandler = require("../modules/events"); const instagramEventShareHandler = async (req, res) => { try { const userId = req.userOid; - // const eventId = + const eventId = "650302f799c6f338d5ea5427"; + const transactionResult = await eventHandler(userId, eventId); + res.json({ result: !!transactionResult ? true : false }); } catch (err) { logger.err(err); res.status(500).json({ error: "Events/Insagram/Share-Event" }); @@ -15,7 +17,9 @@ const instagramEventShareHandler = async (req, res) => { const instagramPurchaseShareHandler = async (req, res) => { try { const userId = req.userOid; - // const eventId = + const eventId = "6503030e99c6f338d5ea5433"; + const transactionResult = await eventHandler(userId, eventId); + res.json({ result: !!transactionResult ? true : false }); } catch (err) { logger.err(err); res.status(500).json({ error: "Events/Insagram/Share-Purchase" }); diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js new file mode 100644 index 00000000..5b653bcb --- /dev/null +++ b/src/lottery/services/publicNotice.js @@ -0,0 +1,62 @@ +const { transactionModel } = require("../modules/stores/mongo"); +const logger = require("../../modules/logger"); +const { + transactionPopulateOption, +} = require("../modules/populates/transactions"); + +const test = (req, res) => { + console.log("test"); + res.status(200).json({ msg: "success" }); +}; + +const getRecentTransaction = async (req, res) => { + try { + const transactions = await transactionModel + .find({ type: "use" }) + .sort({ doneat: -1 }) + .limit(5) + .populate(transactionPopulateOption) + .lean(); + let transactionListString = []; + if (transactions) { + transactions.forEach((item, index) => { + let purchaceMessage = ""; + + console.log(item, index); + + if (item.comment.includes("구매")) { + purchaceMessage = "구입하셨습니다."; + } else if (item.comment.includes("획득")) { + purchaceMessage = "뽑았습니다."; + } else { + purchaceMessage = "획득하셨습니다."; + } + transactionListString[index] = `${item.userId + .toString() + .slice(0, 2)}${"*".repeat(item.userId.length - 2)}"님께서 "${ + item.item + }"을(를) " ${purchaceMessage}`; + console.log(transactionListString[index]); + }); + console.log(transactionListString); + + res.json({ + transactionListString, + }); + } else { + res.status(500).json({ + error: "PublicNotice/get-recent-transaction : internal server error", + }); + } + } catch (err) { + logger.error(err); + res.status(500).json({ + error: "PublicNotice/get-recent-transaction : internal server error", + }); + } +}; + +module.exports = { + test, + getRecentTransaction, +}; From 0436396e076c0d26037a366508d4b88317b9bcc5 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Fri, 15 Sep 2023 14:40:15 +0900 Subject: [PATCH 04/67] =?UTF-8?q?ADD:=20publicNotice.js=20=EA=B5=AC?= =?UTF-8?q?=EB=A7=A4,=20=ED=9A=8D=EB=93=9D=20=EB=BD=91=EA=B8=B0=20?= =?UTF-8?q?=EA=B3=B5=EC=A7=80=20=EC=A0=95=EC=83=81=20=EB=B0=98=ED=99=98?= =?UTF-8?q?=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lottery/modules/populates/publicNotice.js | 5 +- src/lottery/routes/docs/publicNotice.js | 8 ++- src/lottery/routes/publicNotice.js | 1 - src/lottery/services/publicNotice.js | 54 ++++++++++++------- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/lottery/modules/populates/publicNotice.js b/src/lottery/modules/populates/publicNotice.js index d0c3e5c5..5709ed7d 100644 --- a/src/lottery/modules/populates/publicNotice.js +++ b/src/lottery/modules/populates/publicNotice.js @@ -2,10 +2,7 @@ const publicNoticePopulateOption = [ { path: "event" }, { path: "item", - select: "name imageUrl price description isDisabled stock itemType", - }, - { - path: "", + select: "name price description itemType", }, ]; diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index de51e82e..2910361f 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -15,7 +15,13 @@ publicNoticeDocs[`${apiPrefix}/get-recent-transaction`] = { type: "array", description: "use 트랜젝션의 정보를 담은 string 배열. 구매자의 익명 이름과 구매 내역을 문자열로 변환 후 이를 배열로 담아 5개 반환", - example: ["AB****님께서 송편을(를) 구매하셨습니다.", "..."], + example: [ + "su****님께서 매점2000원권을(를) 구입하셨습니다.", + "mo****님께서 치킨을(를) 구입하셨습니다.", + "tu*****님께서 매점2000원권을(를) 구입하셨습니다.", + "as**님께서 진짜송편을(를) 구입하셨습니다.", + "qw**님께서 전동킥보드을(를) 뽑았습니다.", + ], }, }, }, diff --git a/src/lottery/routes/publicNotice.js b/src/lottery/routes/publicNotice.js index 454183a6..6f5f20c3 100644 --- a/src/lottery/routes/publicNotice.js +++ b/src/lottery/routes/publicNotice.js @@ -4,7 +4,6 @@ const publicNotice = require("../services/publicNotice"); // 상점이용은 로그인을 요구합니다. router.use(require("../../middlewares/auth")); -router.get("/", publicNotice.test); router.get("/get-recent-transaction", publicNotice.getRecentTransaction); module.exports = router; diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 5b653bcb..4e229962 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -1,29 +1,46 @@ const { transactionModel } = require("../modules/stores/mongo"); +const { userModel } = require("../../modules/stores/mongo"); const logger = require("../../modules/logger"); const { - transactionPopulateOption, -} = require("../modules/populates/transactions"); + publicNoticePopulateOption, +} = require("../modules/populates/publicNotice"); -const test = (req, res) => { - console.log("test"); - res.status(200).json({ msg: "success" }); -}; - -const getRecentTransaction = async (req, res) => { +const getTransactions = async () => { try { const transactions = await transactionModel .find({ type: "use" }) .sort({ doneat: -1 }) .limit(5) - .populate(transactionPopulateOption) + .populate(publicNoticePopulateOption) .lean(); - let transactionListString = []; if (transactions) { + return await getTransactionsCallbackGetUser(transactions); + } else { + return undefined; + } + } catch (err) { + return undefined; + } +}; +const getTransactionsCallbackGetUser = async (transactions) => { + const users = await userModel.find(); + for (let user of users) { + for (let transaction of transactions) { + if (user._id.equals(transaction.userId)) { + transaction.id = user.id; + } + } + } + return transactions; +}; +const getRecentTransaction = async (req, res) => { + try { + let transactionListString = []; + await getTransactions(); + const transactions = await getTransactions(); + if (!!transactions) { transactions.forEach((item, index) => { let purchaceMessage = ""; - - console.log(item, index); - if (item.comment.includes("구매")) { purchaceMessage = "구입하셨습니다."; } else if (item.comment.includes("획득")) { @@ -31,15 +48,13 @@ const getRecentTransaction = async (req, res) => { } else { purchaceMessage = "획득하셨습니다."; } - transactionListString[index] = `${item.userId + transactionListString[index] = `${item.id .toString() - .slice(0, 2)}${"*".repeat(item.userId.length - 2)}"님께서 "${ - item.item - }"을(를) " ${purchaceMessage}`; - console.log(transactionListString[index]); + .slice(0, 2)}${"*".repeat(item.id.length - 2)}님께서 ${ + item.item.name + }을(를) ${purchaceMessage}`; }); console.log(transactionListString); - res.json({ transactionListString, }); @@ -57,6 +72,5 @@ const getRecentTransaction = async (req, res) => { }; module.exports = { - test, getRecentTransaction, }; From 73f82b3345f48564ffa60bdaaec9ae9e0b966e0b Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 14:38:22 +0900 Subject: [PATCH 05/67] Remove: routes/publicNotice.js auth middle ware deleted. --- src/lottery/modules/contracts/2023fall.js | 8 ++--- src/lottery/routes/events.js | 7 +++-- src/lottery/routes/publicNotice.js | 3 +- src/lottery/services/events.js | 37 +++++++++++++++-------- src/lottery/services/publicNotice.js | 1 - 5 files changed, 34 insertions(+), 22 deletions(-) diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 6cd91fe1..6840d502 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -106,12 +106,12 @@ const requestAdPushAgreementEvent = async () => { // TODO }; -const requestEventSharingOnInstagram = async () => { - // TODO +const requestEventSharingOnInstagram = async (userId) => { + return eventHandler(userId, "이벤트 인스타그램 스토리에 공유"); }; -const requestPurchaseSharingOnInstagram = async () => { - // TODO +const requestPurchaseSharingOnInstagram = async (userId) => { + return eventHandler(userId, "아이템 구매 후 인스타그램 스토리에 공유"); }; module.exports = { diff --git a/src/lottery/routes/events.js b/src/lottery/routes/events.js index 9f2e6b18..38d77cbd 100644 --- a/src/lottery/routes/events.js +++ b/src/lottery/routes/events.js @@ -3,7 +3,10 @@ const router = express.Router(); const events = require("../services/events"); router.use(require("../../middlewares/auth")); -router.post("/instagram/share-event", events.instagramEventShareHandler); -router.post("/instagram/share-purchase", events.instagramPurchaseShareHandler); +router.post("/instagram/share-event", events.instagramEventShareHandler()); +router.post( + "/instagram/share-purchase", + events.instagramPurchaseShareHandler() +); module.exports = router; diff --git a/src/lottery/routes/publicNotice.js b/src/lottery/routes/publicNotice.js index 6f5f20c3..7176b20c 100644 --- a/src/lottery/routes/publicNotice.js +++ b/src/lottery/routes/publicNotice.js @@ -2,8 +2,7 @@ const express = require("express"); const router = express.Router(); const publicNotice = require("../services/publicNotice"); -// 상점이용은 로그인을 요구합니다. -router.use(require("../../middlewares/auth")); +// 로그인 없이 공지를 볼 수 있습니다. router.get("/get-recent-transaction", publicNotice.getRecentTransaction); module.exports = router; diff --git a/src/lottery/services/events.js b/src/lottery/services/events.js index 8ea9bedc..4b4b3943 100644 --- a/src/lottery/services/events.js +++ b/src/lottery/services/events.js @@ -1,28 +1,39 @@ const logger = require("../../modules/logger"); -const eventHandler = require("../modules/events"); +const contracts = require("../modules/contracts/2023fall"); + // 인스타그램 스토리에 이벤트를 공유했을 때. const instagramEventShareHandler = async (req, res) => { try { - const userId = req.userOid; - const eventId = "650302f799c6f338d5ea5427"; - const transactionResult = await eventHandler(userId, eventId); - res.json({ result: !!transactionResult ? true : false }); + const contractsResult = contracts.requestEventSharingOnInstagram(); + if (contractsResult) { + res.json({ contractsResult }); + } else if (contractsResult === null) { + res.json({}); + } else { + logger.error(err); + res.json({ error: "Events/Insagram/Share-Event" }); + } } catch (err) { - logger.err(err); - res.status(500).json({ error: "Events/Insagram/Share-Event" }); + logger.error(err); + res.json({ error: "Events/Insagram/Share-Event" }); } }; // 인스타그램 스토리에 아이템 구매 내역을 공유했을 때. const instagramPurchaseShareHandler = async (req, res) => { try { - const userId = req.userOid; - const eventId = "6503030e99c6f338d5ea5433"; - const transactionResult = await eventHandler(userId, eventId); - res.json({ result: !!transactionResult ? true : false }); + const contractsResult = contracts.requestEventSharingOnInstagram(); + if (contractsResult) { + res.json({ contractsResult }); + } else if (contractsResult === null) { + res.json({}); + } else { + logger.error(err); + return { error: "Events/Insagram/Share-Purchase" }; + } } catch (err) { - logger.err(err); - res.status(500).json({ error: "Events/Insagram/Share-Purchase" }); + logger.error(err); + return { error: "Events/Insagram/Share-Purchase" }; } }; diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 4e229962..4863aa1f 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -54,7 +54,6 @@ const getRecentTransaction = async (req, res) => { item.item.name }을(를) ${purchaceMessage}`; }); - console.log(transactionListString); res.json({ transactionListString, }); From d5d3450c57d203b05c49753c40899f538346aeb5 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 19:55:25 +0900 Subject: [PATCH 06/67] ADD: Merging process... --- src/lottery/index.js | 4 ---- src/lottery/routes/docs/swaggerDocs.js | 12 ------------ src/lottery/services/events.js | 4 ++-- 3 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/lottery/index.js b/src/lottery/index.js index 2ad979ca..2329d28d 100644 --- a/src/lottery/index.js +++ b/src/lottery/index.js @@ -21,10 +21,6 @@ lotteryRouter.use(require("../middlewares/originValidator")); lotteryRouter.use("/events", require("./routes/events")); lotteryRouter.use("/global-state", require("./routes/globalState")); lotteryRouter.use("/items", require("./routes/items")); -<<<<<<< HEAD -lotteryRouter.use("/transactions", require("./routes/transactions")); -======= ->>>>>>> dev lotteryRouter.use("/public-notice", require("./routes/publicNotice")); const resources = [ diff --git a/src/lottery/routes/docs/swaggerDocs.js b/src/lottery/routes/docs/swaggerDocs.js index b0ec1912..a780293d 100644 --- a/src/lottery/routes/docs/swaggerDocs.js +++ b/src/lottery/routes/docs/swaggerDocs.js @@ -2,11 +2,7 @@ const { eventMode } = require("../../../../loadenv"); const globalStateDocs = require("./globalState"); const itemsDocs = require("./items"); const transactionsDocs = require("./transactions"); -<<<<<<< HEAD const eventsDocs = require("./events"); -======= -const itemsSchema = require("./itemsSchema"); ->>>>>>> dev const publicNoticeDocs = require("./publicNotice"); const apiPrefix = `/events/${eventMode}`; @@ -26,27 +22,19 @@ const eventSwaggerDocs = { description: "이벤트 - 입출금 내역 관련 API", }, { -<<<<<<< HEAD name: `${apiPrefix}/instagram`, description: "이벤트 - 인스타그램 이벤트 관련 API", }, { name: `${apiPrefix}/public-notice`, description: "이벤트 - 아이템 구매, 뽑기, 획득 공지 관련 API", -======= - name: `${apiPrefix}/public-notice`, - description: "이벤트 - 공지사항 관련 API", ->>>>>>> dev }, ], paths: { ...globalStateDocs, ...itemsDocs, ...transactionsDocs, -<<<<<<< HEAD ...eventsDocs, -======= ->>>>>>> dev ...publicNoticeDocs, }, components: { diff --git a/src/lottery/services/events.js b/src/lottery/services/events.js index 4b4b3943..8420fe15 100644 --- a/src/lottery/services/events.js +++ b/src/lottery/services/events.js @@ -4,7 +4,7 @@ const contracts = require("../modules/contracts/2023fall"); // 인스타그램 스토리에 이벤트를 공유했을 때. const instagramEventShareHandler = async (req, res) => { try { - const contractsResult = contracts.requestEventSharingOnInstagram(); + const contractsResult = contracts.completeEventSharingOnInstagramQuest(); if (contractsResult) { res.json({ contractsResult }); } else if (contractsResult === null) { @@ -22,7 +22,7 @@ const instagramEventShareHandler = async (req, res) => { // 인스타그램 스토리에 아이템 구매 내역을 공유했을 때. const instagramPurchaseShareHandler = async (req, res) => { try { - const contractsResult = contracts.requestEventSharingOnInstagram(); + const contractsResult = contracts.completePurchaseSharingOnInstagramQuest(); if (contractsResult) { res.json({ contractsResult }); } else if (contractsResult === null) { From f5fb681f5fb5812459421bcafc1c05e62b7640bf Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 19:57:47 +0900 Subject: [PATCH 07/67] ADD: Merging process... --- src/lottery/routes/docs/publicNotice.js | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index 32d3b6a6..cda93fa1 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -2,28 +2,6 @@ const { eventMode } = require("../../../../loadenv"); const apiPrefix = `/events/${eventMode}/public-notice`; const publicNoticeDocs = {}; -<<<<<<< HEAD -publicNoticeDocs[`${apiPrefix}/get-recent-transaction`] = { - get: { - tags: [`${apiPrefix}`], - summary: "공지를 띄움", - description: "상품 구매, 뽑기 획득 등등에 대한 공지를 반환", - responses: { - 200: { - content: { - "application/json": { - schema: { - type: "array", - description: - "use 트랜젝션의 정보를 담은 string 배열. 구매자의 익명 이름과 구매 내역을 문자열로 변환 후 이를 배열로 담아 5개 반환", - example: [ - "su****님께서 매점2000원권을(를) 구입하셨습니다.", - "mo****님께서 치킨을(를) 구입하셨습니다.", - "tu*****님께서 매점2000원권을(를) 구입하셨습니다.", - "as**님께서 진짜송편을(를) 구입하셨습니다.", - "qw**님께서 전동킥보드을(를) 뽑았습니다.", - ], -======= publicNoticeDocs[`${apiPrefix}/leaderboard`] = { get: { tags: [`${apiPrefix}`], @@ -91,7 +69,6 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = { example: 0.00003, }, }, ->>>>>>> dev }, }, }, From ac10f658565640a8befa011d1d3af99432d47aa5 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 20:05:24 +0900 Subject: [PATCH 08/67] ADD: Merging process... --- src/lottery/routes/docs/swaggerDocs.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/lottery/routes/docs/swaggerDocs.js b/src/lottery/routes/docs/swaggerDocs.js index a780293d..f2a2e5ef 100644 --- a/src/lottery/routes/docs/swaggerDocs.js +++ b/src/lottery/routes/docs/swaggerDocs.js @@ -3,6 +3,7 @@ const globalStateDocs = require("./globalState"); const itemsDocs = require("./items"); const transactionsDocs = require("./transactions"); const eventsDocs = require("./events"); +const itemsSchema = require("./itemsSchema"); const publicNoticeDocs = require("./publicNotice"); const apiPrefix = `/events/${eventMode}`; @@ -29,6 +30,10 @@ const eventSwaggerDocs = { name: `${apiPrefix}/public-notice`, description: "이벤트 - 아이템 구매, 뽑기, 획득 공지 관련 API", }, + { + name: `${apiPrefix}/public-notice`, + description: "이벤트 - 공지사항 관련 API", + }, ], paths: { ...globalStateDocs, From c50556153f89d1fe5861ef853e9243a0697e8e5d Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 20:13:53 +0900 Subject: [PATCH 09/67] ADD: Mergin process... --- src/lottery/services/events.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lottery/services/events.js b/src/lottery/services/events.js index 8420fe15..6cba4de2 100644 --- a/src/lottery/services/events.js +++ b/src/lottery/services/events.js @@ -4,11 +4,11 @@ const contracts = require("../modules/contracts/2023fall"); // 인스타그램 스토리에 이벤트를 공유했을 때. const instagramEventShareHandler = async (req, res) => { try { - const contractsResult = contracts.completeEventSharingOnInstagramQuest(); + const contractsResult = contracts.completeEventSharingOnInstagramQuest(req.userOid); if (contractsResult) { res.json({ contractsResult }); } else if (contractsResult === null) { - res.json({}); + res.json({ contractsResult }); } else { logger.error(err); res.json({ error: "Events/Insagram/Share-Event" }); @@ -22,11 +22,11 @@ const instagramEventShareHandler = async (req, res) => { // 인스타그램 스토리에 아이템 구매 내역을 공유했을 때. const instagramPurchaseShareHandler = async (req, res) => { try { - const contractsResult = contracts.completePurchaseSharingOnInstagramQuest(); + const contractsResult = contracts.completePurchaseSharingOnInstagramQuest(req.userOid); if (contractsResult) { res.json({ contractsResult }); } else if (contractsResult === null) { - res.json({}); + res.json({ contractsResult }); } else { logger.error(err); return { error: "Events/Insagram/Share-Purchase" }; From 326d9d2dfe4176b882c53c36bdc0919e32ef5fc1 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 22:18:32 +0900 Subject: [PATCH 10/67] ADD: Merging process... --- src/lottery/index.js | 1 + src/lottery/modules/contracts/2023fall.js | 5 ++++ src/lottery/routes/publicNotice.js | 6 ++++ src/lottery/routes/{events.js => quests.js} | 2 +- src/lottery/services/quests.js | 31 +++++++++++++++++++++ 5 files changed, 44 insertions(+), 1 deletion(-) rename src/lottery/routes/{events.js => quests.js} (86%) create mode 100644 src/lottery/services/quests.js diff --git a/src/lottery/index.js b/src/lottery/index.js index 2329d28d..02a8cb23 100644 --- a/src/lottery/index.js +++ b/src/lottery/index.js @@ -22,6 +22,7 @@ lotteryRouter.use("/events", require("./routes/events")); lotteryRouter.use("/global-state", require("./routes/globalState")); lotteryRouter.use("/items", require("./routes/items")); lotteryRouter.use("/public-notice", require("./routes/publicNotice")); +lotteryRouter.use("/quests", require("./routes/quests")); const resources = [ eventStatusModel, diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 2665d84f..4899bf49 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -193,8 +193,13 @@ const completeEventSharingOnInstagramQuest = async (userId) => { return await completeQuest(userId, eventPeriod, quests.eventSharingOnInstagram); }; +<<<<<<< HEAD const completePurchaseSharingOnInstagramQuest = async (userId) => { return await completeQuest(userId, eventPeriod, quests.purchaseSharingOnInstagram); +======= +const completePurchaseSharingOnInstagramQuest = async () => { + return await completeQuest(userId, eventPeriod, quests.purchaseSharingOnInstagram) +>>>>>>> 6148984 (ADD: Merging process...) }; module.exports = { diff --git a/src/lottery/routes/publicNotice.js b/src/lottery/routes/publicNotice.js index dd86aeee..f8a8cade 100644 --- a/src/lottery/routes/publicNotice.js +++ b/src/lottery/routes/publicNotice.js @@ -10,7 +10,13 @@ router.get("/get-recent-transaction", publicNotice.getRecentTransaction); const router = express.Router(); const publicNoticeHandlers = require("../services/publicNotice"); +<<<<<<< HEAD router.get("/leaderboard", publicNoticeHandlers.getTicketLeaderboardHandler); >>>>>>> dev +======= +// 상점 공지는 로그인을 요구하지 않습니다. +router.get("/recent-transactions", publicNoticeHandlers.getRecentTransaction); +router.get("/leaderboard", publicNoticeHandlers.getTicketLeaderboardHandler); +>>>>>>> 6148984 (ADD: Merging process...) module.exports = router; diff --git a/src/lottery/routes/events.js b/src/lottery/routes/quests.js similarity index 86% rename from src/lottery/routes/events.js rename to src/lottery/routes/quests.js index 38d77cbd..5f6bfd50 100644 --- a/src/lottery/routes/events.js +++ b/src/lottery/routes/quests.js @@ -1,6 +1,6 @@ const express = require("express"); const router = express.Router(); -const events = require("../services/events"); +const events = require("../services/quests"); router.use(require("../../middlewares/auth")); router.post("/instagram/share-event", events.instagramEventShareHandler()); diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js new file mode 100644 index 00000000..c1e6715e --- /dev/null +++ b/src/lottery/services/quests.js @@ -0,0 +1,31 @@ +const logger = require("../../modules/logger"); +const contracts = require("../modules/contracts/2023fall"); + +// 인스타그램 스토리에 이벤트를 공유했을 때. +const instagramEventShareHandler = async (req, res) => { + try { + const userId = req.userOid; + const contractResult = await contracts.completeEventSharingOnInstagramQuest(userId); + res.json({ result: !!contractResult ? true : false }); + } catch (err) { + logger.error(err); + res.status(500).json({ error: "Quests/Insagram/Share-Event" }); + } +}; + +// 인스타그램 스토리에 아이템 구매 내역을 공유했을 때. +const instagramPurchaseShareHandler = async (req, res) => { + try { + const userId = req.userOid; + const contractResult = await contracts.completePurchaseSharingOnInstagramQuest(userId); + res.json({ result: !!contractResult ? true : false }); + } catch (err) { + logger.error(err); + res.status(500).json({ error: "Quests/Insagram/Share-Purchase" }); + } +}; + +module.exports = { + instagramEventShareHandler, + instagramPurchaseShareHandler, +}; From 99cc4147e9fcc8152796d891469edb0a6f301aa4 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 22:18:32 +0900 Subject: [PATCH 11/67] ADD: Merging process... --- src/lottery/modules/contracts/2023fall.js | 18 ++++++++++-------- src/lottery/routes/publicNotice.js | 22 ++++++---------------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 4899bf49..2b534a87 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -190,16 +190,18 @@ const completeAdPushAgreementQuest = async () => { }; const completeEventSharingOnInstagramQuest = async (userId) => { - return await completeQuest(userId, eventPeriod, quests.eventSharingOnInstagram); + return await completeQuest( + userId, + eventPeriod, + quests.eventSharingOnInstagram + ); }; - -<<<<<<< HEAD const completePurchaseSharingOnInstagramQuest = async (userId) => { - return await completeQuest(userId, eventPeriod, quests.purchaseSharingOnInstagram); -======= -const completePurchaseSharingOnInstagramQuest = async () => { - return await completeQuest(userId, eventPeriod, quests.purchaseSharingOnInstagram) ->>>>>>> 6148984 (ADD: Merging process...) + return await completeQuest( + userId, + eventPeriod, + quests.purchaseSharingOnInstagram + ); }; module.exports = { diff --git a/src/lottery/routes/publicNotice.js b/src/lottery/routes/publicNotice.js index f8a8cade..620d077a 100644 --- a/src/lottery/routes/publicNotice.js +++ b/src/lottery/routes/publicNotice.js @@ -1,22 +1,12 @@ const express = require("express"); -<<<<<<< HEAD -const router = express.Router(); -const publicNotice = require("../services/publicNotice"); - -// 로그인 없이 공지를 볼 수 있습니다. -router.get("/get-recent-transaction", publicNotice.getRecentTransaction); -======= - const router = express.Router(); const publicNoticeHandlers = require("../services/publicNotice"); -<<<<<<< HEAD -router.get("/leaderboard", publicNoticeHandlers.getTicketLeaderboardHandler); ->>>>>>> dev -======= -// 상점 공지는 로그인을 요구하지 않습니다. -router.get("/recent-transactions", publicNoticeHandlers.getRecentTransaction); -router.get("/leaderboard", publicNoticeHandlers.getTicketLeaderboardHandler); ->>>>>>> 6148984 (ADD: Merging process...) +// 상점이용은 로그인을 요구합니다. +router.use(require("../../middlewares/auth")); +router.get( + "/get-recent-transaction", + publicNoticeHandlers.getRecentTransaction +); module.exports = router; From 6fd370ab82a7a779064e77e9da05f8121ee17d6d Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 22:37:49 +0900 Subject: [PATCH 12/67] ADD: Merging process...... --- src/lottery/index.js | 1 - src/lottery/routes/publicNotice.js | 3 +-- src/lottery/routes/quests.js | 6 ++--- src/lottery/services/events.js | 43 ------------------------------ 4 files changed, 4 insertions(+), 49 deletions(-) delete mode 100644 src/lottery/services/events.js diff --git a/src/lottery/index.js b/src/lottery/index.js index 02a8cb23..5777b4e4 100644 --- a/src/lottery/index.js +++ b/src/lottery/index.js @@ -18,7 +18,6 @@ const lotteryRouter = express.Router(); lotteryRouter.use(require("../middlewares/originValidator")); // [Router] APIs -lotteryRouter.use("/events", require("./routes/events")); lotteryRouter.use("/global-state", require("./routes/globalState")); lotteryRouter.use("/items", require("./routes/items")); lotteryRouter.use("/public-notice", require("./routes/publicNotice")); diff --git a/src/lottery/routes/publicNotice.js b/src/lottery/routes/publicNotice.js index 620d077a..6481ae12 100644 --- a/src/lottery/routes/publicNotice.js +++ b/src/lottery/routes/publicNotice.js @@ -2,8 +2,7 @@ const express = require("express"); const router = express.Router(); const publicNoticeHandlers = require("../services/publicNotice"); -// 상점이용은 로그인을 요구합니다. -router.use(require("../../middlewares/auth")); +// 상점 공지 이용은 로그인을 요구하지 않습니다. router.get( "/get-recent-transaction", publicNoticeHandlers.getRecentTransaction diff --git a/src/lottery/routes/quests.js b/src/lottery/routes/quests.js index 5f6bfd50..45c662cd 100644 --- a/src/lottery/routes/quests.js +++ b/src/lottery/routes/quests.js @@ -1,12 +1,12 @@ const express = require("express"); const router = express.Router(); -const events = require("../services/quests"); +const quests = require("../services/quests"); router.use(require("../../middlewares/auth")); -router.post("/instagram/share-event", events.instagramEventShareHandler()); +router.post("/instagram/share-event", quests.instagramEventShareHandler()); router.post( "/instagram/share-purchase", - events.instagramPurchaseShareHandler() + quests.instagramPurchaseShareHandler() ); module.exports = router; diff --git a/src/lottery/services/events.js b/src/lottery/services/events.js deleted file mode 100644 index 6cba4de2..00000000 --- a/src/lottery/services/events.js +++ /dev/null @@ -1,43 +0,0 @@ -const logger = require("../../modules/logger"); -const contracts = require("../modules/contracts/2023fall"); - -// 인스타그램 스토리에 이벤트를 공유했을 때. -const instagramEventShareHandler = async (req, res) => { - try { - const contractsResult = contracts.completeEventSharingOnInstagramQuest(req.userOid); - if (contractsResult) { - res.json({ contractsResult }); - } else if (contractsResult === null) { - res.json({ contractsResult }); - } else { - logger.error(err); - res.json({ error: "Events/Insagram/Share-Event" }); - } - } catch (err) { - logger.error(err); - res.json({ error: "Events/Insagram/Share-Event" }); - } -}; - -// 인스타그램 스토리에 아이템 구매 내역을 공유했을 때. -const instagramPurchaseShareHandler = async (req, res) => { - try { - const contractsResult = contracts.completePurchaseSharingOnInstagramQuest(req.userOid); - if (contractsResult) { - res.json({ contractsResult }); - } else if (contractsResult === null) { - res.json({ contractsResult }); - } else { - logger.error(err); - return { error: "Events/Insagram/Share-Purchase" }; - } - } catch (err) { - logger.error(err); - return { error: "Events/Insagram/Share-Purchase" }; - } -}; - -module.exports = { - instagramEventShareHandler, - instagramPurchaseShareHandler, -}; From fecbbc1ee5d3afd94b16b98fd219fa521b683663 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 22:46:12 +0900 Subject: [PATCH 13/67] ADD: Merging process! --- src/lottery/index.js | 1 + src/lottery/modules/contracts/2023fall.js | 3 ++- src/lottery/routes/publicNotice.js | 9 ++++----- src/lottery/services/quests.js | 7 +++++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/lottery/index.js b/src/lottery/index.js index 5777b4e4..38a99dce 100644 --- a/src/lottery/index.js +++ b/src/lottery/index.js @@ -19,6 +19,7 @@ lotteryRouter.use(require("../middlewares/originValidator")); // [Router] APIs lotteryRouter.use("/global-state", require("./routes/globalState")); +lotteryRouter.use("/transactions", require("./routes/transactions")); lotteryRouter.use("/items", require("./routes/items")); lotteryRouter.use("/public-notice", require("./routes/publicNotice")); lotteryRouter.use("/quests", require("./routes/quests")); diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 2b534a87..8bc6350a 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -196,7 +196,8 @@ const completeEventSharingOnInstagramQuest = async (userId) => { quests.eventSharingOnInstagram ); }; -const completePurchaseSharingOnInstagramQuest = async (userId) => { + +const completePurchaseSharingOnInstagramQuest = async () => { return await completeQuest( userId, eventPeriod, diff --git a/src/lottery/routes/publicNotice.js b/src/lottery/routes/publicNotice.js index 6481ae12..8f98e5bf 100644 --- a/src/lottery/routes/publicNotice.js +++ b/src/lottery/routes/publicNotice.js @@ -1,11 +1,10 @@ const express = require("express"); + const router = express.Router(); const publicNoticeHandlers = require("../services/publicNotice"); -// 상점 공지 이용은 로그인을 요구하지 않습니다. -router.get( - "/get-recent-transaction", - publicNoticeHandlers.getRecentTransaction -); +// 상점 공지는 로그인을 요구하지 않습니다. +router.get("/recent-transactions", publicNoticeHandlers.getRecentTransaction); +router.get("/leaderboard", publicNoticeHandlers.getTicketLeaderboardHandler); module.exports = router; diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js index c1e6715e..ffe913cd 100644 --- a/src/lottery/services/quests.js +++ b/src/lottery/services/quests.js @@ -5,7 +5,9 @@ const contracts = require("../modules/contracts/2023fall"); const instagramEventShareHandler = async (req, res) => { try { const userId = req.userOid; - const contractResult = await contracts.completeEventSharingOnInstagramQuest(userId); + const contractResult = await contracts.completeEventSharingOnInstagramQuest( + userId + ); res.json({ result: !!contractResult ? true : false }); } catch (err) { logger.error(err); @@ -17,7 +19,8 @@ const instagramEventShareHandler = async (req, res) => { const instagramPurchaseShareHandler = async (req, res) => { try { const userId = req.userOid; - const contractResult = await contracts.completePurchaseSharingOnInstagramQuest(userId); + const contractResult = + await contracts.completePurchaseSharingOnInstagramQuest(userId); res.json({ result: !!contractResult ? true : false }); } catch (err) { logger.error(err); From 19e57970c3a0c829615fbaf4d4f2a1ed399d6ce9 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 22:47:55 +0900 Subject: [PATCH 14/67] ADD: Merging process!!! --- src/lottery/services/publicNotice.js | 72 +++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index ac4b2b9d..8e8227d9 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -1,7 +1,76 @@ +const { transactionModel } = require("../modules/stores/mongo"); const { eventStatusModel } = require("../modules/stores/mongo"); const { userModel } = require("../../modules/stores/mongo"); const logger = require("../../modules/logger"); -const { isLogin, getLoginInfo } = require("../../modules/auths/login"); +const { + publicNoticePopulateOption, +} = require("../modules/populates/publicNotice"); + +const getTransactions = async () => { + try { + const transactions = await transactionModel + .find({ type: "use" }) + .sort({ doneat: -1 }) + .limit(5) + .populate(publicNoticePopulateOption) + .lean(); + if (transactions) { + return await getTransactionsCallbackGetUser(transactions); + } else { + return undefined; + } + } catch (err) { + return undefined; + } +}; +const getTransactionsCallbackGetUser = async (transactions) => { + const users = await userModel.find(); + for (let user of users) { + for (let transaction of transactions) { + if (user._id.equals(transaction.userId)) { + transaction.id = user.id; + } + } + } + return transactions; +}; +const getRecentTransaction = async (req, res) => { + try { + let transactionListString = []; + await getTransactions(); + const transactions = await getTransactions(); + if (!!transactions) { + transactions.forEach((item, index) => { + let purchaceMessage = ""; + if (item.comment.includes("구매")) { + purchaceMessage = "구입하셨습니다."; + } else if (item.comment.includes("획득")) { + purchaceMessage = "뽑았습니다."; + } else { + purchaceMessage = "획득하셨습니다."; + } + transactionListString[index] = `${item.id + .toString() + .slice(0, 2)}${"*".repeat(item.id.length - 2)}님께서 ${ + item.item.name + }을(를) ${purchaceMessage}`; + }); + console.log(transactionListString); + res.json({ + transactionListString, + }); + } else { + res.status(500).json({ + error: "PublicNotice/get-recent-transaction : internal server error", + }); + } + } catch (err) { + logger.error(err); + res.status(500).json({ + error: "PublicNotice/get-recent-transaction : internal server error", + }); + } +}; const getTicketLeaderboardHandler = async (req, res) => { try { @@ -67,5 +136,6 @@ const getTicketLeaderboardHandler = async (req, res) => { }; module.exports = { + getRecentTransaction, getTicketLeaderboardHandler, }; From 47d9ce5964f5168d325ccf09c7abf29fe9ec41cd Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 22:54:27 +0900 Subject: [PATCH 15/67] ADD: Temporary commit --- src/lottery/routes/quests.js | 7 ++----- src/lottery/services/publicNotice.js | 1 + 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/src/lottery/routes/quests.js b/src/lottery/routes/quests.js index 45c662cd..a522596a 100644 --- a/src/lottery/routes/quests.js +++ b/src/lottery/routes/quests.js @@ -3,10 +3,7 @@ const router = express.Router(); const quests = require("../services/quests"); router.use(require("../../middlewares/auth")); -router.post("/instagram/share-event", quests.instagramEventShareHandler()); -router.post( - "/instagram/share-purchase", - quests.instagramPurchaseShareHandler() -); +router.post("/instagram/share-event", quests.instagramEventShareHandler); +router.post("/instagram/share-purchase", quests.instagramPurchaseShareHandler); module.exports = router; diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 8e8227d9..8d402799 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -36,6 +36,7 @@ const getTransactionsCallbackGetUser = async (transactions) => { }; const getRecentTransaction = async (req, res) => { try { + console.log("[CALLED]"); let transactionListString = []; await getTransactions(); const transactions = await getTransactions(); From a5d88f25d78227e00eecef4842e24161160e13d8 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Mon, 18 Sep 2023 23:31:03 +0900 Subject: [PATCH 16/67] ADD: Merging process~! --- src/lottery/modules/populates/publicNotice.js | 1 - src/lottery/services/publicNotice.js | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lottery/modules/populates/publicNotice.js b/src/lottery/modules/populates/publicNotice.js index 5709ed7d..a821bfd5 100644 --- a/src/lottery/modules/populates/publicNotice.js +++ b/src/lottery/modules/populates/publicNotice.js @@ -1,5 +1,4 @@ const publicNoticePopulateOption = [ - { path: "event" }, { path: "item", select: "name price description itemType", diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 8d402799..a2bc53bb 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -1,6 +1,7 @@ const { transactionModel } = require("../modules/stores/mongo"); const { eventStatusModel } = require("../modules/stores/mongo"); const { userModel } = require("../../modules/stores/mongo"); +const { isLogin, getLoginInfo } = require("../../modules/auths/login"); const logger = require("../../modules/logger"); const { publicNoticePopulateOption, @@ -36,7 +37,6 @@ const getTransactionsCallbackGetUser = async (transactions) => { }; const getRecentTransaction = async (req, res) => { try { - console.log("[CALLED]"); let transactionListString = []; await getTransactions(); const transactions = await getTransactions(); @@ -56,7 +56,6 @@ const getRecentTransaction = async (req, res) => { item.item.name }을(를) ${purchaceMessage}`; }); - console.log(transactionListString); res.json({ transactionListString, }); From 52108698057515934415797b568f901fcd81263b Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Tue, 19 Sep 2023 00:47:29 +0900 Subject: [PATCH 17/67] Add: TransactionSchema itemType attribute added. --- src/lottery/modules/populates/publicNotice.js | 4 +++ src/lottery/modules/stores/mongo.js | 4 +++ .../routes/docs/{events.js => quests.js} | 0 src/lottery/routes/docs/swaggerDocs.js | 8 ++--- src/lottery/routes/quests.js | 1 + src/lottery/services/items.js | 2 ++ src/lottery/services/publicNotice.js | 35 ++++--------------- 7 files changed, 19 insertions(+), 35 deletions(-) rename src/lottery/routes/docs/{events.js => quests.js} (100%) diff --git a/src/lottery/modules/populates/publicNotice.js b/src/lottery/modules/populates/publicNotice.js index a821bfd5..d662e5fa 100644 --- a/src/lottery/modules/populates/publicNotice.js +++ b/src/lottery/modules/populates/publicNotice.js @@ -1,4 +1,8 @@ const publicNoticePopulateOption = [ + { + path: "userId", + select: "id", + }, { path: "item", select: "name price description itemType", diff --git a/src/lottery/modules/stores/mongo.js b/src/lottery/modules/stores/mongo.js index 296db681..2e32978d 100644 --- a/src/lottery/modules/stores/mongo.js +++ b/src/lottery/modules/stores/mongo.js @@ -118,6 +118,10 @@ const transactionSchema = Schema({ type: Schema.Types.ObjectId, ref: "Item", }, + itemType: { + type: Number, + enum: [0, 1, 2, 3], + }, comment: { type: String, required: true, diff --git a/src/lottery/routes/docs/events.js b/src/lottery/routes/docs/quests.js similarity index 100% rename from src/lottery/routes/docs/events.js rename to src/lottery/routes/docs/quests.js diff --git a/src/lottery/routes/docs/swaggerDocs.js b/src/lottery/routes/docs/swaggerDocs.js index f2a2e5ef..8136a470 100644 --- a/src/lottery/routes/docs/swaggerDocs.js +++ b/src/lottery/routes/docs/swaggerDocs.js @@ -2,7 +2,7 @@ const { eventMode } = require("../../../../loadenv"); const globalStateDocs = require("./globalState"); const itemsDocs = require("./items"); const transactionsDocs = require("./transactions"); -const eventsDocs = require("./events"); +const questsDocs = require("./quests"); const itemsSchema = require("./itemsSchema"); const publicNoticeDocs = require("./publicNotice"); @@ -30,16 +30,12 @@ const eventSwaggerDocs = { name: `${apiPrefix}/public-notice`, description: "이벤트 - 아이템 구매, 뽑기, 획득 공지 관련 API", }, - { - name: `${apiPrefix}/public-notice`, - description: "이벤트 - 공지사항 관련 API", - }, ], paths: { ...globalStateDocs, ...itemsDocs, ...transactionsDocs, - ...eventsDocs, + ...questsDocs, ...publicNoticeDocs, }, components: { diff --git a/src/lottery/routes/quests.js b/src/lottery/routes/quests.js index a522596a..f8cf8177 100644 --- a/src/lottery/routes/quests.js +++ b/src/lottery/routes/quests.js @@ -3,6 +3,7 @@ const router = express.Router(); const quests = require("../services/quests"); router.use(require("../../middlewares/auth")); + router.post("/instagram/share-event", quests.instagramEventShareHandler); router.post("/instagram/share-purchase", quests.instagramPurchaseShareHandler); diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index 36b4bf10..1a11e5ba 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -91,6 +91,7 @@ const getRandomItem = async (req, depth) => { amount: 0, userId: req.userOid, item: randomItem._id, + itemType: randomItem.itemType, comment: `랜덤 박스에서 "${randomItem.name}" 1개를 획득했습니다.`, }); await transaction.save(); @@ -173,6 +174,7 @@ const purchaseHandler = async (req, res) => { amount: item.price, userId: req.userOid, item: item._id, + itemType: item.itemType, comment: `송편 ${item.price}개를 사용해 "${item.name}" 1개를 획득했습니다.`, }); await transaction.save(); diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index a2bc53bb..b86406f7 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -7,39 +7,15 @@ const { publicNoticePopulateOption, } = require("../modules/populates/publicNotice"); -const getTransactions = async () => { +const getRecentTransaction = async (req, res) => { try { + let transactionListString = []; const transactions = await transactionModel .find({ type: "use" }) - .sort({ doneat: -1 }) + .sort({ createAt: -1 }) .limit(5) .populate(publicNoticePopulateOption) .lean(); - if (transactions) { - return await getTransactionsCallbackGetUser(transactions); - } else { - return undefined; - } - } catch (err) { - return undefined; - } -}; -const getTransactionsCallbackGetUser = async (transactions) => { - const users = await userModel.find(); - for (let user of users) { - for (let transaction of transactions) { - if (user._id.equals(transaction.userId)) { - transaction.id = user.id; - } - } - } - return transactions; -}; -const getRecentTransaction = async (req, res) => { - try { - let transactionListString = []; - await getTransactions(); - const transactions = await getTransactions(); if (!!transactions) { transactions.forEach((item, index) => { let purchaceMessage = ""; @@ -50,12 +26,13 @@ const getRecentTransaction = async (req, res) => { } else { purchaceMessage = "획득하셨습니다."; } - transactionListString[index] = `${item.id + transactionListString[index] = `${item.userId.id .toString() - .slice(0, 2)}${"*".repeat(item.id.length - 2)}님께서 ${ + .slice(0, 2)}${"*".repeat(item.userId.id.length - 2)}님께서 ${ item.item.name }을(를) ${purchaceMessage}`; }); + console.log(transactionListString); res.json({ transactionListString, }); From 4a93b09bc5b492b4df6c0b8cfd9296795ff732ed Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Tue, 19 Sep 2023 22:05:44 +0900 Subject: [PATCH 18/67] Add: Resolve comments --- src/lottery/routes/docs/quests.js | 8 +++----- src/lottery/routes/publicNotice.js | 5 ++++- src/lottery/services/publicNotice.js | 8 ++++---- src/lottery/services/quests.js | 14 +++++++++----- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/src/lottery/routes/docs/quests.js b/src/lottery/routes/docs/quests.js index 9998e4ce..2663f277 100644 --- a/src/lottery/routes/docs/quests.js +++ b/src/lottery/routes/docs/quests.js @@ -1,8 +1,8 @@ const { eventMode } = require("../../../../loadenv"); -const apiPrefix = `/events/${eventMode}/instagram`; +const apiPrefix = `/events/${eventMode}/quests`; const eventsDocs = {}; -eventsDocs[`${apiPrefix}/share-event`] = { +eventsDocs[`${apiPrefix}/instagram/share-event`] = { post: { tags: [`${apiPrefix}`], summary: "이벤트 공유시 보상 반환", @@ -19,12 +19,11 @@ eventsDocs[`${apiPrefix}/share-event`] = { }, }, }, - 500: { error: "Events/Insagram/Share-Event" }, }, }, }; -eventsDocs[`${apiPrefix}/share-purchase`] = { +eventsDocs[`${apiPrefix}/instagram/share-purchase`] = { post: { tags: [`${apiPrefix}`], summary: "이벤트 공유시 보상 반환", @@ -41,7 +40,6 @@ eventsDocs[`${apiPrefix}/share-purchase`] = { }, }, }, - 500: { error: "Events/Insagram/Share-Purchase" }, }, }, }; diff --git a/src/lottery/routes/publicNotice.js b/src/lottery/routes/publicNotice.js index 8f98e5bf..79fc17ab 100644 --- a/src/lottery/routes/publicNotice.js +++ b/src/lottery/routes/publicNotice.js @@ -4,7 +4,10 @@ const router = express.Router(); const publicNoticeHandlers = require("../services/publicNotice"); // 상점 공지는 로그인을 요구하지 않습니다. -router.get("/recent-transactions", publicNoticeHandlers.getRecentTransaction); +router.get( + "/recent-transactions", + publicNoticeHandlers.getRecentPurchaceItemListHandler +); router.get("/leaderboard", publicNoticeHandlers.getTicketLeaderboardHandler); module.exports = router; diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index b86406f7..930df9e3 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -7,7 +7,7 @@ const { publicNoticePopulateOption, } = require("../modules/populates/publicNotice"); -const getRecentTransaction = async (req, res) => { +const getRecentPurchaceItemListHandler = async (req, res) => { try { let transactionListString = []; const transactions = await transactionModel @@ -19,9 +19,9 @@ const getRecentTransaction = async (req, res) => { if (!!transactions) { transactions.forEach((item, index) => { let purchaceMessage = ""; - if (item.comment.includes("구매")) { + if (item.comment.startsWith("송편")) { purchaceMessage = "구입하셨습니다."; - } else if (item.comment.includes("획득")) { + } else if (item.comment.startsWith("랜덤 박스")) { purchaceMessage = "뽑았습니다."; } else { purchaceMessage = "획득하셨습니다."; @@ -113,6 +113,6 @@ const getTicketLeaderboardHandler = async (req, res) => { }; module.exports = { - getRecentTransaction, + getRecentPurchaceItemListHandler, getTicketLeaderboardHandler, }; diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js index ffe913cd..68be355a 100644 --- a/src/lottery/services/quests.js +++ b/src/lottery/services/quests.js @@ -4,14 +4,16 @@ const contracts = require("../modules/contracts/2023fall"); // 인스타그램 스토리에 이벤트를 공유했을 때. const instagramEventShareHandler = async (req, res) => { try { - const userId = req.userOid; + const { userOid: userId } = req; const contractResult = await contracts.completeEventSharingOnInstagramQuest( userId ); - res.json({ result: !!contractResult ? true : false }); + res.json({ result: !!contractResult }); } catch (err) { logger.error(err); - res.status(500).json({ error: "Quests/Insagram/Share-Event" }); + res + .status(500) + .json({ error: "Quests/Instagram/ShareEvent: internal server error" }); } }; @@ -21,10 +23,12 @@ const instagramPurchaseShareHandler = async (req, res) => { const userId = req.userOid; const contractResult = await contracts.completePurchaseSharingOnInstagramQuest(userId); - res.json({ result: !!contractResult ? true : false }); + res.json({ result: !!contractResult }); } catch (err) { logger.error(err); - res.status(500).json({ error: "Quests/Insagram/Share-Purchase" }); + res + .status(500) + .json({ error: "Quests/Instagram/SharePurchase: internal server error" }); } }; From 73c5068c2bd0a4fcd47cef897c67537de4fed5a8 Mon Sep 17 00:00:00 2001 From: static Date: Tue, 19 Sep 2023 22:13:52 +0900 Subject: [PATCH 19/67] Add: eventConfig to .env --- app.js | 9 +++-- loadenv.js | 2 +- src/lottery/index.js | 7 ++-- src/lottery/modules/contracts/2023fall.js | 41 ++++------------------- src/lottery/modules/quests.js | 13 ++++--- src/lottery/routes/docs/globalState.js | 4 +-- src/lottery/routes/docs/items.js | 4 +-- src/lottery/routes/docs/publicNotice.js | 4 +-- src/lottery/routes/docs/swaggerDocs.js | 4 +-- src/lottery/routes/docs/transactions.js | 4 +-- src/lottery/services/globalState.js | 11 +++--- src/lottery/services/items.js | 14 +++++--- src/routes/admin.js | 4 +-- 13 files changed, 52 insertions(+), 69 deletions(-) diff --git a/app.js b/app.js index d8a1f0f3..689de7a9 100644 --- a/app.js +++ b/app.js @@ -1,7 +1,7 @@ // 모듈 require const express = require("express"); const http = require("http"); -const { port: httpPort, eventMode } = require("./loadenv"); +const { port: httpPort, eventConfig } = require("./loadenv"); const logger = require("./src/modules/logger"); const { connectDatabase } = require("./src/modules/stores/mongo"); const { startSocketServer } = require("./src/modules/socket"); @@ -43,8 +43,11 @@ app.use(require("./src/middlewares/limitRate")); app.use("/docs", require("./src/routes/docs")); // 2023 추석 이벤트 전용 라우터입니다. -eventMode && - app.use(`/events/${eventMode}`, require("./src/lottery").lotteryRouter); +eventConfig && + app.use( + `/events/${eventConfig.mode}`, + require("./src/lottery").lotteryRouter + ); // [Middleware] 모든 API 요청에 대하여 origin 검증 app.use(require("./src/middlewares/originValidator")); diff --git a/loadenv.js b/loadenv.js index 2bce4fb9..1f9528b6 100644 --- a/loadenv.js +++ b/loadenv.js @@ -38,5 +38,5 @@ module.exports = { slackWebhookUrl: { report: process.env.SLACK_REPORT_WEBHOOK_URL || "", // optional }, - eventMode: undefined, + eventConfig: process.env.EVENT_CONFIG && JSON.parse(process.env.EVENT_CONFIG), // optional }; diff --git a/src/lottery/index.js b/src/lottery/index.js index 7ca282fe..faed346b 100644 --- a/src/lottery/index.js +++ b/src/lottery/index.js @@ -6,11 +6,11 @@ const { transactionModel, } = require("./modules/stores/mongo"); -const { eventMode } = require("../../loadenv"); +const { eventConfig } = require("../../loadenv"); const { buildResource } = require("../modules/adminResource"); // [Routes] 기존 docs 라우터의 docs extend -require("./routes/docs")(); +eventConfig && require("./routes/docs")(); const lotteryRouter = express.Router(); @@ -30,7 +30,8 @@ const resources = [ transactionModel, ].map(buildResource()); -const contracts = eventMode && require(`./modules/contracts/${eventMode}`); +const contracts = + eventConfig && require(`./modules/contracts/${eventConfig.mode}`); module.exports = { lotteryRouter, diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index abe2f0d8..650c0d76 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -76,11 +76,6 @@ const quests = buildQuests({ }, }); -const eventPeriod = { - start: new Date("2023-09-25T00:00:00+09:00"), // Inclusive - end: new Date("2023-10-10T00:00:00+09:00"), // Exclusive -}; - /** * firstLogin 퀘스트의 완료를 요청합니다. * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. @@ -89,7 +84,7 @@ const eventPeriod = { * @usage lottery/globalState/createUserGlobalStateHandler */ const completeFirstLoginQuest = async (userId, timestamp) => { - return await completeQuest(userId, timestamp, eventPeriod, quests.firstLogin); + return await completeQuest(userId, timestamp, quests.firstLogin); }; /** @@ -112,7 +107,6 @@ const completePayingAndSendingQuest = async (timestamp, roomObject) => { await completeQuest( participant.user._id, timestamp, - eventPeriod, quests.payingAndSending ) ) @@ -128,12 +122,7 @@ const completePayingAndSendingQuest = async (timestamp, roomObject) => { * @usage rooms/createHandler */ const completeFirstRoomCreationQuest = async (userId, timestamp) => { - return await completeQuest( - userId, - timestamp, - eventPeriod, - quests.firstRoomCreation - ); + return await completeQuest(userId, timestamp, quests.firstRoomCreation); }; const completeRoomSharingQuest = async () => { @@ -153,7 +142,7 @@ const completeRoomSharingQuest = async () => { const completePayingQuest = async (userId, timestamp, roomObject) => { if (roomObject.part.length < 2) return null; - return await completeQuest(userId, timestamp, eventPeriod, quests.paying); + return await completeQuest(userId, timestamp, quests.paying); }; /** @@ -169,7 +158,7 @@ const completePayingQuest = async (userId, timestamp, roomObject) => { const completeSendingQuest = async (userId, timestamp, roomObject) => { if (roomObject.part.length < 2) return null; - return await completeQuest(userId, timestamp, eventPeriod, quests.sending); + return await completeQuest(userId, timestamp, quests.sending); }; /** @@ -181,12 +170,7 @@ const completeSendingQuest = async (userId, timestamp, roomObject) => { * @usage users/editNicknameHandler */ const completeNicknameChangingQuest = async (userId, timestamp) => { - return await completeQuest( - userId, - timestamp, - eventPeriod, - quests.nicknameChanging - ); + return await completeQuest(userId, timestamp, quests.nicknameChanging); }; /** @@ -201,12 +185,7 @@ const completeNicknameChangingQuest = async (userId, timestamp) => { const completeAccountChangingQuest = async (userId, timestamp, newAccount) => { if (newAccount === "") return null; - return await completeQuest( - userId, - timestamp, - eventPeriod, - quests.accountChanging - ); + return await completeQuest(userId, timestamp, quests.accountChanging); }; /** @@ -225,12 +204,7 @@ const completeAdPushAgreementQuest = async ( ) => { if (!advertisement) return null; - return await completeQuest( - userId, - timestamp, - eventPeriod, - quests.adPushAgreement - ); + return await completeQuest(userId, timestamp, quests.adPushAgreement); }; const completeEventSharingOnInstagramQuest = async () => { @@ -243,7 +217,6 @@ const completePurchaseSharingOnInstagramQuest = async () => { module.exports = { quests, - eventPeriod, completeFirstLoginQuest, completePayingAndSendingQuest, completeFirstRoomCreationQuest, diff --git a/src/lottery/modules/quests.js b/src/lottery/modules/quests.js index ce2d22d4..9aa5c77f 100644 --- a/src/lottery/modules/quests.js +++ b/src/lottery/modules/quests.js @@ -7,6 +7,12 @@ const { const logger = require("../../modules/logger"); const mongoose = require("mongoose"); +const { eventConfig } = require("../../../loadenv"); +const eventPeriod = { + startAt: new Date(eventConfig.startAt), + endAt: new Date(eventConfig.endAt), +}; + const requiredQuestFields = ["name", "description", "imageUrl", "reward"]; const buildQuests = (quests) => { for (const [id, quest] of Object.entries(quests)) { @@ -45,9 +51,6 @@ const buildQuests = (quests) => { * 퀘스트 완료를 요청합니다. * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. * @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다. - * @param {Object} eventPeriod - 이벤트의 기간입니다. - * @param {Date} eventPeriod.start - 이벤트의 시작 시각(Inclusive)입니다. - * @param {Date} eventPeriod.end - 이벤트의 종료 시각(Exclusive)입니다. * @param {Object} quest - 퀘스트의 정보입니다. * @param {string} quest.id - 퀘스트의 Id입니다. * @param {string} quest.name - 퀘스트의 이름입니다. @@ -57,14 +60,14 @@ const buildQuests = (quests) => { * @param {number} quest.maxCount - 퀘스트의 최대 완료 가능 횟수입니다. * @returns {Object|null} 성공한 경우 Object를, 실패한 경우 null을 반환합니다. 이미 최대 완료 횟수에 도달했거나, 퀘스트가 원격으로 비활성화 된 경우에도 실패로 처리됩니다. */ -const completeQuest = async (userId, timestamp, eventPeriod, quest) => { +const completeQuest = async (userId, timestamp, quest) => { try { // 1단계: 유저의 EventStatus를 가져옵니다. const eventStatus = await eventStatusModel.findOne({ userId }).lean(); if (!eventStatus) return null; // 2단계: 이벤트 기간인지 확인합니다. - if (timestamp >= eventPeriod.end || timestamp < eventPeriod.start) { + if (timestamp >= eventPeriod.endAt || timestamp < eventPeriod.startAt) { logger.info( `User ${userId} failed to complete auto-disabled ${quest.id}Quest` ); diff --git a/src/lottery/routes/docs/globalState.js b/src/lottery/routes/docs/globalState.js index 6883ac33..ffaddf3a 100644 --- a/src/lottery/routes/docs/globalState.js +++ b/src/lottery/routes/docs/globalState.js @@ -1,5 +1,5 @@ -const { eventMode } = require("../../../../loadenv"); -const apiPrefix = `/events/${eventMode}/global-state`; +const { eventConfig } = require("../../../../loadenv"); +const apiPrefix = `/events/${eventConfig.mode}/global-state`; const globalStateDocs = {}; globalStateDocs[`${apiPrefix}/`] = { diff --git a/src/lottery/routes/docs/items.js b/src/lottery/routes/docs/items.js index d1c0956e..4079795a 100644 --- a/src/lottery/routes/docs/items.js +++ b/src/lottery/routes/docs/items.js @@ -1,5 +1,5 @@ -const { eventMode } = require("../../../../loadenv"); -const apiPrefix = `/events/${eventMode}/items`; +const { eventConfig } = require("../../../../loadenv"); +const apiPrefix = `/events/${eventConfig.mode}/items`; const itemsDocs = {}; itemsDocs[`${apiPrefix}/list`] = { diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index cda93fa1..649ac5a6 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -1,5 +1,5 @@ -const { eventMode } = require("../../../../loadenv"); -const apiPrefix = `/events/${eventMode}/public-notice`; +const { eventConfig } = require("../../../../loadenv"); +const apiPrefix = `/events/${eventConfig.mode}/public-notice`; const publicNoticeDocs = {}; publicNoticeDocs[`${apiPrefix}/leaderboard`] = { diff --git a/src/lottery/routes/docs/swaggerDocs.js b/src/lottery/routes/docs/swaggerDocs.js index 96c7fe86..12771ce7 100644 --- a/src/lottery/routes/docs/swaggerDocs.js +++ b/src/lottery/routes/docs/swaggerDocs.js @@ -1,11 +1,11 @@ -const { eventMode } = require("../../../../loadenv"); +const { eventConfig } = require("../../../../loadenv"); const globalStateDocs = require("./globalState"); const itemsDocs = require("./items"); const transactionsDocs = require("./transactions"); const itemsSchema = require("./itemsSchema"); const publicNoticeDocs = require("./publicNotice"); -const apiPrefix = `/events/${eventMode}`; +const apiPrefix = `/events/${eventConfig.mode}`; const eventSwaggerDocs = { tags: [ diff --git a/src/lottery/routes/docs/transactions.js b/src/lottery/routes/docs/transactions.js index 60e7435b..9bb82f41 100644 --- a/src/lottery/routes/docs/transactions.js +++ b/src/lottery/routes/docs/transactions.js @@ -1,5 +1,5 @@ -const { eventMode } = require("../../../../loadenv"); -const apiPrefix = `/events/${eventMode}/transactions`; +const { eventConfig } = require("../../../../loadenv"); +const apiPrefix = `/events/${eventConfig.mode}/transactions`; const transactionsDocs = {}; transactionsDocs[`${apiPrefix}/`] = { diff --git a/src/lottery/services/globalState.js b/src/lottery/services/globalState.js index 7c7f7c5f..0cf4315c 100644 --- a/src/lottery/services/globalState.js +++ b/src/lottery/services/globalState.js @@ -2,11 +2,10 @@ const { eventStatusModel } = require("../modules/stores/mongo"); const logger = require("../../modules/logger"); const { isLogin, getLoginInfo } = require("../../modules/auths/login"); -const { eventMode } = require("../../../loadenv"); -const contract = eventMode - ? require(`../modules/contracts/${eventMode}`) - : undefined; -const quests = contract ? Object.values(contract.quests) : undefined; +const { eventConfig } = require("../../../loadenv"); +const contracts = + eventConfig && require(`../modules/contracts/${eventConfig.mode}`); +const quests = contracts ? Object.values(contracts.quests) : undefined; const getUserGlobalStateHandler = async (req, res) => { try { @@ -50,7 +49,7 @@ const createUserGlobalStateHandler = async (req, res) => { }); await eventStatus.save(); - await contract.completeFirstLoginQuest(req.userOid, req.timestamp); + await contracts.completeFirstLoginQuest(req.userOid, req.timestamp); res.json({ result: true }); } catch (err) { diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index 5e3a5a1c..26afb6b0 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -20,10 +20,11 @@ const updateEventStatus = async ( } ); -const { eventMode } = require("../../../loadenv"); -const eventPeriod = eventMode - ? require(`../modules/contracts/${eventMode}`).eventPeriod - : undefined; +const { eventConfig } = require("../../../loadenv"); +const eventPeriod = eventConfig && { + startAt: new Date(eventConfig.startAt), + endAt: new Date(eventConfig.endAt), +}; const getRandomItem = async (req, depth) => { if (depth >= 10) { @@ -126,7 +127,10 @@ const purchaseHandler = async (req, res) => { .status(400) .json({ error: "Items/Purchase : nonexistent eventStatus" }); - if (req.timestamp >= eventPeriod.end || req.timestamp < eventPeriod.start) + if ( + req.timestamp >= eventPeriod.endAt || + req.timestamp < eventPeriod.startAt + ) return res.status(400).json({ error: "Items/Purchase : out of date" }); const { itemId } = req.params; diff --git a/src/routes/admin.js b/src/routes/admin.js index d476142e..e2ddaae4 100644 --- a/src/routes/admin.js +++ b/src/routes/admin.js @@ -13,7 +13,7 @@ const { deviceTokenModel, notificationOptionModel, } = require("../modules/stores/mongo"); -const { eventMode } = require("../../loadenv"); +const { eventConfig } = require("../../loadenv"); const { buildResource } = require("../modules/adminResource"); const router = express.Router(); @@ -37,7 +37,7 @@ const baseResources = [ notificationOptionModel, ].map(buildResource()); const resources = baseResources.concat( - eventMode === "2023fall" ? require("../lottery").resources : [] + eventConfig?.mode === "2023fall" ? require("../lottery").resources : [] ); // Create router for admin page From db0ed2ce95ef02d7729099b9aeae8ff0a9d8bf3b Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Tue, 19 Sep 2023 22:44:25 +0900 Subject: [PATCH 20/67] Add: Resolve comment --- src/lottery/modules/contracts/2023fall.js | 14 +++++++++++++ src/lottery/modules/populates/publicNotice.js | 14 ------------- src/lottery/modules/populates/transactions.js | 12 +++++++++++ src/lottery/routes/docs/quests.js | 20 +++++++++++++++---- src/lottery/routes/docs/swaggerDocs.js | 4 ++-- src/lottery/services/publicNotice.js | 13 ++++++------ 6 files changed, 50 insertions(+), 27 deletions(-) delete mode 100644 src/lottery/modules/populates/publicNotice.js diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 8bc6350a..86660420 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -189,6 +189,13 @@ const completeAdPushAgreementQuest = async () => { // TODO }; +/** + * eventSharingOnInstagram 퀘스트의 완료를 요청합니다. + * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. + * @returns {Promise} + * @description 인스타그램 스토리에 추석 이벤트를 공유할 때마다 호출해 주세요. + * @usage users/editAccountHandler + */ const completeEventSharingOnInstagramQuest = async (userId) => { return await completeQuest( userId, @@ -197,6 +204,13 @@ const completeEventSharingOnInstagramQuest = async (userId) => { ); }; +/** + * purchaseSharingOnInstagram 퀘스트의 완료를 요청합니다. + * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. + * @returns {Promise} + * @description 인스타그램 스토리에 구매한 아이템을 공유할 때마다 호출해 주세요. + * @usage users/editAccountHandler + */ const completePurchaseSharingOnInstagramQuest = async () => { return await completeQuest( userId, diff --git a/src/lottery/modules/populates/publicNotice.js b/src/lottery/modules/populates/publicNotice.js deleted file mode 100644 index d662e5fa..00000000 --- a/src/lottery/modules/populates/publicNotice.js +++ /dev/null @@ -1,14 +0,0 @@ -const publicNoticePopulateOption = [ - { - path: "userId", - select: "id", - }, - { - path: "item", - select: "name price description itemType", - }, -]; - -module.exports = { - publicNoticePopulateOption, -}; diff --git a/src/lottery/modules/populates/transactions.js b/src/lottery/modules/populates/transactions.js index 9f3a958e..bcf84863 100644 --- a/src/lottery/modules/populates/transactions.js +++ b/src/lottery/modules/populates/transactions.js @@ -5,6 +5,18 @@ const transactionPopulateOption = [ }, ]; +const publicNoticePopulateOption = [ + { + path: "userId", + select: "nickname", + }, + { + path: "item", + select: "name price description", + }, +]; + module.exports = { transactionPopulateOption, + publicNoticePopulateOption, }; diff --git a/src/lottery/routes/docs/quests.js b/src/lottery/routes/docs/quests.js index 2663f277..69b701c8 100644 --- a/src/lottery/routes/docs/quests.js +++ b/src/lottery/routes/docs/quests.js @@ -13,8 +13,14 @@ eventsDocs[`${apiPrefix}/instagram/share-event`] = { content: { "application/json": { schema: { - type: "boolean", - example: { result: true }, + type: "object", + required: ["result"], + properties: { + result: { + type: "boolean", + example: true, + }, + }, }, }, }, @@ -34,8 +40,14 @@ eventsDocs[`${apiPrefix}/instagram/share-purchase`] = { content: { "application/json": { schema: { - type: "boolean", - example: { result: true }, + type: "object", + required: ["result"], + properties: { + result: { + type: "boolean", + example: true, + }, + }, }, }, }, diff --git a/src/lottery/routes/docs/swaggerDocs.js b/src/lottery/routes/docs/swaggerDocs.js index 8136a470..0aac0bc0 100644 --- a/src/lottery/routes/docs/swaggerDocs.js +++ b/src/lottery/routes/docs/swaggerDocs.js @@ -23,8 +23,8 @@ const eventSwaggerDocs = { description: "이벤트 - 입출금 내역 관련 API", }, { - name: `${apiPrefix}/instagram`, - description: "이벤트 - 인스타그램 이벤트 관련 API", + name: `${apiPrefix}/quests`, + description: "이벤트 - 퀘스트 관련 API", }, { name: `${apiPrefix}/public-notice`, diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 930df9e3..afebe595 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -5,7 +5,7 @@ const { isLogin, getLoginInfo } = require("../../modules/auths/login"); const logger = require("../../modules/logger"); const { publicNoticePopulateOption, -} = require("../modules/populates/publicNotice"); +} = require("../modules/populates/transactions"); const getRecentPurchaceItemListHandler = async (req, res) => { try { @@ -16,7 +16,7 @@ const getRecentPurchaceItemListHandler = async (req, res) => { .limit(5) .populate(publicNoticePopulateOption) .lean(); - if (!!transactions) { + if (transactions) { transactions.forEach((item, index) => { let purchaceMessage = ""; if (item.comment.startsWith("송편")) { @@ -26,25 +26,24 @@ const getRecentPurchaceItemListHandler = async (req, res) => { } else { purchaceMessage = "획득하셨습니다."; } - transactionListString[index] = `${item.userId.id + transactionListString[index] = `${item.userId.nickname .toString() - .slice(0, 2)}${"*".repeat(item.userId.id.length - 2)}님께서 ${ + .slice(0, 2)}${"*".repeat(item.userId.nickname.length - 2)}님께서 ${ item.item.name }을(를) ${purchaceMessage}`; }); - console.log(transactionListString); res.json({ transactionListString, }); } else { res.status(500).json({ - error: "PublicNotice/get-recent-transaction : internal server error", + error: "PublicNotice/RecentTransaction : internal server error", }); } } catch (err) { logger.error(err); res.status(500).json({ - error: "PublicNotice/get-recent-transaction : internal server error", + error: "PublicNotice/RecentTransaction : internal server error", }); } }; From a4bcf6a9c6016fb41a9fe34107da38999499b20b Mon Sep 17 00:00:00 2001 From: static Date: Tue, 19 Sep 2023 23:06:01 +0900 Subject: [PATCH 21/67] Refactor: modules/quests.js --- src/lottery/modules/quests.js | 2 +- src/lottery/routes/docs/globalState.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lottery/modules/quests.js b/src/lottery/modules/quests.js index 9aa5c77f..26bf2e24 100644 --- a/src/lottery/modules/quests.js +++ b/src/lottery/modules/quests.js @@ -8,7 +8,7 @@ const logger = require("../../modules/logger"); const mongoose = require("mongoose"); const { eventConfig } = require("../../../loadenv"); -const eventPeriod = { +const eventPeriod = eventConfig && { startAt: new Date(eventConfig.startAt), endAt: new Date(eventConfig.endAt), }; diff --git a/src/lottery/routes/docs/globalState.js b/src/lottery/routes/docs/globalState.js index ffaddf3a..d5a52a16 100644 --- a/src/lottery/routes/docs/globalState.js +++ b/src/lottery/routes/docs/globalState.js @@ -16,7 +16,7 @@ globalStateDocs[`${apiPrefix}/`] = { schema: { type: "object", required: [ - "isAgree", + "isAgreeOnTermsOfEvent", "creditAmount", "completedQuests", "ticket1Amount", From 3fa55a3edb1482a64a6476c75f96424cd19d933b Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Tue, 19 Sep 2023 23:07:12 +0900 Subject: [PATCH 22/67] Add: Resolve comments --- src/lottery/services/publicNotice.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index afebe595..654afd15 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -7,6 +7,10 @@ const { publicNoticePopulateOption, } = require("../modules/populates/transactions"); +const returnAnonymousedNickname = (nickname) => { + return `${nickname.toString().slice(0, 2)}${"*".repeat(nickname.length - 2)}`; +}; + const getRecentPurchaceItemListHandler = async (req, res) => { try { let transactionListString = []; @@ -26,11 +30,9 @@ const getRecentPurchaceItemListHandler = async (req, res) => { } else { purchaceMessage = "획득하셨습니다."; } - transactionListString[index] = `${item.userId.nickname - .toString() - .slice(0, 2)}${"*".repeat(item.userId.nickname.length - 2)}님께서 ${ - item.item.name - }을(를) ${purchaceMessage}`; + transactionListString[index] = `${returnAnonymousedNickname( + item.userId.nickname + )}님께서 ${item.item.name}을(를) ${purchaceMessage}`; }); res.json({ transactionListString, @@ -81,9 +83,8 @@ const getTicketLeaderboardHandler = async (req, res) => { logger.error(`Fail to find user ${user.userId}`); return null; } - return { - nickname: userInfo.nickname, + nickname: returnAnonymousedNickname(userInfo.nickname), profileImageUrl: userInfo.profileImageUrl, ticket1Amount: user.ticket1Amount, ticket2Amount: user.ticket2Amount, @@ -95,7 +96,6 @@ const getTicketLeaderboardHandler = async (req, res) => { return res .status(500) .json({ error: "PublicNotice/Leaderboard : internal server error" }); - if (rank >= 0) res.json({ leaderboard, From 5163cf1e14e5cc590ebeecd1a872861638433ee4 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Tue, 19 Sep 2023 23:21:03 +0900 Subject: [PATCH 23/67] Add: Resolve commits --- src/lottery/modules/contracts/2023fall.js | 16 ++++++++-------- src/lottery/routes/docs/quests.js | 2 ++ src/lottery/services/publicNotice.js | 4 ++-- src/lottery/services/quests.js | 2 +- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 86660420..2d6e6a39 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -97,7 +97,7 @@ const completeFirstLoginQuest = async (userId) => { * @param {number} roomObject.settlementTotal - 정산 또는 송금이 완료된 참여자 수입니다. * @returns {Promise} * @description 정산 요청 또는 송금이 이루어질 때마다 호출해 주세요. - * @usage rooms/commitPaymentHandler, rooms/settlementHandler + * @usage rooms - commitPaymentHandler, rooms - settlementHandler */ const completePayingAndSendingQuest = async (roomObject) => { if (roomObject.part.length < 2) return null; @@ -120,7 +120,7 @@ const completePayingAndSendingQuest = async (roomObject) => { * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. * @returns {Promise} * @description 방을 만들 때마다 호출해 주세요. - * @usage rooms/createHandler + * @usage rooms - createHandler */ const completeFirstRoomCreationQuest = async (userId) => { return await completeQuest(userId, eventPeriod, quests.firstRoomCreation); @@ -137,7 +137,7 @@ const completeRoomSharingQuest = async () => { * @param {Array<{ user: mongoose.Types.ObjectId }>} roomObject.part - 참여자 목록입니다. * @returns {Promise} * @description 정산 요청이 이루어질 때마다 호출해 주세요. - * @usage rooms/commitPaymentHandler + * @usage rooms - commitPaymentHandler */ const completePayingQuest = async (userId, roomObject) => { if (roomObject.part.length < 2) return null; @@ -152,7 +152,7 @@ const completePayingQuest = async (userId, roomObject) => { * @param {Array<{ user: mongoose.Types.ObjectId }>} roomObject.part - 참여자 목록입니다. * @returns {Promise} * @description 송금이 이루어질 때마다 호출해 주세요. - * @usage rooms/settlementHandler + * @usage rooms - settlementHandler */ const completeSendingQuest = async (userId, roomObject) => { if (roomObject.part.length < 2) return null; @@ -165,7 +165,7 @@ const completeSendingQuest = async (userId, roomObject) => { * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. * @returns {Promise} * @description 닉네임을 변경할 때마다 호출해 주세요. - * @usage users/editNicknameHandler + * @usage users - editNicknameHandler */ const completeNicknameChangingQuest = async (userId) => { return await completeQuest(userId, eventPeriod, quests.nicknameChaning); @@ -177,7 +177,7 @@ const completeNicknameChangingQuest = async (userId) => { * @param {string} newAccount - 변경된 계좌입니다. * @returns {Promise} * @description 계좌를 변경할 때마다 호출해 주세요. - * @usage users/editAccountHandler + * @usage users - editAccountHandler */ const completeAccountChangingQuest = async (userId, newAccount) => { if (newAccount === "") return null; @@ -194,7 +194,7 @@ const completeAdPushAgreementQuest = async () => { * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. * @returns {Promise} * @description 인스타그램 스토리에 추석 이벤트를 공유할 때마다 호출해 주세요. - * @usage users/editAccountHandler + * @usage quests - instagramEventShareHandler */ const completeEventSharingOnInstagramQuest = async (userId) => { return await completeQuest( @@ -209,7 +209,7 @@ const completeEventSharingOnInstagramQuest = async (userId) => { * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. * @returns {Promise} * @description 인스타그램 스토리에 구매한 아이템을 공유할 때마다 호출해 주세요. - * @usage users/editAccountHandler + * @usage quests - instagramPurchaseShareHandler */ const completePurchaseSharingOnInstagramQuest = async () => { return await completeQuest( diff --git a/src/lottery/routes/docs/quests.js b/src/lottery/routes/docs/quests.js index 69b701c8..39410352 100644 --- a/src/lottery/routes/docs/quests.js +++ b/src/lottery/routes/docs/quests.js @@ -17,6 +17,7 @@ eventsDocs[`${apiPrefix}/instagram/share-event`] = { required: ["result"], properties: { result: { + description: "성공 여부. 항상 true입니다.", type: "boolean", example: true, }, @@ -44,6 +45,7 @@ eventsDocs[`${apiPrefix}/instagram/share-purchase`] = { required: ["result"], properties: { result: { + description: "성공 여부. 항상 true입니다.", type: "boolean", example: true, }, diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 654afd15..fab01010 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -39,13 +39,13 @@ const getRecentPurchaceItemListHandler = async (req, res) => { }); } else { res.status(500).json({ - error: "PublicNotice/RecentTransaction : internal server error", + error: "PublicNotice/RecentTransactions : internal server error", }); } } catch (err) { logger.error(err); res.status(500).json({ - error: "PublicNotice/RecentTransaction : internal server error", + error: "PublicNotice/RecentTransactions : internal server error", }); } }; diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js index 68be355a..90cbf8f9 100644 --- a/src/lottery/services/quests.js +++ b/src/lottery/services/quests.js @@ -20,7 +20,7 @@ const instagramEventShareHandler = async (req, res) => { // 인스타그램 스토리에 아이템 구매 내역을 공유했을 때. const instagramPurchaseShareHandler = async (req, res) => { try { - const userId = req.userOid; + const { userOid: userId } = req; const contractResult = await contracts.completePurchaseSharingOnInstagramQuest(userId); res.json({ result: !!contractResult }); From 5eff8894c231d0b96c7aadf641c8a866901b0107 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Tue, 19 Sep 2023 23:42:27 +0900 Subject: [PATCH 24/67] Add: Resolve comments --- src/lottery/routes/docs/publicNotice.js | 132 ++++++++++++++---------- src/lottery/routes/publicNotice.js | 2 +- src/lottery/services/publicNotice.js | 7 +- src/lottery/services/quests.js | 8 +- 4 files changed, 87 insertions(+), 62 deletions(-) diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index cda93fa1..fbf94f5c 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -2,71 +2,92 @@ const { eventMode } = require("../../../../loadenv"); const apiPrefix = `/events/${eventMode}/public-notice`; const publicNoticeDocs = {}; -publicNoticeDocs[`${apiPrefix}/leaderboard`] = { +(publicNoticeDocs[`${apiPrefix}/recentTransactions`] = { get: { tags: [`${apiPrefix}`], - summary: "리더보드 반환", - description: - "티켓 개수(고급 티켓은 일반 티켓 5개와 등가입니다.) 기준의 리더보드와 관련된 정보를 가져옵니다.", + summary: "최근 트랜젝션의 명단 리스트 반환", + description: "", responses: { 200: { description: "", content: { "application/json": { schema: { - type: "object", - required: ["leaderboard"], - properties: { - leaderboard: { - type: "array", - description: "상위 20명만 포함된 리더보드", - items: { - type: "object", - required: [ - "nickname", - "profileImageUrl", - "ticket1Amount", - "ticket2Amount", - "probability", - ], - properties: { - nickname: { - type: "string", - description: "유저의 닉네임", - example: "asdf", - }, - profileImageUrl: { - type: "string", - description: "프로필 이미지 URL", - example: "IMAGE URL", - }, - ticket1Amount: { - type: "number", - description: "일반 티켓의 개수. 0 이상입니다.", - example: 10, - }, - ticket2Amount: { - type: "number", - description: "고급 티켓의 개수. 0 이상입니다.", - example: 10, - }, - probability: { - type: "number", - description: "1등 당첨 확률", - example: 0.001, + type: "array", + items: ["string"], + }, + }, + }, + }, + }, + }, +}), + (publicNoticeDocs[`${apiPrefix}/leaderboard`] = { + get: { + tags: [`${apiPrefix}`], + summary: "리더보드 반환", + description: + "티켓 개수(고급 티켓은 일반 티켓 5개와 등가입니다.) 기준의 리더보드와 관련된 정보를 가져옵니다.", + responses: { + 200: { + description: "", + content: { + "application/json": { + schema: { + type: "object", + required: ["leaderboard"], + properties: { + leaderboard: { + type: "array", + description: "상위 20명만 포함된 리더보드", + items: { + type: "object", + required: [ + "nickname", + "profileImageUrl", + "ticket1Amount", + "ticket2Amount", + "probability", + ], + properties: { + nickname: { + type: "string", + description: "유저의 닉네임", + example: "asdf", + }, + profileImageUrl: { + type: "string", + description: "프로필 이미지 URL", + example: "IMAGE URL", + }, + ticket1Amount: { + type: "number", + description: "일반 티켓의 개수. 0 이상입니다.", + example: 10, + }, + ticket2Amount: { + type: "number", + description: "고급 티켓의 개수. 0 이상입니다.", + example: 10, + }, + probability: { + type: "number", + description: "1등 당첨 확률", + example: 0.001, + }, }, }, }, - }, - rank: { - type: "number", - description: "유저의 리더보드 순위. 1부터 시작합니다.", - example: 30, - }, - probability: { - type: "number", - description: "1등 당첨 확률", - example: 0.00003, + rank: { + type: "number", + description: "유저의 리더보드 순위. 1부터 시작합니다.", + example: 30, + }, + probability: { + type: "number", + description: "1등 당첨 확률", + example: 0.00003, + }, }, }, }, @@ -74,7 +95,6 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = { }, }, }, - }, -}; + }); module.exports = publicNoticeDocs; diff --git a/src/lottery/routes/publicNotice.js b/src/lottery/routes/publicNotice.js index 79fc17ab..ac17e481 100644 --- a/src/lottery/routes/publicNotice.js +++ b/src/lottery/routes/publicNotice.js @@ -5,7 +5,7 @@ const publicNoticeHandlers = require("../services/publicNotice"); // 상점 공지는 로그인을 요구하지 않습니다. router.get( - "/recent-transactions", + "/recentTransactions", publicNoticeHandlers.getRecentPurchaceItemListHandler ); router.get("/leaderboard", publicNoticeHandlers.getTicketLeaderboardHandler); diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index fab01010..0a62d3d2 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -7,7 +7,7 @@ const { publicNoticePopulateOption, } = require("../modules/populates/transactions"); -const returnAnonymousedNickname = (nickname) => { +const hideNickname = (nickname) => { return `${nickname.toString().slice(0, 2)}${"*".repeat(nickname.length - 2)}`; }; @@ -30,7 +30,7 @@ const getRecentPurchaceItemListHandler = async (req, res) => { } else { purchaceMessage = "획득하셨습니다."; } - transactionListString[index] = `${returnAnonymousedNickname( + transactionListString[index] = `${hideNickname( item.userId.nickname )}님께서 ${item.item.name}을(를) ${purchaceMessage}`; }); @@ -84,7 +84,7 @@ const getTicketLeaderboardHandler = async (req, res) => { return null; } return { - nickname: returnAnonymousedNickname(userInfo.nickname), + nickname: hideNickname(userInfo.nickname), profileImageUrl: userInfo.profileImageUrl, ticket1Amount: user.ticket1Amount, ticket2Amount: user.ticket2Amount, @@ -96,6 +96,7 @@ const getTicketLeaderboardHandler = async (req, res) => { return res .status(500) .json({ error: "PublicNotice/Leaderboard : internal server error" }); + if (rank >= 0) res.json({ leaderboard, diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js index 90cbf8f9..b11b3a73 100644 --- a/src/lottery/services/quests.js +++ b/src/lottery/services/quests.js @@ -1,7 +1,9 @@ const logger = require("../../modules/logger"); const contracts = require("../modules/contracts/2023fall"); -// 인스타그램 스토리에 이벤트를 공유했을 때. +/** + * 인스타그램 스토리에 이벤트를 공유했을 때. + */ const instagramEventShareHandler = async (req, res) => { try { const { userOid: userId } = req; @@ -17,7 +19,9 @@ const instagramEventShareHandler = async (req, res) => { } }; -// 인스타그램 스토리에 아이템 구매 내역을 공유했을 때. +/** + * 인스타그램 스토리에 아이템 구매 내역을 공유했을 때. + */ const instagramPurchaseShareHandler = async (req, res) => { try { const { userOid: userId } = req; From 3b4f0d83be7c381a8deede1482acb7c92c892d62 Mon Sep 17 00:00:00 2001 From: withsang Date: Tue, 19 Sep 2023 23:48:34 +0900 Subject: [PATCH 25/67] Add: early return func if the item is sold out --- src/lottery/services/items.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index 26afb6b0..aba495cd 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -62,14 +62,13 @@ const getRandomItem = async (req, depth) => { // 1단계: 재고를 차감합니다. const newRandomItem = await itemModel .findOneAndUpdate( - { _id: randomItem._id }, + { _id: randomItem._id, stock: { $gt: 0 } }, { $inc: { stock: -1, }, }, { - runValidators: true, new: true, fields: { itemType: 0, @@ -79,6 +78,9 @@ const getRandomItem = async (req, depth) => { } ) .lean(); + if (!newRandomItem) { + throw new Error("The item was already sold out"); + } // 2단계: 유저 정보를 업데이트합니다. await updateEventStatus(req.userOid, { From f0a83cfed18b02edfd4f0bf829638a41309916d2 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Tue, 19 Sep 2023 23:51:51 +0900 Subject: [PATCH 26/67] Add: Resolve comments --- src/lottery/routes/docs/publicNotice.js | 133 ++++++++++++------------ src/lottery/services/publicNotice.js | 2 +- 2 files changed, 68 insertions(+), 67 deletions(-) diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index fbf94f5c..846b20d1 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -2,7 +2,7 @@ const { eventMode } = require("../../../../loadenv"); const apiPrefix = `/events/${eventMode}/public-notice`; const publicNoticeDocs = {}; -(publicNoticeDocs[`${apiPrefix}/recentTransactions`] = { +publicNoticeDocs[`${apiPrefix}/recentTransactions`] = { get: { tags: [`${apiPrefix}`], summary: "최근 트랜젝션의 명단 리스트 반환", @@ -21,73 +21,73 @@ const publicNoticeDocs = {}; }, }, }, -}), - (publicNoticeDocs[`${apiPrefix}/leaderboard`] = { - get: { - tags: [`${apiPrefix}`], - summary: "리더보드 반환", - description: - "티켓 개수(고급 티켓은 일반 티켓 5개와 등가입니다.) 기준의 리더보드와 관련된 정보를 가져옵니다.", - responses: { - 200: { - description: "", - content: { - "application/json": { - schema: { - type: "object", - required: ["leaderboard"], - properties: { - leaderboard: { - type: "array", - description: "상위 20명만 포함된 리더보드", - items: { - type: "object", - required: [ - "nickname", - "profileImageUrl", - "ticket1Amount", - "ticket2Amount", - "probability", - ], - properties: { - nickname: { - type: "string", - description: "유저의 닉네임", - example: "asdf", - }, - profileImageUrl: { - type: "string", - description: "프로필 이미지 URL", - example: "IMAGE URL", - }, - ticket1Amount: { - type: "number", - description: "일반 티켓의 개수. 0 이상입니다.", - example: 10, - }, - ticket2Amount: { - type: "number", - description: "고급 티켓의 개수. 0 이상입니다.", - example: 10, - }, - probability: { - type: "number", - description: "1등 당첨 확률", - example: 0.001, - }, +}; + +publicNoticeDocs[`${apiPrefix}/leaderboard`] = { + get: { + tags: [`${apiPrefix}`], + summary: "리더보드 반환", + description: + "티켓 개수(고급 티켓은 일반 티켓 5개와 등가입니다.) 기준의 리더보드와 관련된 정보를 가져옵니다.", + responses: { + 200: { + description: "", + content: { + "application/json": { + schema: { + type: "object", + required: ["leaderboard"], + properties: { + leaderboard: { + type: "array", + description: "상위 20명만 포함된 리더보드", + items: { + type: "object", + required: [ + "nickname", + "profileImageUrl", + "ticket1Amount", + "ticket2Amount", + "probability", + ], + properties: { + nickname: { + type: "string", + description: "유저의 닉네임", + example: "asdf", + }, + profileImageUrl: { + type: "string", + description: "프로필 이미지 URL", + example: "IMAGE URL", + }, + ticket1Amount: { + type: "number", + description: "일반 티켓의 개수. 0 이상입니다.", + example: 10, + }, + ticket2Amount: { + type: "number", + description: "고급 티켓의 개수. 0 이상입니다.", + example: 10, + }, + probability: { + type: "number", + description: "1등 당첨 확률", + example: 0.001, }, }, }, - rank: { - type: "number", - description: "유저의 리더보드 순위. 1부터 시작합니다.", - example: 30, - }, - probability: { - type: "number", - description: "1등 당첨 확률", - example: 0.00003, - }, + }, + rank: { + type: "number", + description: "유저의 리더보드 순위. 1부터 시작합니다.", + example: 30, + }, + probability: { + type: "number", + description: "1등 당첨 확률", + example: 0.00003, }, }, }, @@ -95,6 +95,7 @@ const publicNoticeDocs = {}; }, }, }, - }); + }, +}; module.exports = publicNoticeDocs; diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 0a62d3d2..32413b15 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -15,7 +15,7 @@ const getRecentPurchaceItemListHandler = async (req, res) => { try { let transactionListString = []; const transactions = await transactionModel - .find({ type: "use" }) + .find({ type: "use", itemType: 0 }) .sort({ createAt: -1 }) .limit(5) .populate(publicNoticePopulateOption) From d6a578bcadef69aa309665549ae61b8a7ac25a67 Mon Sep 17 00:00:00 2001 From: static Date: Wed, 20 Sep 2023 00:01:13 +0900 Subject: [PATCH 27/67] Add: early return func if the item is sold out --- src/lottery/services/items.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index aba495cd..40482621 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -153,17 +153,15 @@ const purchaseHandler = async (req, res) => { .json({ error: "Items/Purchase : item out of stock" }); // 1단계: 재고를 차감합니다. - await itemModel.updateOne( - { _id: item._id }, + const { modifiedCount } = await itemModel.updateOne( + { _id: item._id, stock: { $gt: 0 } }, { $inc: { stock: -1, }, - }, - { - runValidators: true, } ); + if (modifiedCount === 0) throw new Error("The item was already sold out"); // 2단계: 유저 정보를 업데이트합니다. await updateEventStatus(req.userOid, { From c1d2eb3a1b242ed5ff911d784c77d5bfc5522a3b Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Wed, 20 Sep 2023 00:06:24 +0900 Subject: [PATCH 28/67] Add: resolve comment --- src/lottery/routes/docs/publicNotice.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index 846b20d1..18f90cbf 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -14,7 +14,9 @@ publicNoticeDocs[`${apiPrefix}/recentTransactions`] = { "application/json": { schema: { type: "array", - items: ["string"], + items: { + type: "string", + }, }, }, }, From cc09f0b179d24f2c5256ba28dc0c423f69aaabda Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Wed, 20 Sep 2023 00:17:07 +0900 Subject: [PATCH 29/67] Add: resolve comments --- src/lottery/routes/docs/publicNotice.js | 15 ++++++++++++--- src/lottery/services/publicNotice.js | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index 18f90cbf..58e4b7dd 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -13,9 +13,18 @@ publicNoticeDocs[`${apiPrefix}/recentTransactions`] = { content: { "application/json": { schema: { - type: "array", - items: { - type: "string", + type: "object", + description: "5개의 상점 공지", + required: ["transactions"], + properties: { + transactions: { + type: "array", + items: { + type: "string", + example: + "tu**************님께서 일반응모권을(를) 획득하셨습니다.", + }, + }, }, }, }, diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 32413b15..7f045aec 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -35,7 +35,7 @@ const getRecentPurchaceItemListHandler = async (req, res) => { )}님께서 ${item.item.name}을(를) ${purchaceMessage}`; }); res.json({ - transactionListString, + transactions: transactionListString, }); } else { res.status(500).json({ From 3086cd85b2eb7b302a3648f7c6b0b0da973b848c Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Wed, 20 Sep 2023 01:21:28 +0900 Subject: [PATCH 30/67] Add: resolve comments --- src/lottery/modules/contracts/2023fall.js | 14 +++---- src/lottery/services/publicNotice.js | 49 +++++++++-------------- src/lottery/services/quests.js | 8 +++- 3 files changed, 32 insertions(+), 39 deletions(-) diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 7d1d4546..12a4d41d 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -210,29 +210,27 @@ const completeAdPushAgreementQuest = async ( /** * eventSharingOnInstagram 퀘스트의 완료를 요청합니다. * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. + * @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다. * @returns {Promise} * @description 인스타그램 스토리에 추석 이벤트를 공유할 때마다 호출해 주세요. * @usage quests - instagramEventShareHandler */ -const completeEventSharingOnInstagramQuest = async (userId) => { - return await completeQuest( - userId, - eventPeriod, - quests.eventSharingOnInstagram - ); +const completeEventSharingOnInstagramQuest = async (userId, timestamp) => { + return await completeQuest(userId, timestamp, quests.eventSharingOnInstagram); }; /** * purchaseSharingOnInstagram 퀘스트의 완료를 요청합니다. * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. + * @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다. * @returns {Promise} * @description 인스타그램 스토리에 구매한 아이템을 공유할 때마다 호출해 주세요. * @usage quests - instagramPurchaseShareHandler */ -const completePurchaseSharingOnInstagramQuest = async () => { +const completePurchaseSharingOnInstagramQuest = async (userId, timestamp) => { return await completeQuest( userId, - eventPeriod, + timestamp, quests.purchaseSharingOnInstagram ); }; diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 7f045aec..e19f577a 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -13,35 +13,26 @@ const hideNickname = (nickname) => { const getRecentPurchaceItemListHandler = async (req, res) => { try { - let transactionListString = []; - const transactions = await transactionModel - .find({ type: "use", itemType: 0 }) - .sort({ createAt: -1 }) - .limit(5) - .populate(publicNoticePopulateOption) - .lean(); - if (transactions) { - transactions.forEach((item, index) => { - let purchaceMessage = ""; - if (item.comment.startsWith("송편")) { - purchaceMessage = "구입하셨습니다."; - } else if (item.comment.startsWith("랜덤 박스")) { - purchaceMessage = "뽑았습니다."; - } else { - purchaceMessage = "획득하셨습니다."; - } - transactionListString[index] = `${hideNickname( - item.userId.nickname - )}님께서 ${item.item.name}을(를) ${purchaceMessage}`; - }); - res.json({ - transactions: transactionListString, - }); - } else { - res.status(500).json({ - error: "PublicNotice/RecentTransactions : internal server error", - }); - } + const transactions = ( + await transactionModel + .find({ type: "use", itemType: 0 }) + .sort({ createAt: -1 }) + .limit(5) + .populate(publicNoticePopulateOption) + .lean() + ).map( + ({ userId, item, comment }) => + `${hideNickname(userId.nickname)}님께서 ${item.name}을(를) ${ + comment.startsWith("송편") + ? "을(를) 구입하셨습니다." + : comment.startsWith("랜덤 박스") + ? "을(를) 뽑았습니다." + : "을(를) 획득하셨습니다." + }` + ); + res.json({ + transactions: transactions, + }); } catch (err) { logger.error(err); res.status(500).json({ diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js index b11b3a73..7d8eefa0 100644 --- a/src/lottery/services/quests.js +++ b/src/lottery/services/quests.js @@ -8,7 +8,8 @@ const instagramEventShareHandler = async (req, res) => { try { const { userOid: userId } = req; const contractResult = await contracts.completeEventSharingOnInstagramQuest( - userId + userId, + req.timestamp ); res.json({ result: !!contractResult }); } catch (err) { @@ -26,7 +27,10 @@ const instagramPurchaseShareHandler = async (req, res) => { try { const { userOid: userId } = req; const contractResult = - await contracts.completePurchaseSharingOnInstagramQuest(userId); + await contracts.completePurchaseSharingOnInstagramQuest( + userId, + req.timestamp + ); res.json({ result: !!contractResult }); } catch (err) { logger.error(err); From e77a03f71f5ac84ea3bc56a748d1ebec8da79df8 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Wed, 20 Sep 2023 01:30:12 +0900 Subject: [PATCH 31/67] Refactor: publicNotice --- src/lottery/services/publicNotice.js | 4 +--- src/lottery/services/quests.js | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index e19f577a..2ce717af 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -30,9 +30,7 @@ const getRecentPurchaceItemListHandler = async (req, res) => { : "을(를) 획득하셨습니다." }` ); - res.json({ - transactions: transactions, - }); + res.json({ transactions }); } catch (err) { logger.error(err); res.status(500).json({ diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js index 7d8eefa0..7212893f 100644 --- a/src/lottery/services/quests.js +++ b/src/lottery/services/quests.js @@ -6,9 +6,9 @@ const contracts = require("../modules/contracts/2023fall"); */ const instagramEventShareHandler = async (req, res) => { try { - const { userOid: userId } = req; + const { userOid } = req; const contractResult = await contracts.completeEventSharingOnInstagramQuest( - userId, + userOid, req.timestamp ); res.json({ result: !!contractResult }); @@ -25,10 +25,10 @@ const instagramEventShareHandler = async (req, res) => { */ const instagramPurchaseShareHandler = async (req, res) => { try { - const { userOid: userId } = req; + const { userOid } = req; const contractResult = await contracts.completePurchaseSharingOnInstagramQuest( - userId, + userOid, req.timestamp ); res.json({ result: !!contractResult }); From 7322b3e8882216edafa81f893a0531f7a77b3205 Mon Sep 17 00:00:00 2001 From: cokia Date: Wed, 20 Sep 2023 02:14:33 +0900 Subject: [PATCH 32/67] fix: add phoneNumber field to user scheme --- src/modules/stores/mongo.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/stores/mongo.js b/src/modules/stores/mongo.js index c255d7b6..d8af344d 100755 --- a/src/modules/stores/mongo.js +++ b/src/modules/stores/mongo.js @@ -12,6 +12,7 @@ const userSchema = Schema({ ongoingRoom: [{ type: Schema.Types.ObjectId, ref: "Room" }], // 참여중인 진행중인 방 배열 doneRoom: [{ type: Schema.Types.ObjectId, ref: "Room" }], // 참여중인 완료된 방 배열 withdraw: { type: Boolean, default: false }, + phoneNumber: { type: String, default: "" }, // 전화번호 (2023FALL 이벤트부터 추가) ban: { type: Boolean, default: false }, //계정 정지 여부 joinat: { type: Date, required: true }, //가입 시각 agreeOnTermsOfService: { type: Boolean, default: false }, //이용약관 동의 여부 From 450a4ceb57d9edf675f0828b5126cb1dd0e9e016 Mon Sep 17 00:00:00 2001 From: cokia Date: Wed, 20 Sep 2023 02:14:54 +0900 Subject: [PATCH 33/67] fix: collect phonenumber when event agree --- src/lottery/routes/globalState.js | 5 ++++- src/lottery/services/globalState.js | 6 ++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/lottery/routes/globalState.js b/src/lottery/routes/globalState.js index d0461218..54bb83d5 100644 --- a/src/lottery/routes/globalState.js +++ b/src/lottery/routes/globalState.js @@ -2,12 +2,15 @@ const express = require("express"); const router = express.Router(); const globalStateHandlers = require("../services/globalState"); +const { validateBody } = require("../../middlewares/ajv"); router.get("/", globalStateHandlers.getUserGlobalStateHandler); // 아래의 Endpoint 접근 시 로그인 필요 router.use(require("../../middlewares/auth")); -router.post("/create", globalStateHandlers.createUserGlobalStateHandler); +router.post("/create", + validateBody({phoneNumber: {required: true, type: "string", pattern: "/^010-?([0-9]{3,4})-?([0-9]{4})$/"}}), + globalStateHandlers.createUserGlobalStateHandler); module.exports = router; diff --git a/src/lottery/services/globalState.js b/src/lottery/services/globalState.js index 0cf4315c..32ff76cb 100644 --- a/src/lottery/services/globalState.js +++ b/src/lottery/services/globalState.js @@ -3,6 +3,7 @@ const logger = require("../../modules/logger"); const { isLogin, getLoginInfo } = require("../../modules/auths/login"); const { eventConfig } = require("../../../loadenv"); +const { userModel } = require("../../modules/stores/mongo"); const contracts = eventConfig && require(`../modules/contracts/${eventConfig.mode}`); const quests = contracts ? Object.values(contracts.quests) : undefined; @@ -49,6 +50,11 @@ const createUserGlobalStateHandler = async (req, res) => { }); await eventStatus.save(); + //logic2. 수집한 유저 전화번호 user Scheme 에 저장 + const user = await userModel.findOne({ _id: req.userOid }); + user.phoneNumber = req.body.phoneNumber; + await user.save(); + await contracts.completeFirstLoginQuest(req.userOid, req.timestamp); res.json({ result: true }); From 784801d21ff0b439b303bf8f0849fe39c549fa3f Mon Sep 17 00:00:00 2001 From: cokia Date: Wed, 20 Sep 2023 02:28:10 +0900 Subject: [PATCH 34/67] fix: migrate scheme info to globalStateSchema --- src/lottery/routes/docs/globalState.js | 10 ++++++++++ src/lottery/routes/docs/globalStateSchema.js | 15 +++++++++++++++ src/lottery/routes/docs/swaggerDocs.js | 3 ++- src/lottery/routes/globalState.js | 2 +- 4 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 src/lottery/routes/docs/globalStateSchema.js diff --git a/src/lottery/routes/docs/globalState.js b/src/lottery/routes/docs/globalState.js index d5a52a16..4cf9630b 100644 --- a/src/lottery/routes/docs/globalState.js +++ b/src/lottery/routes/docs/globalState.js @@ -128,6 +128,16 @@ globalStateDocs[`${apiPrefix}/create`] = { summary: "Frontend에서 Global state로 관리하는 정보 생성", description: "유저의 재화 개수, 퀘스트 완료 상태 등 Frontend에서 Global state로 관리할 정보를 생성합니다.", + requestBody: { + description: "Update an existent user in the store", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/createUserGlobalStateHandler", + }, + }, + }, + }, responses: { 200: { description: "", diff --git a/src/lottery/routes/docs/globalStateSchema.js b/src/lottery/routes/docs/globalStateSchema.js new file mode 100644 index 00000000..7a9a5260 --- /dev/null +++ b/src/lottery/routes/docs/globalStateSchema.js @@ -0,0 +1,15 @@ +const globalStateSchema = { + createUserGlobalStateHandler: { + type: "object", + required: ["phoneNumber"], + properties: { + phoneNumber: { + type: "string", + pattern: "^010-?([0-9]{3,4})-?([0-9]{4})$", + }, + }, + errorMessage: "validation: bad request", + }, +}; + +module.exports = globalStateSchema; diff --git a/src/lottery/routes/docs/swaggerDocs.js b/src/lottery/routes/docs/swaggerDocs.js index ddd82098..1c87be93 100644 --- a/src/lottery/routes/docs/swaggerDocs.js +++ b/src/lottery/routes/docs/swaggerDocs.js @@ -5,7 +5,7 @@ const transactionsDocs = require("./transactions"); const questsDocs = require("./quests"); const itemsSchema = require("./itemsSchema"); const publicNoticeDocs = require("./publicNotice"); - +const globalStateSchema=require("./globalStateSchema"); const apiPrefix = `/events/${eventConfig.mode}`; const eventSwaggerDocs = { @@ -41,6 +41,7 @@ const eventSwaggerDocs = { components: { schemas: { ...itemsSchema, + ...globalStateSchema, }, }, }; diff --git a/src/lottery/routes/globalState.js b/src/lottery/routes/globalState.js index 54bb83d5..2118c895 100644 --- a/src/lottery/routes/globalState.js +++ b/src/lottery/routes/globalState.js @@ -10,7 +10,7 @@ router.get("/", globalStateHandlers.getUserGlobalStateHandler); router.use(require("../../middlewares/auth")); router.post("/create", - validateBody({phoneNumber: {required: true, type: "string", pattern: "/^010-?([0-9]{3,4})-?([0-9]{4})$/"}}), + validateBody(globalStateHandlers.createUserGlobalStateSchema), globalStateHandlers.createUserGlobalStateHandler); module.exports = router; From 506bf00c794cd59ff52163cedd6391d2054e8a66 Mon Sep 17 00:00:00 2001 From: static Date: Wed, 20 Sep 2023 01:50:21 +0900 Subject: [PATCH 35/67] Refactor: apply prettier --- src/lottery/routes/docs/swaggerDocs.js | 2 +- src/lottery/routes/globalState.js | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lottery/routes/docs/swaggerDocs.js b/src/lottery/routes/docs/swaggerDocs.js index 1c87be93..77a85f61 100644 --- a/src/lottery/routes/docs/swaggerDocs.js +++ b/src/lottery/routes/docs/swaggerDocs.js @@ -5,7 +5,7 @@ const transactionsDocs = require("./transactions"); const questsDocs = require("./quests"); const itemsSchema = require("./itemsSchema"); const publicNoticeDocs = require("./publicNotice"); -const globalStateSchema=require("./globalStateSchema"); +const globalStateSchema = require("./globalStateSchema"); const apiPrefix = `/events/${eventConfig.mode}`; const eventSwaggerDocs = { diff --git a/src/lottery/routes/globalState.js b/src/lottery/routes/globalState.js index 2118c895..be63bb3f 100644 --- a/src/lottery/routes/globalState.js +++ b/src/lottery/routes/globalState.js @@ -3,14 +3,17 @@ const express = require("express"); const router = express.Router(); const globalStateHandlers = require("../services/globalState"); const { validateBody } = require("../../middlewares/ajv"); +const globalStateSchema = require("./docs/globalStateSchema"); router.get("/", globalStateHandlers.getUserGlobalStateHandler); // 아래의 Endpoint 접근 시 로그인 필요 router.use(require("../../middlewares/auth")); -router.post("/create", - validateBody(globalStateHandlers.createUserGlobalStateSchema), - globalStateHandlers.createUserGlobalStateHandler); +router.post( + "/create", + validateBody(globalStateSchema.createUserGlobalStateHandler), + globalStateHandlers.createUserGlobalStateHandler +); module.exports = router; From 2b65567e32e142772bba169f14fa4f221affae00 Mon Sep 17 00:00:00 2001 From: static Date: Wed, 20 Sep 2023 01:55:17 +0900 Subject: [PATCH 36/67] Docs: update some descriptions --- src/lottery/routes/docs/publicNotice.js | 7 +++---- src/lottery/routes/docs/quests.js | 14 +++++++------- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index 36f28b79..ca30cfb9 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -5,8 +5,8 @@ const publicNoticeDocs = {}; publicNoticeDocs[`${apiPrefix}/recentTransactions`] = { get: { tags: [`${apiPrefix}`], - summary: "최근 트랜젝션의 명단 리스트 반환", - description: "", + summary: "최근의 유의미한 상품 획득 기록 반환", + description: "모든 유저의 상품 획득 내역 중 유의미한 기록을 가져옵니다.", responses: { 200: { description: "", @@ -14,11 +14,11 @@ publicNoticeDocs[`${apiPrefix}/recentTransactions`] = { "application/json": { schema: { type: "object", - description: "5개의 상점 공지", required: ["transactions"], properties: { transactions: { type: "array", + description: "상품 획득 기록의 배열", items: { type: "string", example: @@ -33,7 +33,6 @@ publicNoticeDocs[`${apiPrefix}/recentTransactions`] = { }, }, }; - publicNoticeDocs[`${apiPrefix}/leaderboard`] = { get: { tags: [`${apiPrefix}`], diff --git a/src/lottery/routes/docs/quests.js b/src/lottery/routes/docs/quests.js index 39410352..e85f934c 100644 --- a/src/lottery/routes/docs/quests.js +++ b/src/lottery/routes/docs/quests.js @@ -1,12 +1,12 @@ -const { eventMode } = require("../../../../loadenv"); -const apiPrefix = `/events/${eventMode}/quests`; +const { eventConfig } = require("../../../../loadenv"); +const apiPrefix = `/events/${eventConfig.mode}/quests`; const eventsDocs = {}; eventsDocs[`${apiPrefix}/instagram/share-event`] = { post: { tags: [`${apiPrefix}`], - summary: "이벤트 공유시 보상 반환", - description: "인스타그램 스토리에 이벤트를 공유하면 보상 반환", + summary: "eventSharingOnInstagram 퀘스트 완료 요청", + description: "eventSharingOnInstagram 퀘스트의 완료를 요청합니다.", responses: { 200: { description: "", @@ -17,8 +17,8 @@ eventsDocs[`${apiPrefix}/instagram/share-event`] = { required: ["result"], properties: { result: { - description: "성공 여부. 항상 true입니다.", type: "boolean", + description: "성공 여부. 항상 true입니다.", example: true, }, }, @@ -33,8 +33,8 @@ eventsDocs[`${apiPrefix}/instagram/share-event`] = { eventsDocs[`${apiPrefix}/instagram/share-purchase`] = { post: { tags: [`${apiPrefix}`], - summary: "이벤트 공유시 보상 반환", - description: "인스타그램 스토리에 구매내역을 공유하면 보상 반환", + summary: "purchaseSharingOnInstagram 퀘스트 완료 요청", + description: "purchaseSharingOnInstagram 퀘스트의 완료를 요청합니다.", responses: { 200: { description: "", From 66969ad08c096a0cfd72d0e921a4e9934fe18cf2 Mon Sep 17 00:00:00 2001 From: cokia Date: Wed, 20 Sep 2023 02:41:00 +0900 Subject: [PATCH 37/67] fix: change user.phoneNumber default to undefiend --- src/modules/stores/mongo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/stores/mongo.js b/src/modules/stores/mongo.js index d8af344d..998aecdd 100755 --- a/src/modules/stores/mongo.js +++ b/src/modules/stores/mongo.js @@ -12,7 +12,7 @@ const userSchema = Schema({ ongoingRoom: [{ type: Schema.Types.ObjectId, ref: "Room" }], // 참여중인 진행중인 방 배열 doneRoom: [{ type: Schema.Types.ObjectId, ref: "Room" }], // 참여중인 완료된 방 배열 withdraw: { type: Boolean, default: false }, - phoneNumber: { type: String, default: "" }, // 전화번호 (2023FALL 이벤트부터 추가) + phoneNumber: { type: String }, // 전화번호 (2023FALL 이벤트부터 추가) ban: { type: Boolean, default: false }, //계정 정지 여부 joinat: { type: Date, required: true }, //가입 시각 agreeOnTermsOfService: { type: Boolean, default: false }, //이용약관 동의 여부 From 91490156dce716b451448773e2209dacc868653e Mon Sep 17 00:00:00 2001 From: 14Kgun Date: Wed, 20 Sep 2023 05:58:00 +0900 Subject: [PATCH 38/67] Add: detail in contracts/2023fall.js --- src/lottery/modules/contracts/2023fall.js | 86 ++++++++++++++--------- 1 file changed, 54 insertions(+), 32 deletions(-) diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 12a4d41d..295ae4eb 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -4,74 +4,96 @@ const mongoose = require("mongoose"); /** 전체 퀘스트 목록입니다. */ const quests = buildQuests({ firstLogin: { - name: "이벤트 기간 첫 로그인", - description: "", - imageUrl: "", + name: "첫 발걸음", + description: + "로그인만 해도 송편을 얻을 수 있다고?? 이벤트 기간에 처음으로 SPARCS Taxi 서비스에 로그인하여 송편을 받아보세요.", + imageUrl: + "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_firstLogin.png", reward: { ticket1: 1, }, }, payingAndSending: { - name: "2명 이상 탑승한 방에서 정산/송금 완료", - description: "", - imageUrl: "", + name: "함께하는 택시의 여정", + description: + "2명 이상과 함께 택시를 타고 정산/송금까지 완료해보세요. 최대 3번까지 송편을 받을 수 있어요. 정산/송금 버튼은 채팅 페이지 좌측 하단의 +버튼을 눌러 확인할 수 있어요.", + imageUrl: + "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_payingAndSending.png", reward: 300, maxCount: 3, }, firstRoomCreation: { name: "첫 방 개설", - description: "", - imageUrl: "", + description: + "원하는 택시팟을 찾을 수 없다면? 원하는 조건으로 방 개설 페이지에서 방을 직접 개설해보세요.", + imageUrl: + "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_firstRoomCreation.png", reward: 50, }, roomSharing: { - name: "방 공유하기", - description: "", - imageUrl: "", + name: "Taxi로 모여라", + description: + "방을 공유해 친구들을 택시에 초대해보세요. 채팅창 상단의 햄버거 버튼을 누르면 공유하기 버튼을 찾을 수 있어요.", + imageUrl: + "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_roomSharing.png", reward: 50, }, paying: { - name: "2명 이상 탑승한 방에서 정산하기", - description: "", - imageUrl: "", + name: "정산해요 택시의 숲", + description: + "2명 이상과 함께 택시를 타고 택시비를 결제한 후 정산하기를 요청해보세요. 정산하기 버튼은 채팅 페이지 좌측 하단의 +버튼을 눌러 확인할 수 있어요.", + imageUrl: + "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_paying.png", reward: 100, maxCount: 3, }, sending: { - name: "2명 이상 탑승한 방에서 송금하기", - description: "", - imageUrl: "", + name: "송금 완료! 친구야 고마워", + description: + "2명 이상과 함께 택시를 타고 택시비를 결제한 분께 송금해주세요. 송금하기 버튼은 채팅 페이지 좌측 하단의 +버튼을 눌러 확인할 수 있어요.", + imageUrl: + "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_sending.png", reward: 50, maxCount: 3, }, nicknameChanging: { - name: "닉네임 변경", - description: "", - imageUrl: "", + name: "닉네임 변신", + description: + "닉네임을 변경하여 자신을 표현하세요. 마이페이지의 수정하기 버튼을 눌러 닉네임을 수정할 수 있어요.", + imageUrl: + "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_nicknameChanging.png", reward: 50, }, accountChanging: { - name: "계좌 등록 또는 변경", - description: "", - imageUrl: "", + name: "계좌 등록은 정산의 시작", + description: + "정산하기 기능을 더욱 빠르고 이용할 수 있다고? 계좌번호를 등록하면 정산하기를 할 때 계좌가 자동으로 입력돼요. 마이페이지수정하기 버튼을 눌러 계좌번호를 등록 또는 수정할 수 있어요.", + imageUrl: + "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_accountChanging.png", reward: 50, }, adPushAgreement: { - name: "광고성 푸시 알림 수신 동의", - description: "", - imageUrl: "", + name: "Taxi의 소울메이트", + description: + "Taxi 서비스를 잊지 않도록 가끔 찾아갈게요! 광고성 푸시 알림 수신 동의를 해주시면 방이 많이 모이는 시즌, 주변에 택시앱 사용자가 있을 때 알려드릴 수 있어요.", + imageUrl: + "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_adPushAgreement.png", reward: 50, }, eventSharingOnInstagram: { - name: "이벤트 인스타그램 스토리에 공유", - description: "", - imageUrl: "", + name: "나만 알기에는 아까운 이벤트", + description: + "추석에 맞춰 쏟아지는 혜택들. 나만 알 순 없죠. 인스타그램 친구들에게 스토리로 공유해보아요. 이벤트 안내 페이지에서 인스타그램 스토리로 이벤트 공유하기 버튼을 눌러보세요.", + imageUrl: + "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_eventSharingOnInstagram.png", reward: 100, }, purchaseSharingOnInstagram: { - name: "아이템 구매 후 인스타그램 스토리에 공유", - description: "", - imageUrl: "", + name: "상품 획득을 축하합니다", + description: + "이벤트를 열심히 즐긴 당신. 그 상품 획득을 축하 받을 자격이 충분합니다. 달토끼 상점에서 상품 구매 후 뜨는 버튼을 눌러 상품 획득을 공유하세요.", + imageUrl: + "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_purchaseSharingOnInstagram.png", reward: 100, }, }); From ec8183a0031f33ebabc3dd3a6715a43e7e1c6d73 Mon Sep 17 00:00:00 2001 From: Geon Kim Date: Wed, 20 Sep 2023 13:21:10 +0900 Subject: [PATCH 39/67] Fix: detail in contracts/2023fall.js --- src/lottery/modules/contracts/2023fall.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 295ae4eb..3b7a9c22 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -83,7 +83,7 @@ const quests = buildQuests({ eventSharingOnInstagram: { name: "나만 알기에는 아까운 이벤트", description: - "추석에 맞춰 쏟아지는 혜택들. 나만 알 순 없죠. 인스타그램 친구들에게 스토리로 공유해보아요. 이벤트 안내 페이지에서 인스타그램 스토리로 이벤트 공유하기 버튼을 눌러보세요.", + "추석에 맞춰 쏟아지는 혜택들. 나만 알 순 없죠. 인스타그램 친구들에게 스토리로 공유해보아요. 이벤트 안내 페이지에서 인스타그램 스토리에 공유하기을 눌러보세요.", imageUrl: "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_eventSharingOnInstagram.png", reward: 100, @@ -91,7 +91,7 @@ const quests = buildQuests({ purchaseSharingOnInstagram: { name: "상품 획득을 축하합니다", description: - "이벤트를 열심히 즐긴 당신. 그 상품 획득을 축하 받을 자격이 충분합니다. 달토끼 상점에서 상품 구매 후 뜨는 버튼을 눌러 상품 획득을 공유하세요.", + "이벤트를 열심히 즐긴 당신. 그 상품 획득을 축하 받을 자격이 충분합니다. 달토끼 상점에서 상품 구매 후 뜨는 인스타그램 스토리에 공유하기 버튼을 눌러 상품 획득을 공유하세요.", imageUrl: "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_purchaseSharingOnInstagram.png", reward: 100, From 9d47eb764ab80a0fd6fe7bf596041b2e5843851a Mon Sep 17 00:00:00 2001 From: static Date: Wed, 20 Sep 2023 21:58:27 +0900 Subject: [PATCH 40/67] Refactor: loadenv.js --- loadenv.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/loadenv.js b/loadenv.js index 1f9528b6..3dfc5be6 100644 --- a/loadenv.js +++ b/loadenv.js @@ -38,5 +38,10 @@ module.exports = { slackWebhookUrl: { report: process.env.SLACK_REPORT_WEBHOOK_URL || "", // optional }, - eventConfig: process.env.EVENT_CONFIG && JSON.parse(process.env.EVENT_CONFIG), // optional + eventConfig: (process.env.EVENT_CONFIG && + JSON.parse(process.env.EVENT_CONFIG)) || { + mode: "2023fall", + startAt: "2023-09-25T00:00:00+09:00", + endAt: "2023-10-10T00:00:00+09:00", + }, }; From 535fbbbe27b5f064a3cf110e1d79c593b5c65ddd Mon Sep 17 00:00:00 2001 From: static Date: Wed, 20 Sep 2023 22:09:00 +0900 Subject: [PATCH 41/67] Fix: the condition of payingAndSendingQuest --- src/lottery/modules/contracts/2023fall.js | 22 ++++++---------------- src/services/rooms.js | 12 ++++++++++-- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 3b7a9c22..1677c26e 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -33,7 +33,7 @@ const quests = buildQuests({ roomSharing: { name: "Taxi로 모여라", description: - "방을 공유해 친구들을 택시에 초대해보세요. 채팅창 상단의 햄버거 버튼을 누르면 공유하기 버튼을 찾을 수 있어요.", + "방을 공유해 친구들을 택시에 초대해보세요. 채팅창 상단의 햄버거(☰) 버튼을 누르면 공유하기 버튼을 찾을 수 있어요.", imageUrl: "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_roomSharing.png", reward: 50, @@ -59,7 +59,7 @@ const quests = buildQuests({ nicknameChanging: { name: "닉네임 변신", description: - "닉네임을 변경하여 자신을 표현하세요. 마이페이지의 수정하기 버튼을 눌러 닉네임을 수정할 수 있어요.", + "닉네임을 변경하여 자신을 표현하세요. 마이페이지수정하기 버튼을 눌러 닉네임을 수정할 수 있어요.", imageUrl: "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_nicknameChanging.png", reward: 50, @@ -110,29 +110,19 @@ const completeFirstLoginQuest = async (userId, timestamp) => { }; /** - * payingAndSending 퀘스트의 완료를 요청합니다. 방의 참가자 수가 2명 미만이거나, 모든 참가자가 정산 또는 송금을 완료하지 않았다면 요청하지 않습니다. + * payingAndSending 퀘스트의 완료를 요청합니다. 방의 참가자 수가 2명 미만이면 요청하지 않습니다. + * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. * @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다. * @param {Object} roomObject - 방의 정보입니다. * @param {Array<{ user: mongoose.Types.ObjectId }>} roomObject.part - 참여자 목록입니다. - * @param {number} roomObject.settlementTotal - 정산 또는 송금이 완료된 참여자 수입니다. * @returns {Promise} * @description 정산 요청 또는 송금이 이루어질 때마다 호출해 주세요. * @usage rooms - commitPaymentHandler, rooms - settlementHandler */ -const completePayingAndSendingQuest = async (timestamp, roomObject) => { +const completePayingAndSendingQuest = async (userId, timestamp, roomObject) => { if (roomObject.part.length < 2) return null; - if (roomObject.part.length > roomObject.settlementTotal) return null; - return await Promise.all( - roomObject.part.map( - async (participant) => - await completeQuest( - participant.user._id, - timestamp, - quests.payingAndSending - ) - ) - ); + return await completeQuest(userId, timestamp, quests.payingAndSending); }; /** diff --git a/src/services/rooms.js b/src/services/rooms.js index 4ef35731..d4b7557e 100644 --- a/src/services/rooms.js +++ b/src/services/rooms.js @@ -497,7 +497,11 @@ const commitPaymentHandler = async (req, res) => { req.timestamp, roomObject ); - await contracts?.completePayingAndSendingQuest(req.timestamp, roomObject); + await contracts?.completePayingAndSendingQuest( + req.userOid, + req.timestamp, + roomObject + ); // 수정한 방 정보를 반환합니다. res.send(formatSettlement(roomObject, { isOver: true })); @@ -571,7 +575,11 @@ const settlementHandler = async (req, res) => { req.timestamp, roomObject ); - await contracts?.completePayingAndSendingQuest(req.timestamp, roomObject); + await contracts?.completePayingAndSendingQuest( + req.userOid, + req.timestamp, + roomObject + ); // 수정한 방 정보를 반환합니다. res.send(formatSettlement(roomObject, { isOver: true })); From 0a15da94337586fbeb6766cfbddaede5935b336d Mon Sep 17 00:00:00 2001 From: static Date: Wed, 20 Sep 2023 22:43:27 +0900 Subject: [PATCH 42/67] Add: check event period in some endpoints --- src/lottery/routes/docs/quests.js | 4 ++-- src/lottery/services/globalState.js | 14 +++++++++++++- src/lottery/services/quests.js | 22 ++++++++++++++++++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/lottery/routes/docs/quests.js b/src/lottery/routes/docs/quests.js index e85f934c..1d6a89c5 100644 --- a/src/lottery/routes/docs/quests.js +++ b/src/lottery/routes/docs/quests.js @@ -18,7 +18,7 @@ eventsDocs[`${apiPrefix}/instagram/share-event`] = { properties: { result: { type: "boolean", - description: "성공 여부. 항상 true입니다.", + description: "성공 여부", example: true, }, }, @@ -45,7 +45,7 @@ eventsDocs[`${apiPrefix}/instagram/share-purchase`] = { required: ["result"], properties: { result: { - description: "성공 여부. 항상 true입니다.", + description: "성공 여부", type: "boolean", example: true, }, diff --git a/src/lottery/services/globalState.js b/src/lottery/services/globalState.js index 32ff76cb..4e96f610 100644 --- a/src/lottery/services/globalState.js +++ b/src/lottery/services/globalState.js @@ -1,9 +1,13 @@ const { eventStatusModel } = require("../modules/stores/mongo"); +const { userModel } = require("../../modules/stores/mongo"); const logger = require("../../modules/logger"); const { isLogin, getLoginInfo } = require("../../modules/auths/login"); const { eventConfig } = require("../../../loadenv"); -const { userModel } = require("../../modules/stores/mongo"); +const eventPeriod = eventConfig && { + startAt: new Date(eventConfig.startAt), + endAt: new Date(eventConfig.endAt), +}; const contracts = eventConfig && require(`../modules/contracts/${eventConfig.mode}`); const quests = contracts ? Object.values(contracts.quests) : undefined; @@ -37,6 +41,14 @@ const getUserGlobalStateHandler = async (req, res) => { const createUserGlobalStateHandler = async (req, res) => { try { + if ( + req.timestamp >= eventPeriod.endAt || + req.timestamp < eventPeriod.startAt + ) + return res + .status(400) + .json({ error: "GlobalState/Create : out of date" }); + let eventStatus = await eventStatusModel .findOne({ userId: req.userOid }) .lean(); diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js index 7212893f..bdfa89ac 100644 --- a/src/lottery/services/quests.js +++ b/src/lottery/services/quests.js @@ -1,11 +1,25 @@ const logger = require("../../modules/logger"); const contracts = require("../modules/contracts/2023fall"); +const { eventConfig } = require("../../../loadenv"); +const eventPeriod = eventConfig && { + startAt: new Date(eventConfig.startAt), + endAt: new Date(eventConfig.endAt), +}; + /** * 인스타그램 스토리에 이벤트를 공유했을 때. */ const instagramEventShareHandler = async (req, res) => { try { + if ( + req.timestamp >= eventPeriod.endAt || + req.timestamp < eventPeriod.startAt + ) + return res + .status(400) + .json({ error: "Quests/Instagram/ShareEvent : out of date" }); + const { userOid } = req; const contractResult = await contracts.completeEventSharingOnInstagramQuest( userOid, @@ -25,6 +39,14 @@ const instagramEventShareHandler = async (req, res) => { */ const instagramPurchaseShareHandler = async (req, res) => { try { + if ( + req.timestamp >= eventPeriod.endAt || + req.timestamp < eventPeriod.startAt + ) + return res + .status(400) + .json({ error: "Quests/Instagram/SharePurchase : out of date" }); + const { userOid } = req; const contractResult = await contracts.completePurchaseSharingOnInstagramQuest( From 1fef3b18161b2178cadea9f7270b9955b0ab3096 Mon Sep 17 00:00:00 2001 From: static Date: Wed, 20 Sep 2023 22:56:52 +0900 Subject: [PATCH 43/67] Add: timestampValidator middleware --- src/lottery/middlewares/timestampValidator.js | 18 +++++++++++++++ src/lottery/routes/globalState.js | 3 ++- src/lottery/routes/items.js | 3 ++- src/lottery/routes/quests.js | 1 + src/lottery/services/globalState.js | 12 ---------- src/lottery/services/items.js | 12 ---------- src/lottery/services/quests.js | 22 ------------------- 7 files changed, 23 insertions(+), 48 deletions(-) create mode 100644 src/lottery/middlewares/timestampValidator.js diff --git a/src/lottery/middlewares/timestampValidator.js b/src/lottery/middlewares/timestampValidator.js new file mode 100644 index 00000000..781ec265 --- /dev/null +++ b/src/lottery/middlewares/timestampValidator.js @@ -0,0 +1,18 @@ +const { eventConfig } = require("../../../loadenv"); +const eventPeriod = eventConfig && { + startAt: new Date(eventConfig.startAt), + endAt: new Date(eventConfig.endAt), +}; + +const timestampValidator = (req, res, next) => { + if ( + req.timestamp >= eventPeriod.endAt || + req.timestamp < eventPeriod.startAt + ) { + return res.status(400).json({ error: "out of date" }); + } else { + next(); + } +}; + +module.exports = timestampValidator; diff --git a/src/lottery/routes/globalState.js b/src/lottery/routes/globalState.js index be63bb3f..cb69d782 100644 --- a/src/lottery/routes/globalState.js +++ b/src/lottery/routes/globalState.js @@ -7,8 +7,9 @@ const globalStateSchema = require("./docs/globalStateSchema"); router.get("/", globalStateHandlers.getUserGlobalStateHandler); -// 아래의 Endpoint 접근 시 로그인 필요 +// 아래의 Endpoint 접근 시 로그인 및 시각 체크 필요 router.use(require("../../middlewares/auth")); +router.use(require("../middlewares/timestampValidator")); router.post( "/create", diff --git a/src/lottery/routes/items.js b/src/lottery/routes/items.js index 95136a5f..21dc47ae 100644 --- a/src/lottery/routes/items.js +++ b/src/lottery/routes/items.js @@ -8,8 +8,9 @@ const itemsSchema = require("./docs/itemsSchema"); router.get("/list", itemsHandlers.listHandler); -// 아래의 Endpoint 접근 시 로그인 필요 +// 아래의 Endpoint 접근 시 로그인 및 시각 체크 필요 router.use(require("../../middlewares/auth")); +router.use(require("../middlewares/timestampValidator")); router.post( "/purchase/:itemId", diff --git a/src/lottery/routes/quests.js b/src/lottery/routes/quests.js index f8cf8177..032c96c7 100644 --- a/src/lottery/routes/quests.js +++ b/src/lottery/routes/quests.js @@ -3,6 +3,7 @@ const router = express.Router(); const quests = require("../services/quests"); router.use(require("../../middlewares/auth")); +router.use(require("../middlewares/timestampValidator")); router.post("/instagram/share-event", quests.instagramEventShareHandler); router.post("/instagram/share-purchase", quests.instagramPurchaseShareHandler); diff --git a/src/lottery/services/globalState.js b/src/lottery/services/globalState.js index 4e96f610..c36ac9f1 100644 --- a/src/lottery/services/globalState.js +++ b/src/lottery/services/globalState.js @@ -4,10 +4,6 @@ const logger = require("../../modules/logger"); const { isLogin, getLoginInfo } = require("../../modules/auths/login"); const { eventConfig } = require("../../../loadenv"); -const eventPeriod = eventConfig && { - startAt: new Date(eventConfig.startAt), - endAt: new Date(eventConfig.endAt), -}; const contracts = eventConfig && require(`../modules/contracts/${eventConfig.mode}`); const quests = contracts ? Object.values(contracts.quests) : undefined; @@ -41,14 +37,6 @@ const getUserGlobalStateHandler = async (req, res) => { const createUserGlobalStateHandler = async (req, res) => { try { - if ( - req.timestamp >= eventPeriod.endAt || - req.timestamp < eventPeriod.startAt - ) - return res - .status(400) - .json({ error: "GlobalState/Create : out of date" }); - let eventStatus = await eventStatusModel .findOne({ userId: req.userOid }) .lean(); diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index fa860077..ece89fec 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -20,12 +20,6 @@ const updateEventStatus = async ( } ); -const { eventConfig } = require("../../../loadenv"); -const eventPeriod = eventConfig && { - startAt: new Date(eventConfig.startAt), - endAt: new Date(eventConfig.endAt), -}; - const getRandomItem = async (req, depth) => { if (depth >= 10) { logger.error(`User ${req.userOid} failed to open random box`); @@ -130,12 +124,6 @@ const purchaseHandler = async (req, res) => { .status(400) .json({ error: "Items/Purchase : nonexistent eventStatus" }); - if ( - req.timestamp >= eventPeriod.endAt || - req.timestamp < eventPeriod.startAt - ) - return res.status(400).json({ error: "Items/Purchase : out of date" }); - const { itemId } = req.params; const item = await itemModel.findOne({ _id: itemId }).lean(); if (!item) diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js index bdfa89ac..7212893f 100644 --- a/src/lottery/services/quests.js +++ b/src/lottery/services/quests.js @@ -1,25 +1,11 @@ const logger = require("../../modules/logger"); const contracts = require("../modules/contracts/2023fall"); -const { eventConfig } = require("../../../loadenv"); -const eventPeriod = eventConfig && { - startAt: new Date(eventConfig.startAt), - endAt: new Date(eventConfig.endAt), -}; - /** * 인스타그램 스토리에 이벤트를 공유했을 때. */ const instagramEventShareHandler = async (req, res) => { try { - if ( - req.timestamp >= eventPeriod.endAt || - req.timestamp < eventPeriod.startAt - ) - return res - .status(400) - .json({ error: "Quests/Instagram/ShareEvent : out of date" }); - const { userOid } = req; const contractResult = await contracts.completeEventSharingOnInstagramQuest( userOid, @@ -39,14 +25,6 @@ const instagramEventShareHandler = async (req, res) => { */ const instagramPurchaseShareHandler = async (req, res) => { try { - if ( - req.timestamp >= eventPeriod.endAt || - req.timestamp < eventPeriod.startAt - ) - return res - .status(400) - .json({ error: "Quests/Instagram/SharePurchase : out of date" }); - const { userOid } = req; const contractResult = await contracts.completePurchaseSharingOnInstagramQuest( From 719aad6b2a660878d31a412a82ba1b8c8f47ed4b Mon Sep 17 00:00:00 2001 From: static Date: Wed, 20 Sep 2023 22:57:56 +0900 Subject: [PATCH 44/67] Remove: hideNickname function --- src/lottery/services/publicNotice.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 2ce717af..e6b287fa 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -7,10 +7,6 @@ const { publicNoticePopulateOption, } = require("../modules/populates/transactions"); -const hideNickname = (nickname) => { - return `${nickname.toString().slice(0, 2)}${"*".repeat(nickname.length - 2)}`; -}; - const getRecentPurchaceItemListHandler = async (req, res) => { try { const transactions = ( @@ -22,7 +18,7 @@ const getRecentPurchaceItemListHandler = async (req, res) => { .lean() ).map( ({ userId, item, comment }) => - `${hideNickname(userId.nickname)}님께서 ${item.name}을(를) ${ + `${userId.nickname}님께서 ${item.name}을(를) ${ comment.startsWith("송편") ? "을(를) 구입하셨습니다." : comment.startsWith("랜덤 박스") @@ -73,7 +69,7 @@ const getTicketLeaderboardHandler = async (req, res) => { return null; } return { - nickname: hideNickname(userInfo.nickname), + nickname: userInfo.nickname, profileImageUrl: userInfo.profileImageUrl, ticket1Amount: user.ticket1Amount, ticket2Amount: user.ticket2Amount, From 7d54a704b9fee3f3a4b04cd40a62788631c97102 Mon Sep 17 00:00:00 2001 From: static Date: Wed, 20 Sep 2023 23:03:11 +0900 Subject: [PATCH 45/67] Fix: check if eventPeriod is defined in timestampValidator --- src/lottery/middlewares/timestampValidator.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lottery/middlewares/timestampValidator.js b/src/lottery/middlewares/timestampValidator.js index 781ec265..22511536 100644 --- a/src/lottery/middlewares/timestampValidator.js +++ b/src/lottery/middlewares/timestampValidator.js @@ -6,6 +6,7 @@ const eventPeriod = eventConfig && { const timestampValidator = (req, res, next) => { if ( + !eventPeriod || req.timestamp >= eventPeriod.endAt || req.timestamp < eventPeriod.startAt ) { From 4ce23a0c450dd2431dbe590f17239dd104dcb86a Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Thu, 21 Sep 2023 00:41:56 +0900 Subject: [PATCH 46/67] Add: getValueRank evaluate the rank of public notice --- src/lottery/services/publicNotice.js | 45 ++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index e6b287fa..ce0f5633 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -7,25 +7,46 @@ const { publicNoticePopulateOption, } = require("../modules/populates/transactions"); +const hideNickname = (nickname) => { + return `${nickname.toString().slice(0, 2)}${"*".repeat(nickname.length - 2)}`; +}; + +const getValueRank = (item, createAt, timestamp) => { + return ( + (parseInt( + (new Date(createAt).getTime() / timestamp).toString().slice(7, 10) + ) * + item.price) / + 1000 + ); +}; + const getRecentPurchaceItemListHandler = async (req, res) => { try { const transactions = ( await transactionModel .find({ type: "use", itemType: 0 }) .sort({ createAt: -1 }) - .limit(5) + .limit(1000) .populate(publicNoticePopulateOption) .lean() - ).map( - ({ userId, item, comment }) => - `${userId.nickname}님께서 ${item.name}을(를) ${ - comment.startsWith("송편") - ? "을(를) 구입하셨습니다." - : comment.startsWith("랜덤 박스") - ? "을(를) 뽑았습니다." - : "을(를) 획득하셨습니다." - }` - ); + ) + .sort( + (x, y) => + getValueRank(y.item, y.createAt, req.timestamp) - + getValueRank(x.item, x.createAt, req.timestamp) + ) + .slice(0, 5) + .map( + ({ userId, item, comment }) => + `${hideNickname(userId.nickname)}님께서 ${item.name}을(를) ${ + comment.startsWith("송편") + ? " 을(를) 구입하셨습니다." + : comment.startsWith("랜덤 박스") + ? "을(를) 뽑았습니다." + : "을(를) 획득하셨습니다." + }` + ); res.json({ transactions }); } catch (err) { logger.error(err); @@ -69,7 +90,7 @@ const getTicketLeaderboardHandler = async (req, res) => { return null; } return { - nickname: userInfo.nickname, + nickname: hideNickname(userInfo.nickname), profileImageUrl: userInfo.profileImageUrl, ticket1Amount: user.ticket1Amount, ticket2Amount: user.ticket2Amount, From 91c442566f35366f24d26b0710f7648f8b8424e9 Mon Sep 17 00:00:00 2001 From: static Date: Thu, 21 Sep 2023 11:18:36 +0900 Subject: [PATCH 47/67] Add: hideItemStock function --- src/lottery/routes/docs/itemsSchema.js | 2 +- src/lottery/services/items.js | 11 ++++++++--- src/lottery/services/publicNotice.js | 2 +- src/lottery/services/transactions.js | 11 ++++++++++- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/lottery/routes/docs/itemsSchema.js b/src/lottery/routes/docs/itemsSchema.js index 1601f237..5f592053 100644 --- a/src/lottery/routes/docs/itemsSchema.js +++ b/src/lottery/routes/docs/itemsSchema.js @@ -43,7 +43,7 @@ const itemBase = { }, stock: { type: "number", - description: "남은 상품 재고. 0 이상입니다.", + description: "남은 상품 재고. 재고가 있는 경우 1, 없는 경우 0입니다.", example: 10, }, }, diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index ece89fec..760c2e90 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -20,6 +20,11 @@ const updateEventStatus = async ( } ); +const hideItemStock = (item) => { + item.stock = item.stock > 0 ? 1 : 0; + return item; +}; + const getRandomItem = async (req, depth) => { if (depth >= 10) { logger.error(`User ${req.userOid} failed to open random box`); @@ -89,7 +94,7 @@ const getRandomItem = async (req, depth) => { userId: req.userOid, item: randomItem._id, itemType: randomItem.itemType, - comment: `랜덤 박스에서 "${randomItem.name}" 1개를 획득했습니다.`, + comment: `랜덤박스에서 "${randomItem.name}" 1개를 획득했습니다.`, }); await transaction.save(); @@ -109,7 +114,7 @@ const listHandler = async (_, res) => { const items = await itemModel .find({}, "name imageUrl price description isDisabled stock itemType") .lean(); - res.json({ items }); + res.json({ items: items.map((item) => hideItemStock(item)) }); } catch (err) { logger.error(err); res.status(500).json({ error: "Items/List : internal server error" }); @@ -181,7 +186,7 @@ const purchaseHandler = async (req, res) => { res.json({ result: true, - reward: randomItem, + reward: hideItemStock(randomItem), }); } catch (err) { logger.error(err); diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index e6b287fa..62421bbd 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -21,7 +21,7 @@ const getRecentPurchaceItemListHandler = async (req, res) => { `${userId.nickname}님께서 ${item.name}을(를) ${ comment.startsWith("송편") ? "을(를) 구입하셨습니다." - : comment.startsWith("랜덤 박스") + : comment.startsWith("랜덤박스") ? "을(를) 뽑았습니다." : "을(를) 획득하셨습니다." }` diff --git a/src/lottery/services/transactions.js b/src/lottery/services/transactions.js index c151d81f..22b8c4a1 100644 --- a/src/lottery/services/transactions.js +++ b/src/lottery/services/transactions.js @@ -4,6 +4,13 @@ const { transactionPopulateOption, } = require("../modules/populates/transactions"); +const hideItemStock = (transaction) => { + if (transaction.item) { + transaction.item.stock = transaction.item.stock > 0 ? 1 : 0; + } + return transaction; +}; + const getUserTransactionsHandler = async (req, res) => { try { // userId는 이미 Frontend에서 알고 있고, 중복되는 값이므로 제외합니다. @@ -13,7 +20,9 @@ const getUserTransactionsHandler = async (req, res) => { .lean(); if (transactions) res.json({ - transactions, + transactions: transactions.map((transaction) => + hideItemStock(transaction) + ), }); else res.status(500).json({ error: "Transactions/ : internal server error" }); From f3f2ff2930a72bfade19ac602de589d4552aaf80 Mon Sep 17 00:00:00 2001 From: static Date: Thu, 21 Sep 2023 11:26:59 +0900 Subject: [PATCH 48/67] Refactor: send 400 code when item is out of stock --- src/lottery/modules/quests.js | 3 ++- src/lottery/services/items.js | 7 +++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/lottery/modules/quests.js b/src/lottery/modules/quests.js index 26bf2e24..d93ec66d 100644 --- a/src/lottery/modules/quests.js +++ b/src/lottery/modules/quests.js @@ -98,7 +98,8 @@ const completeQuest = async (userId, timestamp, quest) => { // 5단계: 완료 보상 중 티켓이 있는 경우, 티켓 정보를 가져옵니다. const ticket1 = quest.reward.ticket1 && (await itemModel.findOne({ itemType: 1 }).lean()); - if (quest.reward.ticket1 && !ticket1) throw "Fail to find ticket1"; + if (quest.reward.ticket1 && !ticket1) + throw new Error("Fail to find ticket1"); // 6단계: 유저의 EventStatus를 업데이트합니다. await eventStatusModel.updateOne( diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index 760c2e90..bf84b04a 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -78,7 +78,7 @@ const getRandomItem = async (req, depth) => { ) .lean(); if (!newRandomItem) { - throw new Error("The item was already sold out"); + throw new Error(`Item ${randomItem._id.toString()} was already sold out`); } // 2단계: 유저 정보를 업데이트합니다. @@ -155,7 +155,10 @@ const purchaseHandler = async (req, res) => { }, } ); - if (modifiedCount === 0) throw new Error("The item was already sold out"); + if (modifiedCount === 0) + return res + .status(400) + .json({ error: "Items/Purchase : item out of stock" }); // 2단계: 유저 정보를 업데이트합니다. await updateEventStatus(req.userOid, { From 2f0b3f8807f88e26dd151cedbcbc8822fdf495c0 Mon Sep 17 00:00:00 2001 From: static Date: Thu, 21 Sep 2023 11:59:32 +0900 Subject: [PATCH 49/67] Add: status restore when random box failing --- src/lottery/services/items.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index bf84b04a..160bb644 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -39,9 +39,7 @@ const getRandomItem = async (req, depth) => { }) .lean(); const randomItems = items - .map((item) => { - return Array(item.randomWeight).fill(item); - }) + .map((item) => Array(item.randomWeight).fill(item)) .reduce((a, b) => a.concat(b), []); const dumpRandomItems = randomItems .map((item) => item._id.toString()) @@ -182,10 +180,30 @@ const purchaseHandler = async (req, res) => { if (item.itemType !== 3) return res.json({ result: true }); const randomItem = await getRandomItem(req, 0); - if (!randomItem) + if (!randomItem) { + // 랜덤박스가 실패한 경우, 상태를 구매 이전으로 되돌립니다. + // TODO: Transactions 도입 후 이 코드는 삭제합니다. + logger.info(`User ${req.userOid}'s status will be restored`); + + await transactionModel.deleteOne({ _id: transaction._id }); + await updateEventStatus(req.userOid, { + creditDelta: item.price, + }); + await itemModel.updateOne( + { _id: item._id }, + { + $inc: { + stock: 1, + }, + } + ); + + logger.info(`User ${req.userOid}'s status was successfully restored`); + return res .status(500) .json({ error: "Items/Purchase : random box error" }); + } res.json({ result: true, From 0c1fe65b9c8f9112128b7c0d07b1ecc0a9563f22 Mon Sep 17 00:00:00 2001 From: static Date: Thu, 21 Sep 2023 13:54:30 +0900 Subject: [PATCH 50/67] Docs: update example of stock field --- src/lottery/routes/docs/itemsSchema.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lottery/routes/docs/itemsSchema.js b/src/lottery/routes/docs/itemsSchema.js index 5f592053..2a4eca64 100644 --- a/src/lottery/routes/docs/itemsSchema.js +++ b/src/lottery/routes/docs/itemsSchema.js @@ -44,7 +44,7 @@ const itemBase = { stock: { type: "number", description: "남은 상품 재고. 재고가 있는 경우 1, 없는 경우 0입니다.", - example: 10, + example: 1, }, }, }; From 5d4f9002764ca8eb1bd610d26555449eea80aad3 Mon Sep 17 00:00:00 2001 From: static Date: Thu, 21 Sep 2023 18:28:16 +0900 Subject: [PATCH 51/67] Refactor: resolve comment by @14KGun --- src/lottery/services/items.js | 2 +- src/lottery/services/transactions.js | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index 160bb644..f4eeccdb 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -112,7 +112,7 @@ const listHandler = async (_, res) => { const items = await itemModel .find({}, "name imageUrl price description isDisabled stock itemType") .lean(); - res.json({ items: items.map((item) => hideItemStock(item)) }); + res.json({ items: items.map(hideItemStock) }); } catch (err) { logger.error(err); res.status(500).json({ error: "Items/List : internal server error" }); diff --git a/src/lottery/services/transactions.js b/src/lottery/services/transactions.js index 22b8c4a1..695cf3bf 100644 --- a/src/lottery/services/transactions.js +++ b/src/lottery/services/transactions.js @@ -20,9 +20,7 @@ const getUserTransactionsHandler = async (req, res) => { .lean(); if (transactions) res.json({ - transactions: transactions.map((transaction) => - hideItemStock(transaction) - ), + transactions: transactions.map(hideItemStock), }); else res.status(500).json({ error: "Transactions/ : internal server error" }); From d2fd93d24aeacb23384dcab74cce87f89ecbbfc5 Mon Sep 17 00:00:00 2001 From: static Date: Fri, 22 Sep 2023 05:00:26 +0900 Subject: [PATCH 52/67] Add: addOneItemStockAction and addFiveItemStockAction --- src/lottery/index.js | 22 +++++++----- src/lottery/modules/items.js | 66 ++++++++++++++++++++++++++++++++++++ src/modules/adminResource.js | 4 +-- 3 files changed, 82 insertions(+), 10 deletions(-) create mode 100644 src/lottery/modules/items.js diff --git a/src/lottery/index.js b/src/lottery/index.js index a24af419..9fc6f2c4 100644 --- a/src/lottery/index.js +++ b/src/lottery/index.js @@ -6,8 +6,13 @@ const { transactionModel, } = require("./modules/stores/mongo"); -const { eventConfig } = require("../../loadenv"); const { buildResource } = require("../modules/adminResource"); +const { + addOneItemStockAction, + addFiveItemStockAction, +} = require("./modules/items"); + +const { eventConfig } = require("../../loadenv"); // [Routes] 기존 docs 라우터의 docs extend eventConfig && require("./routes/docs")(); @@ -24,18 +29,19 @@ lotteryRouter.use("/items", require("./routes/items")); lotteryRouter.use("/public-notice", require("./routes/publicNotice")); lotteryRouter.use("/quests", require("./routes/quests")); -const resources = [ - eventStatusModel, - questModel, - itemModel, - transactionModel, -].map(buildResource()); +const itemResource = buildResource([ + addOneItemStockAction, + addFiveItemStockAction, +])(itemModel); +const otherResources = [eventStatusModel, questModel, transactionModel].map( + buildResource() +); const contracts = eventConfig && require(`./modules/contracts/${eventConfig.mode}`); module.exports = { lotteryRouter, - resources, + resources: [itemResource, ...otherResources], contracts, }; diff --git a/src/lottery/modules/items.js b/src/lottery/modules/items.js new file mode 100644 index 00000000..dae8e906 --- /dev/null +++ b/src/lottery/modules/items.js @@ -0,0 +1,66 @@ +const { itemModel } = require("./stores/mongo"); +const { buildRecordAction } = require("../../modules/adminResource"); +const logger = require("../../modules/logger"); + +const addItemStockActionHandler = (count) => async (req, res, context) => { + const itemId = context.record.params._id; + const oldStock = context.record.params.stock; + + try { + const item = await itemModel + .findOneAndUpdate( + { _id: itemId }, + { + $inc: { + stock: count, + }, + }, + { + new: true, + } + ) + .lean(); + if (!item) throw new Error("Fail to update stock"); + + let record = context.record.toJSON(context.currentAdmin); + record.params = item; + + return { + record, + notice: { + message: `성공적으로 재고 ${count}개를 추가했습니다. (${oldStock} → ${item.stock})`, + }, + response: {}, + }; + } catch (err) { + logger.error(err); + logger.error( + `Fail to process addItemStockActionHandler(${count}) for Item ${itemId}` + ); + + return { + record: context.record.toJSON(context.currentAdmin), + notice: { + message: `재고를 추가하지 못했습니다. 오류 메세지: ${err}`, + type: "error", + }, + }; + } +}; +const addItemStockActionLogs = ["update"]; + +const addOneItemStockAction = buildRecordAction( + "addOneItemStock", + addItemStockActionHandler(1), + addItemStockActionLogs +); +const addFiveItemStockAction = buildRecordAction( + "addFiveItemStock", + addItemStockActionHandler(5), + addItemStockActionLogs +); + +module.exports = { + addOneItemStockAction, + addFiveItemStockAction, +}; diff --git a/src/modules/adminResource.js b/src/modules/adminResource.js index b75013b3..f5ba3eb8 100644 --- a/src/modules/adminResource.js +++ b/src/modules/adminResource.js @@ -72,7 +72,7 @@ const recordActionAfterHandler = (actions) => async (res, req, context) => { return res; }; -const recordAction = (actionName, handler, logActions) => ({ +const buildRecordAction = (actionName, handler, logActions) => ({ actionName, actionType: "record", component: false, @@ -100,6 +100,6 @@ const buildResource = module.exports = { generateTarget, - recordAction, + buildRecordAction, buildResource, }; From a106e8587bf75231c2a7ad5269531aee7643c01c Mon Sep 17 00:00:00 2001 From: static Date: Fri, 22 Sep 2023 22:28:00 +0900 Subject: [PATCH 53/67] Add: /events/2023fall/quests/share-room endpoint --- src/lottery/modules/contracts/2023fall.js | 12 ++++++++++-- src/lottery/routes/quests.js | 1 + src/lottery/services/quests.js | 18 ++++++++++++++++++ 3 files changed, 29 insertions(+), 2 deletions(-) diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 1677c26e..10f562c1 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -137,8 +137,16 @@ const completeFirstRoomCreationQuest = async (userId, timestamp) => { return await completeQuest(userId, timestamp, quests.firstRoomCreation); }; -const completeRoomSharingQuest = async () => { - // TODO +/** + * roomSharing 퀘스트의 완료를 요청합니다. + * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. + * @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다. + * @returns {Promise} + * @description 인스타그램 스토리에 추석 이벤트를 공유할 때마다 호출해 주세요. + * @usage quests - instagramEventShareHandler + */ +const completeRoomSharingQuest = async (userId, timestamp) => { + return await completeQuest(userId, timestamp, quests.roomSharing); }; /** diff --git a/src/lottery/routes/quests.js b/src/lottery/routes/quests.js index 032c96c7..b1ec591b 100644 --- a/src/lottery/routes/quests.js +++ b/src/lottery/routes/quests.js @@ -5,6 +5,7 @@ const quests = require("../services/quests"); router.use(require("../../middlewares/auth")); router.use(require("../middlewares/timestampValidator")); +router.post("/share-room", quests.roomShareHandler); router.post("/instagram/share-event", quests.instagramEventShareHandler); router.post("/instagram/share-purchase", quests.instagramPurchaseShareHandler); diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js index 7212893f..1ba5df08 100644 --- a/src/lottery/services/quests.js +++ b/src/lottery/services/quests.js @@ -1,6 +1,23 @@ const logger = require("../../modules/logger"); const contracts = require("../modules/contracts/2023fall"); +/** + * 방을 공유했을 때. + */ +const roomShareHandler = async (req, res) => { + try { + const { userOid } = req; + const contractResult = await contracts.completeRoomSharingQuest( + userOid, + req.timestamp + ); + res.json({ result: !!contractResult }); + } catch (err) { + logger.error(err); + res.status(500).json({ error: "Quests/ShareRoom: internal server error" }); + } +}; + /** * 인스타그램 스토리에 이벤트를 공유했을 때. */ @@ -41,6 +58,7 @@ const instagramPurchaseShareHandler = async (req, res) => { }; module.exports = { + roomShareHandler, instagramEventShareHandler, instagramPurchaseShareHandler, }; From 58602dd85e9b2503c523f3e7d8943dc487c678c4 Mon Sep 17 00:00:00 2001 From: static Date: Sat, 23 Sep 2023 00:12:29 +0900 Subject: [PATCH 54/67] Add: /event/2023fall/quests/complete endpoint --- src/lottery/modules/contracts/2023fall.js | 46 ++-------------- src/lottery/modules/quests.js | 3 ++ src/lottery/routes/docs/globalState.js | 10 +++- src/lottery/routes/docs/items.js | 10 ++++ src/lottery/routes/docs/quests.js | 38 ++++--------- src/lottery/routes/docs/questsSchema.js | 13 +++++ src/lottery/routes/docs/swaggerDocs.js | 24 +++++---- src/lottery/routes/quests.js | 14 +++-- src/lottery/services/quests.js | 65 +++++------------------ 9 files changed, 84 insertions(+), 139 deletions(-) create mode 100644 src/lottery/routes/docs/questsSchema.js diff --git a/src/lottery/modules/contracts/2023fall.js b/src/lottery/modules/contracts/2023fall.js index 10f562c1..a896a6ce 100644 --- a/src/lottery/modules/contracts/2023fall.js +++ b/src/lottery/modules/contracts/2023fall.js @@ -37,6 +37,7 @@ const quests = buildQuests({ imageUrl: "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_roomSharing.png", reward: 50, + isApiRequired: true, }, paying: { name: "정산해요 택시의 숲", @@ -87,6 +88,7 @@ const quests = buildQuests({ imageUrl: "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_eventSharingOnInstagram.png", reward: 100, + isApiRequired: true, }, purchaseSharingOnInstagram: { name: "상품 획득을 축하합니다", @@ -95,6 +97,7 @@ const quests = buildQuests({ imageUrl: "https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_purchaseSharingOnInstagram.png", reward: 100, + isApiRequired: true, }, }); @@ -137,18 +140,6 @@ const completeFirstRoomCreationQuest = async (userId, timestamp) => { return await completeQuest(userId, timestamp, quests.firstRoomCreation); }; -/** - * roomSharing 퀘스트의 완료를 요청합니다. - * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. - * @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다. - * @returns {Promise} - * @description 인스타그램 스토리에 추석 이벤트를 공유할 때마다 호출해 주세요. - * @usage quests - instagramEventShareHandler - */ -const completeRoomSharingQuest = async (userId, timestamp) => { - return await completeQuest(userId, timestamp, quests.roomSharing); -}; - /** * paying 퀘스트의 완료를 요청합니다. 방의 참가자 수가 2명 미만이면 요청하지 않습니다. * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. @@ -227,45 +218,14 @@ const completeAdPushAgreementQuest = async ( return await completeQuest(userId, timestamp, quests.adPushAgreement); }; -/** - * eventSharingOnInstagram 퀘스트의 완료를 요청합니다. - * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. - * @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다. - * @returns {Promise} - * @description 인스타그램 스토리에 추석 이벤트를 공유할 때마다 호출해 주세요. - * @usage quests - instagramEventShareHandler - */ -const completeEventSharingOnInstagramQuest = async (userId, timestamp) => { - return await completeQuest(userId, timestamp, quests.eventSharingOnInstagram); -}; - -/** - * purchaseSharingOnInstagram 퀘스트의 완료를 요청합니다. - * @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다. - * @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다. - * @returns {Promise} - * @description 인스타그램 스토리에 구매한 아이템을 공유할 때마다 호출해 주세요. - * @usage quests - instagramPurchaseShareHandler - */ -const completePurchaseSharingOnInstagramQuest = async (userId, timestamp) => { - return await completeQuest( - userId, - timestamp, - quests.purchaseSharingOnInstagram - ); -}; - module.exports = { quests, completeFirstLoginQuest, completePayingAndSendingQuest, completeFirstRoomCreationQuest, - completeRoomSharingQuest, completePayingQuest, completeSendingQuest, completeNicknameChangingQuest, completeAccountChangingQuest, completeAdPushAgreementQuest, - completeEventSharingOnInstagramQuest, - completePurchaseSharingOnInstagramQuest, }; diff --git a/src/lottery/modules/quests.js b/src/lottery/modules/quests.js index d93ec66d..b61b3b77 100644 --- a/src/lottery/modules/quests.js +++ b/src/lottery/modules/quests.js @@ -42,6 +42,9 @@ const buildQuests = (quests) => { // quest.maxCount가 없는 경우, 기본값(1)으로 설정합니다. quest.maxCount = quest.maxCount || 1; + + // quest.isApiRequired가 없는 경우, 기본값(false)으로 설정합니다. + quest.isApiRequired = quest.isApiRequired || false; } return quests; diff --git a/src/lottery/routes/docs/globalState.js b/src/lottery/routes/docs/globalState.js index 4cf9630b..077f7628 100644 --- a/src/lottery/routes/docs/globalState.js +++ b/src/lottery/routes/docs/globalState.js @@ -66,6 +66,7 @@ globalStateDocs[`${apiPrefix}/`] = { "imageUrl", "reward", "maxCount", + "isApiRequired", ], properties: { id: { @@ -111,6 +112,11 @@ globalStateDocs[`${apiPrefix}/`] = { description: "최대 완료 가능 횟수", example: 1, }, + isApiRequired: { + type: "boolean", + description: `/events/${eventConfig.mode}/quests/complete/:questId API를 통해 퀘스트 완료를 요청할 수 있는지 여부`, + example: false, + }, }, }, }, @@ -123,13 +129,13 @@ globalStateDocs[`${apiPrefix}/`] = { }, }; globalStateDocs[`${apiPrefix}/create`] = { - get: { + post: { tags: [`${apiPrefix}`], summary: "Frontend에서 Global state로 관리하는 정보 생성", description: "유저의 재화 개수, 퀘스트 완료 상태 등 Frontend에서 Global state로 관리할 정보를 생성합니다.", requestBody: { - description: "Update an existent user in the store", + description: "", content: { "application/json": { schema: { diff --git a/src/lottery/routes/docs/items.js b/src/lottery/routes/docs/items.js index 4079795a..39bc9111 100644 --- a/src/lottery/routes/docs/items.js +++ b/src/lottery/routes/docs/items.js @@ -37,6 +37,16 @@ itemsDocs[`${apiPrefix}/purchase/:itemId`] = { tags: [`${apiPrefix}`], summary: "상품 구매", description: "상품을 구매합니다.", + requestBody: { + description: "", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/purchaseHandler", + }, + }, + }, + }, responses: { 200: { description: "", diff --git a/src/lottery/routes/docs/quests.js b/src/lottery/routes/docs/quests.js index 1d6a89c5..7ba5a42c 100644 --- a/src/lottery/routes/docs/quests.js +++ b/src/lottery/routes/docs/quests.js @@ -2,39 +2,21 @@ const { eventConfig } = require("../../../../loadenv"); const apiPrefix = `/events/${eventConfig.mode}/quests`; const eventsDocs = {}; -eventsDocs[`${apiPrefix}/instagram/share-event`] = { +eventsDocs[`${apiPrefix}/complete/:questId`] = { post: { tags: [`${apiPrefix}`], - summary: "eventSharingOnInstagram 퀘스트 완료 요청", - description: "eventSharingOnInstagram 퀘스트의 완료를 요청합니다.", - responses: { - 200: { - description: "", - content: { - "application/json": { - schema: { - type: "object", - required: ["result"], - properties: { - result: { - type: "boolean", - description: "성공 여부", - example: true, - }, - }, - }, + summary: "퀘스트 완료 요청", + description: "퀘스트의 완료를 요청합니다.", + requestBody: { + description: "", + content: { + "application/json": { + schema: { + $ref: "#/components/schemas/completeHandler", }, }, }, }, - }, -}; - -eventsDocs[`${apiPrefix}/instagram/share-purchase`] = { - post: { - tags: [`${apiPrefix}`], - summary: "purchaseSharingOnInstagram 퀘스트 완료 요청", - description: "purchaseSharingOnInstagram 퀘스트의 완료를 요청합니다.", responses: { 200: { description: "", @@ -45,8 +27,8 @@ eventsDocs[`${apiPrefix}/instagram/share-purchase`] = { required: ["result"], properties: { result: { - description: "성공 여부", type: "boolean", + description: "성공 여부", example: true, }, }, diff --git a/src/lottery/routes/docs/questsSchema.js b/src/lottery/routes/docs/questsSchema.js new file mode 100644 index 00000000..f1a14a4e --- /dev/null +++ b/src/lottery/routes/docs/questsSchema.js @@ -0,0 +1,13 @@ +const questsSchema = { + completeHandler: { + type: "object", + required: ["questId"], + properties: { + questId: { + type: "string", + }, + }, + }, +}; + +module.exports = questsSchema; diff --git a/src/lottery/routes/docs/swaggerDocs.js b/src/lottery/routes/docs/swaggerDocs.js index 77a85f61..73fa7b66 100644 --- a/src/lottery/routes/docs/swaggerDocs.js +++ b/src/lottery/routes/docs/swaggerDocs.js @@ -1,11 +1,14 @@ -const { eventConfig } = require("../../../../loadenv"); const globalStateDocs = require("./globalState"); const itemsDocs = require("./items"); -const transactionsDocs = require("./transactions"); +const publicNoticeDocs = require("./publicNotice"); const questsDocs = require("./quests"); +const transactionsDocs = require("./transactions"); + const itemsSchema = require("./itemsSchema"); -const publicNoticeDocs = require("./publicNotice"); const globalStateSchema = require("./globalStateSchema"); +const questsSchema = require("./questsSchema"); + +const { eventConfig } = require("../../../../loadenv"); const apiPrefix = `/events/${eventConfig.mode}`; const eventSwaggerDocs = { @@ -19,29 +22,30 @@ const eventSwaggerDocs = { description: "이벤트 - 아이템 관련 API", }, { - name: `${apiPrefix}/transactions`, - description: "이벤트 - 입출금 내역 관련 API", + name: `${apiPrefix}/public-notice`, + description: "이벤트 - 아이템 구매, 뽑기, 획득 공지 관련 API", }, { name: `${apiPrefix}/quests`, description: "이벤트 - 퀘스트 관련 API", }, { - name: `${apiPrefix}/public-notice`, - description: "이벤트 - 아이템 구매, 뽑기, 획득 공지 관련 API", + name: `${apiPrefix}/transactions`, + description: "이벤트 - 입출금 내역 관련 API", }, ], paths: { ...globalStateDocs, ...itemsDocs, - ...transactionsDocs, - ...questsDocs, ...publicNoticeDocs, + ...questsDocs, + ...transactionsDocs, }, components: { schemas: { - ...itemsSchema, ...globalStateSchema, + ...itemsSchema, + ...questsSchema, }, }, }; diff --git a/src/lottery/routes/quests.js b/src/lottery/routes/quests.js index b1ec591b..6ff1246b 100644 --- a/src/lottery/routes/quests.js +++ b/src/lottery/routes/quests.js @@ -1,12 +1,18 @@ const express = require("express"); + const router = express.Router(); -const quests = require("../services/quests"); +const questsHandlers = require("../services/quests"); + +const { validateParams } = require("../../middlewares/ajv"); +const questsSchema = require("./docs/questsSchema"); router.use(require("../../middlewares/auth")); router.use(require("../middlewares/timestampValidator")); -router.post("/share-room", quests.roomShareHandler); -router.post("/instagram/share-event", quests.instagramEventShareHandler); -router.post("/instagram/share-purchase", quests.instagramPurchaseShareHandler); +router.post( + "/complete/:questId", + validateParams(questsSchema.completeHandler), + questsHandlers.completeHandler +); module.exports = router; diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js index 1ba5df08..4f6c3271 100644 --- a/src/lottery/services/quests.js +++ b/src/lottery/services/quests.js @@ -1,64 +1,25 @@ +const { completeQuest } = require("../modules/quests"); const logger = require("../../modules/logger"); -const contracts = require("../modules/contracts/2023fall"); -/** - * 방을 공유했을 때. - */ -const roomShareHandler = async (req, res) => { - try { - const { userOid } = req; - const contractResult = await contracts.completeRoomSharingQuest( - userOid, - req.timestamp - ); - res.json({ result: !!contractResult }); - } catch (err) { - logger.error(err); - res.status(500).json({ error: "Quests/ShareRoom: internal server error" }); - } -}; +const { eventConfig } = require("../../../loadenv"); +const quests = eventConfig + ? require(`../modules/contracts/${eventConfig.mode}`).quests + : undefined; -/** - * 인스타그램 스토리에 이벤트를 공유했을 때. - */ -const instagramEventShareHandler = async (req, res) => { +const completeHandler = async (req, res) => { try { - const { userOid } = req; - const contractResult = await contracts.completeEventSharingOnInstagramQuest( - userOid, - req.timestamp - ); - res.json({ result: !!contractResult }); - } catch (err) { - logger.error(err); - res - .status(500) - .json({ error: "Quests/Instagram/ShareEvent: internal server error" }); - } -}; + const quest = quests[req.params.questId]; + if (!quest?.isApiRequired) + return res.status(400).json({ error: "Quests/Complete: invalid Quest" }); -/** - * 인스타그램 스토리에 아이템 구매 내역을 공유했을 때. - */ -const instagramPurchaseShareHandler = async (req, res) => { - try { - const { userOid } = req; - const contractResult = - await contracts.completePurchaseSharingOnInstagramQuest( - userOid, - req.timestamp - ); - res.json({ result: !!contractResult }); + const result = await completeQuest(req.userOid, req.timestamp, quest); + res.json({ result: !!result }); // boolean으로 변환하기 위해 !!를 사용합니다. } catch (err) { logger.error(err); - res - .status(500) - .json({ error: "Quests/Instagram/SharePurchase: internal server error" }); + res.status(500).json({ error: "Quests/Complete: internal server error" }); } }; module.exports = { - roomShareHandler, - instagramEventShareHandler, - instagramPurchaseShareHandler, + completeHandler, }; From 34e2bcb9cfeb6c8d44ecdf10972d8b24293e8761 Mon Sep 17 00:00:00 2001 From: static Date: Sat, 23 Sep 2023 00:22:01 +0900 Subject: [PATCH 55/67] Add: instagramStoryStickerImageUrl field in itemSchema --- src/lottery/modules/populates/transactions.js | 2 +- src/lottery/modules/stores/mongo.js | 3 +++ src/lottery/routes/docs/itemsSchema.js | 5 +++++ src/lottery/services/items.js | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/lottery/modules/populates/transactions.js b/src/lottery/modules/populates/transactions.js index bcf84863..4dfc11e9 100644 --- a/src/lottery/modules/populates/transactions.js +++ b/src/lottery/modules/populates/transactions.js @@ -1,7 +1,7 @@ const transactionPopulateOption = [ { path: "item", - select: "name imageUrl price description isDisabled stock itemType", + select: "-isRandomItem -randomWeight", }, ]; diff --git a/src/lottery/modules/stores/mongo.js b/src/lottery/modules/stores/mongo.js index 2e32978d..9db6905d 100644 --- a/src/lottery/modules/stores/mongo.js +++ b/src/lottery/modules/stores/mongo.js @@ -57,6 +57,9 @@ const itemSchema = Schema({ type: String, required: true, }, + instagramStoryStickerImageUrl: { + type: String, + }, price: { type: Number, required: true, diff --git a/src/lottery/routes/docs/itemsSchema.js b/src/lottery/routes/docs/itemsSchema.js index 2a4eca64..2549540b 100644 --- a/src/lottery/routes/docs/itemsSchema.js +++ b/src/lottery/routes/docs/itemsSchema.js @@ -26,6 +26,11 @@ const itemBase = { description: "이미지 썸네일 URL", example: "THUMBNAIL URL", }, + instagramStoryStickerImageUrl: { + type: "string", + description: "인스타그램 스토리 스티커 이미지 URL", + example: "STICKER URL", + }, price: { type: "number", description: "상품의 가격. 0 이상입니다.", diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index f4eeccdb..eba71923 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -110,7 +110,7 @@ const getRandomItem = async (req, depth) => { const listHandler = async (_, res) => { try { const items = await itemModel - .find({}, "name imageUrl price description isDisabled stock itemType") + .find({}, "-isRandomItem -randomWeight") .lean(); res.json({ items: items.map(hideItemStock) }); } catch (err) { From 08451e9db353f802fe1ff20a57bd11f05dec78de Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Sat, 23 Sep 2023 00:26:50 +0900 Subject: [PATCH 56/67] Add: Advanced getValueRank calculating function --- src/lottery/services/publicNotice.js | 54 ++++++++++++++++------------ 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index ce0f5633..2359462b 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -7,18 +7,27 @@ const { publicNoticePopulateOption, } = require("../modules/populates/transactions"); -const hideNickname = (nickname) => { - return `${nickname.toString().slice(0, 2)}${"*".repeat(nickname.length - 2)}`; -}; - +/** + * getValueRank 사용자의 상품 구매 내역 또는 경품 추첨 내역의 순위 결정을 위한 가치를 평가하는 함수 + * 상품 가격이 높을수록, 상품 구매 일시가 최근일 수록 가치가 높습니다. + * 요청이 들어온 시간과 트랜젝션이 있었던 시간의 차를 로그스케일로 변환후 이를 가격에 곱하여 가치를 구합니다. + * 시간의 단위는 millisecond입니다. + * t_1/2(반감기, half-life)는 4일입니다 . + * (2일 = 2 * 24 * 60 * 60 * 1000 = 172800000ms) + * Tau는 반감기를 결정하는 상수입니다. + * Tau = t_1/2 / ln(2) 로 구할 수 있습니다. + * Tau = 249297703 + * N_0(초기값)는 item.price를 사용합니다. + * @param {Object} item + * @param {number|Date} createAt + * @param {number|Date} timestamp + * @returns {Promise} + * @description 가치를 기준으로 정렬하기 위해 사용됨 + */ const getValueRank = (item, createAt, timestamp) => { - return ( - (parseInt( - (new Date(createAt).getTime() / timestamp).toString().slice(7, 10) - ) * - item.price) / - 1000 - ); + const t = timestamp - new Date(createAt).getTime(); // millisecond + const Tau = 249297703; + return item.price * Math.exp(-t / Tau); }; const getRecentPurchaceItemListHandler = async (req, res) => { @@ -37,16 +46,17 @@ const getRecentPurchaceItemListHandler = async (req, res) => { getValueRank(x.item, x.createAt, req.timestamp) ) .slice(0, 5) - .map( - ({ userId, item, comment }) => - `${hideNickname(userId.nickname)}님께서 ${item.name}을(를) ${ - comment.startsWith("송편") - ? " 을(를) 구입하셨습니다." - : comment.startsWith("랜덤 박스") - ? "을(를) 뽑았습니다." - : "을(를) 획득하셨습니다." - }` - ); + .map(({ userId, item, comment, createAt }) => ({ + text: `${userId.nickname}님께서 ${item.name}${ + comment.startsWith("송편") + ? "을(를) 구입하셨습니다." + : comment.startsWith("랜덤 박스") + ? "을(를) 뽑았습니다." + : "을(를) 획득하셨습니다." + }`, + createAt, + })); + console.log(transactions); res.json({ transactions }); } catch (err) { logger.error(err); @@ -90,7 +100,7 @@ const getTicketLeaderboardHandler = async (req, res) => { return null; } return { - nickname: hideNickname(userInfo.nickname), + nickname: userInfo.nickname, profileImageUrl: userInfo.profileImageUrl, ticket1Amount: user.ticket1Amount, ticket2Amount: user.ticket2Amount, From 96d0f9480370fd57d51a0bce8d1a1a926b1ebe14 Mon Sep 17 00:00:00 2001 From: TaehyeonPark Date: Sat, 23 Sep 2023 00:33:29 +0900 Subject: [PATCH 57/67] Remove: console log deleted --- src/lottery/services/publicNotice.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index fe5d853d..475f1d2a 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -56,7 +56,6 @@ const getRecentPurchaceItemListHandler = async (req, res) => { }`, createAt, })); - console.log(transactions); res.json({ transactions }); } catch (err) { logger.error(err); From e6c5a0a957406ca452f59881a95e8d1272d37b68 Mon Sep 17 00:00:00 2001 From: static Date: Sat, 23 Sep 2023 01:01:02 +0900 Subject: [PATCH 58/67] Refactor: use nullish coalescing operator --- src/lottery/modules/quests.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/lottery/modules/quests.js b/src/lottery/modules/quests.js index b61b3b77..539bfc41 100644 --- a/src/lottery/modules/quests.js +++ b/src/lottery/modules/quests.js @@ -37,14 +37,14 @@ const buildQuests = (quests) => { } // quest.reward에 누락된 필드가 있는 경우, 기본값(0)으로 설정합니다. - quest.reward.credit = quest.reward.credit || 0; - quest.reward.ticket1 = quest.reward.ticket1 || 0; + quest.reward.credit = quest.reward.credit ?? 0; + quest.reward.ticket1 = quest.reward.ticket1 ?? 0; // quest.maxCount가 없는 경우, 기본값(1)으로 설정합니다. - quest.maxCount = quest.maxCount || 1; + quest.maxCount = quest.maxCount ?? 1; // quest.isApiRequired가 없는 경우, 기본값(false)으로 설정합니다. - quest.isApiRequired = quest.isApiRequired || false; + quest.isApiRequired = quest.isApiRequired ?? false; } return quests; From 276495ed9bb5daa5f8a4353abfd35eab04854045 Mon Sep 17 00:00:00 2001 From: static Date: Sat, 23 Sep 2023 14:59:48 +0900 Subject: [PATCH 59/67] Refactor: resolve comments --- src/lottery/modules/populates/transactions.js | 3 ++- src/lottery/routes/docs/questsSchema.js | 5 +++++ src/lottery/services/items.js | 5 ++++- src/lottery/services/quests.js | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/lottery/modules/populates/transactions.js b/src/lottery/modules/populates/transactions.js index 4dfc11e9..6d965258 100644 --- a/src/lottery/modules/populates/transactions.js +++ b/src/lottery/modules/populates/transactions.js @@ -1,7 +1,8 @@ const transactionPopulateOption = [ { path: "item", - select: "-isRandomItem -randomWeight", + select: + "name imageUrl instagramStoryStickerImageUrl price description isDisabled stock itemType", }, ]; diff --git a/src/lottery/routes/docs/questsSchema.js b/src/lottery/routes/docs/questsSchema.js index f1a14a4e..3432cc8c 100644 --- a/src/lottery/routes/docs/questsSchema.js +++ b/src/lottery/routes/docs/questsSchema.js @@ -5,6 +5,11 @@ const questsSchema = { properties: { questId: { type: "string", + enum: [ + "roomSharing", + "eventSharingOnInstagram", + "purchaseSharingOnInstagram", + ], }, }, }, diff --git a/src/lottery/services/items.js b/src/lottery/services/items.js index eba71923..f122481a 100644 --- a/src/lottery/services/items.js +++ b/src/lottery/services/items.js @@ -110,7 +110,10 @@ const getRandomItem = async (req, depth) => { const listHandler = async (_, res) => { try { const items = await itemModel - .find({}, "-isRandomItem -randomWeight") + .find( + {}, + "name imageUrl instagramStoryStickerImageUrl price description isDisabled stock itemType" + ) .lean(); res.json({ items: items.map(hideItemStock) }); } catch (err) { diff --git a/src/lottery/services/quests.js b/src/lottery/services/quests.js index 4f6c3271..5dd4f0e5 100644 --- a/src/lottery/services/quests.js +++ b/src/lottery/services/quests.js @@ -9,7 +9,7 @@ const quests = eventConfig const completeHandler = async (req, res) => { try { const quest = quests[req.params.questId]; - if (!quest?.isApiRequired) + if (!quest || !quest.isApiRequired) return res.status(400).json({ error: "Quests/Complete: invalid Quest" }); const result = await completeQuest(req.userOid, req.timestamp, quest); From 1dea168ada4c4d3b36ef6564316643e80e8bf8f0 Mon Sep 17 00:00:00 2001 From: static Date: Sat, 23 Sep 2023 20:48:39 +0900 Subject: [PATCH 60/67] Fix: initial creditAmount --- src/lottery/services/globalState.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/lottery/services/globalState.js b/src/lottery/services/globalState.js index c36ac9f1..13ce9bb7 100644 --- a/src/lottery/services/globalState.js +++ b/src/lottery/services/globalState.js @@ -47,6 +47,7 @@ const createUserGlobalStateHandler = async (req, res) => { eventStatus = new eventStatusModel({ userId: req.userOid, + creditAmount: 100, // 초기 송편 개수는 0개가 아닌 100개입니다. }); await eventStatus.save(); From ae6f35f906ab4b195f7e26cf68ad96f175798b0e Mon Sep 17 00:00:00 2001 From: static Date: Sun, 24 Sep 2023 18:32:33 +0900 Subject: [PATCH 61/67] Add: new leaderboard probability model --- src/lottery/services/publicNotice.js | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 62421bbd..e6e1e6f9 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -35,6 +35,25 @@ const getRecentPurchaceItemListHandler = async (req, res) => { } }; +const calculateProbabilityV2 = (users, weightSum, base, weight) => { + // 유저 수가 상품 수보다 적거나 같으면 무조건 상품을 받게된다. + if (users.length <= 15) return 1; + + /** + * 경험적으로 발견한 사실 + * + * p를 에어팟 당첨 확률이라고 하자. + * 모든 유저의 p값이 1/15 미만일 경우, 실제 당첨 확률은 15p이다. + * 그렇지 않은 경우, 실제 당첨 확률은 1-a^p꼴의 지수함수를 따른다. + * + * 계산 과정 + * a는 유저 수, 전체 티켓 수, 티켓 분포에 의해 결정되는 값으로, 현실적으로 계산하기 어렵다. + * 따라서, 모든 유저가 같은 수의 티켓을 가지고 있다고 가정하고 a를 계산한 뒤, 이를 확률 계산에 사용한다. + */ + if (base !== null) return 1 - Math.pow(base, weight); + else return (weight / weightSum) * 15; +}; + const getTicketLeaderboardHandler = async (req, res) => { try { const users = await eventStatusModel @@ -60,6 +79,11 @@ const getTicketLeaderboardHandler = async (req, res) => { } return before + user.weight; }, 0); + const isExponential = + sortedUsers.find((user) => user.weight >= weightSum / 15) != undefined; + const base = isExponential + ? Math.pow(1 - 15 / users.length, users.length / weightSum) + : null; const leaderboard = await Promise.all( sortedUsers.slice(0, 20).map(async (user) => { @@ -74,6 +98,12 @@ const getTicketLeaderboardHandler = async (req, res) => { ticket1Amount: user.ticket1Amount, ticket2Amount: user.ticket2Amount, probability: user.weight / weightSum, + probabilityV2: calculateProbabilityV2( + users, + weightSum, + base, + user.weight + ), }; }) ); @@ -87,6 +117,12 @@ const getTicketLeaderboardHandler = async (req, res) => { leaderboard, rank: rank + 1, probability: sortedUsers[rank].weight / weightSum, + probabilityV2: calculateProbabilityV2( + users, + weightSum, + base, + sortedUsers[rank].weight + ), }); else res.json({ leaderboard }); } catch (err) { From 86ffd0729ee8abb7c794ca167658d0d6f6dcc70b Mon Sep 17 00:00:00 2001 From: static Date: Sun, 24 Sep 2023 21:04:00 +0900 Subject: [PATCH 62/67] Docs: describe probabilityV2 field --- src/lottery/routes/docs/publicNotice.js | 11 +++++++++++ src/lottery/services/publicNotice.js | 11 +++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index ca30cfb9..71e9e3a3 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -59,6 +59,7 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = { "ticket1Amount", "ticket2Amount", "probability", + "probabilityV2", ], properties: { nickname: { @@ -86,6 +87,11 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = { description: "1등 당첨 확률", example: 0.001, }, + probabilityV2: { + type: "number", + description: "근사적인 상품 당첨 확률", + example: 0.015, + }, }, }, }, @@ -99,6 +105,11 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = { description: "1등 당첨 확률", example: 0.00003, }, + probabilityV2: { + type: "number", + description: "근사적인 상품 당첨 확률", + example: 0.00045, + }, }, }, }, diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index e6e1e6f9..b58e41be 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -42,13 +42,20 @@ const calculateProbabilityV2 = (users, weightSum, base, weight) => { /** * 경험적으로 발견한 사실 * - * p를 에어팟 당첨 확률이라고 하자. + * p를 에어팟 당첨 확률, M을 전체 티켓 수라고 하자. * 모든 유저의 p값이 1/15 미만일 경우, 실제 당첨 확률은 15p이다. - * 그렇지 않은 경우, 실제 당첨 확률은 1-a^p꼴의 지수함수를 따른다. + * 그렇지 않은 경우, 실제 당첨 확률은 1-a^Mp꼴의 지수함수를 따른다. (Note: Mp는 티켓 수이다.) * * 계산 과정 + * * a는 유저 수, 전체 티켓 수, 티켓 분포에 의해 결정되는 값으로, 현실적으로 계산하기 어렵다. * 따라서, 모든 유저가 같은 수의 티켓을 가지고 있다고 가정하고 a를 계산한 뒤, 이를 확률 계산에 사용한다. + * + * a값의 계산 과정 + * + * N을 유저 수라고 하자. 모든 유저가 같은 수의 티켓 M/N개를 가지고 있다고 하자. + * 이때 기대되는 당첨 확률은 직관적으로 15/N임을 알 수 있다. 즉, 1-a^(M/N) = 15/N이다. + * a에 대해 정리하면, a = (1-15/N)^(N/M)임을 알 수 있다. */ if (base !== null) return 1 - Math.pow(base, weight); else return (weight / weightSum) * 15; From c4a69c3fa5f745129862af08716a60e4b55400e9 Mon Sep 17 00:00:00 2001 From: static Date: Sun, 24 Sep 2023 21:41:29 +0900 Subject: [PATCH 63/67] Add: totalTicketAmount and totalUserAmount field --- src/lottery/routes/docs/publicNotice.js | 12 +++++++++++- src/lottery/services/publicNotice.js | 9 ++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index 71e9e3a3..eed3a652 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -46,7 +46,7 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = { "application/json": { schema: { type: "object", - required: ["leaderboard"], + required: ["leaderboard", "totalTicketAmount", "totalUserAmount"], properties: { leaderboard: { type: "array", @@ -95,6 +95,16 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = { }, }, }, + totalTicketAmount: { + type: "number", + description: "전체 티켓의 수", + example: 300, + }, + totalUserAmount: { + type: "number", + description: "리더보드에 포함된 유저의 수", + example: 100, + }, rank: { type: "number", description: "유저의 리더보드 순위. 1부터 시작합니다.", diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index b58e41be..ccd66aa0 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -122,6 +122,8 @@ const getTicketLeaderboardHandler = async (req, res) => { if (rank >= 0) res.json({ leaderboard, + totalTicketAmount: weightSum, + totalUserAmount: users.length, rank: rank + 1, probability: sortedUsers[rank].weight / weightSum, probabilityV2: calculateProbabilityV2( @@ -131,7 +133,12 @@ const getTicketLeaderboardHandler = async (req, res) => { sortedUsers[rank].weight ), }); - else res.json({ leaderboard }); + else + res.json({ + leaderboard, + totalTicketAmount: weightSum, + totalUserAmount: users.length, + }); } catch (err) { logger.error(err); res From a9ee2367d1d2ed50e45d4e5cb0157390f8188158 Mon Sep 17 00:00:00 2001 From: static Date: Sun, 24 Sep 2023 23:12:32 +0900 Subject: [PATCH 64/67] Add: totalTicket1Amount and totalTicket2Amount field --- src/lottery/routes/docs/publicNotice.js | 16 +++++++++++++--- src/lottery/services/publicNotice.js | 19 ++++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/lottery/routes/docs/publicNotice.js b/src/lottery/routes/docs/publicNotice.js index eed3a652..e5ce2f55 100644 --- a/src/lottery/routes/docs/publicNotice.js +++ b/src/lottery/routes/docs/publicNotice.js @@ -46,7 +46,12 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = { "application/json": { schema: { type: "object", - required: ["leaderboard", "totalTicketAmount", "totalUserAmount"], + required: [ + "leaderboard", + "totalTicket1Amount", + "totalTicket2Amount", + "totalUserAmount", + ], properties: { leaderboard: { type: "array", @@ -95,11 +100,16 @@ publicNoticeDocs[`${apiPrefix}/leaderboard`] = { }, }, }, - totalTicketAmount: { + totalTicket1Amount: { type: "number", - description: "전체 티켓의 수", + description: "전체 일반 티켓의 수", example: 300, }, + totalTicket2Amount: { + type: "number", + description: "전체 고급 티켓의 수", + example: 100, + }, totalUserAmount: { type: "number", description: "리더보드에 포함된 유저의 수", diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 94d25e8c..87b77428 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -110,12 +110,19 @@ const getTicketLeaderboardHandler = async (req, res) => { const userId = isLogin(req) ? getLoginInfo(req).oid : null; let rank = -1; - const weightSum = sortedUsers.reduce((before, user, index) => { + let weightSum = 0; + let totalTicket1Amount = 0; + let totalTicket2Amount = 0; + for (const user of sortedUsers) { + weightSum += user.weight; + totalTicket1Amount += user.ticket1Amount; + totalTicket2Amount += user.ticket2Amount; + if (rank < 0 && user.userId === userId) { rank = index; } - return before + user.weight; - }, 0); + } + const isExponential = sortedUsers.find((user) => user.weight >= weightSum / 15) != undefined; const base = isExponential @@ -152,7 +159,8 @@ const getTicketLeaderboardHandler = async (req, res) => { if (rank >= 0) res.json({ leaderboard, - totalTicketAmount: weightSum, + totalTicket1Amount, + totalTicket2Amount, totalUserAmount: users.length, rank: rank + 1, probability: sortedUsers[rank].weight / weightSum, @@ -166,7 +174,8 @@ const getTicketLeaderboardHandler = async (req, res) => { else res.json({ leaderboard, - totalTicketAmount: weightSum, + totalTicket1Amount, + totalTicket2Amount, totalUserAmount: users.length, }); } catch (err) { From fbf7cc945bf2c05f58feb7af0b12ed55ab28d517 Mon Sep 17 00:00:00 2001 From: static Date: Sun, 24 Sep 2023 23:22:59 +0900 Subject: [PATCH 65/67] Refactor: use reduce function instead of for loop --- src/lottery/services/publicNotice.js | 30 +++++++++++++++++----------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index 87b77428..cd78b84d 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -110,18 +110,24 @@ const getTicketLeaderboardHandler = async (req, res) => { const userId = isLogin(req) ? getLoginInfo(req).oid : null; let rank = -1; - let weightSum = 0; - let totalTicket1Amount = 0; - let totalTicket2Amount = 0; - for (const user of sortedUsers) { - weightSum += user.weight; - totalTicket1Amount += user.ticket1Amount; - totalTicket2Amount += user.ticket2Amount; - - if (rank < 0 && user.userId === userId) { - rank = index; - } - } + const [weightSum, totalTicket1Amount, totalTicket2Amount] = + sortedUsers.reduce( + ( + [_weightSum, _totalTicket1Amount, _totalTicket2Amount], + user, + index + ) => { + if (rank < 0 && user.userId === userId) { + rank = index; + } + return [ + _weightSum + user.weight, + _totalTicket1Amount + user.ticket1Amount, + _totalTicket2Amount + user.ticket2Amount, + ]; + }, + [0, 0, 0] + ); const isExponential = sortedUsers.find((user) => user.weight >= weightSum / 15) != undefined; From 560b1743fb9aaef488aeae4432893f0000444ede Mon Sep 17 00:00:00 2001 From: static Date: Sun, 24 Sep 2023 23:33:45 +0900 Subject: [PATCH 66/67] Refactor: resolve comment --- src/lottery/services/publicNotice.js | 42 +++++++++++++--------------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/src/lottery/services/publicNotice.js b/src/lottery/services/publicNotice.js index cd78b84d..379e08da 100644 --- a/src/lottery/services/publicNotice.js +++ b/src/lottery/services/publicNotice.js @@ -130,7 +130,7 @@ const getTicketLeaderboardHandler = async (req, res) => { ); const isExponential = - sortedUsers.find((user) => user.weight >= weightSum / 15) != undefined; + sortedUsers.find((user) => user.weight >= weightSum / 15) !== undefined; const base = isExponential ? Math.pow(1 - 15 / users.length, users.length / weightSum) : null; @@ -162,28 +162,24 @@ const getTicketLeaderboardHandler = async (req, res) => { .status(500) .json({ error: "PublicNotice/Leaderboard : internal server error" }); - if (rank >= 0) - res.json({ - leaderboard, - totalTicket1Amount, - totalTicket2Amount, - totalUserAmount: users.length, - rank: rank + 1, - probability: sortedUsers[rank].weight / weightSum, - probabilityV2: calculateProbabilityV2( - users, - weightSum, - base, - sortedUsers[rank].weight - ), - }); - else - res.json({ - leaderboard, - totalTicket1Amount, - totalTicket2Amount, - totalUserAmount: users.length, - }); + res.json({ + leaderboard, + totalTicket1Amount, + totalTicket2Amount, + totalUserAmount: users.length, + ...(rank >= 0 + ? { + rank: rank + 1, + probability: sortedUsers[rank].weight / weightSum, + probabilityV2: calculateProbabilityV2( + users, + weightSum, + base, + sortedUsers[rank].weight + ), + } + : {}), + }); } catch (err) { logger.error(err); res From 9e0fce447deae456b76b0c937f392664c5c24387 Mon Sep 17 00:00:00 2001 From: withsang Date: Mon, 25 Sep 2023 00:30:53 +0900 Subject: [PATCH 67/67] Fix: set express to trust proxy --- app.js | 3 +++ src/middlewares/limitRate.js | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app.js b/app.js index 689de7a9..3394dec0 100644 --- a/app.js +++ b/app.js @@ -19,6 +19,9 @@ connectDatabase(); app.use(express.urlencoded({ extended: false })); app.use(express.json()); +// reverse proxy가 설정한 헤더를 신뢰합니다. +app.set("trust proxy", true); + // [Middleware] CORS 설정 app.use(require("./src/middlewares/cors")); diff --git a/src/middlewares/limitRate.js b/src/middlewares/limitRate.js index b218b0ba..4cba6af3 100644 --- a/src/middlewares/limitRate.js +++ b/src/middlewares/limitRate.js @@ -2,7 +2,7 @@ const rateLimit = require("express-rate-limit"); const limiter = rateLimit({ windowMs: 15 * 60 * 1000, // 15 minutes - max: 1500, // Limit each IP to 100 requests per `window` (here, per 15 minutes) + max: 1500, // Limit each IP to 1500 requests per `window` (here, per 15 minutes) standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers legacyHeaders: false, // Disable the `X-RateLimit-*` headers });