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

Main branch update from Dev branch #576

Merged
merged 35 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3351f43
Add: quiz
thxx132 Feb 13, 2025
6d1ed62
Fix: feedback, Add: determine answer status
thxx132 Feb 15, 2025
e7bd4cf
Fix: feedback-2
thxx132 Feb 16, 2025
b796e04
Add: items/useCoupon endpoint
kmc7468 Feb 17, 2025
265d3ab
Fix: logger import error
kmc7468 Feb 17, 2025
85c3849
Add: restrict withdrawal for event participants
kmc7468 Feb 17, 2025
a3a5508
Docs: update comments
kmc7468 Feb 17, 2025
0bbbf16
Add: give coin, Fix: add draw for quizschema
thxx132 Feb 17, 2025
7af0f24
Fix: mistake
thxx132 Feb 17, 2025
a95921e
Fix: dailyAttendance credit
thxx132 Feb 18, 2025
192007e
Fix: feedback-3
thxx132 Feb 18, 2025
ae8b575
Fix: answer: any
thxx132 Feb 18, 2025
679ae5c
Add: option A and B
thxx132 Feb 18, 2025
c998dd1
Fix: feedback
thxx132 Feb 18, 2025
411e2e7
Add: add timelock to submit and cancel
thxx132 Feb 18, 2025
5575277
Fix: feedback
thxx132 Feb 19, 2025
3ad5897
Fix: error
thxx132 Feb 19, 2025
ea169a3
Add: ongoing
thxx132 Feb 19, 2025
809b526
Fix: coupon purchase disable
kmc7468 Feb 19, 2025
5fe2a8b
Merge pull request #571 from sparcs-kaist/570-2025-spring-event-coupo…
kmc7468 Feb 19, 2025
591cf84
Add: indirect event sharing
thxx132 Feb 19, 2025
b5297ad
Remove: console log
thxx132 Feb 19, 2025
6a9c582
Fix: resolve conflicts
thxx132 Feb 19, 2025
313f185
Fix: build error
thxx132 Feb 19, 2025
0fdbd03
Fix: resolve conflict
thxx132 Feb 19, 2025
aa824e8
Fix: feedback and res status about cancelHandler
thxx132 Feb 19, 2025
c4b9df4
Fix: cancelHandler
thxx132 Feb 19, 2025
b1fa1eb
Fix: cancelAnswerHandler status
thxx132 Feb 19, 2025
fffe2ca
Merge pull request #568 from sparcs-kaist/567-2025-event-quiz
thxx132 Feb 19, 2025
503a25e
Fix: feedback
thxx132 Feb 19, 2025
047aca9
Merge pull request #574 from sparcs-kaist/573-indirect-Event-Sharing
thxx132 Feb 19, 2025
f9201bf
Refactor: update contracts.js
kmc7468 Feb 19, 2025
6ee8c15
Refactor: update contracts.js
kmc7468 Feb 20, 2025
7269a17
Fix: dailyQuiz cronjob error
kmc7468 Feb 20, 2025
20791ff
Merge pull request #575 from sparcs-kaist/572-2025-spring-event-updat…
kmc7468 Feb 20, 2025
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
4 changes: 2 additions & 2 deletions src/loadenv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ export const eventConfig = (process.env.EVENT_CONFIG &&
mode: "2025spring",
credit: { name: "넙죽코인", initialAmount: 0 },
period: {
startAt: "2025-02-19T00:00:00+09:00",
endAt: "2024-03-13T00:00:00+09:00",
startAt: "2025-02-21T00:00:00+09:00",
endAt: "2025-03-13T00:00:00+09:00",
},
};
export const naverMap = {
Expand Down
3 changes: 3 additions & 0 deletions src/lottery/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const {
questModel,
itemModel,
transactionModel,
quizModel,
} = require("./modules/stores/mongo");

const { buildResource } = require("../modules/adminResource");
Expand Down Expand Up @@ -33,6 +34,7 @@ lotteryRouter.use("/transactions", require("./routes/transactions"));
lotteryRouter.use("/items", require("./routes/items"));
// lotteryRouter.use("/publicNotice", require("./routes/publicNotice"));
lotteryRouter.use("/quests", require("./routes/quests"));
lotteryRouter.use("/quizzes", require("./routes/quizzes").default);

// [AdminJS] AdminJS에 표시할 Resource 생성
const resources =
Expand All @@ -41,6 +43,7 @@ const resources =
buildResource()(questModel),
buildResource([addOneItemStockAction, addFiveItemStockAction])(itemModel),
buildResource()(transactionModel),
buildResource()(quizModel),
]) ||
[];

Expand Down
2 changes: 1 addition & 1 deletion src/lottery/middlewares/checkBanned.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { eventStatusModel } = require("../modules/stores/mongo");
const logger = require("../../modules/logger");
const logger = require("@/modules/logger").default;

/**
* 사용자가 차단 되었는지 여부를 판단합니다.
Expand Down
84 changes: 63 additions & 21 deletions src/lottery/modules/contracts.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { buildQuests, completeQuest } = require("./quests");
const mongoose = require("mongoose");
import logger from "../../modules/logger";
const logger = require("@/modules/logger").default;

const { eventConfig } = require("@/loadenv");
const eventPeriod = eventConfig && {
Expand All @@ -13,25 +13,25 @@ const quests = buildQuests({
firstLogin: {
name: "첫 발걸음",
description:
"이벤트 참여만 해도 송편코인을 얻을 수 있다고?? 이벤트 참여에 동의하고 송편코인을 받아 보세요.",
"이벤트 참여만 해도 넙죽코인을 얻을 수 있다고?? 이벤트 참여에 동의하고 넙죽코인을 받아보세요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024fall/quest_firstLogin.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_firstLogin.png",
reward: 200,
},
firstRoomCreation: {
name: "첫 방 개설",
description:
"원하는 택시팟을 찾을 수 없다면? 원하는 조건으로 <b>방 개설 페이지</b>에서 방을 직접 개설해 보세요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024fall/quest_firstRoomCreation.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_firstRoomCreation.png",
reward: 500,
},
roomSharing: {
name: "이 택시팟은 진짜 유명한 택시팟임",
name: "4명!",
description:
"방을 공유해 친구들을 택시팟에 초대해 보세요. 채팅창 상단의 햄버거(☰) 버튼을 누르면 <b>공유하기 버튼</b>을 찾을 수 있어요.",
"방을 공유해 친구들을 택시팟에 초대해 보세요. 최대 4명까지 참여할 수 있어요. 채팅창 상단의 햄버거(☰) 버튼을 누르면 <b>공유하기 버튼</b>을 찾을 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024fall/quest_roomSharing.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_roomSharing.png",
reward: 500,
isApiRequired: true,
},
Expand All @@ -40,7 +40,7 @@ const quests = buildQuests({
description:
"2명 이상과 함께 택시를 타고 택시비를 결제한 후 정산을 요청해 보세요. 정산하기 버튼은 채팅 페이지 좌측 하단의 <b>+ 버튼</b>을 눌러 찾을 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024fall/quest_fareSettlement.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_fareSettlement.png",
reward: 2000,
maxCount: 0,
},
Expand All @@ -49,7 +49,7 @@ const quests = buildQuests({
description:
"2명 이상과 함께 택시를 타고 택시비를 결제한 분께 송금해 주세요. 송금하기 버튼은 채팅 페이지 좌측 하단의 <b>+ 버튼</b>을 눌러 찾을 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024fall/quest_farePayment.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_farePayment.png",
reward: 2000,
maxCount: 0,
},
Expand All @@ -58,50 +58,68 @@ const quests = buildQuests({
description:
"닉네임을 변경하여 자신을 표현하세요. <b>마이 페이지</b>의 <b>수정하기</b> 버튼을 눌러 닉네임을 수정할 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024fall/quest_nicknameChanging.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_nicknameChanging.png",
reward: 500,
},
accountChanging: {
name: "계좌 등록을 해야 능률이 올라갑니다",
description:
"정산하기 기능을 더욱 빠르게 이용할 수 있다고? 계좌 번호를 등록하면 정산하기를 할 때 계좌가 자동으로 입력돼요. <b>마이 페이지</b>의 <b>수정하기</b> 버튼을 눌러 계좌 번호를 등록 또는 수정할 수 있어요.",
"정산하기 기능을 더욱 빠르게 이용할 수 있다고? 계좌 번호를 등록하면 정산하기를 할 때 계좌가 자동으로 입력돼요. <b>마이 페이지</b>의 <b>수정하기</b> 버튼을 눌러 계좌 번호를 등록할 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024fall/quest_accountChanging.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_accountChanging.png",
reward: 500,
},
adPushAgreement: {
name: "Taxi의 소울메이트",
name: "잊을만하면 찾아오는 Taxi",
description:
"Taxi 서비스를 잊지 않도록 가끔 찾아갈게요! 광고성 푸시 알림 수신 동의를 해주시면 방이 많이 모이는 시즌, 주변에 Taxi 앱 사용자가 있을 때 알려드릴 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024fall/quest_adPushAgreement.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_adPushAgreement.png",
reward: 500,
},
eventSharing: {
name: "Taxi를 아십니까",
description:
"내가 초대한 사람이 이벤트에 참여하면 송편코인을 드려요. 다른 사람의 초대를 받아 이벤트에 참여한 경우에도 이 퀘스트가 달성돼요.",
"내가 초대한 사람이 이벤트에 참여하면 넙죽코인을 드려요. 다른 사람의 초대를 받아 이벤트에 참여한 경우에도 이 퀘스트가 달성돼요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024fall/quest_eventSharing.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_eventSharing.png",
reward: 700,
maxCount: 0,
},
indirectEventSharing: {
name: "코인이 복사가 된다고?",
description:
"내가 초대한 사람이 다른 누군가를 이벤트에 초대하면 넙죽코인을 받아요. 그 사람이 또 다른 누군가를 초대하면 또 넙죽코인을 받아요. 그 사람이 또 …",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_indirectEventSharing.png",
reward: 300,
maxCount: 10,
},
dailyAttendance: {
name: "매일매일 출석 췤!",
description:
"매일 Taxi에 접속하면 하루 한 번 송편코인을 드려요! 하루에 한 번, 택시팟도 둘러보고 송편코인도 받아 가세요.",
"매일 Taxi에 접속해서 <b>밸런스 게임에 참여</b>하면 하루에 한 번씩 넙죽코인을 드려요! 매일 Taxi에서 택시팟 둘러보고 넙죽코인도 받아가세요. 밸런스 게임은 23시 55분까지만 참여할 수 있어요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024fall/quest_dailyAttendance.png",
reward: 700,
maxCount: 17,
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_dailyAttendance.png",
reward: 100,
maxCount: 20,
isApiRequired: true,
},
answerCorrectly: {
name: "이기는 편 우리 편",
description:
"전날의 밸런스 게임에서 응답자 수가 많은 선택지를 고른 경우 추가 넙죽코인을 드려요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_answerCorrectly.png",
reward: 600,
maxCount: 20,
},
itemPurchase: {
name: "Taxi에서 산 응모권",
description:
"응모권 교환소에서 아무 경품 응모권이나 구매해 보세요. Taxi에서 판매하는 응모권은 모두 정품이니 안심해도 좋아요.",
imageUrl:
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2024fall/quest_itemPurchase.png",
"https://sparcs-taxi-prod.s3.ap-northeast-2.amazonaws.com/assets/event-2025spring/quest_itemPurchase.png",
reward: 500,
},
});
Expand Down Expand Up @@ -242,6 +260,28 @@ const completeEventSharingQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, quests.eventSharing);
};

/**
* indirectEventSharing 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @returns {Promise}
* @usage lottery/globalState - createUserGlobalStateHandler
*/
const completeIndirectEventSharingQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, quests.indirectEventSharing);
};

/**
* completeAnswerCorrectlyQuest 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
* @param {number|Date} timestamp - 퀘스트 완료를 요청한 시각입니다.
* @returns {Promise}
* @usage lottery/schedules/dailyQuiz - determineQuizResult
*/
export const completeAnswerCorrectlyQuest = async (userId, timestamp) => {
return await completeQuest(userId, timestamp, quests.answerCorrectly);
};

/**
* itemPurchase 퀘스트의 완료를 요청합니다.
* @param {string|mongoose.Types.ObjectId} userId - 퀘스트를 완료한 사용자의 ObjectId입니다.
Expand All @@ -263,5 +303,7 @@ module.exports = {
completeAccountChangingQuest,
completeAdPushAgreementQuest,
completeEventSharingQuest,
completeIndirectEventSharingQuest,
completeAnswerCorrectlyQuest,
completeItemPurchaseQuest,
};
4 changes: 2 additions & 2 deletions src/lottery/modules/items.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { itemModel } = require("./stores/mongo");
const { buildRecordAction } = require("../../modules/adminResource");
const logger = require("../../modules/logger");
const { buildRecordAction } = require("@/modules/adminResource");
const logger = require("@/modules/logger").default;

const addItemStockActionHandler = (count) => async (req, res, context) => {
const itemId = context.record.params._id;
Expand Down
2 changes: 1 addition & 1 deletion src/lottery/modules/quests.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const {
itemModel,
transactionModel,
} = require("./stores/mongo");
const logger = require("../../modules/logger");
const logger = require("@/modules/logger").default;
const mongoose = require("mongoose");

const { eventConfig } = require("@/loadenv");
Expand Down
21 changes: 21 additions & 0 deletions src/lottery/modules/quizzes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { quizModel } from "../modules/stores/mongo";

/**
* 특정 날짜의 퀴즈를 조회하는 함수
* @param {Date} date - 조회할 날짜
* @returns {Promise<any>} - 조회된 퀴즈 객체 반환 (없으면 null)
*/
export const getQuizByDate = async (date: Date) => {
const todayMidnight = new Date(date);
todayMidnight.setHours(0, 0, 0, 0);

const tomorrowMidnight = new Date(todayMidnight);
tomorrowMidnight.setDate(todayMidnight.getDate() + 1);

return await quizModel.findOne({
quizDate: {
$gte: todayMidnight,
$lt: tomorrowMidnight,
},
});
};
2 changes: 1 addition & 1 deletion src/lottery/modules/slackNotification.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { sendTextToReportChannel } = require("../../modules/slackNotification");
const { sendTextToReportChannel } = require("@/modules/slackNotification");

const generateContent = (name, userIds, roomIds = []) => {
if (userIds.length === 0) return "";
Expand Down
35 changes: 34 additions & 1 deletion src/lottery/modules/stores/mongo.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ const itemSchema = Schema({
}, // 상품의 실제 재고
itemType: {
type: Number,
enum: [0, 1, 2, 3],
enum: [0, 1, 2, 3, 4], // 0: 일반 상품, 1: 일반 응모권, 2: 고급 응모권, 3: 랜덤 박스, 4: 쿠폰
required: true,
},
isRandomItem: {
Expand All @@ -128,6 +128,15 @@ const itemSchema = Schema({
min: 0,
validate: integerValidator,
},
couponCode: {
type: String,
unique: true,
},
couponReward: {
type: Number,
min: 0,
validate: integerValidator,
},
});

const transactionSchema = Schema({
Expand Down Expand Up @@ -169,6 +178,29 @@ transactionSchema.set("timestamps", {
updatedAt: false,
});

const quizSchema = Schema({
quizDate: { type: Date, required: true, unique: true },
title: { type: String, required: true },
content: { type: String, required: true },
image: { type: String, required: true },
// A와 B는 최종 답안, C와 D는 인원 수에 따라 dailyQuiz.ts 에서 A와 B로 결정됩니다.
answer: { type: String, enum: ["A", "B", "C", "D"], default: "C" },
countA: { type: Number, default: 0 },
countB: { type: Number, default: 0 },
answers: [
{
userId: { type: Schema.Types.ObjectId, ref: "User", required: true },
answer: { type: String, enum: ["A", "B"] },
submittedAt: { type: Date, required: true },
status: {
type: String,
enum: ["correct", "wrong", "unknown", "draw"],
default: "unknown",
},
},
],
});

module.exports = {
eventStatusModel: mongoose.model(
`${modelNamePrefix}EventStatus`,
Expand All @@ -180,4 +212,5 @@ module.exports = {
`${modelNamePrefix}Transaction`,
transactionSchema
),
quizModel: mongoose.model(`${modelNamePrefix}Quiz`, quizSchema),
};
42 changes: 41 additions & 1 deletion src/lottery/routes/docs/items.js
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ itemsDocs[`${apiPrefix}/purchase/{itemId}`] = {
in: "path",
name: "itemId",
required: true,
description: "리더보드를 조회할 상품의 ObjectId",
description: "구입할 상품의 ObjectId",
example: "ITEM ID",
},
],
Expand Down Expand Up @@ -316,5 +316,45 @@ itemsDocs[`${apiPrefix}/purchase/{itemId}`] = {
},
},
};
itemsDocs[`${apiPrefix}/useCoupon/{couponCode}`] = {
post: {
tags: [`${apiPrefix}`],
summary: "쿠폰 사용",
description: "쿠폰을 사용합니다.",
parameters: [
{
in: "path",
name: "couponCode",
required: true,
description: "사용할 쿠폰의 코드",
example: "COUPON CODE",
},
],
responses: {
200: {
content: {
"application/json": {
schema: {
type: "object",
required: ["result", "reward"],
properties: {
result: {
type: "boolean",
description: "성공 여부. 항상 true입니다.",
example: true,
},
reward: {
type: "number",
description: "쿠폰의 사용 보상. 0 이상의 정수입니다.",
example: 500,
},
},
},
},
},
},
},
},
};

module.exports = itemsDocs;
3 changes: 3 additions & 0 deletions src/lottery/routes/docs/schemas/itemsSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ const itemsZod = {
purchaseItemHandlerBody: z.object({
amount: z.number().int().positive(),
}),
useCouponHandlerParams: z.object({
couponCode: z.string().regex(/^[a-zA-Z0-9]+$/),
}),
};

const itemsSchema = zodToSchemaObject(itemsZod);
Expand Down
Loading
Loading