chore: PsProblem/백준 연동 데드코드 전면 제거
PsProblem, PsBookmark, PsSyncState 모델 3개 삭제. User.bojHandle, StudyLog.psProblemId FK 삭제. 12곳 psProblemId: null 쿼리 필터 제거. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
-- Remove PS (competitive programming) integration dead code
|
||||
-- FK 의존성 순서: ps_bookmarks → ps_problems → users/study_logs
|
||||
|
||||
-- DropForeignKey: ps_bookmarks
|
||||
ALTER TABLE `ps_bookmarks` DROP FOREIGN KEY `ps_bookmarks_psProblemId_fkey`;
|
||||
ALTER TABLE `ps_bookmarks` DROP FOREIGN KEY `ps_bookmarks_userId_fkey`;
|
||||
|
||||
-- DropForeignKey: ps_sync_states
|
||||
ALTER TABLE `ps_sync_states` DROP FOREIGN KEY `ps_sync_states_userId_fkey`;
|
||||
|
||||
-- DropForeignKey: study_logs.psProblemId
|
||||
ALTER TABLE `study_logs` DROP FOREIGN KEY `study_logs_psProblemId_fkey`;
|
||||
|
||||
-- DropTable (PsBookmark 먼저: PsProblem에 FK 의존)
|
||||
DROP TABLE `ps_bookmarks`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `ps_sync_states`;
|
||||
|
||||
-- DropTable
|
||||
DROP TABLE `ps_problems`;
|
||||
|
||||
-- AlterTable: study_logs에서 psProblemId 컬럼 및 인덱스 제거
|
||||
DROP INDEX `study_logs_psProblemId_idx` ON `study_logs`;
|
||||
ALTER TABLE `study_logs` DROP COLUMN `psProblemId`;
|
||||
|
||||
-- AlterTable: users에서 bojHandle 컬럼 제거
|
||||
ALTER TABLE `users` DROP COLUMN `bojHandle`;
|
||||
@@ -92,7 +92,6 @@ model User {
|
||||
targetExamYear Int?
|
||||
focusSubjects Json?
|
||||
focusUnits Json?
|
||||
bojHandle String? @db.VarChar(32)
|
||||
reviewIntensity ReviewIntensity @default(moderate)
|
||||
onboardedAt DateTime?
|
||||
subscriptionTier SubscriptionTier @default(free)
|
||||
@@ -102,8 +101,6 @@ model User {
|
||||
|
||||
subjects Subject[]
|
||||
studyLogs StudyLog[]
|
||||
psBookmarks PsBookmark[]
|
||||
psSyncState PsSyncState?
|
||||
reviewSchedules ReviewSchedule[]
|
||||
skillSnapshots SkillSnapshot[]
|
||||
organizationMembers OrganizationMember[]
|
||||
@@ -156,8 +153,6 @@ model StudyLog {
|
||||
tag Tag? @relation(fields: [tagId], references: [id], onDelete: SetNull)
|
||||
problemId Int?
|
||||
problem Problem? @relation(fields: [problemId], references: [id], onDelete: SetNull)
|
||||
psProblemId Int?
|
||||
psProblem PsProblem? @relation(fields: [psProblemId], references: [id], onDelete: SetNull)
|
||||
|
||||
title String
|
||||
difficulty Float
|
||||
@@ -176,7 +171,6 @@ model StudyLog {
|
||||
@@index([subjectId])
|
||||
@@index([tagId])
|
||||
@@index([problemId])
|
||||
@@index([psProblemId])
|
||||
@@map("study_logs")
|
||||
}
|
||||
|
||||
@@ -402,48 +396,3 @@ model SkillSnapshot {
|
||||
@@map("skill_snapshots")
|
||||
}
|
||||
|
||||
model PsProblem {
|
||||
id Int @id @default(autoincrement())
|
||||
bojId Int @unique
|
||||
title String
|
||||
titleKo String?
|
||||
level Int
|
||||
tags Json?
|
||||
acceptedUserCount Int?
|
||||
averageTries Float?
|
||||
solvedacUpdatedAt DateTime?
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
bookmarks PsBookmark[]
|
||||
studyLogs StudyLog[]
|
||||
|
||||
@@index([level])
|
||||
@@map("ps_problems")
|
||||
}
|
||||
|
||||
model PsBookmark {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
psProblemId Int
|
||||
psProblem PsProblem @relation(fields: [psProblemId], references: [id], onDelete: Cascade)
|
||||
memo String?
|
||||
createdAt DateTime @default(now())
|
||||
|
||||
@@unique([userId, psProblemId])
|
||||
@@index([userId])
|
||||
@@map("ps_bookmarks")
|
||||
}
|
||||
|
||||
model PsSyncState {
|
||||
id Int @id @default(autoincrement())
|
||||
userId Int @unique
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
lastSyncedAt DateTime?
|
||||
lastSolvedCount Int @default(0)
|
||||
lastTier Int?
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
@@map("ps_sync_states")
|
||||
}
|
||||
|
||||
@@ -98,7 +98,6 @@ export class AuthService {
|
||||
targetExamYear: number | null;
|
||||
focusSubjects: Prisma.JsonValue | null;
|
||||
focusUnits: Prisma.JsonValue | null;
|
||||
bojHandle: string | null;
|
||||
reviewIntensity: string;
|
||||
onboardedAt: Date | null;
|
||||
subscriptionTier: string;
|
||||
@@ -120,7 +119,6 @@ export class AuthService {
|
||||
? u.focusSubjects.filter((subject): subject is string => typeof subject === 'string')
|
||||
: [],
|
||||
focusUnits: this.normalizeFocusUnits(u.focusUnits),
|
||||
bojHandle: u.bojHandle ?? null,
|
||||
reviewIntensity: u.reviewIntensity,
|
||||
onboarded: u.onboardedAt !== null,
|
||||
subscriptionTier: u.subscriptionTier,
|
||||
|
||||
@@ -26,9 +26,6 @@ export class DashboardService {
|
||||
userId,
|
||||
status: ReviewStatus.pending,
|
||||
scheduledAt: { lte: now },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
},
|
||||
},
|
||||
}),
|
||||
this.prisma.reviewSchedule.count({
|
||||
@@ -36,15 +33,11 @@ export class DashboardService {
|
||||
userId,
|
||||
status: ReviewStatus.pending,
|
||||
scheduledAt: { gt: now, lte: todayEnd },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
},
|
||||
},
|
||||
}),
|
||||
this.prisma.studyLog.findMany({
|
||||
where: {
|
||||
userId,
|
||||
psProblemId: null,
|
||||
},
|
||||
orderBy: { studiedAt: 'desc' },
|
||||
take: 5,
|
||||
@@ -57,7 +50,6 @@ export class DashboardService {
|
||||
by: ['result'],
|
||||
where: {
|
||||
userId,
|
||||
psProblemId: null,
|
||||
studiedAt: { gte: weekStart },
|
||||
},
|
||||
_count: { result: true },
|
||||
|
||||
@@ -20,7 +20,6 @@ import {
|
||||
IsOptional,
|
||||
IsString,
|
||||
Length,
|
||||
Matches,
|
||||
Max,
|
||||
Min,
|
||||
} from 'class-validator';
|
||||
@@ -80,10 +79,6 @@ class OnboardingDto {
|
||||
@IsEnum(MathUnit, { each: true })
|
||||
focusUnits?: MathUnit[];
|
||||
|
||||
@IsOptional()
|
||||
@Matches(/^[a-zA-Z0-9_-]{3,20}$/)
|
||||
bojHandle?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsBoolean()
|
||||
onboarded?: boolean;
|
||||
@@ -137,9 +132,6 @@ class ProfilePatchDto {
|
||||
@IsEnum(MathUnit, { each: true })
|
||||
focusUnits?: MathUnit[];
|
||||
|
||||
@IsOptional()
|
||||
@Matches(/^[a-zA-Z0-9_-]{3,20}$/)
|
||||
bojHandle?: string | null;
|
||||
}
|
||||
|
||||
@Controller('me')
|
||||
|
||||
@@ -17,7 +17,6 @@ export interface OnboardingInput {
|
||||
focusSubjects?: string[];
|
||||
reviewIntensity: ReviewIntensity;
|
||||
focusUnits?: MathUnit[];
|
||||
bojHandle?: string | null;
|
||||
onboarded?: boolean;
|
||||
}
|
||||
|
||||
@@ -31,7 +30,6 @@ interface ProfileInput {
|
||||
reviewIntensity?: ReviewIntensity;
|
||||
onboarded?: boolean;
|
||||
focusUnits?: MathUnit[];
|
||||
bojHandle?: string | null;
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
@@ -53,7 +51,6 @@ export class MeService {
|
||||
if (input.currentGrade !== undefined) data.currentGrade = input.currentGrade;
|
||||
if (input.targetGrade !== undefined) data.targetGrade = input.targetGrade;
|
||||
if (input.targetExamYear !== undefined) data.targetExamYear = input.targetExamYear;
|
||||
if (input.bojHandle !== undefined) data.bojHandle = input.bojHandle;
|
||||
if (input.onboarded) data.onboardedAt = new Date();
|
||||
|
||||
const user = await this.prisma.user.update({
|
||||
@@ -80,7 +77,6 @@ export class MeService {
|
||||
if (input.reviewIntensity !== undefined) data.reviewIntensity = input.reviewIntensity;
|
||||
if (input.onboarded) data.onboardedAt = new Date();
|
||||
if (input.focusUnits !== undefined) data.focusUnits = input.focusUnits;
|
||||
if (input.bojHandle !== undefined) data.bojHandle = input.bojHandle ?? null;
|
||||
|
||||
const user = await this.prisma.user.update({
|
||||
where: { id: userId },
|
||||
@@ -241,7 +237,6 @@ export class MeService {
|
||||
targetExamYear: number | null;
|
||||
focusSubjects: Prisma.JsonValue | null;
|
||||
focusUnits: Prisma.JsonValue | null;
|
||||
bojHandle: string | null;
|
||||
reviewIntensity: string;
|
||||
onboardedAt: Date | null;
|
||||
subscriptionTier: string;
|
||||
@@ -261,7 +256,6 @@ export class MeService {
|
||||
? u.focusSubjects.filter((subject): subject is string => typeof subject === 'string')
|
||||
: [],
|
||||
focusUnits: this.normalizeFocusUnits(u.focusUnits),
|
||||
bojHandle: u.bojHandle ?? null,
|
||||
reviewIntensity: u.reviewIntensity,
|
||||
onboarded: u.onboardedAt !== null,
|
||||
subscriptionTier: u.subscriptionTier,
|
||||
|
||||
@@ -35,9 +35,6 @@ export class ReviewsService {
|
||||
userId,
|
||||
status: ReviewStatus.pending,
|
||||
scheduledAt: { lte: soon },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
studyLog: {
|
||||
@@ -78,9 +75,6 @@ export class ReviewsService {
|
||||
userId,
|
||||
status: ReviewStatus.pending,
|
||||
scheduledAt: { lt: utcTodayStartKST },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
},
|
||||
},
|
||||
select: { id: true },
|
||||
});
|
||||
@@ -298,9 +292,6 @@ export class ReviewsService {
|
||||
where: {
|
||||
userId,
|
||||
status: { in: [ReviewStatus.done, ReviewStatus.skipped] },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
studyLog: {
|
||||
@@ -327,9 +318,6 @@ export class ReviewsService {
|
||||
where: {
|
||||
userId,
|
||||
scheduledAt: { gte: start, lt: end },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
},
|
||||
},
|
||||
select: {
|
||||
scheduledAt: true,
|
||||
@@ -373,9 +361,6 @@ export class ReviewsService {
|
||||
where: {
|
||||
userId,
|
||||
scheduledAt: { gte: start, lt: end },
|
||||
studyLog: {
|
||||
psProblemId: null,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
studyLog: {
|
||||
|
||||
@@ -444,21 +444,6 @@ export class StudyLogsService {
|
||||
});
|
||||
|
||||
return this.prisma.$transaction(async (tx) => {
|
||||
const psLogs = await tx.studyLog.findMany({
|
||||
where: { userId, psProblemId: { not: null } },
|
||||
select: { id: true },
|
||||
});
|
||||
const psLogIds = psLogs.map((l) => l.id);
|
||||
|
||||
let psReviewsDeleted = 0;
|
||||
let psLogsDeleted = 0;
|
||||
if (psLogIds.length > 0) {
|
||||
const r = await tx.reviewSchedule.deleteMany({ where: { studyLogId: { in: psLogIds } } });
|
||||
psReviewsDeleted = r.count;
|
||||
const s = await tx.studyLog.deleteMany({ where: { id: { in: psLogIds } } });
|
||||
psLogsDeleted = s.count;
|
||||
}
|
||||
|
||||
let nonMathReviewsDeleted = 0;
|
||||
let nonMathLogsDeleted = 0;
|
||||
if (mathSubject) {
|
||||
@@ -485,8 +470,6 @@ export class StudyLogsService {
|
||||
|
||||
return {
|
||||
purged: {
|
||||
psStudyLogs: psLogsDeleted,
|
||||
psReviewSchedules: psReviewsDeleted,
|
||||
nonMathStudyLogs: nonMathLogsDeleted,
|
||||
nonMathReviewSchedules: nonMathReviewsDeleted,
|
||||
nonMathSubjects: nonMathSubjectsDeleted,
|
||||
@@ -507,7 +490,6 @@ export class StudyLogsService {
|
||||
return this.prisma.studyLog.findMany({
|
||||
where: {
|
||||
userId,
|
||||
psProblemId: null,
|
||||
...(opts.subjectId && { subjectId: opts.subjectId }),
|
||||
...(opts.tagId && { tagId: opts.tagId }),
|
||||
},
|
||||
@@ -527,7 +509,7 @@ export class StudyLogsService {
|
||||
|
||||
async getOne(userId: number, id: number) {
|
||||
const log = await this.prisma.studyLog.findFirst({
|
||||
where: { id, userId, psProblemId: null },
|
||||
where: { id, userId },
|
||||
include: {
|
||||
subject: { select: { id: true, name: true, color: true } },
|
||||
tag: { select: { id: true, name: true } },
|
||||
|
||||
@@ -43,7 +43,6 @@ type ProfilePatch = Partial<
|
||||
| 'focusUnits'
|
||||
| 'persona'
|
||||
| 'reviewIntensity'
|
||||
| 'bojHandle'
|
||||
> & { onboarded?: boolean }
|
||||
>;
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ export interface MeUser {
|
||||
targetExamYear: number | null;
|
||||
focusSubjects: string[];
|
||||
focusUnits: MathUnit[] | null;
|
||||
bojHandle: string | null;
|
||||
goalType?: GoalType | null;
|
||||
reviewIntensity: ReviewIntensity;
|
||||
onboarded: boolean;
|
||||
|
||||
Reference in New Issue
Block a user