Skip to content

Commit

Permalink
Merge branch 'dev' of https://github.com/sparcs-kaist/taxi-back into #…
Browse files Browse the repository at this point in the history
…273-안-읽은-메세지-확인
  • Loading branch information
chlehdwon committed Sep 19, 2023
2 parents 341b11f + 978c78d commit b5a9331
Show file tree
Hide file tree
Showing 11 changed files with 216 additions and 63 deletions.
78 changes: 61 additions & 17 deletions src/lottery/modules/contracts/2023fall.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const quests = buildQuests({
reward: 50,
maxCount: 3,
},
nicknameChaning: {
nicknameChanging: {
name: "닉네임 변경",
description: "",
imageUrl: "",
Expand Down Expand Up @@ -84,22 +84,25 @@ const eventPeriod = {
/**
* firstLogin 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @returns {Promise}
* @usage lottery/globalState/createUserGlobalStateHandler
*/
const completeFirstLoginQuest = async (userId) => {
return await completeQuest(userId, eventPeriod, quests.firstLogin);
const completeFirstLoginQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, eventPeriod, quests.firstLogin);
};

/**
* payingAndSending 퀘스트의 완료를 요청합니다. 방의 참가자 수가 2명 미만이거나, 모든 참가자가 정산 또는 송금을 완료하지 않았다면 요청하지 않습니다.
* @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 (roomObject) => {
const completePayingAndSendingQuest = async (timestamp, roomObject) => {
if (roomObject.part.length < 2) return null;
if (roomObject.part.length > roomObject.settlementTotal) return null;

Expand All @@ -108,6 +111,7 @@ const completePayingAndSendingQuest = async (roomObject) => {
async (participant) =>
await completeQuest(
participant.user._id,
timestamp,
eventPeriod,
quests.payingAndSending
)
Expand All @@ -118,12 +122,18 @@ const completePayingAndSendingQuest = async (roomObject) => {
/**
* firstRoomCreation 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @returns {Promise}
* @description 방을 만들 때마다 호출해 주세요.
* @usage rooms/createHandler
*/
const completeFirstRoomCreationQuest = async (userId) => {
return await completeQuest(userId, eventPeriod, quests.firstRoomCreation);
const completeFirstRoomCreationQuest = async (userId, timestamp) => {
return await completeQuest(
userId,
timestamp,
eventPeriod,
quests.firstRoomCreation
);
};

const completeRoomSharingQuest = async () => {
Expand All @@ -133,60 +143,94 @@ const completeRoomSharingQuest = async () => {
/**
* paying 퀘스트의 완료를 요청합니다. 방의 참가자 수가 2명 미만이면 요청하지 않습니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {Object} roomObject - 방의 정보입니다.
* @param {Array<{ user: mongoose.Types.ObjectId }>} roomObject.part - 참여자 목록입니다.
* @returns {Promise}
* @description 정산 요청이 이루어질 때마다 호출해 주세요.
* @usage rooms/commitPaymentHandler
*/
const completePayingQuest = async (userId, roomObject) => {
const completePayingQuest = async (userId, timestamp, roomObject) => {
if (roomObject.part.length < 2) return null;

return await completeQuest(userId, eventPeriod, quests.paying);
return await completeQuest(userId, timestamp, eventPeriod, quests.paying);
};

/**
* sending 퀘스트의 완료를 요청합니다. 방의 참가자 수가 2명 미만이면 요청하지 않습니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {Object} roomObject - 방의 정보입니다.
* @param {Array<{ user: mongoose.Types.ObjectId }>} roomObject.part - 참여자 목록입니다.
* @returns {Promise}
* @description 송금이 이루어질 때마다 호출해 주세요.
* @usage rooms/settlementHandler
*/
const completeSendingQuest = async (userId, roomObject) => {
const completeSendingQuest = async (userId, timestamp, roomObject) => {
if (roomObject.part.length < 2) return null;

return await completeQuest(userId, eventPeriod, quests.sending);
return await completeQuest(userId, timestamp, eventPeriod, quests.sending);
};

/**
* nicknameChaning 퀘스트의 완료를 요청합니다.
* nicknameChanging 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @returns {Promise}
* @description 닉네임을 변경할 때마다 호출해 주세요.
* @usage users/editNicknameHandler
*/
const completeNicknameChangingQuest = async (userId) => {
return await completeQuest(userId, eventPeriod, quests.nicknameChaning);
const completeNicknameChangingQuest = async (userId, timestamp) => {
return await completeQuest(
userId,
timestamp,
eventPeriod,
quests.nicknameChanging
);
};

/**
* accountChanging 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {string} newAccount - 변경된 계좌입니다.
* @returns {Promise}
* @description 계좌를 변경할 때마다 호출해 주세요.
* @usage users/editAccountHandler
*/
const completeAccountChangingQuest = async (userId, newAccount) => {
const completeAccountChangingQuest = async (userId, timestamp, newAccount) => {
if (newAccount === "") return null;

return await completeQuest(userId, eventPeriod, quests.accountChanging);
return await completeQuest(
userId,
timestamp,
eventPeriod,
quests.accountChanging
);
};

const completeAdPushAgreementQuest = async () => {
// TODO
/**
* adPushAgreementQuest 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @param {boolean} advertisement - 변경된 광고성 알림 수신 동의 여부입니다.
* @returns {Promise}
* @description 알림 옵션을 변경할 때마다 호출해 주세요.
* @usage notifications/editOptionsHandler
*/
const completeAdPushAgreementQuest = async (
userId,
timestamp,
advertisement
) => {
if (!advertisement) return null;

return await completeQuest(
userId,
timestamp,
eventPeriod,
quests.adPushAgreement
);
};

const completeEventSharingOnInstagramQuest = async () => {
Expand Down
19 changes: 8 additions & 11 deletions src/lottery/modules/quests.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ 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)입니다.
Expand All @@ -56,24 +57,20 @@ const buildQuests = (quests) => {
* @param {number} quest.maxCount - 퀘스트의 최대 완료 가능 횟수입니다.
* @returns {Object|null} 성공한 경우 Object를, 실패한 경우 null을 반환합니다. 이미 최대 완료 횟수에 도달했거나, 퀘스트가 원격으로 비활성화 된 경우에도 실패로 처리됩니다.
*/
const completeQuest = async (userId, eventPeriod, quest) => {
const completeQuest = async (userId, timestamp, eventPeriod, quest) => {
try {
// 1단계: 이벤트 기간인지 확인합니다.
const now = Date.now();
if (now >= eventPeriod.end || now < eventPeriod.start) {
// 1단계: 유저의 EventStatus를 가져옵니다.
const eventStatus = await eventStatusModel.findOne({ userId }).lean();
if (!eventStatus) return null;

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

// 2단계: 유저의 EventStatus를 가져옵니다. 없으면 새롭게 생성합니다.
let eventStatus = await eventStatusModel.findOne({ userId }).lean();
if (!eventStatus) {
eventStatus = new eventStatusModel({ userId });
await eventStatus.save();
}

// 3단계: 유저의 퀘스트 완료 횟수를 확인합니다.
const questCount = eventStatus.completedQuests.filter(
(completedQuestId) => completedQuestId === quest.id
Expand Down
36 changes: 35 additions & 1 deletion src/lottery/routes/docs/globalState.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ globalStateDocs[`${apiPrefix}/`] = {
tags: [`${apiPrefix}`],
summary: "Frontend에서 Global state로 관리하는 정보 반환",
description:
"유저의 재화 개수, 퀘스트 완료 상태 등 Frontend에서 Global state로 관리할 정보를 가져옵니다. 유저에 대한 EventStatus Document가 없을 경우 새롭게 생성합니다.",
"유저의 재화 개수, 퀘스트 완료 상태 등 Frontend에서 Global state로 관리할 정보를 가져옵니다.",
responses: {
200: {
description: "",
Expand All @@ -16,13 +16,19 @@ globalStateDocs[`${apiPrefix}/`] = {
schema: {
type: "object",
required: [
"isAgree",
"creditAmount",
"completedQuests",
"ticket1Amount",
"ticket2Amount",
"quests",
],
properties: {
isAgreeOnTermsOfEvent: {
type: "boolean",
description: "유저의 이벤트 참여 동의 여부",
example: true,
},
creditAmount: {
type: "number",
description: "재화 개수. 0 이상입니다.",
Expand Down Expand Up @@ -116,5 +122,33 @@ globalStateDocs[`${apiPrefix}/`] = {
},
},
};
globalStateDocs[`${apiPrefix}/create`] = {
get: {
tags: [`${apiPrefix}`],
summary: "Frontend에서 Global state로 관리하는 정보 생성",
description:
"유저의 재화 개수, 퀘스트 완료 상태 등 Frontend에서 Global state로 관리할 정보를 생성합니다.",
responses: {
200: {
description: "",
content: {
"application/json": {
schema: {
type: "object",
required: ["result"],
properties: {
result: {
type: "boolean",
description: "성공 여부. 항상 true입니다.",
example: true,
},
},
},
},
},
},
},
},
};

module.exports = globalStateDocs;
11 changes: 11 additions & 0 deletions src/lottery/routes/docs/itemsSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,17 @@ const itemsSchema = {
...itemBase,
description: "랜덤박스를 구입한 경우에만 포함됩니다.",
},
purchaseHandler: {
type: "object",
required: ["itemId"],
properties: {
itemId: {
type: "string",
pattern: "^[a-fA-F\\d]{24}$",
},
},
errorMessage: "validation: bad request",
},
};

module.exports = itemsSchema;
6 changes: 4 additions & 2 deletions src/lottery/routes/globalState.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ const express = require("express");
const router = express.Router();
const globalStateHandlers = require("../services/globalState");

// 라우터 접근 시 로그인 필요
router.get("/", globalStateHandlers.getUserGlobalStateHandler);

// 아래의 Endpoint 접근 시 로그인 필요
router.use(require("../../middlewares/auth"));

router.get("/", globalStateHandlers.getUserGlobalStateHandler);
router.post("/create", globalStateHandlers.createUserGlobalStateHandler);

module.exports = router;
14 changes: 12 additions & 2 deletions src/lottery/routes/items.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@ const express = require("express");

const router = express.Router();
const itemsHandlers = require("../services/items");
const auth = require("../../middlewares/auth");

const { validateParams } = require("../../middlewares/ajv");
const itemsSchema = require("./docs/itemsSchema");

router.get("/list", itemsHandlers.listHandler);
router.post("/purchase/:itemId", auth, itemsHandlers.purchaseHandler);

// 아래의 Endpoint 접근 시 로그인 필요
router.use(require("../../middlewares/auth"));

router.post(
"/purchase/:itemId",
validateParams(itemsSchema.purchaseHandler),
itemsHandlers.purchaseHandler
);

module.exports = router;
Loading

0 comments on commit b5a9331

Please sign in to comment.