From e5d7e468cc3d2d36dde4a21eaf34b42cd2e721aa Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Wed, 2 Sep 2026 22:15:07 +0800 Subject: [PATCH] =?UTF-8?q?fix(ops):=20=E4=BC=81=E5=BE=AE=E6=8A=A5?= =?UTF-8?q?=E5=91=8A=E4=BF=9D=E5=AD=98=E6=94=B9=E8=B5=B0=20Prisma=20Client?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 空 @成员 会把 null 插进 tagged raw SQL,MySQL 下 Prisma 报 Code N/A。 --- .../ops/admin-wecom-reports.service.ts | 127 ++++++------------ 1 file changed, 43 insertions(+), 84 deletions(-) diff --git a/server/dukang-api/src/modules/ops/admin-wecom-reports.service.ts b/server/dukang-api/src/modules/ops/admin-wecom-reports.service.ts index 74c4118..9716066 100644 --- a/server/dukang-api/src/modules/ops/admin-wecom-reports.service.ts +++ b/server/dukang-api/src/modules/ops/admin-wecom-reports.service.ts @@ -6,7 +6,7 @@ import { OnModuleInit, } from '@nestjs/common'; import { Cron } from '@nestjs/schedule'; -import { Prisma } from '@prisma/client'; +import { Prisma, type WecomReportPush } from '@prisma/client'; import { formatWecomReportMarkdown, isWecomReportKind, @@ -56,26 +56,7 @@ function clampMinute(n: number | undefined, fallback: number): number { return Math.min(59, Math.max(0, Math.floor(n))); } -type ReportRow = { - id: bigint; - kind: string; - name: string; - webhookUrl: string; - enabled: boolean | number; - mentionWecomUserId: string | null; - sendHour: number; - sendMinute: number; - sendWeekday: number; - sendMonthDay: number; - lastSentPeriod: string | null; - lastSentAt: Date | null; - createdAt: Date; - updatedAt: Date; -}; - -function asBool(v: boolean | number): boolean { - return v === true || v === 1; -} +type ReportRow = WecomReportPush; @Injectable() export class AdminWecomReportsService implements OnModuleInit { @@ -98,42 +79,23 @@ export class AdminWecomReportsService implements OnModuleInit { async ensureDefaults(): Promise { for (const seed of KIND_SEED) { - await this.prisma.$executeRaw` - INSERT IGNORE INTO wecom_report_push - (kind, name, webhook_url, enabled, send_hour, send_minute, send_weekday, send_month_day) - VALUES - (${seed.kind}, ${seed.name}, ${WECOM_STORE_AUDIT_PLACEHOLDER_WEBHOOK}, 0, ${seed.sendHour}, ${seed.sendMinute}, 1, 1) - `; + await this.prisma.wecomReportPush.upsert({ + where: { kind: seed.kind }, + create: { + kind: seed.kind, + name: seed.name, + webhookUrl: WECOM_STORE_AUDIT_PLACEHOLDER_WEBHOOK, + enabled: false, + sendHour: seed.sendHour, + sendMinute: seed.sendMinute, + sendWeekday: 1, + sendMonthDay: 1, + }, + update: {}, + }); } } - private async findByKind(kind: string): Promise { - const rows = await this.prisma.$queryRaw` - SELECT id, kind, name, - webhook_url AS webhookUrl, enabled, - mention_wecom_user_id AS mentionWecomUserId, - send_hour AS sendHour, send_minute AS sendMinute, - send_weekday AS sendWeekday, send_month_day AS sendMonthDay, - last_sent_period AS lastSentPeriod, last_sent_at AS lastSentAt, - created_at AS createdAt, updated_at AS updatedAt - FROM wecom_report_push WHERE kind = ${kind} LIMIT 1 - `; - return rows[0] ?? null; - } - - private async findAll(): Promise { - return this.prisma.$queryRaw` - SELECT id, kind, name, - webhook_url AS webhookUrl, enabled, - mention_wecom_user_id AS mentionWecomUserId, - send_hour AS sendHour, send_minute AS sendMinute, - send_weekday AS sendWeekday, send_month_day AS sendMonthDay, - last_sent_period AS lastSentPeriod, last_sent_at AS lastSentAt, - created_at AS createdAt, updated_at AS updatedAt - FROM wecom_report_push - `; - } - parseKind(raw: string): WecomReportKind { if (!isWecomReportKind(raw)) { throw new BadRequestException('报告类型须为 daily / weekly / monthly'); @@ -143,7 +105,7 @@ export class AdminWecomReportsService implements OnModuleInit { async list(): Promise { await this.ensureDefaults(); - const rows = await this.findAll(); + const rows = await this.prisma.wecomReportPush.findMany(); const byKind = new Map(rows.map((r) => [r.kind, r])); return KIND_SEED.map((s) => { const row = byKind.get(s.kind); @@ -154,14 +116,14 @@ export class AdminWecomReportsService implements OnModuleInit { async detail(kind: WecomReportKind): Promise { await this.ensureDefaults(); - const row = await this.findByKind(kind); + const row = await this.prisma.wecomReportPush.findUnique({ where: { kind } }); if (!row) throw new NotFoundException('报告配置不存在'); return this.toDto(row); } async update(kind: WecomReportKind, dto: UpdateWecomReportPushRequest): Promise { await this.ensureDefaults(); - const existing = await this.findByKind(kind); + const existing = await this.prisma.wecomReportPush.findUnique({ where: { kind } }); if (!existing) throw new NotFoundException('报告配置不存在'); const webhookUrl = @@ -169,8 +131,8 @@ export class AdminWecomReportsService implements OnModuleInit { if (!webhookUrl) throw new BadRequestException('请填写 Webhook URL'); const name = dto.name !== undefined ? dto.name.trim() || existing.name : existing.name; - const enabled = dto.enabled !== undefined ? (dto.enabled ? 1 : 0) : asBool(existing.enabled) ? 1 : 0; - const mention = + const enabled = dto.enabled !== undefined ? dto.enabled : existing.enabled; + const mentionWecomUserId = dto.mentionWecomUserId === undefined ? existing.mentionWecomUserId : dto.mentionWecomUserId?.trim() || null; @@ -186,20 +148,19 @@ export class AdminWecomReportsService implements OnModuleInit { ? Math.min(31, Math.max(1, Math.floor(dto.sendMonthDay) || 1)) : existing.sendMonthDay; - await this.prisma.$executeRaw` - UPDATE wecom_report_push SET - name = ${name}, - webhook_url = ${webhookUrl}, - enabled = ${enabled}, - mention_wecom_user_id = ${mention}, - send_hour = ${sendHour}, - send_minute = ${sendMinute}, - send_weekday = ${sendWeekday}, - send_month_day = ${sendMonthDay} - WHERE kind = ${kind} - `; - const row = await this.findByKind(kind); - if (!row) throw new NotFoundException('报告配置不存在'); + const row = await this.prisma.wecomReportPush.update({ + where: { kind }, + data: { + name, + webhookUrl, + enabled, + mentionWecomUserId, + sendHour, + sendMinute, + sendWeekday, + sendMonthDay, + }, + }); return this.toDto(row); } @@ -218,7 +179,7 @@ export class AdminWecomReportsService implements OnModuleInit { async send(kind: WecomReportKind, opts?: { markSent?: boolean }): Promise { await this.ensureDefaults(); - const row = await this.findByKind(kind); + const row = await this.prisma.wecomReportPush.findUnique({ where: { kind } }); if (!row) throw new NotFoundException('报告配置不存在'); const url = row.webhookUrl.trim(); if (!url || url.includes('key=PENDING')) { @@ -236,12 +197,10 @@ export class AdminWecomReportsService implements OnModuleInit { throw new BadRequestException('Webhook 发送失败,请检查地址或群机器人是否可用'); } if (opts?.markSent !== false) { - const sentAt = new Date(); - await this.prisma.$executeRaw` - UPDATE wecom_report_push - SET last_sent_period = ${period.periodKey}, last_sent_at = ${sentAt} - WHERE kind = ${kind} - `; + await this.prisma.wecomReportPush.update({ + where: { kind }, + data: { lastSentPeriod: period.periodKey, lastSentAt: new Date() }, + }); } return { ok: true, @@ -258,14 +217,14 @@ export class AdminWecomReportsService implements OnModuleInit { return; } const now = new Date(); - const rows = await this.findAll(); + const rows = await this.prisma.wecomReportPush.findMany(); for (const row of rows) { - if (!asBool(row.enabled)) continue; + if (!row.enabled) continue; if (!isWecomReportKind(row.kind)) continue; const due = wecomReportShouldFire( row.kind, { - enabled: asBool(row.enabled), + enabled: row.enabled, sendHour: row.sendHour, sendMinute: row.sendMinute, sendWeekday: row.sendWeekday, @@ -366,7 +325,7 @@ export class AdminWecomReportsService implements OnModuleInit { name: row.name, webhookUrl: row.webhookUrl, webhookUrlMasked: maskWecomWebhookUrl(row.webhookUrl), - enabled: asBool(row.enabled), + enabled: row.enabled, mentionWecomUserId: row.mentionWecomUserId, sendHour: row.sendHour, sendMinute: row.sendMinute,