Skip to content

Commit

Permalink
Merge branch 'dev' into #450-택시비용-보여주기
Browse files Browse the repository at this point in the history
  • Loading branch information
ybmin authored Mar 9, 2024
2 parents 163abbf + 345966c commit 9d72e86
Show file tree
Hide file tree
Showing 55 changed files with 1,445 additions and 446 deletions.
2 changes: 1 addition & 1 deletion app.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ app.use(require("./src/middlewares/limitRate"));
// [Router] Swagger (API 문서)
app.use("/docs", require("./src/routes/docs"));

// 2023 추석 이벤트 전용 라우터입니다.
// [Router] 이벤트 전용 라우터입니다.
eventConfig &&
app.use(
`/events/${eventConfig.mode}`,
Expand Down
16 changes: 13 additions & 3 deletions loadenv.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
require("dotenv").config({ path: `./.env.${process.env.NODE_ENV}` });

module.exports = {
nodeEnv: process.env.NODE_ENV, // required
nodeEnv: process.env.NODE_ENV, // required ("production" or "development" or "test")
mongo: process.env.DB_PATH, // required
session: {
secret: process.env.SESSION_KEY || "TAXI_SESSION_KEY", // optional
Expand Down Expand Up @@ -43,8 +43,18 @@ 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: "2024spring",
credit: {
name: "넙죽코인",
initialAmount: 0,
},
period: {
startAt: "2024-02-23T00:00:00+09:00",
endAt: "2024-03-19T00:00:00+09:00",
},
}, // optional
// Naver Cloud Platform Maps Directions 5 API Keys
naverCloudApiId: process.env.NAVER_MAP_API_ID, //required
naverCloudApiKey: process.env.NAVER_MAP_API_KEY, //required
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"mongoose": "^6.12.0",
"node-cron": "3.0.2",
"node-mocks-http": "^1.12.1",
"nodemailer": "^6.9.9",
"querystring": "^0.2.1",
"redis": "^4.2.0",
"response-time": "^2.3.2",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 17 additions & 13 deletions src/lottery/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,39 @@ const {
} = require("./modules/items");

const { eventConfig } = require("../../loadenv");
const contracts = eventConfig && require("./modules/contracts");

// [Routes] 기존 docs 라우터의 docs extend
eventConfig && require("./routes/docs")();

// [Schedule] 스케줄러 시작
eventConfig && require("./schedules")();

const lotteryRouter = express.Router();

// [Middleware] 모든 API 요청에 대하여 origin 검증
lotteryRouter.use(require("../middlewares/originValidator"));

// [Router] APIs
lotteryRouter.use("/global-state", require("./routes/globalState"));
lotteryRouter.use("/globalState", require("./routes/globalState"));
lotteryRouter.use("/invite", require("./routes/invite"));
lotteryRouter.use("/transactions", require("./routes/transactions"));
lotteryRouter.use("/items", require("./routes/items"));
lotteryRouter.use("/public-notice", require("./routes/publicNotice"));
lotteryRouter.use("/publicNotice", require("./routes/publicNotice"));
lotteryRouter.use("/quests", require("./routes/quests"));

const itemResource = buildResource([
addOneItemStockAction,
addFiveItemStockAction,
])(itemModel);
const otherResources = [eventStatusModel, questModel, transactionModel].map(
buildResource()
);

const contracts =
eventConfig && require(`./modules/contracts/${eventConfig.mode}`);
// [AdminJS] AdminJS에 표시할 Resource 생성
const resources =
(eventConfig && [
buildResource()(eventStatusModel),
buildResource()(questModel),
buildResource([addOneItemStockAction, addFiveItemStockAction])(itemModel),
buildResource()(transactionModel),
]) ||
[];

module.exports = {
lotteryRouter,
resources: [itemResource, ...otherResources],
contracts,
resources,
};
4 changes: 2 additions & 2 deletions src/lottery/middlewares/timestampValidator.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const { eventConfig } = require("../../../loadenv");
const eventPeriod = eventConfig && {
startAt: new Date(eventConfig.startAt),
endAt: new Date(eventConfig.endAt),
startAt: new Date(eventConfig.period.startAt),
endAt: new Date(eventConfig.period.endAt),
};

const timestampValidator = (req, res, next) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,48 +1,46 @@
const { buildQuests, completeQuest } = require("../quests");
const { buildQuests, completeQuest } = require("./quests");
const mongoose = require("mongoose");
const logger = require("../../../modules/logger");
const logger = require("../../modules/logger");

const { eventConfig } = require("../../../../loadenv");
const { eventConfig } = require("../../../loadenv");
const eventPeriod = eventConfig && {
startAt: new Date(eventConfig.startAt),
endAt: new Date(eventConfig.endAt),
startAt: new Date(eventConfig.period.startAt),
endAt: new Date(eventConfig.period.endAt),
};

/** 전체 퀘스트 목록입니다. */
const quests = buildQuests({
firstLogin: {
name: "첫 발걸음",
description:
"로그인만 해도 송편을 얻을 수 있다고?? 이벤트 기간에 처음으로 SPARCS Taxi 서비스에 로그인하여 송편을 받아보세요.",
"로그인만 해도 넙죽코인을 얻을 수 있다고?? 이벤트 기간에 처음으로 SPARCS Taxi 서비스에 로그인하여 넙죽코인을 받아보세요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_firstLogin.png",
reward: {
ticket1: 1,
},
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024spring/quest_firstLogin.png",
reward: 50,
},
payingAndSending: {
name: "함께하는 택시의 여정",
description:
"2명 이상과 함께 택시를 타고 정산/송금까지 완료해보세요. 최대 3번까지 송편을 받을 수 있어요. 정산/송금 버튼은 채팅 페이지 좌측 하단의 <b>+버튼</b>을 눌러 확인할 수 있어요.",
"2명 이상과 함께 택시를 타고 정산/송금까지 완료해보세요. 최대 3번까지 넙죽코인을 받을 수 있어요. 정산/송금 버튼은 채팅 페이지 좌측 하단의 <b>+버튼</b>을 눌러 확인할 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_payingAndSending.png",
reward: 300,
maxCount: 3,
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024spring/quest_payingAndSending.png",
reward: 150,
maxCount: 0,
},
firstRoomCreation: {
name: "첫 방 개설",
description:
"원하는 택시팟을 찾을 수 없다면? 원하는 조건으로 <b>방 개설 페이지</b>에서 방을 직접 개설해보세요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_firstRoomCreation.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024spring/quest_firstRoomCreation.png",
reward: 50,
},
roomSharing: {
name: "Taxi로 모여라",
name: "너 T야? Taxi",
description:
"방을 공유해 친구들을 택시에 초대해보세요. 채팅창 상단의 햄버거(☰) 버튼을 누르면 <b>공유하기</b> 버튼을 찾을 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_roomSharing.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024spring/quest_roomSharing.png",
reward: 50,
isApiRequired: true,
},
Expand All @@ -51,60 +49,60 @@ const quests = buildQuests({
description:
"2명 이상과 함께 택시를 타고 택시비를 결제한 후 정산하기를 요청해보세요. 정산하기 버튼은 채팅 페이지 좌측 하단의 <b>+버튼</b>을 눌러 확인할 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_paying.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024spring/quest_paying.png",
reward: 100,
maxCount: 3,
maxCount: 0,
},
sending: {
name: "송금 완료! 친구야 고마워",
name: "송금 완료면 I am 신뢰에요",
description:
"2명 이상과 함께 택시를 타고 택시비를 결제한 분께 송금해주세요. 송금하기 버튼은 채팅 페이지 좌측 하단의 <b>+버튼</b>을 눌러 확인할 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_sending.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024spring/quest_sending.png",
reward: 50,
maxCount: 3,
maxCount: 0,
},
nicknameChanging: {
name: "닉네임 변신",
name: "닉네임 폼 미쳤다",
description:
"닉네임을 변경하여 자신을 표현하세요. <b>마이페이지</b>의 <b>수정하기</b> 버튼을 눌러 닉네임을 수정할 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_nicknameChanging.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024spring/quest_nicknameChanging.png",
reward: 50,
},
accountChanging: {
name: "계좌 등록은 정산의 시작",
name: "계좌 등록을 해야 능률이 올라갑니다",
description:
"정산하기 기능을 더욱 빠르고 이용할 수 있다고? 계좌번호를 등록하면 정산하기를 할 때 계좌가 자동으로 입력돼요. <b>마이페이지</b>의 <b>수정하기</b> 버튼을 눌러 계좌번호를 등록 또는 수정할 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_accountChanging.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024spring/quest_accountChanging.png",
reward: 50,
},
adPushAgreement: {
name: "Taxi의 소울메이트",
description:
"Taxi 서비스를 잊지 않도록 가끔 찾아갈게요! 광고성 푸시 알림 수신 동의를 해주시면 방이 많이 모이는 시즌, 주변에 택시앱 사용자가 있을 때 알려드릴 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_adPushAgreement.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024spring/quest_adPushAgreement.png",
reward: 50,
},
eventSharingOnInstagram: {
name: "나만 알기에는 아까운 이벤트",
eventSharing: {
name: "너 나랑 ㅌ태태택 (1명)",
description:
"추석에 맞춰 쏟아지는 혜택들. 나만 알 순 없죠. 인스타그램 친구들에게 스토리로 공유해보아요. <b>이벤트 안내 페이지</b>에서 <b>인스타그램 스토리에 공유하기</b>을 눌러보세요.",
"내가 초대한 사람이 Taxi에 가입하여 이벤트에 참여하면 넙죽코인을 드려요. 내가 초대한 사람도 넙죽코인을 받아요. 이벤트 안내 페이지의 <b>이벤트 공유하기</b> 버튼을 통해 카카오톡으로 초대 문자를 보낼 수 있어요!",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_eventSharingOnInstagram.png",
reward: 100,
isApiRequired: true,
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024spring/quest_eventSharing.png",
reward: 50,
maxCount: 0,
},
purchaseSharingOnInstagram: {
name: "상품 획득을 축하합니다",
eventSharing5: {
name: "너 나랑 ㅌ태태택 (5명)",
description:
"이벤트를 열심히 즐긴 당신. 그 상품 획득을 축하 받을 자격이 충분합니다. <b>달토끼 상점</b>에서 상품 구매 후 뜨는 <b>인스타그램 스토리에 공유하기</b> 버튼을 눌러 상품 획득을 공유하세요.",
"내가 초대한 사람이 5명이 Taxi에 가입하여 이벤트에 참여하면 넙죽코인을 드려요. 내가 초대한 사람도 넙죽코인을 받아요. 이벤트 안내 페이지의 <b>이벤트 공유하기</b> 버튼을 통해 카카오톡으로 초대 문자를 보낼 수 있어요!",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2023fall/quest_purchaseSharingOnInstagram.png",
reward: 100,
isApiRequired: true,
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024spring/quest_eventSharing.png",
reward: 250,
maxCount: 0,
},
});

Expand Down Expand Up @@ -138,6 +136,7 @@ const completePayingAndSendingQuest = async (userId, timestamp, roomObject) => {

if (roomObject.part.length < 2) return null;
if (
!eventPeriod ||
roomObject.time >= eventPeriod.endAt ||
roomObject.time < eventPeriod.startAt
)
Expand Down Expand Up @@ -177,6 +176,7 @@ const completePayingQuest = async (userId, timestamp, roomObject) => {

if (roomObject.part.length < 2) return null;
if (
!eventPeriod ||
roomObject.time >= eventPeriod.endAt ||
roomObject.time < eventPeriod.startAt
)
Expand Down Expand Up @@ -204,6 +204,7 @@ const completeSendingQuest = async (userId, timestamp, roomObject) => {

if (roomObject.part.length < 2) return null;
if (
!eventPeriod ||
roomObject.time >= eventPeriod.endAt ||
roomObject.time < eventPeriod.startAt
)
Expand Down Expand Up @@ -258,6 +259,30 @@ const completeAdPushAgreementQuest = async (
return await completeQuest(userId, timestamp, quests.adPushAgreement);
};

/**
* eventSharing, eventSharing5 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @returns {Promise}
* @description 초대 링크를 통해 사용자가 이벤트에 참여할 때마다, 초대한 사용자 및 초대받은 사용자에 대해 각각 호출해 주세요.
*/
const completeEventSharingQuest = async (userId, timestamp) => {
const eventSharingResult = await completeQuest(
userId,
timestamp,
quests.eventSharing
);
if (!eventSharingResult || eventSharingResult.questCount % 5 !== 0)
return [eventSharingResult, null];

const eventSharing5Result = await completeQuest(
userId,
timestamp,
quests.eventSharing5
);
return [eventSharingResult, eventSharing5Result];
};

module.exports = {
quests,
completeFirstLoginQuest,
Expand All @@ -268,4 +293,5 @@ module.exports = {
completeNicknameChangingQuest,
completeAccountChangingQuest,
completeAdPushAgreementQuest,
completeEventSharingQuest,
};
16 changes: 11 additions & 5 deletions src/lottery/modules/quests.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const mongoose = require("mongoose");

const { eventConfig } = require("../../../loadenv");
const eventPeriod = eventConfig && {
startAt: new Date(eventConfig.startAt),
endAt: new Date(eventConfig.endAt),
startAt: new Date(eventConfig.period.startAt),
endAt: new Date(eventConfig.period.endAt),
};

const requiredQuestFields = ["name", "description", "imageUrl", "reward"];
Expand Down Expand Up @@ -70,18 +70,23 @@ const completeQuest = async (userId, timestamp, quest) => {
if (!eventStatus || eventStatus.isBanned) return null;

// 2단계: 이벤트 기간인지 확인합니다.
if (timestamp >= eventPeriod.endAt || timestamp < eventPeriod.startAt) {
if (
!eventPeriod ||
timestamp >= eventPeriod.endAt ||
timestamp < eventPeriod.startAt
) {
logger.info(
`User ${userId} failed to complete auto-disabled ${quest.id}Quest`
);
return null;
}

// 3단계: 유저의 퀘스트 완료 횟수를 확인합니다.
// maxCount가 0인 경우, 무제한으로 퀘스트를 완료할 수 있습니다.
const questCount = eventStatus.completedQuests.filter(
(completedQuestId) => completedQuestId === quest.id
).length;
if (questCount >= quest.maxCount) {
if (quest.maxCount > 0 && questCount >= quest.maxCount) {
logger.info(
`User ${userId} already completed ${quest.id}Quest ${questCount} times`
);
Expand Down Expand Up @@ -126,7 +131,7 @@ const completeQuest = async (userId, timestamp, quest) => {
amount: quest.reward.credit,
userId,
questId: quest.id,
comment: `"${quest.name}" 퀘스트를 완료해 송편 ${quest.reward.credit}개를 획득했습니다.`,
comment: `"${quest.name}" 퀘스트를 완료해 ${eventConfig?.credit.name} ${quest.reward.credit}개를 획득했습니다.`,
});
await transaction.save();

Expand All @@ -149,6 +154,7 @@ const completeQuest = async (userId, timestamp, quest) => {
logger.info(`User ${userId} successfully completed ${quest.id}Quest`);
return {
quest,
questCount: questCount + 1,
transactionsId,
};
} catch (err) {
Expand Down
Loading

0 comments on commit 9d72e86

Please sign in to comment.