Files
TriliumMind/Services/AppDbService.cs

226 lines
7.1 KiB
C#
Raw Normal View History

2025-12-11 10:20:10 +09:00
using Microsoft.EntityFrameworkCore;
using Serilog;
using TriliumMind.Data;
using TriliumMind.Data.Entities;
namespace TriliumMind.Services;
public class AppDbService
{
private readonly Serilog.ILogger _log;
private readonly IAppDbContext _db;
public AppDbService(IAppDbContext dbContext)
{
_log = Log.ForContext<AppDbService>();
_db = dbContext;
}
public async Task InitializeDatabaseAsync()
{
try
{
_log.Information("Checking database...");
// Create db if not exists
var created = await _db.Database.EnsureCreatedAsync();
if (created)
{
_log.Information("Database created successfully");
}
else
{
_log.Information("Database already exists");
}
}
catch (Exception ex)
{
_log.Error(ex, "Failed to initialize database");
throw;
}
}
#region Table: Configs
2025-12-11 10:20:10 +09:00
public async Task<Dictionary<string, string>> LoadConfigAsync()
{
try
{
_log.Information("Loading configuration from database...");
var configs = await _db.Configs.ToDictionaryAsync(c => c.Key, c => c.Value);
_log.Information("Loaded {count} configuration entries", configs.Count);
return configs;
}
catch (Exception ex)
{
_log.Error(ex, "Failed to load configuration from database");
throw;
}
}
public async Task<string?> GetConfigAsync(string key)
{
try
{
var config = await _db.Configs.FindAsync(key);
return config?.Value;
}
catch (Exception ex)
{
_log.Error(ex, "Failed to get config value for key: {key}", key);
throw;
}
}
public async Task SetConfigAsync(string key, string value)
{
try
{
var config = await _db.Configs.FindAsync(key);
if (config == null)
{
config = new Config { Key = key, Value = value };
_db.Configs.Add(config);
}
else
{
config.Value = value;
_db.Configs.Update(config);
}
await _db.SaveChangesAsync();
_log.Debug("Config saved: {key} = {value}", key, value);
}
catch (Exception ex)
{
_log.Error(ex, "Failed to set config value for key: {key}", key);
throw;
}
}
#endregion Table: Configs
2025-12-11 10:20:10 +09:00
#region Table: JiraIssues
public async Task<(bool isInserted, bool isUpdated)> UpsertJiraIssueAsync(JiraIssue issue, CancellationToken ct = default)
{
try
{
var existing = await _db.JiraIssues.FindAsync([issue.Key], ct);
bool isInserted = false;
bool isUpdated = false;
if (existing == null)
{
_db.JiraIssues.Add(issue);
isInserted = true;
_log.Debug("Inserting new Jira issue: {key}", issue.Key);
}
else
{
if (issue.Updated > existing.Updated)
{
existing.Summary = issue.Summary;
existing.Parent = issue.Parent;
existing.Type = issue.Type;
existing.Status = issue.Status;
existing.Assignee = issue.Assignee;
existing.Manager = issue.Manager;
existing.Due = issue.Due;
existing.Updated = issue.Updated;
existing.ObjectId = issue.ObjectId;
existing.NeedNotify = issue.NeedNotify;
_db.JiraIssues.Update(existing);
isUpdated = true;
_log.Debug("Updating existing Jira issue: {key}", issue.Key);
}
else
{
_log.Debug("Skipping Jira issue (not newer): {key}", issue.Key);
}
}
await _db.SaveChangesAsync(ct);
return (isInserted, isUpdated);
}
catch (Exception ex)
{
_log.Error(ex, "Failed to upsert Jira issue: {key}", issue.Key);
throw;
}
}
public async Task<(List<string> inserted, List<string> updated)> UpsertJiraIssuesBatchAsync(IEnumerable<JiraIssue> issues, CancellationToken ct = default)
{
try
{
var issueList = issues.ToList();
if (!issueList.Any())
return (new List<string>(), new List<string>());
var keys = issueList.Select(i => i.Key).ToList();
var existingIssues = await _db.JiraIssues
.Where(ji => keys.Contains(ji.Key))
.ToDictionaryAsync(ji => ji.Key, ct);
var insertedKeys = new List<string>();
var updatedKeys = new List<string>();
int skipCount = 0;
foreach (var issue in issueList)
{
if (!existingIssues.TryGetValue(issue.Key, out var existing))
{
_db.JiraIssues.Add(issue);
insertedKeys.Add(issue.Key);
}
else if (issue.Updated > existing.Updated)
{
existing.Summary = issue.Summary;
existing.Parent = issue.Parent;
existing.Type = issue.Type;
existing.Status = issue.Status;
existing.Assignee = issue.Assignee;
existing.Manager = issue.Manager;
existing.Due = issue.Due;
existing.Updated = issue.Updated;
existing.ObjectId = issue.ObjectId;
existing.NeedNotify = issue.NeedNotify;
_db.JiraIssues.Update(existing);
updatedKeys.Add(issue.Key);
}
else
{
skipCount++;
}
}
await _db.SaveChangesAsync(ct);
_log.Information("Batch completed: {insert} inserted, {update} updated, {skip} skipped out of {total} issues",
insertedKeys.Count, updatedKeys.Count, skipCount, issueList.Count);
return (insertedKeys, updatedKeys);
}
catch (Exception ex)
{
_log.Error(ex, "Failed to batch upsert Jira issues");
throw;
}
}
public async Task<List<JiraIssue>> GetUnpublishedJiraIssuesAsync(CancellationToken ct = default)
{
try
{
var unpublishedIssues = await _db.JiraIssues
.Where(ji => ji.Published < ji.Updated)
.ToListAsync(ct);
_log.Information("Found {count} unpublished Jira issues", unpublishedIssues.Count);
return unpublishedIssues;
}
catch (Exception ex)
{
_log.Error(ex, "Failed to get unpublished Jira issues");
throw;
}
}
#endregion Table: JiraIssues
2025-12-11 10:20:10 +09:00
}