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")
|
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) ===
|
// === 认种统计 (来自 planting.order.paid) ===
|
||||||
dailyPlantingCount Int @default(0) @map("daily_planting_count")
|
dailyPlantingCount Int @default(0) @map("daily_planting_count")
|
||||||
|
|
@ -405,7 +405,6 @@ model RealtimeStats {
|
||||||
updatedAt DateTime @updatedAt @map("updated_at")
|
updatedAt DateTime @updatedAt @map("updated_at")
|
||||||
|
|
||||||
@@map("realtime_stats")
|
@@map("realtime_stats")
|
||||||
@@unique([statsDate], name: "uk_realtime_stats_date")
|
|
||||||
@@index([statsDate(sort: Desc)], name: "idx_rs_date")
|
@@index([statsDate(sort: Desc)], name: "idx_rs_date")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ export class RealtimeStatsRepository implements IRealtimeStatsRepository {
|
||||||
async getOrCreateByDate(date: Date): Promise<RealtimeStatsData> {
|
async getOrCreateByDate(date: Date): Promise<RealtimeStatsData> {
|
||||||
const statsDate = this.normalizeDate(date);
|
const statsDate = this.normalizeDate(date);
|
||||||
|
|
||||||
const existing = await this.prisma.realtimeStats.findFirst({
|
const existing = await this.prisma.realtimeStats.findUnique({
|
||||||
where: { statsDate },
|
where: { statsDate },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -33,7 +33,7 @@ export class RealtimeStatsRepository implements IRealtimeStatsRepository {
|
||||||
async findByDate(date: Date): Promise<RealtimeStatsData | null> {
|
async findByDate(date: Date): Promise<RealtimeStatsData | null> {
|
||||||
const statsDate = this.normalizeDate(date);
|
const statsDate = this.normalizeDate(date);
|
||||||
|
|
||||||
const found = await this.prisma.realtimeStats.findFirst({
|
const found = await this.prisma.realtimeStats.findUnique({
|
||||||
where: { statsDate },
|
where: { statsDate },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -54,85 +54,57 @@ export class RealtimeStatsRepository implements IRealtimeStatsRepository {
|
||||||
`Incrementing planting: date=${statsDate.toISOString()}, trees=${safeTreeCount}, amount=${safeAmount}`,
|
`Incrementing planting: date=${statsDate.toISOString()}, trees=${safeTreeCount}, amount=${safeAmount}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
// 使用 findFirst + create/update 替代 upsert,避免 Prisma 唯一约束语法问题
|
const result = await this.prisma.realtimeStats.upsert({
|
||||||
const existing = await this.prisma.realtimeStats.findFirst({
|
|
||||||
where: { statsDate },
|
where: { statsDate },
|
||||||
|
create: {
|
||||||
|
statsDate,
|
||||||
|
dailyPlantingCount: safeTreeCount,
|
||||||
|
dailyOrderCount: 1,
|
||||||
|
dailyPlantingAmount: safeAmount,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
dailyPlantingCount: { increment: safeTreeCount },
|
||||||
|
dailyOrderCount: { increment: 1 },
|
||||||
|
dailyPlantingAmount: { increment: safeAmount },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existing) {
|
return this.toDomain(result);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async incrementOrder(date: Date): Promise<RealtimeStatsData> {
|
async incrementOrder(date: Date): Promise<RealtimeStatsData> {
|
||||||
const statsDate = this.normalizeDate(date);
|
const statsDate = this.normalizeDate(date);
|
||||||
|
|
||||||
const existing = await this.prisma.realtimeStats.findFirst({
|
const result = await this.prisma.realtimeStats.upsert({
|
||||||
where: { statsDate },
|
where: { statsDate },
|
||||||
|
create: {
|
||||||
|
statsDate,
|
||||||
|
dailyOrderCount: 1,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
dailyOrderCount: { increment: 1 },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existing) {
|
return this.toDomain(result);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async incrementNewUser(date: Date): Promise<RealtimeStatsData> {
|
async incrementNewUser(date: Date): Promise<RealtimeStatsData> {
|
||||||
const statsDate = this.normalizeDate(date);
|
const statsDate = this.normalizeDate(date);
|
||||||
this.logger.debug(`Incrementing new user: date=${statsDate.toISOString()}`);
|
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 },
|
where: { statsDate },
|
||||||
|
create: {
|
||||||
|
statsDate,
|
||||||
|
dailyNewUserCount: 1,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
dailyNewUserCount: { increment: 1 },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existing) {
|
return this.toDomain(result);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async incrementProvinceAuth(date: Date): Promise<RealtimeStatsData> {
|
async incrementProvinceAuth(date: Date): Promise<RealtimeStatsData> {
|
||||||
|
|
@ -141,54 +113,36 @@ export class RealtimeStatsRepository implements IRealtimeStatsRepository {
|
||||||
`Incrementing province auth: date=${statsDate.toISOString()}`,
|
`Incrementing province auth: date=${statsDate.toISOString()}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const existing = await this.prisma.realtimeStats.findFirst({
|
const result = await this.prisma.realtimeStats.upsert({
|
||||||
where: { statsDate },
|
where: { statsDate },
|
||||||
|
create: {
|
||||||
|
statsDate,
|
||||||
|
dailyProvinceAuthCount: 1,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
dailyProvinceAuthCount: { increment: 1 },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existing) {
|
return this.toDomain(result);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async incrementCityAuth(date: Date): Promise<RealtimeStatsData> {
|
async incrementCityAuth(date: Date): Promise<RealtimeStatsData> {
|
||||||
const statsDate = this.normalizeDate(date);
|
const statsDate = this.normalizeDate(date);
|
||||||
this.logger.debug(`Incrementing city auth: date=${statsDate.toISOString()}`);
|
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 },
|
where: { statsDate },
|
||||||
|
create: {
|
||||||
|
statsDate,
|
||||||
|
dailyCityAuthCount: 1,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
dailyCityAuthCount: { increment: 1 },
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (existing) {
|
return this.toDomain(result);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async findRecentDays(days: number): Promise<RealtimeStatsData[]> {
|
async findRecentDays(days: number): Promise<RealtimeStatsData[]> {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue