fix(reporting-service): use field-level @unique for statsDate in schema
Root cause: @@unique([field], name: "xxx") requires { xxx: { field } } syntax
in findUnique/upsert, but code used { field } directly.
Fix: Change to @unique(map: "uk_realtime_stats_date") on the field itself.
This keeps the same database index name while allowing { statsDate } syntax.
No migration needed - only Prisma client type generation changes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4e201d3a66
commit
8c29603f5a
|
|
@ -386,7 +386,7 @@ model RealtimeStats {
|
|||
id BigInt @id @default(autoincrement()) @map("stats_id")
|
||||
|
||||
// === 统计日期 ===
|
||||
statsDate DateTime @map("stats_date") @db.Date
|
||||
statsDate DateTime @unique(map: "uk_realtime_stats_date") @map("stats_date") @db.Date
|
||||
|
||||
// === 认种统计 (来自 planting.order.paid) ===
|
||||
dailyPlantingCount Int @default(0) @map("daily_planting_count")
|
||||
|
|
@ -405,7 +405,6 @@ model RealtimeStats {
|
|||
updatedAt DateTime @updatedAt @map("updated_at")
|
||||
|
||||
@@map("realtime_stats")
|
||||
@@unique([statsDate], name: "uk_realtime_stats_date")
|
||||
@@index([statsDate(sort: Desc)], name: "idx_rs_date")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ export class RealtimeStatsRepository implements IRealtimeStatsRepository {
|
|||
async getOrCreateByDate(date: Date): Promise<RealtimeStatsData> {
|
||||
const statsDate = this.normalizeDate(date);
|
||||
|
||||
const existing = await this.prisma.realtimeStats.findFirst({
|
||||
const existing = await this.prisma.realtimeStats.findUnique({
|
||||
where: { statsDate },
|
||||
});
|
||||
|
||||
|
|
@ -33,7 +33,7 @@ export class RealtimeStatsRepository implements IRealtimeStatsRepository {
|
|||
async findByDate(date: Date): Promise<RealtimeStatsData | null> {
|
||||
const statsDate = this.normalizeDate(date);
|
||||
|
||||
const found = await this.prisma.realtimeStats.findFirst({
|
||||
const found = await this.prisma.realtimeStats.findUnique({
|
||||
where: { statsDate },
|
||||
});
|
||||
|
||||
|
|
@ -54,85 +54,57 @@ export class RealtimeStatsRepository implements IRealtimeStatsRepository {
|
|||
`Incrementing planting: date=${statsDate.toISOString()}, trees=${safeTreeCount}, amount=${safeAmount}`,
|
||||
);
|
||||
|
||||
// 使用 findFirst + create/update 替代 upsert,避免 Prisma 唯一约束语法问题
|
||||
const existing = await this.prisma.realtimeStats.findFirst({
|
||||
const result = await this.prisma.realtimeStats.upsert({
|
||||
where: { statsDate },
|
||||
create: {
|
||||
statsDate,
|
||||
dailyPlantingCount: safeTreeCount,
|
||||
dailyOrderCount: 1,
|
||||
dailyPlantingAmount: safeAmount,
|
||||
},
|
||||
update: {
|
||||
dailyPlantingCount: { increment: safeTreeCount },
|
||||
dailyOrderCount: { increment: 1 },
|
||||
dailyPlantingAmount: { increment: safeAmount },
|
||||
},
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
const result = await this.prisma.realtimeStats.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
dailyPlantingCount: { increment: safeTreeCount },
|
||||
dailyOrderCount: { increment: 1 },
|
||||
dailyPlantingAmount: { increment: safeAmount },
|
||||
},
|
||||
});
|
||||
return this.toDomain(result);
|
||||
} else {
|
||||
const result = await this.prisma.realtimeStats.create({
|
||||
data: {
|
||||
statsDate,
|
||||
dailyPlantingCount: safeTreeCount,
|
||||
dailyOrderCount: 1,
|
||||
dailyPlantingAmount: safeAmount,
|
||||
},
|
||||
});
|
||||
return this.toDomain(result);
|
||||
}
|
||||
return this.toDomain(result);
|
||||
}
|
||||
|
||||
async incrementOrder(date: Date): Promise<RealtimeStatsData> {
|
||||
const statsDate = this.normalizeDate(date);
|
||||
|
||||
const existing = await this.prisma.realtimeStats.findFirst({
|
||||
const result = await this.prisma.realtimeStats.upsert({
|
||||
where: { statsDate },
|
||||
create: {
|
||||
statsDate,
|
||||
dailyOrderCount: 1,
|
||||
},
|
||||
update: {
|
||||
dailyOrderCount: { increment: 1 },
|
||||
},
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
const result = await this.prisma.realtimeStats.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
dailyOrderCount: { increment: 1 },
|
||||
},
|
||||
});
|
||||
return this.toDomain(result);
|
||||
} else {
|
||||
const result = await this.prisma.realtimeStats.create({
|
||||
data: {
|
||||
statsDate,
|
||||
dailyOrderCount: 1,
|
||||
},
|
||||
});
|
||||
return this.toDomain(result);
|
||||
}
|
||||
return this.toDomain(result);
|
||||
}
|
||||
|
||||
async incrementNewUser(date: Date): Promise<RealtimeStatsData> {
|
||||
const statsDate = this.normalizeDate(date);
|
||||
this.logger.debug(`Incrementing new user: date=${statsDate.toISOString()}`);
|
||||
|
||||
const existing = await this.prisma.realtimeStats.findFirst({
|
||||
const result = await this.prisma.realtimeStats.upsert({
|
||||
where: { statsDate },
|
||||
create: {
|
||||
statsDate,
|
||||
dailyNewUserCount: 1,
|
||||
},
|
||||
update: {
|
||||
dailyNewUserCount: { increment: 1 },
|
||||
},
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
const result = await this.prisma.realtimeStats.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
dailyNewUserCount: { increment: 1 },
|
||||
},
|
||||
});
|
||||
return this.toDomain(result);
|
||||
} else {
|
||||
const result = await this.prisma.realtimeStats.create({
|
||||
data: {
|
||||
statsDate,
|
||||
dailyNewUserCount: 1,
|
||||
},
|
||||
});
|
||||
return this.toDomain(result);
|
||||
}
|
||||
return this.toDomain(result);
|
||||
}
|
||||
|
||||
async incrementProvinceAuth(date: Date): Promise<RealtimeStatsData> {
|
||||
|
|
@ -141,54 +113,36 @@ export class RealtimeStatsRepository implements IRealtimeStatsRepository {
|
|||
`Incrementing province auth: date=${statsDate.toISOString()}`,
|
||||
);
|
||||
|
||||
const existing = await this.prisma.realtimeStats.findFirst({
|
||||
const result = await this.prisma.realtimeStats.upsert({
|
||||
where: { statsDate },
|
||||
create: {
|
||||
statsDate,
|
||||
dailyProvinceAuthCount: 1,
|
||||
},
|
||||
update: {
|
||||
dailyProvinceAuthCount: { increment: 1 },
|
||||
},
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
const result = await this.prisma.realtimeStats.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
dailyProvinceAuthCount: { increment: 1 },
|
||||
},
|
||||
});
|
||||
return this.toDomain(result);
|
||||
} else {
|
||||
const result = await this.prisma.realtimeStats.create({
|
||||
data: {
|
||||
statsDate,
|
||||
dailyProvinceAuthCount: 1,
|
||||
},
|
||||
});
|
||||
return this.toDomain(result);
|
||||
}
|
||||
return this.toDomain(result);
|
||||
}
|
||||
|
||||
async incrementCityAuth(date: Date): Promise<RealtimeStatsData> {
|
||||
const statsDate = this.normalizeDate(date);
|
||||
this.logger.debug(`Incrementing city auth: date=${statsDate.toISOString()}`);
|
||||
|
||||
const existing = await this.prisma.realtimeStats.findFirst({
|
||||
const result = await this.prisma.realtimeStats.upsert({
|
||||
where: { statsDate },
|
||||
create: {
|
||||
statsDate,
|
||||
dailyCityAuthCount: 1,
|
||||
},
|
||||
update: {
|
||||
dailyCityAuthCount: { increment: 1 },
|
||||
},
|
||||
});
|
||||
|
||||
if (existing) {
|
||||
const result = await this.prisma.realtimeStats.update({
|
||||
where: { id: existing.id },
|
||||
data: {
|
||||
dailyCityAuthCount: { increment: 1 },
|
||||
},
|
||||
});
|
||||
return this.toDomain(result);
|
||||
} else {
|
||||
const result = await this.prisma.realtimeStats.create({
|
||||
data: {
|
||||
statsDate,
|
||||
dailyCityAuthCount: 1,
|
||||
},
|
||||
});
|
||||
return this.toDomain(result);
|
||||
}
|
||||
return this.toDomain(result);
|
||||
}
|
||||
|
||||
async findRecentDays(days: number): Promise<RealtimeStatsData[]> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue