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. (tenant kapsamı dahil) eşleşmesine bakılarak belirlenir.
- **Dosyadaki değer varsayılanı ezer.** `InsertFieldsDefaultValueJson` yalnızca dosyada bulunmayan - **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. 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 - **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) kayıt güncellenmez (her migrate'te ekrandaki güncel değerler dosyadaki eskisiyle ezilmesin diye)
ve dosyada olmayan satır silinmez. ve dosyada olmayan satır silinmez.

View file

@ -2926,6 +2926,10 @@ public class ListFormSeeder_Administration : IDataSeedContributor, ITransientDep
Description = $"{listFormName}.Description", Description = $"{listFormName}.Description",
SelectCommandType = SelectCommandTypeEnum.Table, SelectCommandType = SelectCommandTypeEnum.Table,
SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Announcement)), SelectCommand = TableNameResolver.GetFullTableName(nameof(TableNameEnum.Announcement)),
SeedFilePath = SeedPathResolver.GetDataFilePath(listFormName),
SeedSyncInsert = false,
SeedSyncUpdate = false,
SeedSyncDelete = false,
KeyFieldName = "Id", KeyFieldName = "Id",
KeyFieldDbSourceType = DbType.Guid, KeyFieldDbSourceType = DbType.Guid,
DefaultFilter = DefaultFilterJson, DefaultFilter = DefaultFilterJson,

View file

@ -13,6 +13,8 @@ using Sozsoft.Platform.ListForms;
using Sozsoft.Platform.Queries; using Sozsoft.Platform.Queries;
using Volo.Abp.DependencyInjection; using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories; using Volo.Abp.Domain.Repositories;
using Volo.Abp.Identity;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.MultiTenancy; using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow; using Volo.Abp.Uow;
@ -48,8 +50,16 @@ public class ListFormSeedDataApplier(
IConfiguration configuration, IConfiguration configuration,
ICurrentTenant currentTenant, ICurrentTenant currentTenant,
IUnitOfWorkManager unitOfWorkManager, IUnitOfWorkManager unitOfWorkManager,
IIdentityUserRepository identityUserRepository,
ILookupNormalizer lookupNormalizer,
ILogger<ListFormSeedDataApplier> logger) : IListFormSeedDataApplier, ITransientDependency 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) public async Task ApplyAsync(Guid? tenantId)
{ {
var scopeFolderName = SeedPathResolver.GetScopeFolderName(tenantId); var scopeFolderName = SeedPathResolver.GetScopeFolderName(tenantId);
@ -191,7 +201,7 @@ public class ListFormSeedDataApplier(
object[] keys = [keyValue]; object[] keys = [keyValue];
var hasKey = keyValue != null; 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 olan kayda dokunulmaz: veritabanindaki guncel deger, dosyadaki eski degerle ezilmemeli.
var match = hasKey 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> /// <summary>Dosyadaki satiri, ekranin alan tanimlarina gore tiplendirilmis parametrelere cevirir.</summary>
private static Dictionary<string, object> BuildParameters( private static Dictionary<string, object> BuildParameters(
List<ListFormField> fields, 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": [ "Surveys": [
{ {
"Title": "Çalışan Memnuniyet Anketi 2024", "Title": "Çalışan Memnuniyet Anketi 2024",

View file

@ -36,7 +36,6 @@ public class TenantSeederDto
public List<ForumCategorySeedDto> ForumCategories { get; set; } public List<ForumCategorySeedDto> ForumCategories { get; set; }
//Intranet //Intranet
public List<AnnouncementSeedDto> Announcements { get; set; }
public List<SurveySeedDto> Surveys { get; set; } public List<SurveySeedDto> Surveys { get; set; }
public List<SurveyQuestionSeedDto> SurveyQuestions { get; set; } public List<SurveyQuestionSeedDto> SurveyQuestions { get; set; }
public List<SurveyQuestionOptionSeedDto> SurveyQuestionOptions { get; set; } public List<SurveyQuestionOptionSeedDto> SurveyQuestionOptions { get; set; }
@ -176,21 +175,6 @@ public class SurveyQuestionOptionSeedDto
public int Order { get; set; } 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 class ForumCategorySeedDto
{ {
public string Name { get; set; } public string Name { get; set; }
@ -289,7 +273,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
private readonly IRepository<Contact, Guid> _contactRepository; private readonly IRepository<Contact, Guid> _contactRepository;
private readonly IRepository<ForumCategory, Guid> _forumCategoryRepository; private readonly IRepository<ForumCategory, Guid> _forumCategoryRepository;
private readonly IIdentityUserRepository _repositoryUser; private readonly IIdentityUserRepository _repositoryUser;
private readonly IRepository<Announcement, Guid> _announcementRepository;
private readonly IRepository<Survey, Guid> _surveyRepository; private readonly IRepository<Survey, Guid> _surveyRepository;
private readonly IRepository<SurveyQuestion, Guid> _surveyQuestionRepository; private readonly IRepository<SurveyQuestion, Guid> _surveyQuestionRepository;
private readonly IRepository<SurveyQuestionOption, Guid> _surveyQuestionOptionRepository; private readonly IRepository<SurveyQuestionOption, Guid> _surveyQuestionOptionRepository;
@ -315,7 +298,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
IRepository<Contact, Guid> contactRepository, IRepository<Contact, Guid> contactRepository,
IRepository<ForumCategory, Guid> forumCategoryRepository, IRepository<ForumCategory, Guid> forumCategoryRepository,
IRepository<Announcement, Guid> announcementRepository,
IRepository<Survey, Guid> surveyRepository, IRepository<Survey, Guid> surveyRepository,
IRepository<SurveyQuestion, Guid> surveyQuestionRepository, IRepository<SurveyQuestion, Guid> surveyQuestionRepository,
IRepository<SurveyQuestionOption, Guid> surveyQuestionOptionRepository, IRepository<SurveyQuestionOption, Guid> surveyQuestionOptionRepository,
@ -338,7 +320,6 @@ public class TenantDataSeeder : IDataSeedContributor, ITransientDependency
_aboutRepository = aboutRepository; _aboutRepository = aboutRepository;
_contactRepository = contactRepository; _contactRepository = contactRepository;
_forumCategoryRepository = forumCategoryRepository; _forumCategoryRepository = forumCategoryRepository;
_announcementRepository = announcementRepository;
_surveyRepository = surveyRepository; _surveyRepository = surveyRepository;
_surveyQuestionRepository = surveyQuestionRepository; _surveyQuestionRepository = surveyQuestionRepository;
_surveyQuestionOptionRepository = surveyQuestionOptionRepository; _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) foreach (var item in items.Surveys)
{ {
var exists = await _surveyRepository.AnyAsync(x => x.Title == item.Title); 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
}
]
}