@@ -6,7 +6,7 @@ import {
|
|||||||
OnModuleInit,
|
OnModuleInit,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { Cron } from '@nestjs/schedule';
|
import { Cron } from '@nestjs/schedule';
|
||||||
import { Prisma } from '@prisma/client';
|
import { Prisma, type WecomReportPush } from '@prisma/client';
|
||||||
import {
|
import {
|
||||||
formatWecomReportMarkdown,
|
formatWecomReportMarkdown,
|
||||||
isWecomReportKind,
|
isWecomReportKind,
|
||||||
@@ -56,26 +56,7 @@ function clampMinute(n: number | undefined, fallback: number): number {
|
|||||||
return Math.min(59, Math.max(0, Math.floor(n)));
|
return Math.min(59, Math.max(0, Math.floor(n)));
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReportRow = {
|
type ReportRow = WecomReportPush;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AdminWecomReportsService implements OnModuleInit {
|
export class AdminWecomReportsService implements OnModuleInit {
|
||||||
@@ -98,42 +79,23 @@ export class AdminWecomReportsService implements OnModuleInit {
|
|||||||
|
|
||||||
async ensureDefaults(): Promise<void> {
|
async ensureDefaults(): Promise<void> {
|
||||||
for (const seed of KIND_SEED) {
|
for (const seed of KIND_SEED) {
|
||||||
await this.prisma.$executeRaw`
|
await this.prisma.wecomReportPush.upsert({
|
||||||
INSERT IGNORE INTO wecom_report_push
|
where: { kind: seed.kind },
|
||||||
(kind, name, webhook_url, enabled, send_hour, send_minute, send_weekday, send_month_day)
|
create: {
|
||||||
VALUES
|
kind: seed.kind,
|
||||||
(${seed.kind}, ${seed.name}, ${WECOM_STORE_AUDIT_PLACEHOLDER_WEBHOOK}, 0, ${seed.sendHour}, ${seed.sendMinute}, 1, 1)
|
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<ReportRow | null> {
|
|
||||||
const rows = await this.prisma.$queryRaw<ReportRow[]>`
|
|
||||||
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<ReportRow[]> {
|
|
||||||
return this.prisma.$queryRaw<ReportRow[]>`
|
|
||||||
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 {
|
parseKind(raw: string): WecomReportKind {
|
||||||
if (!isWecomReportKind(raw)) {
|
if (!isWecomReportKind(raw)) {
|
||||||
throw new BadRequestException('报告类型须为 daily / weekly / monthly');
|
throw new BadRequestException('报告类型须为 daily / weekly / monthly');
|
||||||
@@ -143,7 +105,7 @@ export class AdminWecomReportsService implements OnModuleInit {
|
|||||||
|
|
||||||
async list(): Promise<WecomReportPushDto[]> {
|
async list(): Promise<WecomReportPushDto[]> {
|
||||||
await this.ensureDefaults();
|
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]));
|
const byKind = new Map(rows.map((r) => [r.kind, r]));
|
||||||
return KIND_SEED.map((s) => {
|
return KIND_SEED.map((s) => {
|
||||||
const row = byKind.get(s.kind);
|
const row = byKind.get(s.kind);
|
||||||
@@ -154,14 +116,14 @@ export class AdminWecomReportsService implements OnModuleInit {
|
|||||||
|
|
||||||
async detail(kind: WecomReportKind): Promise<WecomReportPushDto> {
|
async detail(kind: WecomReportKind): Promise<WecomReportPushDto> {
|
||||||
await this.ensureDefaults();
|
await this.ensureDefaults();
|
||||||
const row = await this.findByKind(kind);
|
const row = await this.prisma.wecomReportPush.findUnique({ where: { kind } });
|
||||||
if (!row) throw new NotFoundException('报告配置不存在');
|
if (!row) throw new NotFoundException('报告配置不存在');
|
||||||
return this.toDto(row);
|
return this.toDto(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(kind: WecomReportKind, dto: UpdateWecomReportPushRequest): Promise<WecomReportPushDto> {
|
async update(kind: WecomReportKind, dto: UpdateWecomReportPushRequest): Promise<WecomReportPushDto> {
|
||||||
await this.ensureDefaults();
|
await this.ensureDefaults();
|
||||||
const existing = await this.findByKind(kind);
|
const existing = await this.prisma.wecomReportPush.findUnique({ where: { kind } });
|
||||||
if (!existing) throw new NotFoundException('报告配置不存在');
|
if (!existing) throw new NotFoundException('报告配置不存在');
|
||||||
|
|
||||||
const webhookUrl =
|
const webhookUrl =
|
||||||
@@ -169,8 +131,8 @@ export class AdminWecomReportsService implements OnModuleInit {
|
|||||||
if (!webhookUrl) throw new BadRequestException('请填写 Webhook URL');
|
if (!webhookUrl) throw new BadRequestException('请填写 Webhook URL');
|
||||||
|
|
||||||
const name = dto.name !== undefined ? dto.name.trim() || existing.name : existing.name;
|
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 enabled = dto.enabled !== undefined ? dto.enabled : existing.enabled;
|
||||||
const mention =
|
const mentionWecomUserId =
|
||||||
dto.mentionWecomUserId === undefined
|
dto.mentionWecomUserId === undefined
|
||||||
? existing.mentionWecomUserId
|
? existing.mentionWecomUserId
|
||||||
: dto.mentionWecomUserId?.trim() || null;
|
: dto.mentionWecomUserId?.trim() || null;
|
||||||
@@ -186,20 +148,19 @@ export class AdminWecomReportsService implements OnModuleInit {
|
|||||||
? Math.min(31, Math.max(1, Math.floor(dto.sendMonthDay) || 1))
|
? Math.min(31, Math.max(1, Math.floor(dto.sendMonthDay) || 1))
|
||||||
: existing.sendMonthDay;
|
: existing.sendMonthDay;
|
||||||
|
|
||||||
await this.prisma.$executeRaw`
|
const row = await this.prisma.wecomReportPush.update({
|
||||||
UPDATE wecom_report_push SET
|
where: { kind },
|
||||||
name = ${name},
|
data: {
|
||||||
webhook_url = ${webhookUrl},
|
name,
|
||||||
enabled = ${enabled},
|
webhookUrl,
|
||||||
mention_wecom_user_id = ${mention},
|
enabled,
|
||||||
send_hour = ${sendHour},
|
mentionWecomUserId,
|
||||||
send_minute = ${sendMinute},
|
sendHour,
|
||||||
send_weekday = ${sendWeekday},
|
sendMinute,
|
||||||
send_month_day = ${sendMonthDay}
|
sendWeekday,
|
||||||
WHERE kind = ${kind}
|
sendMonthDay,
|
||||||
`;
|
},
|
||||||
const row = await this.findByKind(kind);
|
});
|
||||||
if (!row) throw new NotFoundException('报告配置不存在');
|
|
||||||
return this.toDto(row);
|
return this.toDto(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -218,7 +179,7 @@ export class AdminWecomReportsService implements OnModuleInit {
|
|||||||
|
|
||||||
async send(kind: WecomReportKind, opts?: { markSent?: boolean }): Promise<WecomReportSendResultDto> {
|
async send(kind: WecomReportKind, opts?: { markSent?: boolean }): Promise<WecomReportSendResultDto> {
|
||||||
await this.ensureDefaults();
|
await this.ensureDefaults();
|
||||||
const row = await this.findByKind(kind);
|
const row = await this.prisma.wecomReportPush.findUnique({ where: { kind } });
|
||||||
if (!row) throw new NotFoundException('报告配置不存在');
|
if (!row) throw new NotFoundException('报告配置不存在');
|
||||||
const url = row.webhookUrl.trim();
|
const url = row.webhookUrl.trim();
|
||||||
if (!url || url.includes('key=PENDING')) {
|
if (!url || url.includes('key=PENDING')) {
|
||||||
@@ -236,12 +197,10 @@ export class AdminWecomReportsService implements OnModuleInit {
|
|||||||
throw new BadRequestException('Webhook 发送失败,请检查地址或群机器人是否可用');
|
throw new BadRequestException('Webhook 发送失败,请检查地址或群机器人是否可用');
|
||||||
}
|
}
|
||||||
if (opts?.markSent !== false) {
|
if (opts?.markSent !== false) {
|
||||||
const sentAt = new Date();
|
await this.prisma.wecomReportPush.update({
|
||||||
await this.prisma.$executeRaw`
|
where: { kind },
|
||||||
UPDATE wecom_report_push
|
data: { lastSentPeriod: period.periodKey, lastSentAt: new Date() },
|
||||||
SET last_sent_period = ${period.periodKey}, last_sent_at = ${sentAt}
|
});
|
||||||
WHERE kind = ${kind}
|
|
||||||
`;
|
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
ok: true,
|
ok: true,
|
||||||
@@ -258,14 +217,14 @@ export class AdminWecomReportsService implements OnModuleInit {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const rows = await this.findAll();
|
const rows = await this.prisma.wecomReportPush.findMany();
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
if (!asBool(row.enabled)) continue;
|
if (!row.enabled) continue;
|
||||||
if (!isWecomReportKind(row.kind)) continue;
|
if (!isWecomReportKind(row.kind)) continue;
|
||||||
const due = wecomReportShouldFire(
|
const due = wecomReportShouldFire(
|
||||||
row.kind,
|
row.kind,
|
||||||
{
|
{
|
||||||
enabled: asBool(row.enabled),
|
enabled: row.enabled,
|
||||||
sendHour: row.sendHour,
|
sendHour: row.sendHour,
|
||||||
sendMinute: row.sendMinute,
|
sendMinute: row.sendMinute,
|
||||||
sendWeekday: row.sendWeekday,
|
sendWeekday: row.sendWeekday,
|
||||||
@@ -366,7 +325,7 @@ export class AdminWecomReportsService implements OnModuleInit {
|
|||||||
name: row.name,
|
name: row.name,
|
||||||
webhookUrl: row.webhookUrl,
|
webhookUrl: row.webhookUrl,
|
||||||
webhookUrlMasked: maskWecomWebhookUrl(row.webhookUrl),
|
webhookUrlMasked: maskWecomWebhookUrl(row.webhookUrl),
|
||||||
enabled: asBool(row.enabled),
|
enabled: row.enabled,
|
||||||
mentionWecomUserId: row.mentionWecomUserId,
|
mentionWecomUserId: row.mentionWecomUserId,
|
||||||
sendHour: row.sendHour,
|
sendHour: row.sendHour,
|
||||||
sendMinute: row.sendMinute,
|
sendMinute: row.sendMinute,
|
||||||
|
|||||||
Reference in New Issue
Block a user