feat: WBS-CRM-01 고객 카드 (Client Card) Phase 1 구현
DB: - V006__CreateClients.sql: clients 테이블 (name, company_name, phone, email, service_type, tax_type, status, source, memo) Domain: - Client 엔티티 - IClientRepository (GetPagedAsync 이름/연락처/회사명 검색 + 상태 필터) Infrastructure: - ClientRepository: ILIKE 검색, 페이징, CRUD Application: - ClientService: ServiceTypes/TaxTypes/Sources 상수 정의 - CreateClientDto Admin UI: - ClientList.razor: 검색바 + 상태 필터 + 페이징 테이블 - ClientEdit.razor: 기본정보/세무정보/관리정보 섹션 폼 - MainLayout: 고객 관리 NavGroup 추가, 홈페이지 메뉴 그룹화 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,7 @@ public static class DependencyInjection
|
||||
services.AddScoped<IInquiryRepository, InquiryRepository>();
|
||||
services.AddScoped<ISiteSettingRepository, SiteSettingRepository>();
|
||||
services.AddScoped<IAnnouncementRepository, AnnouncementRepository>();
|
||||
services.AddScoped<IClientRepository, ClientRepository>();
|
||||
|
||||
return services;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
namespace TaxBaik.Infrastructure.Repositories;
|
||||
|
||||
using Dapper;
|
||||
using TaxBaik.Domain.Entities;
|
||||
using TaxBaik.Domain.Interfaces;
|
||||
|
||||
public class ClientRepository(IDbConnectionFactory connectionFactory) : BaseRepository(connectionFactory), IClientRepository
|
||||
{
|
||||
private const string SelectColumns =
|
||||
"id, name, company_name, phone, email, service_type, tax_type, status, source, memo, created_at, updated_at";
|
||||
|
||||
public async Task<(IEnumerable<Client> Items, int Total)> GetPagedAsync(
|
||||
int page, int pageSize, string? status = null, string? search = null, CancellationToken ct = default)
|
||||
{
|
||||
using var conn = Conn();
|
||||
var offset = (page - 1) * pageSize;
|
||||
|
||||
using var reader = await conn.QueryMultipleAsync(
|
||||
$@"SELECT {SelectColumns} FROM clients
|
||||
WHERE (@Status::text IS NULL OR status = @Status)
|
||||
AND (@Search::text IS NULL OR name ILIKE @SearchLike OR phone ILIKE @SearchLike OR company_name ILIKE @SearchLike)
|
||||
ORDER BY created_at DESC
|
||||
LIMIT @PageSize OFFSET @Offset;
|
||||
|
||||
SELECT COUNT(*) FROM clients
|
||||
WHERE (@Status::text IS NULL OR status = @Status)
|
||||
AND (@Search::text IS NULL OR name ILIKE @SearchLike OR phone ILIKE @SearchLike OR company_name ILIKE @SearchLike);",
|
||||
new { Status = status, Search = search, SearchLike = string.IsNullOrEmpty(search) ? null : $"%{search}%", PageSize = pageSize, Offset = offset });
|
||||
|
||||
var items = (await reader.ReadAsync<Client>()).ToList();
|
||||
var total = await reader.ReadFirstAsync<int>();
|
||||
return (items, total);
|
||||
}
|
||||
|
||||
public async Task<Client?> GetByIdAsync(int id, CancellationToken ct = default)
|
||||
{
|
||||
using var conn = Conn();
|
||||
return await conn.QueryFirstOrDefaultAsync<Client>(
|
||||
$"SELECT {SelectColumns} FROM clients WHERE id = @Id",
|
||||
new { Id = id });
|
||||
}
|
||||
|
||||
public async Task<int> CreateAsync(Client client, CancellationToken ct = default)
|
||||
{
|
||||
using var conn = Conn();
|
||||
return await conn.QueryFirstAsync<int>(
|
||||
@"INSERT INTO clients (name, company_name, phone, email, service_type, tax_type, status, source, memo, created_at, updated_at)
|
||||
VALUES (@Name, @CompanyName, @Phone, @Email, @ServiceType, @TaxType, @Status, @Source, @Memo, NOW(), NOW())
|
||||
RETURNING id",
|
||||
client);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(Client client, CancellationToken ct = default)
|
||||
{
|
||||
using var conn = Conn();
|
||||
await conn.ExecuteAsync(
|
||||
@"UPDATE clients
|
||||
SET name = @Name, company_name = @CompanyName, phone = @Phone, email = @Email,
|
||||
service_type = @ServiceType, tax_type = @TaxType, status = @Status,
|
||||
source = @Source, memo = @Memo, updated_at = NOW()
|
||||
WHERE id = @Id",
|
||||
client);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id, CancellationToken ct = default)
|
||||
{
|
||||
using var conn = Conn();
|
||||
await conn.ExecuteAsync("DELETE FROM clients WHERE id = @Id", new { Id = id });
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user