refactor: optimize repoGetTodayStudyStats with SQL aggregation and use shared constants

- Replace JS counting with Prisma groupBy for better performance
- Add DEFAULT_NEW_PER_DAY and DEFAULT_REV_PER_DAY constants
- Use constants in InDeck.tsx
This commit is contained in:
2026-03-16 09:31:21 +08:00
parent ada2f249ee
commit 1d5732abc8
3 changed files with 36 additions and 21 deletions

View File

@@ -18,6 +18,7 @@ import type { ActionOutputCardWithNote } from "@/modules/card/card-action-dto";
import type { ActionOutputTodayStudyStats } from "@/modules/card/card-action-dto"; import type { ActionOutputTodayStudyStats } from "@/modules/card/card-action-dto";
import type { ActionOutputDeck } from "@/modules/deck/deck-action-dto"; import type { ActionOutputDeck } from "@/modules/deck/deck-action-dto";
import { toast } from "sonner"; import { toast } from "sonner";
import { DEFAULT_NEW_PER_DAY, DEFAULT_REV_PER_DAY } from "@/shared/constant";
export function InDeck({ deckId, isReadOnly }: { deckId: number; isReadOnly: boolean; }) { export function InDeck({ deckId, isReadOnly }: { deckId: number; isReadOnly: boolean; }) {
@@ -29,7 +30,7 @@ export function InDeck({ deckId, isReadOnly }: { deckId: number; isReadOnly: boo
const [deckInfo, setDeckInfo] = useState<ActionOutputDeck | null>(null); const [deckInfo, setDeckInfo] = useState<ActionOutputDeck | null>(null);
const [todayStats, setTodayStats] = useState<ActionOutputTodayStudyStats | null>(null); const [todayStats, setTodayStats] = useState<ActionOutputTodayStudyStats | null>(null);
const [openSettingsModal, setSettingsModal] = useState(false); const [openSettingsModal, setSettingsModal] = useState(false);
const [settingsForm, setSettingsForm] = useState({ newPerDay: 20, revPerDay: 200 }); const [settingsForm, setSettingsForm] = useState({ newPerDay: DEFAULT_NEW_PER_DAY, revPerDay: DEFAULT_REV_PER_DAY });
const [savingSettings, setSavingSettings] = useState(false); const [savingSettings, setSavingSettings] = useState(false);
const router = useRouter(); const router = useRouter();
const t = useTranslations("deck_id"); const t = useTranslations("deck_id");

View File

@@ -346,7 +346,8 @@ export async function repoGetTodayStudyStats(
startOfToday.setUTCHours(0, 0, 0, 0); startOfToday.setUTCHours(0, 0, 0, 0);
const todayStart = startOfToday.getTime(); const todayStart = startOfToday.getTime();
const revlogs = await prisma.revlog.findMany({ const stats = await prisma.revlog.groupBy({
by: ["type"],
where: { where: {
card: { card: {
deckId: input.deckId, deckId: input.deckId,
@@ -355,30 +356,29 @@ export async function repoGetTodayStudyStats(
gte: todayStart, gte: todayStart,
}, },
}, },
select: { _count: {
id: true,
cardId: true,
type: true, type: true,
}, },
}); });
const stats: RepoOutputTodayStudyStats = { let newStudied = 0;
newStudied: 0, let reviewStudied = 0;
reviewStudied: 0, let learningStudied = 0;
learningStudied: 0,
totalStudied: 0,
};
for (const revlog of revlogs) { for (const stat of stats) {
stats.totalStudied++; if (stat.type === 0) {
if (revlog.type === 0) { newStudied = stat._count.type;
stats.newStudied++; } else if (stat.type === 1) {
} else if (revlog.type === 1) { learningStudied = stat._count.type;
stats.learningStudied++; } else if (stat.type === 2 || stat.type === 3) {
} else if (revlog.type === 2 || revlog.type === 3) { reviewStudied += stat._count.type;
stats.reviewStudied++;
} }
} }
return stats; return {
newStudied,
reviewStudied,
learningStudied,
totalStudied: newStudied + reviewStudied + learningStudied,
};
} }

View File

@@ -20,3 +20,17 @@ export const LENGTH_MAX_USERNAME = 30;
export const LENGTH_MIN_USERNAME = 3; export const LENGTH_MIN_USERNAME = 3;
export const LENGTH_MAX_PASSWORD = 100; export const LENGTH_MAX_PASSWORD = 100;
export const LENGTH_MIN_PASSWORD = 8; export const LENGTH_MIN_PASSWORD = 8;
export const FIELD_SEPARATOR = "\x1f";
export const DEFAULT_NEW_PER_DAY = 20;
export const DEFAULT_REV_PER_DAY = 200;
export const SECONDS_PER_MINUTE = 60;
export const SECONDS_PER_HOUR = 3600;
export const SECONDS_PER_DAY = 86400;
export const MS_PER_SECOND = 1000;
export const MS_PER_MINUTE = 60000;
export const MS_PER_HOUR = 3600000;
export const MS_PER_DAY = 86400000;