Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3c8f30af6d | |||
| 7e3b4e2229 | |||
| 67bd5dc666 | |||
| 84161ee2d9 | |||
| 5aec36b155 | |||
| 3ab8971025 | |||
| db30e71e0a |
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 }) => {
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ test.describe('inquiry detail', () => {
|
|||||||
email,
|
email,
|
||||||
serviceType: '기타',
|
serviceType: '기타',
|
||||||
message,
|
message,
|
||||||
|
suppressNotification: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(createResponse.ok()).toBeTruthy();
|
expect(createResponse.ok()).toBeTruthy();
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user