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:
@@ -0,0 +1,69 @@
|
||||
namespace TaxBaik.Application.Services;
|
||||
|
||||
using TaxBaik.Application.DTOs;
|
||||
using TaxBaik.Domain.Entities;
|
||||
using TaxBaik.Domain.Interfaces;
|
||||
|
||||
public class ClientService(IClientRepository repository)
|
||||
{
|
||||
public static readonly string[] ServiceTypes =
|
||||
["기장", "부동산", "증여·상속", "종합소득세", "법인세", "부가가치세", "기타"];
|
||||
|
||||
public static readonly string[] TaxTypes =
|
||||
["개인사업자", "법인사업자", "면세사업자", "근로소득자", "기타"];
|
||||
|
||||
public static readonly string[] Sources =
|
||||
["홈페이지 문의", "소개", "직접 방문", "카카오 채널", "블로그", "기타"];
|
||||
|
||||
public async Task<(IEnumerable<Client> Items, int Total)> GetPagedAsync(
|
||||
int page, int pageSize, string? status = null, string? search = null, CancellationToken ct = default) =>
|
||||
await repository.GetPagedAsync(Math.Max(1, page), Math.Clamp(pageSize, 1, 100), status, search, ct);
|
||||
|
||||
public async Task<Client?> GetByIdAsync(int id, CancellationToken ct = default) =>
|
||||
await repository.GetByIdAsync(id, ct);
|
||||
|
||||
public async Task<int> CreateAsync(CreateClientDto dto, CancellationToken ct = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dto.Name))
|
||||
throw new ValidationException("고객명을 입력하세요.");
|
||||
|
||||
var client = new Client
|
||||
{
|
||||
Name = dto.Name.Trim(),
|
||||
CompanyName = dto.CompanyName?.Trim(),
|
||||
Phone = dto.Phone?.Trim(),
|
||||
Email = dto.Email?.Trim(),
|
||||
ServiceType = dto.ServiceType,
|
||||
TaxType = dto.TaxType,
|
||||
Status = dto.Status,
|
||||
Source = dto.Source,
|
||||
Memo = dto.Memo?.Trim()
|
||||
};
|
||||
|
||||
return await repository.CreateAsync(client, ct);
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(int id, CreateClientDto dto, CancellationToken ct = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(dto.Name))
|
||||
throw new ValidationException("고객명을 입력하세요.");
|
||||
|
||||
var client = await repository.GetByIdAsync(id, ct)
|
||||
?? throw new KeyNotFoundException($"고객 ID {id}를 찾을 수 없습니다.");
|
||||
|
||||
client.Name = dto.Name.Trim();
|
||||
client.CompanyName = dto.CompanyName?.Trim();
|
||||
client.Phone = dto.Phone?.Trim();
|
||||
client.Email = dto.Email?.Trim();
|
||||
client.ServiceType = dto.ServiceType;
|
||||
client.TaxType = dto.TaxType;
|
||||
client.Status = dto.Status;
|
||||
client.Source = dto.Source;
|
||||
client.Memo = dto.Memo?.Trim();
|
||||
|
||||
await repository.UpdateAsync(client, ct);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int id, CancellationToken ct = default) =>
|
||||
await repository.DeleteAsync(id, ct);
|
||||
}
|
||||
Reference in New Issue
Block a user