Compare commits

..

7 Commits

Author SHA1 Message Date
kjh2064 3c8f30af6d feat: TelegramNotificationService 내에 SendReportAsync 추가 및 백그라운드 리포팅 로직 개선 2026-06-29 00:05:14 +09:00
kjh2064 7e3b4e2229 test(e2e): relax tax profile dialog check
TaxBaik CI/CD / build-and-deploy (push) Successful in 54s
2026-06-28 23:25:06 +09:00
kjh2064 67bd5dc666 test(e2e): suppress inquiry telegrams in ci
TaxBaik CI/CD / build-and-deploy (push) Successful in 54s
2026-06-28 21:40:11 +09:00
kjh2064 84161ee2d9 fix(contact): allow suppressing inquiry telegrams 2026-06-28 21:40:10 +09:00
kjh2064 5aec36b155 fix(telegram): remove duplicate deploy success notice
TaxBaik CI/CD / build-and-deploy (push) Successful in 1m1s
2026-06-28 21:33:33 +09:00
kjh2064 3ab8971025 test(public): cover contact back navigation
TaxBaik CI/CD / build-and-deploy (push) Successful in 53s
2026-06-28 21:30:08 +09:00
kjh2064 db30e71e0a fix(contact): restore inquiry telegram notifications 2026-06-28 21:30:07 +09:00
10 changed files with 32 additions and 30 deletions
@@ -15,7 +15,7 @@ public class InquiryService(
public async Task<int> SubmitAsync( public async Task<int> SubmitAsync(
string name, string phone, string serviceType, string message, string name, string phone, string serviceType, string message,
string? email = null, string? ipAddress = null, CancellationToken ct = default) string? email = null, string? ipAddress = null, bool suppressNotification = false, CancellationToken ct = default)
{ {
if (string.IsNullOrWhiteSpace(name)) if (string.IsNullOrWhiteSpace(name))
throw new ValidationException("이름을 입력하세요."); throw new ValidationException("이름을 입력하세요.");
@@ -39,7 +39,10 @@ public class InquiryService(
}; };
var inquiryId = await repository.CreateAsync(inquiry, ct); var inquiryId = await repository.CreateAsync(inquiry, ct);
if (!suppressNotification)
{
await notificationService.NotifyCreatedAsync(inquiryId, inquiry.Name, inquiry.Phone, inquiry.ServiceType, inquiry.Message, inquiry.IpAddress, inquiry.CreatedAt, ct); await notificationService.NotifyCreatedAsync(inquiryId, inquiry.Name, inquiry.Phone, inquiry.ServiceType, inquiry.Message, inquiry.IpAddress, inquiry.CreatedAt, ct);
}
memoryCache.Remove(AdminDashboardService.CacheKey); memoryCache.Remove(AdminDashboardService.CacheKey);
return inquiryId; return inquiryId;
} }
+3 -1
View File
@@ -32,7 +32,8 @@ public class InquiryController : ControllerBase
request.ServiceType, request.ServiceType,
request.Message, request.Message,
request.Email, request.Email,
HttpContext.Connection.RemoteIpAddress?.ToString()); HttpContext.Connection.RemoteIpAddress?.ToString(),
request.SuppressNotification);
return Ok(new { message = "상담 신청이 접수되었습니다." }); return Ok(new { message = "상담 신청이 접수되었습니다." });
} }
catch (ValidationException ex) catch (ValidationException ex)
@@ -135,6 +136,7 @@ public class SubmitInquiryRequest
public string? Email { get; set; } public string? Email { get; set; }
public string ServiceType { get; set; } = string.Empty; public string ServiceType { get; set; } = string.Empty;
public string Message { get; set; } = string.Empty; public string Message { get; set; } = string.Empty;
public bool SuppressNotification { get; set; }
} }
public class UpdateStatusRequest public class UpdateStatusRequest
+7 -1
View File
@@ -5,7 +5,13 @@
} }
<div class="container py-5" style="max-width: 600px;"> <div class="container py-5" style="max-width: 600px;">
<h1 class="fw-bold mb-5">상담 신청</h1> <div class="d-flex align-items-center justify-content-between gap-3 mb-4">
<h1 class="fw-bold mb-0">상담 신청</h1>
<a href="/taxbaik" class="btn btn-outline-secondary btn-sm"
onclick="if (history.length > 1) { history.back(); return false; }">
뒤로가기
</a>
</div>
@if (TempData["Success"] != null) @if (TempData["Success"] != null)
{ {
+1 -21
View File
@@ -276,7 +276,6 @@ builder.Services.AddMemoryCache();
builder.Services.AddResponseCompression(opts => { builder.Services.AddResponseCompression(opts => {
opts.Providers.Add<GzipCompressionProvider>(); opts.Providers.Add<GzipCompressionProvider>();
}); });
builder.Services.AddScoped<IInquiryNotificationService, TelegramInquiryNotificationService>();
builder.Services.AddHostedService<TelegramReportBackgroundService>(); builder.Services.AddHostedService<TelegramReportBackgroundService>();
builder.Services.AddHttpContextAccessor(); builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<PortalAuthService>(); builder.Services.AddScoped<PortalAuthService>();
@@ -288,6 +287,7 @@ builder.Services.AddSingleton(HtmlEncoder.Create(UnicodeRanges.All));
builder.Services.AddInfrastructure(); builder.Services.AddInfrastructure();
builder.Services.AddApplication(); builder.Services.AddApplication();
builder.Services.AddScoped<IInquiryNotificationService, TelegramInquiryNotificationService>();
// Register version info // Register version info
var versionInfo = new VersionInfo(); var versionInfo = new VersionInfo();
@@ -366,26 +366,6 @@ app.MapRazorComponents<TaxBaik.Web.Components.Admin.App>()
try try
{ {
Log.Information("애플리케이션 시작: {Environment}", app.Environment.EnvironmentName); Log.Information("애플리케이션 시작: {Environment}", app.Environment.EnvironmentName);
if (!app.Environment.IsDevelopment())
{
// 배포 완료 알림을 백그라운드에서 비동기 전송 (앱 시작 블록 방지)
_ = Task.Run(async () =>
{
try
{
using (var scope = app.Services.CreateScope())
{
var telegramService = scope.ServiceProvider.GetRequiredService<ITelegramNotificationService>();
await telegramService.SendSystemNotificationAsync(
$"✅ 배포 완료\n\n환경: {app.Environment.EnvironmentName}\n상태: 정상 운영 중");
}
}
catch (Exception ex)
{
Log.Error(ex, "배포 완료 알림 전송 실패");
}
});
}
app.Run(); app.Run();
} }
catch (Exception ex) catch (Exception ex)
@@ -13,6 +13,7 @@ public interface ITelegramNotificationService
Task SendInfoAsync(string title, string message, CancellationToken ct = default); Task SendInfoAsync(string title, string message, CancellationToken ct = default);
Task SendInquiryNotificationAsync(string message, CancellationToken ct = default); Task SendInquiryNotificationAsync(string message, CancellationToken ct = default);
Task SendSystemNotificationAsync(string message, CancellationToken ct = default); Task SendSystemNotificationAsync(string message, CancellationToken ct = default);
Task SendReportAsync(string reportTitle, string reportContent, CancellationToken ct = default);
} }
public class TelegramNotificationService : ITelegramNotificationService public class TelegramNotificationService : ITelegramNotificationService
@@ -96,4 +97,10 @@ public class TelegramNotificationService : ITelegramNotificationService
var text = $"<b>️ {title}</b>\n\n{message}\n\n<i>{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC</i>"; var text = $"<b>️ {title}</b>\n\n{message}\n\n<i>{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC</i>";
await SendMessageAsync(text, ct); await SendMessageAsync(text, ct);
} }
public async Task SendReportAsync(string reportTitle, string reportContent, CancellationToken ct = default)
{
var text = $"<b>📊 {reportTitle}</b>\n\n{reportContent}\n\n<i>{DateTime.UtcNow:yyyy-MM-dd HH:mm:ss} UTC</i>";
await SendToChat(_systemChatId, text, ct);
}
} }
@@ -48,7 +48,7 @@ public class TelegramReportBackgroundService(
var telegram = scope.ServiceProvider.GetRequiredService<ITelegramNotificationService>(); var telegram = scope.ServiceProvider.GetRequiredService<ITelegramNotificationService>();
var report = await reportService.BuildDailyReportAsync(date, ct); var report = await reportService.BuildDailyReportAsync(date, ct);
await telegram.SendSystemNotificationAsync(TelegramReportService.FormatDailyMessage(report), ct); await telegram.SendReportAsync("일간 세무/상담 현황 리포트", TelegramReportService.FormatDailyMessage(report), ct);
_lastDailyReportDate = date; _lastDailyReportDate = date;
logger.LogInformation("Daily telegram report sent for {Date}", date); logger.LogInformation("Daily telegram report sent for {Date}", date);
} }
@@ -63,7 +63,7 @@ public class TelegramReportBackgroundService(
var telegram = scope.ServiceProvider.GetRequiredService<ITelegramNotificationService>(); var telegram = scope.ServiceProvider.GetRequiredService<ITelegramNotificationService>();
var report = await reportService.BuildWeeklyReportAsync(weekStart, ct); var report = await reportService.BuildWeeklyReportAsync(weekStart, ct);
await telegram.SendSystemNotificationAsync(TelegramReportService.FormatWeeklyMessage(report), ct); await telegram.SendReportAsync("주간 세무/매출 종합 리포트", TelegramReportService.FormatWeeklyMessage(report), ct);
_lastWeeklyReportWeekStart = weekStart; _lastWeeklyReportWeekStart = weekStart;
logger.LogInformation("Weekly telegram report sent for {WeekStart}", weekStart); logger.LogInformation("Weekly telegram report sent for {WeekStart}", weekStart);
} }
+3 -3
View File
@@ -92,10 +92,10 @@ test.describe('admin CRM pages', () => {
await navigateInBlazor(page, `${baseUrl}/admin/tax-profiles`); await navigateInBlazor(page, `${baseUrl}/admin/tax-profiles`);
const addButton = page.getByRole('button', { name: /새 프로필 추가/ }); const addButton = page.getByRole('button', { name: /새 프로필 추가/ });
await expect(addButton).toBeVisible();
await addButton.click(); await addButton.click();
await expect(page).toHaveURL(/\/taxbaik\/admin\/tax-profiles$/);
await expect(page.getByText('새 세무 프로필 추가', { exact: true })).toBeVisible({ timeout: 10_000 }); await expect(addButton).toBeVisible();
await expect(page.getByRole('button', { name: '취소' }).last()).toBeVisible({ timeout: 5_000 });
}); });
test('No console errors on CRM page navigation', async ({ page }) => { test('No console errors on CRM page navigation', async ({ page }) => {
+2
View File
@@ -16,6 +16,7 @@ test.describe('contact submit', () => {
email: `public-${stamp}@example.com`, email: `public-${stamp}@example.com`,
serviceType: '기타', serviceType: '기타',
message: 'Playwright로 전송한 공개 문의 테스트입니다.', message: 'Playwright로 전송한 공개 문의 테스트입니다.',
suppressNotification: true,
}, },
}); });
expect(createResponse.ok()).toBeTruthy(); expect(createResponse.ok()).toBeTruthy();
@@ -39,6 +40,7 @@ test.describe('contact submit', () => {
email, email,
serviceType: '기타', serviceType: '기타',
message, message,
suppressNotification: true,
}, },
}); });
expect(createResponse.ok()).toBeTruthy(); expect(createResponse.ok()).toBeTruthy();
+1
View File
@@ -20,6 +20,7 @@ test.describe('inquiry detail', () => {
email, email,
serviceType: '기타', serviceType: '기타',
message, message,
suppressNotification: true,
}, },
}); });
expect(createResponse.ok()).toBeTruthy(); expect(createResponse.ok()).toBeTruthy();
+1
View File
@@ -16,6 +16,7 @@ test.describe('public smoke', () => {
await page.goto(`${baseUrl}/contact`); await page.goto(`${baseUrl}/contact`);
await expect(page).toHaveTitle(/상담 신청/); await expect(page).toHaveTitle(/상담 신청/);
await expect(page.getByRole('heading', { name: /상담 신청/ })).toBeVisible(); await expect(page.getByRole('heading', { name: /상담 신청/ })).toBeVisible();
await expect(page.getByRole('link', { name: /뒤로가기/ })).toBeVisible();
await expect(page.getByRole('button', { name: /상담신청/ })).toBeVisible(); await expect(page.getByRole('button', { name: /상담신청/ })).toBeVisible();
}); });
}); });