fix(reminders): keine Dauermeldung ohne localStorage, Mail nach Zeitaenderung, null-Pruefung
- Merkliste zusaetzlich im Arbeitsspeicher (sonst alle 10 s dieselbe Meldung) - Aendern der Faelligkeit atomar gegen gleichzeitiges Faelligwerden, setzt Mail-Spur zurueck - UpdateReminderDto lehnt null ab (400 statt 500) - keine Mails an deaktivierte Benutzer - Spaeter erinnern beschriftet heute/morgen nach dem berechneten Zeitpunkt - Bearbeiten schickt dueAt nur bei geaenderter Zeit Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,8 +5,8 @@ import {
|
||||
NotFoundException,
|
||||
} from '@nestjs/common';
|
||||
import { PrismaService } from '../prisma/prisma.service';
|
||||
import { SettingsService } from '../settings/settings.service';
|
||||
import { forTenant } from '../prisma/prisma-tenant.extension';
|
||||
import { SettingsService } from '../settings/settings.service';
|
||||
import type { CreateReminderDto, SnoozeReminderDto, UpdateReminderDto } from './dto/reminder.dto';
|
||||
|
||||
/** Antwortfelder — genau diese, nichts anderes verlaesst den Dienst. */
|
||||
@@ -107,26 +107,58 @@ export class RemindersService {
|
||||
/**
|
||||
* Bearbeiten einer noch NICHT faelligen Erinnerung. Eine faellige ist tabu
|
||||
* (409): dafuer gibt es „Erledigt“ und „Spaeter erinnern“ (D-03).
|
||||
*
|
||||
* WARUM `updateMany` MIT `dueAt > jetzt`: Pruefung und Schreiben sind sonst
|
||||
* getrennt — wird die Erinnerung dazwischen faellig (und der E-Mail-Planer
|
||||
* hat sie womoeglich schon beansprucht), wuerde trotzdem geschrieben. Die
|
||||
* Bedingung im selben Schreibzugriff schliesst das aus; `count === 0` heisst
|
||||
* dann 409 (oder 404, falls die Zeile inzwischen geloescht ist).
|
||||
*
|
||||
* Eine neue Faelligkeit setzt wie beim Verschieben die Spuren des
|
||||
* E-Mail-Planers zurueck, damit zur neuen Faelligkeit eine Mail geht.
|
||||
*/
|
||||
async update(tenantId: string, userId: string, id: string, dto: UpdateReminderDto) {
|
||||
const tenantPrisma = forTenant(this.prisma, tenantId, userId);
|
||||
const row = await this.loadOwn(tenantPrisma, tenantId, userId, id);
|
||||
if (row.dueAt.getTime() <= Date.now()) {
|
||||
const now = Date.now();
|
||||
if (row.dueAt.getTime() <= now) {
|
||||
throw new ConflictException('Die Erinnerung ist bereits fällig');
|
||||
}
|
||||
const data: { title?: string; description?: string; dueAt?: Date; emailEnabled?: boolean } = {};
|
||||
const data: {
|
||||
title?: string;
|
||||
description?: string;
|
||||
dueAt?: Date;
|
||||
emailEnabled?: boolean;
|
||||
emailSentAt?: null;
|
||||
emailAttempts?: number;
|
||||
} = {};
|
||||
if (dto.title !== undefined) data.title = dto.title;
|
||||
if (dto.description !== undefined) data.description = dto.description;
|
||||
if (dto.dueAt !== undefined) data.dueAt = this.assertValidDueAt(dto.dueAt);
|
||||
if (dto.dueAt !== undefined) {
|
||||
data.dueAt = this.assertValidDueAt(dto.dueAt);
|
||||
data.emailSentAt = null;
|
||||
data.emailAttempts = 0;
|
||||
}
|
||||
if (dto.emailEnabled !== undefined) {
|
||||
if (dto.emailEnabled) await this.assertEmailAvailable(tenantId, userId);
|
||||
data.emailEnabled = dto.emailEnabled;
|
||||
}
|
||||
return tenantPrisma.reminder.update({
|
||||
where: { id, tenantId, userId },
|
||||
const result = await tenantPrisma.reminder.updateMany({
|
||||
where: { id, tenantId, userId, dueAt: { gt: new Date() } },
|
||||
data,
|
||||
});
|
||||
if (result.count !== 1) {
|
||||
await this.loadOwn(tenantPrisma, tenantId, userId, id); // inzwischen geloescht: 404
|
||||
throw new ConflictException('Die Erinnerung ist bereits fällig');
|
||||
}
|
||||
const updated = await tenantPrisma.reminder.findFirst({
|
||||
where: { id, tenantId, userId },
|
||||
select: REMINDER_SELECT,
|
||||
});
|
||||
if (!updated) {
|
||||
throw new NotFoundException('Erinnerung nicht gefunden');
|
||||
}
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user