작업 시간 업데이트 기능 추가 및 이슈 데이터 저장 확인.
This commit is contained in:
@@ -94,16 +94,20 @@ public class AppDbService
|
|||||||
}
|
}
|
||||||
#endregion Table: Configs
|
#endregion Table: Configs
|
||||||
|
|
||||||
|
#region Table: JiraIssues
|
||||||
public async Task UpsertJiraIssueAsync(JiraIssue issue, CancellationToken ct = default)
|
public async Task<(bool isInserted, bool isUpdated)> UpsertJiraIssueAsync(JiraIssue issue, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var existing = await _db.JiraIssues.FindAsync([issue.Key], ct);
|
var existing = await _db.JiraIssues.FindAsync([issue.Key], ct);
|
||||||
|
|
||||||
|
bool isInserted = false;
|
||||||
|
bool isUpdated = false;
|
||||||
|
|
||||||
if (existing == null)
|
if (existing == null)
|
||||||
{
|
{
|
||||||
_db.JiraIssues.Add(issue);
|
_db.JiraIssues.Add(issue);
|
||||||
|
isInserted = true;
|
||||||
_log.Debug("Inserting new Jira issue: {key}", issue.Key);
|
_log.Debug("Inserting new Jira issue: {key}", issue.Key);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -122,6 +126,7 @@ public class AppDbService
|
|||||||
existing.NeedNotify = issue.NeedNotify;
|
existing.NeedNotify = issue.NeedNotify;
|
||||||
|
|
||||||
_db.JiraIssues.Update(existing);
|
_db.JiraIssues.Update(existing);
|
||||||
|
isUpdated = true;
|
||||||
_log.Debug("Updating existing Jira issue: {key}", issue.Key);
|
_log.Debug("Updating existing Jira issue: {key}", issue.Key);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -131,6 +136,7 @@ public class AppDbService
|
|||||||
}
|
}
|
||||||
|
|
||||||
await _db.SaveChangesAsync(ct);
|
await _db.SaveChangesAsync(ct);
|
||||||
|
return (isInserted, isUpdated);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -139,27 +145,29 @@ public class AppDbService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpsertJiraIssuesBatchAsync(IEnumerable<JiraIssue> issues, CancellationToken ct = default)
|
public async Task<(List<string> inserted, List<string> updated)> UpsertJiraIssuesBatchAsync(IEnumerable<JiraIssue> issues, CancellationToken ct = default)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var issueList = issues.ToList();
|
var issueList = issues.ToList();
|
||||||
if (!issueList.Any())
|
if (!issueList.Any())
|
||||||
return;
|
return (new List<string>(), new List<string>());
|
||||||
|
|
||||||
var keys = issueList.Select(i => i.Key).ToList();
|
var keys = issueList.Select(i => i.Key).ToList();
|
||||||
var existingIssues = await _db.JiraIssues
|
var existingIssues = await _db.JiraIssues
|
||||||
.Where(ji => keys.Contains(ji.Key))
|
.Where(ji => keys.Contains(ji.Key))
|
||||||
.ToDictionaryAsync(ji => ji.Key, ct);
|
.ToDictionaryAsync(ji => ji.Key, ct);
|
||||||
|
|
||||||
int insertCount = 0, updateCount = 0, skipCount = 0;
|
var insertedKeys = new List<string>();
|
||||||
|
var updatedKeys = new List<string>();
|
||||||
|
int skipCount = 0;
|
||||||
|
|
||||||
foreach (var issue in issueList)
|
foreach (var issue in issueList)
|
||||||
{
|
{
|
||||||
if (!existingIssues.TryGetValue(issue.Key, out var existing))
|
if (!existingIssues.TryGetValue(issue.Key, out var existing))
|
||||||
{
|
{
|
||||||
_db.JiraIssues.Add(issue);
|
_db.JiraIssues.Add(issue);
|
||||||
insertCount++;
|
insertedKeys.Add(issue.Key);
|
||||||
}
|
}
|
||||||
else if (issue.Updated > existing.Updated)
|
else if (issue.Updated > existing.Updated)
|
||||||
{
|
{
|
||||||
@@ -175,7 +183,7 @@ public class AppDbService
|
|||||||
existing.NeedNotify = issue.NeedNotify;
|
existing.NeedNotify = issue.NeedNotify;
|
||||||
|
|
||||||
_db.JiraIssues.Update(existing);
|
_db.JiraIssues.Update(existing);
|
||||||
updateCount++;
|
updatedKeys.Add(issue.Key);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -185,7 +193,9 @@ public class AppDbService
|
|||||||
|
|
||||||
await _db.SaveChangesAsync(ct);
|
await _db.SaveChangesAsync(ct);
|
||||||
_log.Information("Batch completed: {insert} inserted, {update} updated, {skip} skipped out of {total} issues",
|
_log.Information("Batch completed: {insert} inserted, {update} updated, {skip} skipped out of {total} issues",
|
||||||
insertCount, updateCount, skipCount, issueList.Count);
|
insertedKeys.Count, updatedKeys.Count, skipCount, issueList.Count);
|
||||||
|
|
||||||
|
return (insertedKeys, updatedKeys);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@@ -193,4 +203,5 @@ public class AppDbService
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endregion Table: JiraIssues
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ public static class JiraIssueMapper
|
|||||||
Status = issue.fields.status?.description ?? string.Empty,
|
Status = issue.fields.status?.description ?? string.Empty,
|
||||||
Assignee = issue.fields.assignee?.displayName ?? string.Empty,
|
Assignee = issue.fields.assignee?.displayName ?? string.Empty,
|
||||||
Manager = issue.fields.reporter?.displayName ?? string.Empty,
|
Manager = issue.fields.reporter?.displayName ?? string.Empty,
|
||||||
Due = issue.fields.duedate?.ToUniversalTime() ?? DateTimeOffset.MinValue,
|
Due = issue.fields.duedate?.ToUniversalTime() ?? DateTimeOffset.MinValue.ToUniversalTime(),
|
||||||
Updated = issue.fields.UpdatedAt,
|
Updated = issue.fields.UpdatedAt.ToUniversalTime(),
|
||||||
Published = DateTimeOffset.MinValue,
|
Published = DateTimeOffset.MinValue.ToUniversalTime(),
|
||||||
ObjectId = null,
|
ObjectId = null,
|
||||||
NeedNotify = 0
|
NeedNotify = 0
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ public class AppDbWorker : BackgroundService
|
|||||||
var db = scope.ServiceProvider.GetRequiredService<AppDbService>();
|
var db = scope.ServiceProvider.GetRequiredService<AppDbService>();
|
||||||
var jiraIssues = batch.Select(i => i.ToEntity()).ToList();
|
var jiraIssues = batch.Select(i => i.ToEntity()).ToList();
|
||||||
await db.UpsertJiraIssuesBatchAsync(jiraIssues, stoppingToken);
|
await db.UpsertJiraIssuesBatchAsync(jiraIssues, stoppingToken);
|
||||||
|
|
||||||
_log.Information("Processed batch of {count} Jira issues", batch.Count);
|
_log.Information("Processed batch of {count} Jira issues", batch.Count);
|
||||||
batch.Clear();
|
batch.Clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,16 @@ public class JiraWorker : BackgroundService
|
|||||||
{
|
{
|
||||||
private readonly Serilog.ILogger _log;
|
private readonly Serilog.ILogger _log;
|
||||||
private readonly AppConfigs _configs;
|
private readonly AppConfigs _configs;
|
||||||
|
private readonly IServiceScopeFactory _scopeFactory; // Singleton services cannot directly inject Scoped services.
|
||||||
private readonly JiraService _jira;
|
private readonly JiraService _jira;
|
||||||
private readonly Channel<Issue> _issueChannel;
|
private readonly Channel<Issue> _issueChannel;
|
||||||
|
|
||||||
public JiraWorker(AppConfigs configs, JiraService jaraService, Channel<Issue> issueChannel)
|
public JiraWorker(AppConfigs configs, IServiceScopeFactory serviceScopeFactory,
|
||||||
|
JiraService jaraService, Channel<Issue> issueChannel)
|
||||||
{
|
{
|
||||||
_log = Log.ForContext<JiraWorker>(); ;
|
_log = Log.ForContext<JiraWorker>(); ;
|
||||||
_configs = configs;
|
_configs = configs;
|
||||||
|
_scopeFactory = serviceScopeFactory;
|
||||||
_jira = jaraService;
|
_jira = jaraService;
|
||||||
_issueChannel = issueChannel;
|
_issueChannel = issueChannel;
|
||||||
}
|
}
|
||||||
@@ -28,15 +31,27 @@ public class JiraWorker : BackgroundService
|
|||||||
{
|
{
|
||||||
_log.Debug("Worker running at: {time}", DateTimeOffset.Now);
|
_log.Debug("Worker running at: {time}", DateTimeOffset.Now);
|
||||||
|
|
||||||
var lastFetchTime = DateTimeOffset.Parse(_configs.RuntimeConfigs[Consts.ConfigLastFetchTimeKey]);
|
var configLastFetchTime = _configs.RuntimeConfigs.GetValueOrDefault(Consts.ConfigLastFetchTimeKey);
|
||||||
|
if (string.IsNullOrEmpty(configLastFetchTime))
|
||||||
|
{
|
||||||
|
using (var scope = _scopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<AppDbService>();
|
||||||
|
var lastFetchTimeText = await db.GetConfigAsync(Consts.ConfigLastFetchTimeKey);
|
||||||
|
configLastFetchTime = (lastFetchTimeText != null)
|
||||||
|
? lastFetchTimeText : DateTimeOffset.Now.AddDays(-1).ToString("yyyy-MM-ddTHH:mm:sszzz");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var lastFetchTime = DateTimeOffset.Parse(configLastFetchTime);
|
||||||
|
|
||||||
var issueList = new List<Issue>();
|
var issueList = new List<Issue>();
|
||||||
|
var currentFetchTime = DateTimeOffset.Now;
|
||||||
foreach (var targetProject in _configs.AppSettings.Jira.TargetProjects)
|
foreach (var targetProject in _configs.AppSettings.Jira.TargetProjects)
|
||||||
{
|
{
|
||||||
int startAt = 0, total = 0;
|
int startAt = 0, total = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
var response = await _jira.FetchJiraIssuesAsync(DateTimeOffset.Now.AddDays(-1), targetProject, startAt, stoppingToken);
|
var response = await _jira.FetchJiraIssuesAsync(lastFetchTime, targetProject, startAt, stoppingToken);
|
||||||
if (response != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
total = response.total;
|
total = response.total;
|
||||||
@@ -49,6 +64,14 @@ public class JiraWorker : BackgroundService
|
|||||||
{
|
{
|
||||||
await _issueChannel.Writer.WriteAsync(item, stoppingToken);
|
await _issueChannel.Writer.WriteAsync(item, stoppingToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using (var scope = _scopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
var db = scope.ServiceProvider.GetRequiredService<AppDbService>();
|
||||||
|
// Update last fetch time
|
||||||
|
await db.SetConfigAsync(Consts.ConfigLastFetchTimeKey, currentFetchTime.ToString("yyyy-MM-ddTHH:mm:sszzz"));
|
||||||
|
_configs.RuntimeConfigs[Consts.ConfigLastFetchTimeKey] = currentFetchTime.ToString("yyyy-MM-ddTHH:mm:sszzz");
|
||||||
|
}
|
||||||
await Task.Delay(_configs.AppSettings.Jira.FetchInterval * 1000, stoppingToken);
|
await Task.Delay(_configs.AppSettings.Jira.FetchInterval * 1000, stoppingToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
|
using TriliumMind.Data.Entities;
|
||||||
using TriliumMind.Models;
|
using TriliumMind.Models;
|
||||||
using TriliumMind.Services;
|
using TriliumMind.Services;
|
||||||
|
|
||||||
@@ -23,11 +24,10 @@ public class TriliumWorker : BackgroundService
|
|||||||
|
|
||||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||||
{
|
{
|
||||||
await foreach(var item in _issueChannel.Reader.ReadAllAsync(stoppingToken))
|
while (!stoppingToken.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
_log.Information("Processing issue {issue_key} - {issue_summary}", item.key, item.fields.summary);
|
_log.Debug("Worker running at: {time}", DateTimeOffset.Now);
|
||||||
// Add your processing logic here
|
await Task.Delay(10 *1000, stoppingToken);
|
||||||
//await _triliumService.FettchPageContentsAsync(item);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user