285 lines
10 KiB
TypeScript
285 lines
10 KiB
TypeScript
/**
|
|
* 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<string, Record<string, number>>;
|
|
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<bigint, MockReferralRelationship> = new Map();
|
|
private _teamStatisticsStore: Map<bigint, MockTeamStatistics> = new Map();
|
|
private _directReferralsStore: Map<string, MockDirectReferral> = new Map();
|
|
private idCounter = 1n;
|
|
|
|
// ReferralRelationship operations
|
|
referralRelationship = {
|
|
upsert: async (args: {
|
|
where: { userId: bigint };
|
|
create: Partial<MockReferralRelationship>;
|
|
update: Partial<MockReferralRelationship>;
|
|
}) => {
|
|
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<MockTeamStatistics>;
|
|
update: Partial<MockTeamStatistics>;
|
|
}) => {
|
|
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<string, Record<string, number>>) ?? {},
|
|
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<MockTeamStatistics> }) => {
|
|
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<string, Record<string, number>>) ?? {},
|
|
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<MockTeamStatistics> }) => {
|
|
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<MockDirectReferral>;
|
|
update: Partial<MockDirectReferral> | { 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 <T>(fn: (tx: MockPrismaService) => Promise<T>): Promise<T> => {
|
|
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() {}
|
|
}
|