feat: daily visit quest with streak bonus
This commit is contained in:
parent
abfca19833
commit
e399805d14
106
server/api/quests/daily-visit.post.ts
Normal file
106
server/api/quests/daily-visit.post.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
import { useSession } from 'h3';
|
||||||
|
|
||||||
|
interface DailyVisitResponse {
|
||||||
|
message: string;
|
||||||
|
reward: {
|
||||||
|
coins: number;
|
||||||
|
streakBonus: boolean;
|
||||||
|
};
|
||||||
|
updatedCoins: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper function to safely get the authenticated user's ID from the session.
|
||||||
|
*/
|
||||||
|
async function getUserIdFromSession(event: any): Promise<number> {
|
||||||
|
const session = await useSession(event, {
|
||||||
|
password: process.env.SESSION_PASSWORD,
|
||||||
|
});
|
||||||
|
const userId = session.data?.user?.id;
|
||||||
|
if (!userId) {
|
||||||
|
throw createError({ statusCode: 401, statusMessage: 'Unauthorized' });
|
||||||
|
}
|
||||||
|
return userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a Date object for the start of a given day in UTC.
|
||||||
|
*/
|
||||||
|
function getStartOfDay(date: Date): Date {
|
||||||
|
const startOfDay = new Date(date);
|
||||||
|
startOfDay.setUTCHours(0, 0, 0, 0);
|
||||||
|
return startOfDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event): Promise<DailyVisitResponse> => {
|
||||||
|
const userId = await getUserIdFromSession(event);
|
||||||
|
const today = getStartOfDay(new Date());
|
||||||
|
|
||||||
|
// 1. Check if the user has already claimed the reward today
|
||||||
|
const existingVisit = await prisma.dailyVisit.findUnique({
|
||||||
|
where: {
|
||||||
|
userId_date: {
|
||||||
|
userId,
|
||||||
|
date: today,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (existingVisit) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 409,
|
||||||
|
statusMessage: 'Daily visit reward has already been claimed today.',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Check for a 5-day consecutive streak (i.e., visits on the 4 previous days)
|
||||||
|
const previousDates = Array.from({ length: 4 }, (_, i) => {
|
||||||
|
const d = new Date(today);
|
||||||
|
d.setUTCDate(d.getUTCDate() - (i + 1));
|
||||||
|
return d;
|
||||||
|
});
|
||||||
|
|
||||||
|
const priorVisitsCount = await prisma.dailyVisit.count({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
date: {
|
||||||
|
in: previousDates,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const hasStreak = priorVisitsCount === 4;
|
||||||
|
|
||||||
|
// 3. Calculate rewards and update the database in a transaction
|
||||||
|
let totalReward = 1; // Base reward
|
||||||
|
if (hasStreak) {
|
||||||
|
totalReward += 10; // Streak bonus
|
||||||
|
}
|
||||||
|
|
||||||
|
const [, updatedUser] = await prisma.$transaction([
|
||||||
|
prisma.dailyVisit.create({
|
||||||
|
data: {
|
||||||
|
userId,
|
||||||
|
date: today,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
prisma.user.update({
|
||||||
|
where: { id: userId },
|
||||||
|
data: {
|
||||||
|
coins: {
|
||||||
|
increment: totalReward,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// 4. Return the response
|
||||||
|
return {
|
||||||
|
message: hasStreak ? 'Daily visit and streak bonus claimed!' : 'Daily visit claimed!',
|
||||||
|
reward: {
|
||||||
|
coins: totalReward,
|
||||||
|
streakBonus: hasStreak,
|
||||||
|
},
|
||||||
|
updatedCoins: updatedUser.coins,
|
||||||
|
};
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user