# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Build & Run ```bash dotnet build BMA.EHR.Recruit.Service.sln dotnet build BMA.EHR.Recruit.Service.sln -c Release dotnet publish -c Release -o /app/publish /p:UseAppHost=false ``` No test projects exist in this solution. ## Architecture **Stack:** ASP.NET Core 7.0 Web API / EF Core 7.0 / MySQL (Pomelo) / MinIO (S3) / Keycloak (JWT) **Pattern:** Controllers → Services → EF Core DbContext (no repository layer for main data) ### Multiple DbContexts Three separate MySQL databases, each with its own `DbContext`: - `ApplicationDbContext` — Recruitment data (Recruits, Scores, Imports) - `OrgDbContext` — Organization data - `MetadataDbContext` — Metadata All registered as `Transient` lifetime. Auto-migration runs on startup. ### Controllers - `BaseController` provides standardized `Success()` / `Error()` response methods returning `ResponseObject` (Status, Message, Result) - `RecruitController` is the sole business controller (route: `api/v{version}/recruit`) - API versioning enabled via `Microsoft.AspNetCore.Mvc.Versioning` ### Background Import System Excel file imports run asynchronously through a Channel-based queue: - `ImportBackgroundService` (BackgroundService) — dequeues and processes jobs - `ImportJobQueue` — bounded Channel (capacity 100) - `ImportJobTracker` — in-memory ConcurrentDictionary tracking Four import types: `CandidateFile`, `CandidateFileById`, `ScoreFile`, `ResultFile` All imports use `EFCore.BulkExtensions.MySql` (v6.7.16) for bulk operations to handle 50,000+ rows without memory issues. Pattern: 1. Insert parent/history entities via `SaveChangesAsync` (small operations) 2. `ChangeTracker.Clear()` to release references 3. Collect entities into separate `List` per table 4. `BulkInsertAsync` with `SetOutputIdentity = true` for parent entities 5. Assign generated Ids to child entities 6. `BulkInsertAsync` for each child entity table separately 7. Batch size: 500 ### Entity Models All entities inherit from `EntityBase` (Guid `Id` PK, audit fields: `CreatedAt`, `CreatedUserId`, `LastUpdatedAt`, etc.). Models are in `Models/` with subdirectories: `Recruits/`, `Documents/`, `HR/`, `MetaData/`, `Placement/`. Key relationships use navigation properties without explicit FK properties (EF shadow properties). Configured via fluent API in `OnModelCreating`. ### External Services - **Authentication:** Keycloak JWT Bearer (`hrmsbkk-id.case-collection.com/realms/hrms`) - **File Storage:** MinIO via `MinIOService` (AWS S3 SDK) - **Search/Logging:** Elasticsearch (NEST client) - **Excel:** EPPlus for reading import files ## Key Files - `Program.cs` — Service registration, middleware pipeline, auto-migration - `Data/ApplicationDbContext.cs` — EF Core fluent API relationship configuration - `Services/ImportBackgroundService.cs` — All bulk import logic (4 import methods) - `Services/RecruitService.cs` — Core business logic - `Controllers/BaseController.cs` — Standard response helpers - `Responses/ResponseObject.cs` — Standard API response envelope ## Conventions - Language: C# with nullable reference types enabled - Naming: PascalCase properties, `_camelCase` parameters in service methods - API responses: Always wrapped in `ResponseObject` - Authorization: `[Authorize]` on controllers, user context from JWT claims