Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#365 이벤트 관련 MongoDB Query에 Transactions 도입 #374

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions app.js
Original file line number Diff line number Diff line change
@@ -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");
Expand Down Expand Up @@ -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"));
Expand Down
2 changes: 1 addition & 1 deletion loadenv.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
7 changes: 4 additions & 3 deletions src/lottery/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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,
Expand Down
41 changes: 7 additions & 34 deletions src/lottery/modules/contracts/2023fall.js
Original file line number Diff line number Diff line change
Expand Up @@ -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입니다.
Expand All @@ -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);
};

/**
Expand All @@ -112,7 +107,6 @@ const completePayingAndSendingQuest = async (timestamp, roomObject) => {
await completeQuest(
participant.user._id,
timestamp,
eventPeriod,
quests.payingAndSending
)
)
Expand All @@ -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 () => {
Expand All @@ -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);
};

/**
Expand All @@ -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);
};

/**
Expand All @@ -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);
};

/**
Expand All @@ -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);
};

/**
Expand All @@ -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 () => {
Expand All @@ -243,7 +217,6 @@ const completePurchaseSharingOnInstagramQuest = async () => {

module.exports = {
quests,
eventPeriod,
completeFirstLoginQuest,
completePayingAndSendingQuest,
completeFirstRoomCreationQuest,
Expand Down
13 changes: 8 additions & 5 deletions src/lottery/modules/quests.js
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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 - 퀘스트의 이름입니다.
Expand All @@ -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`
);
Expand Down
4 changes: 2 additions & 2 deletions src/lottery/routes/docs/globalState.js
Original file line number Diff line number Diff line change
@@ -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}/`] = {
Expand Down
4 changes: 2 additions & 2 deletions src/lottery/routes/docs/items.js
Original file line number Diff line number Diff line change
@@ -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`] = {
Expand Down
4 changes: 2 additions & 2 deletions src/lottery/routes/docs/publicNotice.js
Original file line number Diff line number Diff line change
@@ -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`] = {
Expand Down
4 changes: 2 additions & 2 deletions src/lottery/routes/docs/swaggerDocs.js
Original file line number Diff line number Diff line change
@@ -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: [
Expand Down
4 changes: 2 additions & 2 deletions src/lottery/routes/docs/transactions.js
Original file line number Diff line number Diff line change
@@ -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}/`] = {
Expand Down
11 changes: 5 additions & 6 deletions src/lottery/services/globalState.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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) {
Expand Down
14 changes: 9 additions & 5 deletions src/lottery/services/items.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/routes/admin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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
Expand Down