diff --git a/CHANGELOG.md b/CHANGELOG.md index ae502c01..3ac4d067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,8 +16,11 @@ Versionsschema ist `x.x.x.x` (siehe `release.sh`). Felder aus den Dubletten aufgefuellt werden (kein Datenverlust), die uebrigen werden geloescht. Verwaiste Zuordnungen werden bereinigt, sodass der naechste bidirektionale Sync die Gewinner sauber 1:1 verbindet. Laeuft unter derselben - Sperre wie ein Sync (kein gleichzeitiger Lauf), nutzt dieselbe Kontakt- - Erkennung wie der Sync und wird vollstaendig protokolliert. + Sperre wie ein Sync (kein gleichzeitiger Lauf - wartet bei laufendem Sync bis + zu 60s statt abzubrechen), nutzt dieselbe Kontakt-Erkennung wie der Sync und + wird vollstaendig protokolliert. Outlook- und Anlagen-Seite werden unabhaengig + verarbeitet (eine streikende Anlage verhindert nicht die Outlook-Bereinigung); + fehlgeschlagene Loeschungen werden gemeldet. ### Behoben diff --git a/src/StarfaceOutlookSync/Services/DedupeService.cs b/src/StarfaceOutlookSync/Services/DedupeService.cs index 64992f35..59a92682 100644 --- a/src/StarfaceOutlookSync/Services/DedupeService.cs +++ b/src/StarfaceOutlookSync/Services/DedupeService.cs @@ -90,90 +90,106 @@ namespace StarfaceOutlookSync.Services var deletedStarfaceIds = new HashSet(); var deletedOutlookIds = new HashSet(); - var outlookContacts = _outlookService.GetContacts(profile.OutlookFolderPath); - - using (var backend = ContactBackendFactory.Create(profile.Connection)) + // --- Outlook (lokal, unabhaengig - laeuft auch wenn die Anlage streikt) --- + try { - backend.OnDebug += Log; - if (!await backend.LoginAsync()) - throw new Exception("Login an der Telefonanlage fehlgeschlagen."); - - var remoteContacts = await backend.GetContactsAsync(profile.AddressBook); - - // --- Telefonanlage --- - foreach (var g in GroupDuplicates(remoteContacts, c => mappedStarface.Contains(c.StarfaceId))) + var outlookContacts = _outlookService.GetContacts(profile.OutlookFolderPath); + var groups = GroupDuplicates(outlookContacts, c => mappedOutlook.Contains(c.OutlookEntryId)); + Log($"Outlook: {groups.Count} Gruppe(n) mit Dubletten."); + foreach (var g in groups) { try { bool changed = ContactMerger.FillEmptyInto(g.Survivor, g.Duplicates); - bool survivorOk = true; - if (changed) - survivorOk = await backend.UpdateContactAsync(g.Survivor.StarfaceId, g.Survivor, profile.AddressBook) != null; - - if (!survivorOk) + if (changed && _outlookService.UpdateContact(g.Survivor.OutlookEntryId, g.Survivor) == null) { - // Gewinner konnte nicht ergaenzt werden -> Dubletten NICHT loeschen - // (sonst gingen die nur dort vorhandenen Felder verloren). report.Errors++; - report.Messages.Add($"Anlage: '{g.Survivor.DisplayName}' konnte nicht ergaenzt werden - Gruppe uebersprungen."); + report.Messages.Add($"Outlook: '{g.Survivor.DisplayName}' konnte nicht ergaenzt werden - uebersprungen."); continue; } - foreach (var dup in g.Duplicates) { - if (await backend.DeleteContactAsync(dup.StarfaceId)) + if (_outlookService.DeleteContact(dup.OutlookEntryId)) { - deletedStarfaceIds.Add(dup.StarfaceId); - report.RemoteDeleted++; - Log($" Anlage: Dublette geloescht -> zusammengefuehrt in '{g.Survivor.DisplayName}'"); + deletedOutlookIds.Add(dup.OutlookEntryId); + report.OutlookDeleted++; + Log($" Outlook: Dublette geloescht -> '{g.Survivor.DisplayName}'"); + } + else + { + report.Errors++; + report.Messages.Add($"Outlook: Dublette von '{g.Survivor.DisplayName}' konnte nicht geloescht werden."); } } - report.RemoteMerged++; + report.OutlookMerged++; } catch (Exception ex) { report.Errors++; - report.Messages.Add($"Anlage '{g.Survivor.DisplayName}': {ex.Message}"); + report.Messages.Add($"Outlook '{g.Survivor.DisplayName}': {ex.Message}"); } } - - await backend.LogoutAsync(); + } + catch (Exception ex) + { + report.Errors++; + report.Messages.Add("Outlook gesamt: " + ex.Message); } - // --- Outlook --- - foreach (var g in GroupDuplicates(outlookContacts, c => mappedOutlook.Contains(c.OutlookEntryId))) + // --- Telefonanlage (unabhaengig) --- + try { - try + using (var backend = ContactBackendFactory.Create(profile.Connection)) { - bool changed = ContactMerger.FillEmptyInto(g.Survivor, g.Duplicates); - bool survivorOk = true; - if (changed) - survivorOk = _outlookService.UpdateContact(g.Survivor.OutlookEntryId, g.Survivor) != null; + backend.OnDebug += Log; + if (!await backend.LoginAsync()) + throw new Exception("Login an der Telefonanlage fehlgeschlagen."); - if (!survivorOk) + var remoteContacts = await backend.GetContactsAsync(profile.AddressBook); + var groups = GroupDuplicates(remoteContacts, c => mappedStarface.Contains(c.StarfaceId)); + Log($"Telefonanlage: {groups.Count} Gruppe(n) mit Dubletten."); + foreach (var g in groups) { - report.Errors++; - report.Messages.Add($"Outlook: '{g.Survivor.DisplayName}' konnte nicht ergaenzt werden - Gruppe uebersprungen."); - continue; - } - - foreach (var dup in g.Duplicates) - { - if (_outlookService.DeleteContact(dup.OutlookEntryId)) + try { - deletedOutlookIds.Add(dup.OutlookEntryId); - report.OutlookDeleted++; - Log($" Outlook: Dublette geloescht -> zusammengefuehrt in '{g.Survivor.DisplayName}'"); + bool changed = ContactMerger.FillEmptyInto(g.Survivor, g.Duplicates); + if (changed && await backend.UpdateContactAsync(g.Survivor.StarfaceId, g.Survivor, profile.AddressBook) == null) + { + report.Errors++; + report.Messages.Add($"Anlage: '{g.Survivor.DisplayName}' konnte nicht ergaenzt werden - uebersprungen."); + continue; + } + foreach (var dup in g.Duplicates) + { + if (await backend.DeleteContactAsync(dup.StarfaceId)) + { + deletedStarfaceIds.Add(dup.StarfaceId); + report.RemoteDeleted++; + Log($" Anlage: Dublette geloescht -> '{g.Survivor.DisplayName}'"); + } + else + { + report.Errors++; + report.Messages.Add($"Anlage: Dublette von '{g.Survivor.DisplayName}' konnte nicht geloescht werden."); + } + } + report.RemoteMerged++; + } + catch (Exception ex) + { + report.Errors++; + report.Messages.Add($"Anlage '{g.Survivor.DisplayName}': {ex.Message}"); } } - report.OutlookMerged++; - } - catch (Exception ex) - { - report.Errors++; - report.Messages.Add($"Outlook '{g.Survivor.DisplayName}': {ex.Message}"); + + await backend.LogoutAsync(); } } + catch (Exception ex) + { + report.Errors++; + report.Messages.Add("Telefonanlage gesamt: " + ex.Message); + } // Verwaiste Zuordnungen (zeigen auf geloeschte Dubletten) entfernen. // Der naechste Sync verbindet die Gewinner sauber neu. diff --git a/src/StarfaceOutlookSync/Services/SyncCoordinator.cs b/src/StarfaceOutlookSync/Services/SyncCoordinator.cs index 12273944..f784f91b 100644 --- a/src/StarfaceOutlookSync/Services/SyncCoordinator.cs +++ b/src/StarfaceOutlookSync/Services/SyncCoordinator.cs @@ -123,10 +123,18 @@ namespace StarfaceOutlookSync.Services /// public async Task RunExclusiveAsync(Func action, Action status) { - if (Interlocked.CompareExchange(ref _running, 1, 0) != 0) + // Auf einen gerade laufenden Sync warten (bis 60s), statt sofort + // abzubrechen - der Auto-Sync feuert ja jede Minute. + var guardDeadline = DateTime.UtcNow.AddSeconds(60); + while (Interlocked.CompareExchange(ref _running, 1, 0) != 0) { - status?.Invoke("Es laeuft bereits ein Sync / eine Aktion - bitte warten."); - return false; + if (DateTime.UtcNow >= guardDeadline) + { + status?.Invoke("Es laeuft bereits ein Sync - bitte spaeter erneut versuchen."); + return false; + } + status?.Invoke("Warte, bis der laufende Sync fertig ist..."); + await Task.Delay(1000); } SyncLock crossLock = null; diff --git a/src/StarfaceOutlookSync/UI/DedupeForm.cs b/src/StarfaceOutlookSync/UI/DedupeForm.cs index 1363a84c..789d57b7 100644 --- a/src/StarfaceOutlookSync/UI/DedupeForm.cs +++ b/src/StarfaceOutlookSync/UI/DedupeForm.cs @@ -160,7 +160,15 @@ namespace StarfaceOutlookSync.UI if (!ran) { - AppendLog("Abgebrochen (Sync/Aktion laeuft oder anderer Arbeitsplatz aktiv)."); + AppendLog("Abgebrochen: gerade laeuft ein Sync (oder ein anderer Arbeitsplatz ist aktiv)."); + _lblSummary.Text = "Nicht ausgefuehrt - es laeuft gerade ein Sync. Bitte kurz warten und erneut versuchen."; + _lblSummary.ForeColor = Color.OrangeRed; + MessageBox.Show(this, + "Die Zusammenfuehrung wurde NICHT ausgefuehrt, weil gerade ein Sync laeuft " + + "(oder ein anderer Arbeitsplatz aktiv ist).\n\nBitte kurz warten und erneut auf " + + "'Zusammenfuehren starten' klicken.", + "Dubletten", MessageBoxButtons.OK, MessageBoxIcon.Warning); + _btnStart.Enabled = true; // erneuter Versuch moeglich } else if (report != null) {