/** * Prisma Mock for Integration Tests * 不依赖真实数据库的模拟实现 */ type MockReferralRelationship = { id: bigint; userId: bigint; accountSequence: string; // 格式: D + YYMMDD + 5位序号 referrerId: bigint | null; rootUserId: bigint | null; myReferralCode: string; usedReferralCode: string | null; ancestorPath: bigint[]; depth: number; directReferralCount: number; activeDirectCount: number; createdAt: Date; updatedAt: Date; }; type MockTeamStatistics = { id: bigint; userId: bigint; directReferralCount: number; totalTeamCount: number; selfPlantingCount: number; totalTeamPlantingCount: number; effectivePlantingCountForRanking: number; maxSingleTeamPlantingCount: number; provinceCityDistribution: Record>; lastCalcAt: Date | null; createdAt: Date; updatedAt: Date; }; type MockDirectReferral = { id: bigint; referrerId: bigint; referralId: bigint; referralSequence: bigint; teamPlantingCount: number; createdAt: Date; updatedAt: Date; }; export class MockPrismaService { private _referralRelationshipsStore: Map = new Map(); private _teamStatisticsStore: Map = new Map(); private _directReferralsStore: Map = new Map(); private idCounter = 1n; // ReferralRelationship operations referralRelationship = { upsert: async (args: { where: { userId: bigint }; create: Partial; update: Partial; }) => { const existing = this._referralRelationshipsStore.get(args.where.userId); if (existing) { const updated = { ...existing, ...args.update, updatedAt: new Date() }; this._referralRelationshipsStore.set(args.where.userId, updated); return updated; } const created: MockReferralRelationship = { id: this.idCounter++, userId: args.where.userId, accountSequence: args.create.accountSequence ?? `D${Date.now()}`, referrerId: args.create.referrerId ?? null, rootUserId: args.create.rootUserId ?? null, myReferralCode: args.create.myReferralCode!, usedReferralCode: args.create.usedReferralCode ?? null, ancestorPath: args.create.ancestorPath ?? [], depth: args.create.depth ?? 0, directReferralCount: 0, activeDirectCount: 0, createdAt: new Date(), updatedAt: new Date(), }; this._referralRelationshipsStore.set(args.where.userId, created); return created; }, findUnique: async (args: { where: { userId?: bigint; myReferralCode?: string }; select?: unknown }) => { if (args.where.userId) { return this._referralRelationshipsStore.get(args.where.userId) ?? null; } if (args.where.myReferralCode) { for (const r of this._referralRelationshipsStore.values()) { if (r.myReferralCode === args.where.myReferralCode) return r; } } return null; }, findMany: async (args: { where?: { referrerId?: bigint }; orderBy?: unknown }) => { const results: MockReferralRelationship[] = []; for (const r of this._referralRelationshipsStore.values()) { if (!args.where?.referrerId || r.referrerId === args.where.referrerId) { results.push(r); } } return results; }, count: async (args: { where: { myReferralCode?: string; userId?: bigint } }) => { let count = 0; for (const r of this._referralRelationshipsStore.values()) { if (args.where.myReferralCode && r.myReferralCode === args.where.myReferralCode) count++; if (args.where.userId && r.userId === args.where.userId) count++; } return count; }, }; // TeamStatistics operations teamStatistics = { upsert: async (args: { where: { userId: bigint }; create: Partial; update: Partial; }) => { const existing = this._teamStatisticsStore.get(args.where.userId); if (existing) { const updated = { ...existing, ...args.update, updatedAt: new Date() }; this._teamStatisticsStore.set(args.where.userId, updated); return updated; } const created: MockTeamStatistics = { id: this.idCounter++, userId: args.where.userId, directReferralCount: args.create.directReferralCount ?? 0, totalTeamCount: args.create.totalTeamCount ?? 0, selfPlantingCount: args.create.selfPlantingCount ?? 0, totalTeamPlantingCount: args.create.totalTeamPlantingCount ?? 0, effectivePlantingCountForRanking: args.create.effectivePlantingCountForRanking ?? 0, maxSingleTeamPlantingCount: args.create.maxSingleTeamPlantingCount ?? 0, provinceCityDistribution: (args.create.provinceCityDistribution as Record>) ?? {}, lastCalcAt: args.create.lastCalcAt ?? new Date(), createdAt: new Date(), updatedAt: new Date(), }; this._teamStatisticsStore.set(args.where.userId, created); return created; }, create: async (args: { data: Partial }) => { const created: MockTeamStatistics = { id: this.idCounter++, userId: args.data.userId!, directReferralCount: args.data.directReferralCount ?? 0, totalTeamCount: args.data.totalTeamCount ?? 0, selfPlantingCount: args.data.selfPlantingCount ?? 0, totalTeamPlantingCount: args.data.totalTeamPlantingCount ?? 0, effectivePlantingCountForRanking: args.data.effectivePlantingCountForRanking ?? 0, maxSingleTeamPlantingCount: args.data.maxSingleTeamPlantingCount ?? 0, provinceCityDistribution: (args.data.provinceCityDistribution as Record>) ?? {}, lastCalcAt: args.data.lastCalcAt ?? new Date(), createdAt: new Date(), updatedAt: new Date(), }; this._teamStatisticsStore.set(created.userId, created); return created; }, findUnique: async (args: { where: { userId: bigint } }) => { return this._teamStatisticsStore.get(args.where.userId) ?? null; }, findMany: async (args: { where?: { userId?: { in: bigint[] }; effectivePlantingCountForRanking?: { gt: number } }; orderBy?: unknown[]; skip?: number; take?: number; select?: unknown; }) => { let results = Array.from(this._teamStatisticsStore.values()); if (args.where?.userId?.in) { results = results.filter((s) => args.where!.userId!.in.includes(s.userId)); } if (args.where?.effectivePlantingCountForRanking?.gt !== undefined) { results = results.filter( (s) => s.effectivePlantingCountForRanking > args.where!.effectivePlantingCountForRanking!.gt, ); } // Sort by score descending results.sort((a, b) => b.effectivePlantingCountForRanking - a.effectivePlantingCountForRanking); if (args.skip) results = results.slice(args.skip); if (args.take) results = results.slice(0, args.take); return results; }, count: async (args: { where: { effectivePlantingCountForRanking?: { gt: number } } }) => { let count = 0; for (const s of this._teamStatisticsStore.values()) { if ( args.where.effectivePlantingCountForRanking?.gt !== undefined && s.effectivePlantingCountForRanking > args.where.effectivePlantingCountForRanking.gt ) { count++; } } return count; }, update: async (args: { where: { userId: bigint }; data: Partial }) => { const existing = this._teamStatisticsStore.get(args.where.userId); if (!existing) throw new Error('Not found'); const updated = { ...existing, ...args.data, updatedAt: new Date() }; this._teamStatisticsStore.set(args.where.userId, updated); return updated; }, }; // DirectReferral operations directReferral = { upsert: async (args: { where: { uk_referrer_referral: { referrerId: bigint; referralId: bigint } }; create: Partial; update: Partial | { teamPlantingCount: { increment: number } }; }) => { const key = `${args.where.uk_referrer_referral.referrerId}-${args.where.uk_referrer_referral.referralId}`; const existing = this._directReferralsStore.get(key); if (existing) { let teamPlantingCount = existing.teamPlantingCount; if ('teamPlantingCount' in args.update) { if (typeof args.update.teamPlantingCount === 'object' && 'increment' in args.update.teamPlantingCount) { teamPlantingCount += args.update.teamPlantingCount.increment; } else { teamPlantingCount = args.update.teamPlantingCount as number; } } const updated = { ...existing, teamPlantingCount, updatedAt: new Date() }; this._directReferralsStore.set(key, updated); return updated; } const created: MockDirectReferral = { id: this.idCounter++, referrerId: args.where.uk_referrer_referral.referrerId, referralId: args.where.uk_referrer_referral.referralId, referralSequence: args.create.referralSequence!, teamPlantingCount: args.create.teamPlantingCount ?? 0, createdAt: new Date(), updatedAt: new Date(), }; this._directReferralsStore.set(key, created); return created; }, findMany: async (args: { where: { referrerId: bigint }; select?: unknown }) => { const results: MockDirectReferral[] = []; for (const dr of this._directReferralsStore.values()) { if (dr.referrerId === args.where.referrerId) { results.push(dr); } } return results; }, }; // Transaction support $transaction = async (fn: (tx: MockPrismaService) => Promise): Promise => { return fn(this); }; // Cleanup for tests $cleanup() { this._referralRelationshipsStore.clear(); this._teamStatisticsStore.clear(); this._directReferralsStore.clear(); this.idCounter = 1n; } // Lifecycle methods async onModuleInit() {} async onModuleDestroy() {} }