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:
reloop
2026-04-19 18:41:31 +09:00
parent b27417a10e
commit b59cc8fd57
10 changed files with 29 additions and 111 deletions

View File

@@ -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`;

View File

@@ -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")
}

View File

@@ -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,

View File

@@ -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 },

View File

@@ -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')

View File

@@ -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,

View File

@@ -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: {

View File

@@ -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 } },

View File

@@ -43,7 +43,6 @@ type ProfilePatch = Partial<
| 'focusUnits'
| 'persona'
| 'reviewIntensity'
| 'bojHandle'
> & { onboarded?: boolean }
>;

View File

@@ -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;