seed/data Seeder Announcements

This commit is contained in:
Sedat ÖZTÜRK 2026-09-04 14:52:34 +03:00
parent 4bf6b4c257
commit 8be8fef972
6 changed files with 133 additions and 96 deletions

View file

@ -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.

View file

@ -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,

View file

@ -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<ListFormSeedDataApplier> logger) : IListFormSeedDataApplier, ITransientDependency
{
/// <summary>Satir degerinde kullanicinin adiyla yazilan referans; anahtari seed sirasinda cozulur.</summary>
private const string UserTokenPrefix = "@USER:";
/// <summary>Cozulen kullanici anahtarlari; ayni kullanici her satirda yeniden sorgulanmaz.</summary>
private readonly Dictionary<string, Guid?> 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(
}
}
/// <summary>
/// Satirdaki <c>@USER:kullaniciAdi</c> degerlerini kullanicinin anahtariyla degistirir. Kullanici
/// anahtarlari her kurulumda yeniden uretildigi icin dosyaya yazilamaz; ad uzerinden cozulur.
/// </summary>
private async Task<Dictionary<string, object>> ResolveTokensAsync(Dictionary<string, object> row)
{
var resolved = new Dictionary<string, object>(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;
}
/// <summary>Dosyadaki satiri, ekranin alan tanimlarina gore tiplendirilmis parametrelere cevirir.</summary>
private static Dictionary<string, object> BuildParameters(
List<ListFormField> fields,

View file

@ -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ıı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",

View file

@ -36,7 +36,6 @@ public class TenantSeederDto
public List<ForumCategorySeedDto> ForumCategories { get; set; }
//Intranet
public List<AnnouncementSeedDto> Announcements { get; set; }
public List<SurveySeedDto> Surveys { get; set; }
public List<SurveyQuestionSeedDto> SurveyQuestions { get; set; }
public List<SurveyQuestionOptionSeedDto> 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<Contact, Guid> _contactRepository;
private readonly IRepository<ForumCategory, Guid> _forumCategoryRepository;
private readonly IIdentityUserRepository _repositoryUser;
private readonly IRepository<Announcement, Guid> _announcementRepository;
private readonly IRepository<Survey, Guid> _surveyRepository;
private readonly IRepository<SurveyQuestion, Guid> _surveyQuestionRepository;
private readonly IRepository<SurveyQuestionOption, Guid> _surveyQuestionOptionRepository;
@ -315,7 +298,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
IRepository<Contact, Guid> contactRepository,
IRepository<ForumCategory, Guid> forumCategoryRepository,
IRepository<Announcement, Guid> announcementRepository,
IRepository<Survey, Guid> surveyRepository,
IRepository<SurveyQuestion, Guid> surveyQuestionRepository,
IRepository<SurveyQuestionOption, Guid> 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);

View file

@ -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ıı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
}
]
}