Files
taxbaik/TaxBaik.Web/Services/AdminClients/ITaxProfileBrowserClient.cs
T
kjh2064 a16438dcc6
TaxBaik CI/CD / build-and-deploy (push) Failing after 27s
feat: Phase 5 Browser Clients and deployment notification strategy
Phase 5: Tax & CRM Browser Clients
- 5 API client interfaces (TaxProfile, Filing, Activity, Contract, Revenue)
- Automatic token refresh for all clients
- Error logging with fallback empty lists
- Program.cs DI registration

Telegram Deployment Notifications:
- System chat (-5585148480): deployment success/failure
- Inquiry chat (-5434691215): customer inquiries
- Login alerts disabled (spam prevention)

Architecture:
Blazor -> BrowserClient (HttpClient+TokenRefresh) -> API -> Services -> DB

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-06-28 17:26:28 +09:00

141 lines
5.3 KiB
C#

namespace TaxBaik.Web.Services.AdminClients;
using System.Text.Json;
using TaxBaik.Domain.Entities;
public interface ITaxProfileBrowserClient
{
Task<List<TaxProfile>> GetAllAsync(CancellationToken ct = default);
Task<TaxProfile?> GetByIdAsync(int id, CancellationToken ct = default);
Task<List<TaxProfile>> GetByClientIdAsync(int clientId, CancellationToken ct = default);
Task<List<TaxProfile>> GetHighRiskProfilesAsync(CancellationToken ct = default);
Task<List<TaxProfile>> GetUpcomingFilingDuesAsync(int daysAhead = 30, CancellationToken ct = default);
Task<int> CreateAsync(int clientId, string businessType, string? businessRegistration = null,
string? accountingMethod = null, DateTime? establishmentDate = null, CancellationToken ct = default);
Task UpdateAsync(int id, string? businessType = null, string? accountingMethod = null,
DateTime? nextFilingDueDate = null, string? taxRiskLevel = null, CancellationToken ct = default);
Task DeleteAsync(int id, CancellationToken ct = default);
}
public class TaxProfileBrowserClient(HttpClient httpClient, ILogger<TaxProfileBrowserClient> logger) : ITaxProfileBrowserClient
{
private const string BaseUrl = "/api/taxprofile";
public async Task<List<TaxProfile>> GetAllAsync(CancellationToken ct = default)
{
try
{
return await httpClient.GetFromJsonAsync<List<TaxProfile>>($"{BaseUrl}", ct) ?? [];
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to get tax profiles");
return [];
}
}
public async Task<TaxProfile?> GetByIdAsync(int id, CancellationToken ct = default)
{
try
{
return await httpClient.GetFromJsonAsync<TaxProfile>($"{BaseUrl}/{id}", ct);
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to get tax profile {Id}", id);
return null;
}
}
public async Task<List<TaxProfile>> GetByClientIdAsync(int clientId, CancellationToken ct = default)
{
try
{
return await httpClient.GetFromJsonAsync<List<TaxProfile>>($"{BaseUrl}/client/{clientId}", ct) ?? [];
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to get tax profiles for client {ClientId}", clientId);
return [];
}
}
public async Task<List<TaxProfile>> GetHighRiskProfilesAsync(CancellationToken ct = default)
{
try
{
var response = await httpClient.GetFromJsonAsync<JsonElement>($"{BaseUrl}/high-risk", ct);
if (response.TryGetProperty("data", out var data))
return System.Text.Json.JsonSerializer.Deserialize<List<TaxProfile>>(data.GetRawText()) ?? [];
return [];
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to get high-risk profiles");
return [];
}
}
public async Task<List<TaxProfile>> GetUpcomingFilingDuesAsync(int daysAhead = 30, CancellationToken ct = default)
{
try
{
var response = await httpClient.GetFromJsonAsync<JsonElement>($"{BaseUrl}/upcoming-filings?daysAhead={daysAhead}", ct);
if (response.TryGetProperty("data", out var data))
return System.Text.Json.JsonSerializer.Deserialize<List<TaxProfile>>(data.GetRawText()) ?? [];
return [];
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to get upcoming filings");
return [];
}
}
public async Task<int> CreateAsync(int clientId, string businessType, string? businessRegistration = null,
string? accountingMethod = null, DateTime? establishmentDate = null, CancellationToken ct = default)
{
try
{
var request = new { clientId, businessType, businessRegistration, accountingMethod, establishmentDate };
var response = await httpClient.PostAsJsonAsync(BaseUrl, request, ct);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<dynamic>(cancellationToken: ct);
return result?["id"]?.ToObject<int>() ?? 0;
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to create tax profile");
return 0;
}
}
public async Task UpdateAsync(int id, string? businessType = null, string? accountingMethod = null,
DateTime? nextFilingDueDate = null, string? taxRiskLevel = null, CancellationToken ct = default)
{
try
{
var request = new { businessType, accountingMethod, nextFilingDueDate, taxRiskLevel };
var response = await httpClient.PutAsJsonAsync($"{BaseUrl}/{id}", request, ct);
response.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to update tax profile {Id}", id);
}
}
public async Task DeleteAsync(int id, CancellationToken ct = default)
{
try
{
var response = await httpClient.DeleteAsync($"{BaseUrl}/{id}", ct);
response.EnsureSuccessStatusCode();
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to delete tax profile {Id}", id);
}
}
}