7dd51a1169
TaxBaik CI/CD / build-and-deploy (push) Successful in 48s
Architecture: - Create companies table with company_code as unique identifier - Add company_id foreign key to admin_users for multi-tenant support - Implement backward compatibility with DEFAULT company for existing users Core Components: - Company entity with full CRUD operations - ICompanyRepository interface following Repository pattern - CompanyRepository with Dapper implementation - CompanyService with business logic and validation - CompanyController with REST API endpoints Admin UI: - CompanyForm reusable component (Create/Edit pattern) - CompanyList.razor with pagination and company overview - CompanyCreate.razor for registering new companies - CompanyEdit.razor for managing existing companies with delete - All pages follow admin-page-hero pattern for consistency SOLID Principles: - Single Responsibility: Each component has one reason to change - Open/Closed: Extensible without modifying existing code - Interface Segregation: Clean repository and service contracts - Dependency Inversion: All layers depend on abstractions Database Migration (V014): - Creates companies table with active/inactive status - Assigns existing admin users to DEFAULT company - Provides foundation for role-based access control Future Enhancement: - Admin users can belong to specific companies - Data filtering based on company_id (multi-tenant isolation) - Company-based permission model
83 lines
3.4 KiB
C#
83 lines
3.4 KiB
C#
namespace TaxBaik.Infrastructure.Repositories;
|
|
|
|
using Dapper;
|
|
using TaxBaik.Domain.Entities;
|
|
using TaxBaik.Domain.Interfaces;
|
|
|
|
public class CompanyRepository(IDbConnectionFactory connectionFactory) : BaseRepository(connectionFactory), ICompanyRepository
|
|
{
|
|
public async Task<int> CreateAsync(Company company, CancellationToken cancellationToken = default)
|
|
{
|
|
using var conn = Conn();
|
|
return await conn.QueryFirstAsync<int>(
|
|
@"INSERT INTO companies (company_code, company_name, contact_person, phone, email, memo, is_active, created_at, updated_at)
|
|
VALUES (@CompanyCode, @CompanyName, @ContactPerson, @Phone, @Email, @Memo, @IsActive, NOW(), NOW())
|
|
RETURNING id",
|
|
company);
|
|
}
|
|
|
|
public async Task<Company?> GetByIdAsync(int id, CancellationToken cancellationToken = default)
|
|
{
|
|
using var conn = Conn();
|
|
return await conn.QueryFirstOrDefaultAsync<Company>(
|
|
@"SELECT id, company_code, company_name, contact_person, phone, email, memo, is_active, created_at, updated_at
|
|
FROM companies WHERE id = @Id",
|
|
new { Id = id });
|
|
}
|
|
|
|
public async Task<Company?> GetByCodeAsync(string code, CancellationToken cancellationToken = default)
|
|
{
|
|
using var conn = Conn();
|
|
return await conn.QueryFirstOrDefaultAsync<Company>(
|
|
@"SELECT id, company_code, company_name, contact_person, phone, email, memo, is_active, created_at, updated_at
|
|
FROM companies WHERE company_code = @Code",
|
|
new { Code = code });
|
|
}
|
|
|
|
public async Task<IEnumerable<Company>> GetAllActiveAsync(CancellationToken cancellationToken = default)
|
|
{
|
|
using var conn = Conn();
|
|
return await conn.QueryAsync<Company>(
|
|
@"SELECT id, company_code, company_name, contact_person, phone, email, memo, is_active, created_at, updated_at
|
|
FROM companies WHERE is_active = TRUE ORDER BY company_name");
|
|
}
|
|
|
|
public async Task<(IEnumerable<Company> Items, int Total)> GetPagedAsync(int page, int pageSize, CancellationToken cancellationToken = default)
|
|
{
|
|
using var conn = Conn();
|
|
var offset = (page - 1) * pageSize;
|
|
|
|
using var reader = await conn.QueryMultipleAsync(
|
|
@"SELECT id, company_code, company_name, contact_person, phone, email, memo, is_active, created_at, updated_at
|
|
FROM companies
|
|
ORDER BY company_name
|
|
LIMIT @PageSize OFFSET @Offset;
|
|
|
|
SELECT COUNT(*) FROM companies;",
|
|
new { PageSize = pageSize, Offset = offset });
|
|
|
|
var items = (await reader.ReadAsync<Company>()).ToList();
|
|
var total = await reader.ReadFirstAsync<int>();
|
|
|
|
return (items, total);
|
|
}
|
|
|
|
public async Task UpdateAsync(Company company, CancellationToken cancellationToken = default)
|
|
{
|
|
using var conn = Conn();
|
|
await conn.ExecuteAsync(
|
|
@"UPDATE companies
|
|
SET company_code = @CompanyCode, company_name = @CompanyName,
|
|
contact_person = @ContactPerson, phone = @Phone, email = @Email,
|
|
memo = @Memo, is_active = @IsActive, updated_at = NOW()
|
|
WHERE id = @Id",
|
|
company);
|
|
}
|
|
|
|
public async Task DeleteAsync(int id, CancellationToken cancellationToken = default)
|
|
{
|
|
using var conn = Conn();
|
|
await conn.ExecuteAsync("DELETE FROM companies WHERE id = @Id", new { Id = id });
|
|
}
|
|
}
|