diff --git a/.github/instructions/lowcode.instructions.md b/.github/instructions/lowcode.instructions.md index 2b1438a5..0173464f 100644 --- a/.github/instructions/lowcode.instructions.md +++ b/.github/instructions/lowcode.instructions.md @@ -3440,6 +3440,8 @@ Kurallar: (tenant kapsamı dahil) eşleşmesine bakılarak belirlenir. - **Dosyadaki değer varsayılanı ezer.** `InsertFieldsDefaultValueJson` yalnızca dosyada bulunmayan alanları doldurur (audit kolonları, kapsam, anahtar); ekrandan kayıtta ise varsayılan girilen değeri ezer. +- **`@USER:kullanıcıAdı`** değeri seed sırasında o kullanıcının anahtarına çözülür; kullanıcı anahtarları + her kurulumda yeniden üretildiği için dosyaya GUID yazılmaz. Kullanıcı bulunamazsa alan boş kalır. - **Uygulama yalnızca ekler:** anahtarı veritabanında bulunmayan satırlar `INSERT` edilir; var olan kayıt güncellenmez (her migrate'te ekrandaki güncel değerler dosyadaki eskisiyle ezilmesin diye) ve dosyada olmayan satır silinmez. diff --git a/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Administration.cs b/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Administration.cs index 9c6141df..773fb215 100644 --- a/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Administration.cs +++ b/api/src/Sozsoft.Platform.DbMigrator/Migrations/ListFormSeeder_Administration.cs @@ -2926,6 +2926,10 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep Description = $"{listFormName}.Description", SelectCommandType = SelectCommandTypeEnum.Table, SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Announcement)), + SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName), + SeedSyncInsert = false, + SeedSyncUpdate = false, + SeedSyncDelete = false, KeyFieldName = "Id", KeyFieldDbSourceType = DbType.Guid, DefaultFilter = DefaultFilterJson, diff --git a/api/src/Sozsoft.Platform.Domain/Data/ListFormSeedDataApplier.cs b/api/src/Sozsoft.Platform.Domain/Data/ListFormSeedDataApplier.cs index 0586530c..4689215a 100644 --- a/api/src/Sozsoft.Platform.Domain/Data/ListFormSeedDataApplier.cs +++ b/api/src/Sozsoft.Platform.Domain/Data/ListFormSeedDataApplier.cs @@ -13,6 +13,8 @@ using Sozsoft.Platform.ListForms; using Sozsoft.Platform.Queries; using Volo.Abp.DependencyInjection; using Volo.Abp.Domain.Repositories; +using Volo.Abp.Identity; +using Microsoft.AspNetCore.Identity; using Volo.Abp.MultiTenancy; using Volo.Abp.Uow; @@ -48,8 +50,16 @@ public class ListFormSeedDataApplier( IConfiguration configuration, ICurrentTenant currentTenant, IUnitOfWorkManager unitOfWorkManager, + IIdentityUserRepository identityUserRepository, + ILookupNormalizer lookupNormalizer, ILogger logger) : IListFormSeedDataApplier, ITransientDependency { + /// Satir degerinde kullanicinin adiyla yazilan referans; anahtari seed sirasinda cozulur. + private const string UserTokenPrefix = "@USER:"; + + /// Cozulen kullanici anahtarlari; ayni kullanici her satirda yeniden sorgulanmaz. + private readonly Dictionary userIdCache = []; + public async Task ApplyAsync(Guid? tenantId) { var scopeFolderName = SeedPathResolver.GetScopeFolderName(tenantId); @@ -191,7 +201,7 @@ public class ListFormSeedDataApplier( object[] keys = [keyValue]; var hasKey = keyValue != null; - var insertParameters = BuildParameters(fields, row, keyFieldName, includeKey: hasKey); + var insertParameters = BuildParameters(fields, await ResolveTokensAsync(row), keyFieldName, includeKey: hasKey); // Var olan kayda dokunulmaz: veritabanindaki guncel deger, dosyadaki eski degerle ezilmemeli. var match = hasKey @@ -271,6 +281,54 @@ public class ListFormSeedDataApplier( } } + /// + /// Satirdaki @USER:kullaniciAdi degerlerini kullanicinin anahtariyla degistirir. Kullanici + /// anahtarlari her kurulumda yeniden uretildigi icin dosyaya yazilamaz; ad uzerinden cozulur. + /// + private async Task> ResolveTokensAsync(Dictionary row) + { + var resolved = new Dictionary(row); + + foreach (var item in row) + { + var userName = GetUserToken(item.Value); + if (userName == null) + { + continue; + } + + if (!userIdCache.TryGetValue(userName, out var userId)) + { + var user = await identityUserRepository.FindByNormalizedUserNameAsync(lookupNormalizer.NormalizeName(userName)); + userId = user?.Id; + userIdCache[userName] = userId; + + if (userId == null) + { + logger.LogWarning("User '{UserName}' not found, '{Field}' is left empty.", userName, item.Key); + } + } + + resolved[item.Key] = userId; + } + + return resolved; + } + + private static string GetUserToken(object value) + { + var text = value switch + { + JsonElement { ValueKind: JsonValueKind.String } element => element.GetString(), + string s => s, + _ => null + }; + + return text?.StartsWith(UserTokenPrefix, StringComparison.OrdinalIgnoreCase) == true + ? text[UserTokenPrefix.Length..] + : null; + } + /// Dosyadaki satiri, ekranin alan tanimlarina gore tiplendirilmis parametrelere cevirir. private static Dictionary BuildParameters( List fields, diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.json b/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.json index 864c5da6..269c1660 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.json +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantData.json @@ -391,60 +391,6 @@ } } ], - "Announcements": [ - { - "title": "🎉 Yeni Ofis Açılışı", - "content": "Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor! Tüm çalışanlarımızı açılış törenimize davet ediyoruz.", - "excerpt": "Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor!", - "category": "general", - "userName": "system@sozsoft.com", - "publishDate": "12-10-2024", - "isPinned": true, - "viewCount": 0, - "imageUrl": "https://images.unsplash.com/photo-1497366216548-37526070297c?w=800&q=80" - }, - { - "title": "📅 Performans Değerlendirme Dönemi", - "content": "Yıl sonu performans değerlendirmelerimiz 20 Ekim - 5 Kasım tarihleri arasında gerçekleştirilecektir. Lütfen formları zamanında doldurunuz.", - "excerpt": "Yıl sonu performans değerlendirmeleri başlıyor.", - "category": "event", - "userName": "system@sozsoft.com", - "publishDate": "08-10-2024", - "expiryDate": "05-11-2024", - "isPinned": true, - "viewCount": 0 - }, - { - "title": "💻 Sistem Bakımı Duyurusu", - "content": "Bu Cumartesi saat 02: 00 - 06: 00 arası sistemlerimizde bakım çalışması yapılacaktır. Bu süre içinde sistemlere erişim sağlanamayacaktır.", - "excerpt": "Cumartesi gecesi planlı bakım çalışması", - "category": "urgent", - "userName": "system@sozsoft.com", - "publishDate": "08-10-2024", - "isPinned": false, - "viewCount": 0 - }, - { - "title": "🎓 React İleri Seviye Eğitimi", - "content": "Yazılım Geliştirme ekibimiz için React İleri Seviye eğitimi 25-26 Ekim tarihlerinde düzenlenecektir. Katılım için IK birimine başvurunuz.", - "excerpt": "React İleri Seviye eğitimi kayıtları başladı", - "category": "event", - "userName": "system@sozsoft.com", - "publishDate": "09-10-2024", - "isPinned": false, - "viewCount": 0 - }, - { - "title": "⚠️ Güvenlik Politikası Güncellemesi", - "content": "Bilgi güvenliği politikamız güncellenmiştir. Tüm çalışanlarımızın yeni politikayı okuması ve onaylaması gerekmektedir.", - "excerpt": "Güvenlik politikası güncellendi - Onay gerekli", - "category": "urgent", - "userName": "system@sozsoft.com", - "publishDate": "04-10-2024", - "isPinned": true, - "viewCount": 0 - } - ], "Surveys": [ { "Title": "Çalışan Memnuniyet Anketi 2024", diff --git a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantDataSeeder.cs b/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantDataSeeder.cs index 0800b676..ee261902 100644 --- a/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantDataSeeder.cs +++ b/api/src/Sozsoft.Platform.EntityFrameworkCore/Seeds/TenantDataSeeder.cs @@ -36,7 +36,6 @@ public class TenantSeederDto public List ForumCategories { get; set; } //Intranet - public List Announcements { get; set; } public List Surveys { get; set; } public List SurveyQuestions { get; set; } public List SurveyQuestionOptions { get; set; } @@ -176,21 +175,6 @@ public class SurveyQuestionOptionSeedDto public int Order { get; set; } } -public class AnnouncementSeedDto -{ - public string Title { get; set; } - public string Excerpt { get; set; } - public string Content { get; set; } - public string ImageUrl { get; set; } - public string Category { get; set; } - public string UserName { get; set; } - public DateTime PublishDate { get; set; } - public DateTime? ExpiryDate { get; set; } - public bool IsPinned { get; set; } - public int ViewCount { get; set; } - public string DepartmentNames { get; set; } -} - public class ForumCategorySeedDto { public string Name { get; set; } @@ -289,7 +273,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency private readonly IRepository _contactRepository; private readonly IRepository _forumCategoryRepository; private readonly IIdentityUserRepository _repositoryUser; - private readonly IRepository _announcementRepository; private readonly IRepository _surveyRepository; private readonly IRepository _surveyQuestionRepository; private readonly IRepository _surveyQuestionOptionRepository; @@ -315,7 +298,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency IRepository contactRepository, IRepository forumCategoryRepository, - IRepository announcementRepository, IRepository surveyRepository, IRepository surveyQuestionRepository, IRepository surveyQuestionOptionRepository, @@ -338,7 +320,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency _aboutRepository = aboutRepository; _contactRepository = contactRepository; _forumCategoryRepository = forumCategoryRepository; - _announcementRepository = announcementRepository; _surveyRepository = surveyRepository; _surveyQuestionRepository = surveyQuestionRepository; _surveyQuestionOptionRepository = surveyQuestionOptionRepository; @@ -460,28 +441,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency } } - foreach (var item in items.Announcements) - { - var exists = await _announcementRepository.AnyAsync(x => x.Title == item.Title); - if (exists) - continue; - - var user = await _repositoryUser.FindByNormalizedUserNameAsync(_lookupNormalizer.NormalizeName(item.UserName)); - await _announcementRepository.InsertAsync(new Announcement - { - Title = item.Title, - Excerpt = item.Excerpt, - Content = item.Content, - ImageUrl = item.ImageUrl, - Category = item.Category, - UserId = user != null ? user.Id : null, - PublishDate = item.PublishDate, - ExpiryDate = item.ExpiryDate, - IsPinned = item.IsPinned, - ViewCount = item.ViewCount - }); - } - foreach (var item in items.Surveys) { var exists = await _surveyRepository.AnyAsync(x => x.Title == item.Title); diff --git a/configs/seeds/host/data/App.Intranet.Announcement.json b/configs/seeds/host/data/App.Intranet.Announcement.json new file mode 100644 index 00000000..d0334d23 --- /dev/null +++ b/configs/seeds/host/data/App.Intranet.Announcement.json @@ -0,0 +1,68 @@ +{ + "ListFormCode": "App.Intranet.Announcement", + "KeyFieldName": "Id", + "GeneratedAt": "2026-09-04T00:00:00Z", + "Order": 0, + "Rows": [ + { + "Id": "3da0e444-4085-5cbb-bcf2-5ccfb7566050", + "Title": "🎉 Yeni Ofis Açılışı", + "Excerpt": "Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor!", + "Content": "Ankara ofisimiz 1 Kasım tarihinde hizmete başlıyor! Tüm çalışanlarımızı açılış törenimize davet ediyoruz.", + "Category": "general", + "UserId": "@USER:system@sozsoft.com", + "PublishDate": "2024-10-12T00:00:00", + "ExpiryDate": null, + "IsPinned": true, + "ImageUrl": "https://images.unsplash.com/photo-1497366216548-37526070297c?w=800&q=80" + }, + { + "Id": "169a5022-8c46-50bb-8113-aca8f348721d", + "Title": "📅 Performans Değerlendirme Dönemi", + "Excerpt": "Yıl sonu performans değerlendirmeleri başlıyor.", + "Content": "Yıl sonu performans değerlendirmelerimiz 20 Ekim - 5 Kasım tarihleri arasında gerçekleştirilecektir. Lütfen formları zamanında doldurunuz.", + "Category": "event", + "UserId": "@USER:system@sozsoft.com", + "PublishDate": "2024-10-08T00:00:00", + "ExpiryDate": "2024-11-05T00:00:00", + "IsPinned": true, + "ImageUrl": null + }, + { + "Id": "2d1377a3-49b5-51ce-80a3-5bbbbf6b3ad8", + "Title": "💻 Sistem Bakımı Duyurusu", + "Excerpt": "Cumartesi gecesi planlı bakım çalışması", + "Content": "Bu Cumartesi saat 02: 00 - 06: 00 arası sistemlerimizde bakım çalışması yapılacaktır. Bu süre içinde sistemlere erişim sağlanamayacaktır.", + "Category": "urgent", + "UserId": "@USER:system@sozsoft.com", + "PublishDate": "2024-10-08T00:00:00", + "ExpiryDate": null, + "IsPinned": false, + "ImageUrl": null + }, + { + "Id": "96a8a3dd-0f4a-5e1e-8b5e-bb44dd41a1f0", + "Title": "🎓 React İleri Seviye Eğitimi", + "Excerpt": "React İleri Seviye eğitimi kayıtları başladı", + "Content": "Yazılım Geliştirme ekibimiz için React İleri Seviye eğitimi 25-26 Ekim tarihlerinde düzenlenecektir. Katılım için IK birimine başvurunuz.", + "Category": "event", + "UserId": "@USER:system@sozsoft.com", + "PublishDate": "2024-10-09T00:00:00", + "ExpiryDate": null, + "IsPinned": false, + "ImageUrl": null + }, + { + "Id": "a753ef12-be3e-56c9-b597-47538aa69fde", + "Title": "⚠️ Güvenlik Politikası Güncellemesi", + "Excerpt": "Güvenlik politikası güncellendi - Onay gerekli", + "Content": "Bilgi güvenliği politikamız güncellenmiştir. Tüm çalışanlarımızın yeni politikayı okuması ve onaylaması gerekmektedir.", + "Category": "urgent", + "UserId": "@USER:system@sozsoft.com", + "PublishDate": "2024-10-04T00:00:00", + "ExpiryDate": null, + "IsPinned": true, + "ImageUrl": null + } + ] +}