Tapis.Core.Reminders
1.0.6
dotnet add package Tapis.Core.Reminders --version 1.0.6
NuGet\Install-Package Tapis.Core.Reminders -Version 1.0.6
<PackageReference Include="Tapis.Core.Reminders" Version="1.0.6" />
<PackageVersion Include="Tapis.Core.Reminders" Version="1.0.6" />
<PackageReference Include="Tapis.Core.Reminders" />
paket add Tapis.Core.Reminders --version 1.0.6
#r "nuget: Tapis.Core.Reminders, 1.0.6"
#:package Tapis.Core.Reminders@1.0.6
#addin nuget:?package=Tapis.Core.Reminders&version=1.0.6
#tool nuget:?package=Tapis.Core.Reminders&version=1.0.6
Tapis.Core.Reminders
Notlar ve hatırlatıcılar modülünün sözleşme paketi. Her ürün (ServiceManagement, HostelManagement) bu paketi kendi API'sinde barındırır ve kendi tenant veritabanında kendi tablolarını tutar. Paket tenant çözümlemesi, kimlik doğrulama ya da ürün varlıkları bilmez; ihtiyaç duyduğu her şeyi port üzerinden alır.
1.0.2'den beri uygulama servisleri ürünün içindedir (kullanıcı kararı S1, 2026-10-13). Paket not, hatırlatıcı,
bildirim ve ek servislerinin yalnız arayüzünü verir; implementasyon ürünün kendi repository/UnitOfWork
katmanını kullandığı için ürün projesinde yaşar. Paket container'dan DbContext çözmez.
İçerik
| Alan | Ne yapar |
|---|---|
Entities/ |
Note, Reminder, ReminderChecklistItem, ReminderAlarm, Notification, Attachment — hepsi OwnerUserId taşır |
Persistence/ |
ApplyRemindersModel(ModelBuilder) ile tablolar ürünün kendi DbContext'ine katılır (xmin damgası ve denetim interceptor'ı böylece otomatik gelir) |
Services/ |
servis arayüzleri (INoteService, IReminderService, INotificationService, IAttachmentService), tekrar kuralı (RecurrenceService), alarm işleyici (ReminderDispatchService) ve ek eşlemesi (AttachmentMap) |
RemindersLocalizationConsts |
ileti ve işlem anahtarları (İngilizce); metinler ürünün kaynak dosyalarında, ISharedLocalizer ile çözülür |
Scheduling/ |
sonraki alarma kadar uyuyan zamanlayıcı döngüsü ve sinyal |
Hubs/ |
RemindersHub — kullanıcı bazlı grup, NotificationCreated / UnreadCount olayları; RemindersHubTicket (Master'ın bağlantı bileti, ürün tarafı doğrulama) ve tek kullanım deposu |
Controllers/ |
api/Notes, api/Reminders, api/ReminderAttachments, api/ReminderNotifications |
Ports/ |
IFileStorage, IReminderDbContextFactory, IReminderSchedulerSignal, IReminderRealtimeNotifier, IReminderClock, IReminderRecipientResolver |
Services/RecordAccess |
erişim kuralının tek kaynağı (aşağıda "İşletme görünürlüğü") |
Services/ReminderAlarmPlanner |
bildirim noktalarının tek kaynağı (aşağıda "Bildirim modeli (1.0.6)") |
Ürüne bağlama
// Program.cs
builder.Services.AddRemindersModule(builder.Configuration);
builder.Services.AddScoped<IReminderDbContextFactory, ServiceManagementReminderDbContextFactory>();
// Uygulama servisleri ürünün kendisindedir (S1): paket yalnız arayüzü tanımlar.
builder.Services.AddScoped<INoteService, NotService>();
builder.Services.AddScoped<IReminderService, HatirlaticiService>();
builder.Services.AddScoped<INotificationService, HatirlaticiBildirimService>();
builder.Services.AddScoped<IAttachmentService, HatirlaticiEkService>();
builder.Services.AddControllers().AddRemindersControllers();
...
app.MapRemindersModule(); // hub
// DbContext.OnModelCreating
builder.ApplyRemindersModel();
Yapılandırma Reminders bölümünden okunur (MaxSleepMinutes, BatchSize, IdleShutdownMinutes,
HubConnectionLifetimeMinutes, Attachments:MaxSizeMb, Attachments:AllowedExtensions, Attachments:MaxFilesPerRecord,
Attachments:LocalRootPath).
Kurallar
- Uygulama servisi yazan ürün kendi
IUnitOfWork/repository'lerini kullanır,DbContext'i doğrudan açmaz; doğrulama FluentValidation validator'ıyla ürün tarafında yapılır (paket DTO'ları ürünün globalValidationFilter'ından geçer); kullanıcıya dönen her iletiISharedLocalizerileRemindersLocalizationConstsanahtarından çözülür. - Kayıtlar varsayılan olarak kişiseldir (
Visibility = Personal); işletme kayıtları için aşağıdaki bölüme bakın. Her not/hatırlatıcı sorgusuRecordAccesskuralından geçer, erişilemeyen kayıt olmayan kayıtla aynı 404'ü alır. Bildirimler her zamanOwnerUserId == benile süzülür. - Bildirim yalnız uygulama içidir: e-posta, SMS, WhatsApp gönderilmez ve bunlar için soyutlama yoktur.
- Ek dosyalar yalnız yetki denetimi yapan indirme ucundan akar; depolama anahtarı istemciye hiç verilmez.
- Tüm tarihler yerel saat (Europe/Istanbul) yazılır; modülde UTC kullanılmaz.
- Elle transaction yalnız ürünün yürütme stratejisinin içinde açılır. Ürünler PostgreSQL context'ini
EnableRetryOnFailure(3)ile kuruyor; EF, yeniden deneyen bir strateji altındaBeginTransaction'ıInvalidOperationExceptionile reddeder. Yeni bir transaction gerekiyorsa gövdedb.Database.CreateExecutionStrategy().ExecuteAsync(...)içine konur ve yeniden denenebilir yazılır (her denemedeChangeTracker.Clear()+ veriyi sıfırdan oku). 1.0.0'da alarm turu bu kuralı ihlal ettiği için zamanlayıcı PostgreSQL'de hiç çalışmadı; SQLite testleri yeniden denemediği için göstermedi (regresyon testi:AlarmYenidenDenemeStratejisiTests).
Bildirim modeli ve WebSocket (1.0.6, kırıcı, ürün migration ister)
Kullanıcı kararları 2026-10-19: hatırlatıcının saati bildirimin kendisidir (Apple Anımsatıcılar gibi) ve canlı bildirim WebSocket üzerinden gelir.
- Alarm satırları (
HatirlaticiAlarmlar):Kind=Due(saat; saat varsa her zaman vardır, sistem kurar),EarlyWarning(saattenOffsetMinutesdakika önce, göreli) ya daSnooze(mutlak, bir kez çalar, sonra silinir).FireAtbir sonraki çalma anı (null= bu oluşum için çaldı ya da geçti),LastFiredAtson çaldığı an. Durum, deneme sayısı ve hata kolonları yok; tur başarısızsa transaction geri alınır, noktalar yerinde kalır. ReminderAlarmPlannertek kaynaktır: ürün servisleri kayıt/güncelleme (Planla), tamamlama (TamamlanmaDegisti), erteleme (Ertele) için, zamanlayıcı her tetiklenmede (Caldi) çağırır. Tekrarlı hatırlatıcının saati geçtiyse kuralın sonraki oluşumuna ilerler (OccurrenceCount, RRULECOUNTiçin), göreli satırlar onunla kayar. Geçmiş ve bu an için zaten çalmış nokta kurulmaz.- Zamanlayıcı yalnız alarm satırlarını günceller; hatırlatıcı satırı (eşzamanlılık damgası) yalnız tekrarlı kayıtta saat ilerlerken değişir. Birlikte gecikmiş noktalardan yalnız en geç olanı bildirim üretir.
Notification.AlarmIdkalktı;FireAtgeldi, tekil indeks(ReminderId, FireAt, OwnerUserId).- Hatırlatıcıda
Colorve etiketler (HatirlaticiEtiketlerialt tablosu, notlardaki gibi);GET api/Reminders/Tags. - DTO: girişte
Alarms(mutlak anlar) yerineEarlyWarningMinutes;AddAlarm/RemoveAlarmuçları kalktı. SınırlarReminderLimits'te. Yeni ileti anahtarları:EarlyWarningInvalid,EarlyWarningRequiresDueAt. RemindersOptions.MaxAttemptsveRetryDelayMinuteskalktı;HubConnectionLifetimeMinutes(15, [1, 30]) geldi.
Hub bağlantı bileti
Hub yalnız WebSocket'tir ve yalnız Master'ın imzaladığı biletle bağlanılır (RemindersHubTicket): audience
tapis-hatirlatici-hub, token_use hatirlatici_hub_bilet, kapsam Hatirlatici, hub /api/hubs/reminders,
30 sn. Master POST api/Auth/HatirlaticiHubBileti ile üretir, Gateway durumsuz doğrular, ürün yeniden doğrular:
claim'ler → app = Tenancy:UygulamaId → kullanıcı aktif (biletteki tenant'la, IReminderDbContextFactory +
IReminderRecipientResolver) → jti tüketimi en son. Bağlantı HubConnectionLifetimeMinutes sonunda kapanır;
istemci yeni biletle bağlanır. Pasife alınan kullanıcı en çok bu süre kadar bildirim alabilir (bilinen sınır).
jti deposu ve SignalR grupları süreç içidir: ürün API'leri tek kopya. Birden fazla kopyaya geçilirse paylaşılan
depo, backplane ve yeni bir güvenlik incelemesi gerekir.
Ürün kontrol listesi (1.0.6)
- Migration:
HatirlaticiAlarmlaryeniden şekillenir (Kind,OffsetMinutes,FireAtnullable,LastFiredAt;Status/FiredAt/Error/AttemptCount/FromRecurrencekalkar; tekil indeks(ReminderId, Kind, OffsetMinutes)),Hatirlaticilar.OccurrenceCountveColor,HatirlaticiEtiketleri,Bildirimler.FireAt+ yeni tekil indeks. Veri dönüşümü: saati olan her tamamlanmamış hatırlatıcıyaDuesatırı; bekleyen ve saatten önceki alarmlarEarlyWarning'e (dakika farkı); geçmiş bildirimlerinFireAt'i eski alarmın anından. - Servis:
Planla/TamamlanmaDegisti/Erteleçağrıları,AddAlarm/RemoveAlarmsilinir,TagsAsync, renk/etiket. - Validator:
ReminderLimitsile erken uyarı, etiket ve renk sınırları; saat yokken erken uyarı reddi. - Kimlik:
RemindersHubTicket.Schemeadıyla JwtBearer şeması (Master issuer/anahtarı, audienceRemindersHubTicket.Audience, HS256,RequireExpirationTime,RequireSignedTokens, ClockSkew 5 sn,MapInboundClaims = false); olaylarTokenOku/DogrulaAsync/BaglantiBitisi'yi çağırır. Şema varsayılan ya da fallback değildir.MapRemindersModuleşema ya da çözümleyici yoksa başlangıçta hata verir. - Angular imajı nginx:
location = /api/hubs/reminders(WebSocket upgrade,access_log off).
Bildirim listesi: görünürlük, arama, süzgeç (1.0.5)
Migration gerekmez (şema aynı). Bildirimler hâlâ yalnız alıcısınındır: OwnerUserId == ben tek erişim kuralıdır,
RecordAccess'in işletme kuralı bildirimlere uygulanmaz; hatırlatıcıyla ilişki satır eklemez, yalnız görünürlüğü
taşır ve (süzgeçte) eler.
NotificationDTO.Visibility(RecordVisibility?): kaynak hatırlatıcının şu anki görünürlüğü; hatırlatıcı silinmişse ya daReminderIdboşsanull(arayüz rozet göstermez). Alarmın anlık push DTO'sunda da dolu gelir.NotificationQueryDTO: mevcutSkip,Take,SadeceOkunmamisyanındaArama(string?, kırpılır, en çokNotificationQuery.MaxSearchLength= 100, başlık + metinde düzContains; Türkçe büyük/küçük harf duyarsızlığı ürününtr_cicollation'ından gelir, paketToLower/ILikekullanmaz) veGorunurluk(NotificationVisibilityFilter:All0,Personal1,Business2; hatırlatıcısı silinmiş bildirimler yalnızAll'da).NotificationQuery(Tapis.Core.Reminders.Services):ApplyFilter(hatirlaticilar, ownerUserId, sorgu)(owner + okunmamış + arama + görünürlük),Project(hatirlaticilar)(DTO +Visibility),NormalizeSearch,IsSearchValid,IsValidVisibilityFilter.
Ürün kontrol listesi (HostelManagement / ServiceManagement)
BildirimRepository.SayfaAsync(veINotificationService.ListAsync) kendiOwnerUserId == ben/SadeceOkunmamissüzgecini bırakıpbildirimler.ApplyFilter(hatirlaticilar, kullaniciId, sorgu)çağırır; toplam sayı bu süzülmüş sorgudan, sırala (okunmamışlar önce, sonraOlusturmaTarihiazalan) +Skip/TakesonraProject(hatirlaticilar)ile DTO'ya.hatirlaticilar= ürününSet<Reminder>()'ı (AsNoTracking).UnreadCountsayacı süzgeçsiz kalır (zil rozeti).- Validator:
AramaiçinNotificationQuery.IsSearchValid(ya daMaximumLength(100)),GorunurlukiçinIsInEnum; mesaj Türkçe,WithMessageile. Paketin FluentValidation bağımlılığı yoktur. /api-regen, Angular Bildirimler sayfası:visibilityrozeti (Kişisel/İşletme;null= rozet yok), arama kutusu, görünürlük süzgeci. Başka kullanıcının satırı hiçbir süzgeçle dönmez.- Ürün
CLAUDE.md'sindeki bildirim bölümü güncellenir.
İşletme görünürlüğü (1.0.4)
Note ve Reminder bir Visibility taşır: RecordVisibility.Personal (0, varsayılan, mevcut satırlar) ya da
Business (1). İşletme kaydını tenant'ın kimliği doğrulanmış her kullanıcı oturumu okur ve yazar (düzenleme,
silme, tamamlama, erteleme, alarm, alt madde, etiket, ek). İşletme hatırlatıcısının alarmı tenant'ın her aktif
kullanıcısına ayrı bildirim üretir. Kişisele geri almayı yalnız oluşturan (OwnerUserId) yapar; işletmeye alma
her yazarın hakkıdır. OwnerUserId yalnız oluşturmada yazılır, hiçbir güncelleme DTO'su onu taşımaz ve görünürlük
değişimi onu değiştirmez.
Pakette olanlar
RecordAccess(Tapis.Core.Reminders.Services):NoteReadable/NoteWritable,ReminderReadable/ReminderWritable,ChecklistItemAccessible,AlarmAccessible,NoteTagAccessible,AttachmentAccessible(ek, üst kaydı okunabiliyorsa okunur; ek + üst kayıt + kural tek sorguda),NoteFilter/ReminderFilter(Gorunurluksüzgeci),CanChangeVisibility,IsValidVisibility,IsOwner,RemoveNonOwnerNotificationsAsync. Boş/boşluk kullanıcı kimliği hiçbir satırla eşleşmez (işletme satırıyla da).IReminderRecipientResolver:ActiveUserIdsAsync(DbContext, ct)veDisplayNamesAsync(DbContext, ids, ct).ReminderDispatchServiceişletme hatırlatıcısında alıcıları tur başına bir kez, alarmıFiredyapan transaction'ın içinde çözer; her alıcıya birNotificationsatırı yazar, commit'ten sonra alıcı başına tek push yollar (mevcutt:{tenant}:u:{user}grubu; yeni grup,Clients.All/User/Othersyok; okunmamış sayaçları alıcı başına sorgu değil parça başına gruplu sorguyla). Çözümleyici kayıtsızsa ya da boş dönerse bildirim üretilmez (sahibe ya da başka birine düşülmez). ÇözümleyiciReminderDispatchService'e isteğe bağlı enjekte edilir.Notificationtekil indeksi(AlarmId, OwnerUserId);(ReminderId, FireAt)değişmedi.- Entity/EF:
Visibility(zorunlu, varsayılan 0), indekslerNotlar (Visibility, IsPinned)veHatirlaticilar (Visibility, IsCompleted, DueAt). - DTO: create/update/list/detail'de
Visibility; list/detail'deCreatedByName,IsMine; filtre DTO'larındaGorunurluk.
Faz 2 ürün kontrol listesi (HostelManagement / ServiceManagement)
- Migration:
Notlar.Visibility,Hatirlaticilar.Visibility(int, NOT NULL, default 0), iki yeni indeks veBildirimlertekil indeksininAlarmId→(AlarmId, OwnerUserId)olarak değişmesi (/add-migration). - Fail-closed kapı: servisler ClientToken çağıranlarını ve boş kullanıcı kimliğini kural değerlendirilmeden ÖNCE reddeder (mevcut FU1 denetimi); paket de boş kimlikte hiçbir şey eşleştirmez, ama bu ikinci savunmadır.
RecordAccessher yerde: not, hatırlatıcı, alt madde, alarm/erteleme/tamamlama, etiket, sayaç (Counts), arama, liste ve ek indirme/yükleme/silme sorguları. ElleOwnerUserId == userIdyazılmaz. Bildirim sorguları (liste, sayaç, okundu, silme)OwnerUserId == benolarak KALIR.- Çözümleyici:
IReminderRecipientResolver'ı ürünApplicationUser.Aktif == trueile, yalnız verilenDbContextüzerinde uygular (scopedDbContext/HttpContextenjekte etmez; arka plan yolunda ikisi de yoktur),DisplayNamesAsyncyalnız görünen adı döner. DI'a kaydedilir. - Görünen ad: list/detail DTO'larında
CreatedByName(çözümleyiciden, sayfa başına tek sorgu) veIsMine(OwnerUserId == ben) doldurulur. - Bildirim temizliği: işletme kaydı silinirken ya da
Personal'a çevrilirkenRecordAccess.RemoveNonOwnerNotificationsAsyncAYNISaveChanges'te çağrılır. - Görünürlük değişimi: güncelleme DTO'larında
VisibilityRecordVisibility?'dir;null= mevcut değer korunur (CanChangeVisibilityçağrılmaz, bildirim temizliği yapılmaz). Değer doluysa ve mevcuttan farklıysaRecordAccess.CanChangeVisibilityile denetlenir; işletmeden kişisele yalnız oluşturan.OwnerUserIdyalnız oluşturmada yazılır, update DTO/mapper onu hiç yazmaz.NoteCreateDTO/ReminderCreateDTOartıkNoteUpdateDTO/ReminderUpdateDTO'nun tabanı değildir (ortak taban*InputBaseDTO): mapper'lar buna göre güncellenir. - Validator:
VisibilityiçinIsInEnum(paketin FluentValidation bağımlılığı yoktur; validator'lar ürünündür). Oluşturmada eksik değerPersonalsayılır; güncellemedenullmevcut değeri korur (madde 7). - Denetim: ürünün denetim interceptor'ı
Visibilitydeğişimini zaten kapsar; ayrı kod gerekmez, doğrula. - Güvenlik dokümanları (
DevelopmentStandards):security/AUTHORIZATION.md, Object-level bölümüne Reminders istisnası: Personal = yalnızOwnerUserId; Business = tenant'ın kimliği doğrulanmış her kullanıcı oturumu okur+yazar; Personal'a geri alma yalnız oluşturan; sahip asla yeniden atanmaz; bildirimler yalnız sahibinindir; erişimin dışı 404; ClientToken reddedilir.security/TENANT-SECURITY.md, Background work bölümüne: işletme alarm dağıtımı yalnız turun tenantDbContext'ini ve döngününtenantId'sini kullanır, yedek alıcı yoktur, push yalnız mevcutt:{tenant}:u:{user}gruplarına gider.
- Docs / istemci: ürün
CLAUDE.md'si (Notlar & Hatırlatıcılar bölümü),/api-regen, Angular tarafı.
Bilinen sınırlama
Bir alarm turu çalışırken kayıt kişisele çevrilirse (ya da silinirse), o turun yazdığı sahip-dışı bildirim satırları temizlikten sonra kalabilir (yarış). Bu durumda sahip-dışı alıcılar o alarmın başlığını ve metnini bildirim listelerinde görmeye devam edebilir (kayıt az önceye kadar onlara zaten açıktı); satırı kullanıcı silebilir. Alıcı kimlikleri çözümleyiciden olduğu gibi alınır: kenarında boşluk olan kimlik atılır.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.EntityFrameworkCore (>= 10.0.10)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.10)
- Npgsql.EntityFrameworkCore.PostgreSQL (>= 10.0.0)
- Tapis.Core.Shared (>= 2.0.19)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
1.0.6 (kirici, urun migration ister): saat bildirimin kendisidir - ReminderAlarm yeniden sekillendi (Kind Due/EarlyWarning/Snooze, OffsetMinutes goreli, FireAt nullable, LastFiredAt; Status/FiredAt/Error/AttemptCount/FromRecurrence ve ReminderAlarmStatus kalkti), ReminderAlarmPlanner tek kaynak, Reminder.OccurrenceCount/Color/Tags (ReminderTag), Notification.AlarmId yerine FireAt ve (ReminderId, FireAt, OwnerUserId) tekil indeksi, DTO EarlyWarningMinutes/Color/Tags, AddAlarm/RemoveAlarm kalkti, api/Reminders/Tags, ReminderLimits; hub yalniz WebSocket ve Master bileti (RemindersHubTicket, tek kullanim deposu, HubConnectionLifetimeMinutes), RemindersOptions MaxAttempts/RetryDelayMinutes kalkti. 1.0.5 (migration gerekmez): bildirim listesi - NotificationDTO.Visibility (kaynak hatirlaticinin simdiki gorunurlugu, hatirlatici yoksa null), NotificationQueryDTO.Arama ve Gorunurluk (NotificationVisibilityFilter), NotificationQuery yardimcisi (Normalize/Validate/ApplyFilter/Project); bildirim erisimi yine yalniz OwnerUserId == ben. 1.0.4: Isletme notlari/hatirlatici (kirici, urun migration ister): RecordVisibility (Personal=0, Business=1) ve RecordVisibilityFilter; Note.Visibility, Reminder.Visibility (varsayilan 0) ve indeksler (Visibility, IsPinned) / (Visibility, IsCompleted, DueAt); Notification tekil indeksi AlarmId yerine (AlarmId, OwnerUserId). RecordAccess: erisim kuralinin tek kaynagi (NoteReadable/NoteWritable, ReminderReadable/ReminderWritable, alt madde/alarm/etiket/ek ifadeleri, liste suzgecleri, CanChangeVisibility, RemoveNonOwnerNotificationsAsync); bos kullanici kimligi hicbir satirla eslesmez. Yeni IReminderRecipientResolver (ActiveUserIdsAsync, DisplayNamesAsync): ReminderDispatchService isletme hatirlaticisinda her aktif kullaniciya ayri bildirim uretir (ayni transaction), commit sonrasi alici basina tek push; cozumleyici yoksa ya da bos donerse bildirim uretilmez. DTO'lara Visibility, CreatedByName, IsMine ve Gorunurluk suzgeci eklendi.