source: Add rules for AI Coding

This commit is contained in:
2026-03-09 16:51:44 +07:00
parent 4b7236493f
commit 3003a0ff0b
27 changed files with 2103 additions and 70 deletions

View File

@@ -3,22 +3,22 @@ FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
WORKDIR /src
# Copy csproj files and restore
COPY ["iYHCT360.Domain/iYHCT360.Domain.csproj", "iYHCT360.Domain/"]
COPY ["iYHCT360.Contracts/iYHCT360.Contracts.csproj", "iYHCT360.Contracts/"]
COPY ["iYHCT360.Application/iYHCT360.Application.csproj", "iYHCT360.Application/"]
COPY ["iYHCT360.Infrastructure/iYHCT360.Infrastructure.csproj", "iYHCT360.Infrastructure/"]
COPY ["iYHCT360.WebAPI/iYHCT360.WebAPI.csproj", "iYHCT360.WebAPI/"]
COPY ["MyNewProjectName.Domain/MyNewProjectName.Domain.csproj", "MyNewProjectName.Domain/"]
COPY ["MyNewProjectName.Contracts/MyNewProjectName.Contracts.csproj", "MyNewProjectName.Contracts/"]
COPY ["MyNewProjectName.Application/MyNewProjectName.Application.csproj", "MyNewProjectName.Application/"]
COPY ["MyNewProjectName.Infrastructure/MyNewProjectName.Infrastructure.csproj", "MyNewProjectName.Infrastructure/"]
COPY ["MyNewProjectName.WebAPI/MyNewProjectName.WebAPI.csproj", "MyNewProjectName.WebAPI/"]
RUN dotnet restore "iYHCT360.WebAPI/iYHCT360.WebAPI.csproj"
RUN dotnet restore "MyNewProjectName.WebAPI/MyNewProjectName.WebAPI.csproj"
# Copy everything else and build
COPY . .
WORKDIR "/src/iYHCT360.WebAPI"
RUN dotnet build "iYHCT360.WebAPI.csproj" -c Release -o /app/build
WORKDIR "/src/MyNewProjectName.WebAPI"
RUN dotnet build "MyNewProjectName.WebAPI.csproj" -c Release -o /app/build
# Publish stage
FROM build AS publish
RUN dotnet publish "iYHCT360.WebAPI.csproj" -c Release -o /app/publish /p:UseAppHost=false
RUN dotnet publish "MyNewProjectName.WebAPI.csproj" -c Release -o /app/publish /p:UseAppHost=false
# Final stage
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
@@ -27,4 +27,4 @@ EXPOSE 8080
EXPOSE 8081
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "iYHCT360.WebAPI.dll"]
ENTRYPOINT ["dotnet", "MyNewProjectName.WebAPI.dll"]

View File

@@ -0,0 +1,239 @@
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Reflection;
using System.Text;
using System.Text.Json;
using MyNewProjectName.Infrastructure.Options;
using MyNewProjectName.Contracts.Common;
namespace MyNewProjectName.WebAPI.Extensions;
/// <summary>
/// Extension methods for configuring services in the dependency injection container
/// </summary>
public static class ServiceCollectionExtensions
{
/// <summary>
/// Configures Swagger/OpenAPI documentation with JWT Bearer authentication support
/// </summary>
public static IServiceCollection AddCustomSwagger(this IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "MyNewProjectName API", Version = "v1" });
var securityScheme = new OpenApiSecurityScheme
{
Name = "Authorization",
Description = "Enter JWT Bearer token",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT"
};
c.AddSecurityDefinition("Bearer", securityScheme);
var securityRequirement = new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
};
c.AddSecurityRequirement(securityRequirement);
// Set the comments path for the Swagger JSON and UI (only if XML file exists)
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
if (File.Exists(xmlPath))
{
c.IncludeXmlComments(xmlPath);
}
});
return services;
}
/// <summary>
/// Configures JWT Bearer authentication with custom token validation and error handling
/// </summary>
public static IServiceCollection AddJwtAuthentication(this IServiceCollection services, IConfiguration configuration)
{
var jwtOptionsSection = configuration.GetSection(JwtOptions.SectionName);
var jwtOptions = jwtOptionsSection.Get<JwtOptions>()
?? throw new InvalidOperationException("JwtOptions configuration is missing");
services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateIssuerSigningKey = true,
ValidateLifetime = true,
ValidIssuer = jwtOptions.Issuer,
ValidAudience = jwtOptions.Audience,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtOptions.Secret)),
ClockSkew = TimeSpan.Zero
};
// Log token & claims for debugging
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILoggerFactory>()
.CreateLogger("JwtBearer");
// Có thể bật lại nếu cần debug Authorization header
// logger.LogInformation("JWT received. Authorization header: {Header}", context.Request.Headers["Authorization"].ToString());
return Task.CompletedTask;
},
OnTokenValidated = context =>
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILoggerFactory>()
.CreateLogger("JwtBearer");
var claims = context.Principal?.Claims
.Select(c => $"{c.Type}={c.Value}")
.ToList() ?? new List<string>();
// logger.LogInformation("JWT validated. Claims: {Claims}", string.Join(", ", claims));
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILoggerFactory>()
.CreateLogger("JwtBearer");
logger.LogWarning(context.Exception, "JWT authentication failed");
return Task.CompletedTask;
},
OnChallenge = async context =>
{
// Ngăn không cho middleware mặc định ghi response lần nữa
context.HandleResponse();
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILoggerFactory>()
.CreateLogger("JwtBearer");
if (context.Response.HasStarted)
{
logger.LogWarning("The response has already started, the JWT challenge middleware will not be executed.");
return;
}
var correlationId = context.HttpContext.Request.Headers["X-Correlation-ID"].FirstOrDefault() ?? "unknown";
var isExpired = context.AuthenticateFailure is SecurityTokenExpiredException;
var message = isExpired
? "Token expired"
: "Unauthorized";
logger.LogInformation("JWT challenge. Expired: {Expired}, Error: {Error}, Description: {Description}, CorrelationId: {CorrelationId}",
isExpired, context.Error, context.ErrorDescription, correlationId);
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
context.Response.ContentType = "application/json";
var response = new ServiceResponse<object>
{
Success = false,
Message = message,
Errors = null
};
var json = JsonSerializer.Serialize(response, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
await context.Response.WriteAsync(json);
},
OnForbidden = async context =>
{
var logger = context.HttpContext.RequestServices
.GetRequiredService<ILoggerFactory>()
.CreateLogger("JwtBearer");
logger.LogWarning("User is authenticated but does not have access to the requested resource (403 Forbidden).");
context.Response.StatusCode = StatusCodes.Status403Forbidden;
context.Response.ContentType = "application/json";
var response = new ServiceResponse<object>
{
Success = false,
Message = "You do not have permission to perform this action.",
Errors = null
};
var json = JsonSerializer.Serialize(response, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
});
await context.Response.WriteAsync(json);
}
};
});
return services;
}
// /// <summary>
// /// Configures authorization policies based on user type and roles.
// /// </summary>
// public static IServiceCollection AddCustomAuthorization(this IServiceCollection services)
// {
// services.AddAuthorization(options =>
// {
// // 1. Policy chỉ dành cho SuperAdmin
// options.AddPolicy("RequireSuperAdmin", policy =>
// policy.RequireClaim("userType", "SuperAdmin"));
// // 2. Policy cho TenantAdmin (TenantMember + Role = TenantAdmin)
// options.AddPolicy("RequireTenantAdmin", policy =>
// policy.RequireClaim("userType", "TenantMember")
// .RequireRole("TenantAdmin"));
// // 3. Policy cho TenantMember (TenantMember + Role = Doctor)
// options.AddPolicy("RequireTenantMember", policy =>
// policy.RequireClaim("userType", "TenantMember")
// );
// // 4. Policy SuperAdmin hoặc TenantAdmin
// options.AddPolicy("RequireSuperAdminOrTenantAdmin", policy =>
// policy.RequireAssertion(context =>
// // Điều kiện 1: Là SuperAdmin
// context.User.HasClaim("userType", "SuperAdmin")
// ||
// // HOẶC Điều kiện 2: Là Nhân viên VÀ có Role là TenantAdmin
// (context.User.HasClaim("userType", "TenantMember") && context.User.IsInRole("TenantAdmin"))
// ));
// });
// return services;
// }
}

View File

@@ -6,6 +6,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="9.0.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
<!-- Serilog for structured logging -->

View File

@@ -1,75 +1,89 @@
using Microsoft.Extensions.Hosting;
using MyNewProjectName.Application;
using MyNewProjectName.Infrastructure;
using MyNewProjectName.Infrastructure.Extensions;
using MyNewProjectName.WebAPI.Middleware;
using Serilog;
using MyNewProjectName.WebAPI.Extensions;
using MyNewProjectName.Application.Interfaces;
using MyNewProjectName.WebAPI.Services;
var builder = WebApplication.CreateBuilder(args);
// Configure Serilog
builder.Host.UseSerilogLogging(builder.Configuration);
// Add OpenTelemetry distributed tracing
builder.Services.AddOpenTelemetryTracing("MyNewProjectName.WebAPI");
// Add services to the container.
builder.Services.AddControllers();
// Add Application Layer
builder.Services.AddApplication();
// Add Infrastructure Layer
builder.Services.AddInfrastructure(builder.Configuration);
// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi
builder.Services.AddOpenApi();
// Add Swagger
// ==========================================
// 1. ADD SERVICES TO CONTAINER
// ==========================================
builder.Services.AddControllers()
.AddJsonOptions(options =>
{
// Configure enum serialization to use string representation
options.JsonSerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter());
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddMemoryCache();
builder.Services.AddLogging();
// Extension Methods: Swagger, JWT Authentication & Authorization
builder.Services.AddCustomSwagger();
builder.Services.AddJwtAuthentication(builder.Configuration);
builder.Services.AddCustomAuthorization();
// Layers Registration
builder.Services.AddApplication();
builder.Services.AddInfrastructure(builder.Configuration);
builder.Services.AddHttpContextAccessor();
builder.Services.AddScoped<ICurrentUserService, CurrentUserService>();
// CORS Configuration
builder.Services.AddCors(option =>
{
option.AddPolicy(name: "CorsPolicy",
configurePolicy: builder => builder
.SetIsOriginAllowed((host) => true)
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
builder.WebHost.UseUrls("http://0.0.0.0:5044");
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi();
app.UseSwagger();
app.UseSwaggerUI(option =>
{
option.SwaggerEndpoint("/swagger/v1/swagger.json", "iYHCT360 API");
option.RoutePrefix = "swagger"; // UI tại /swagger
option.DisplayRequestDuration();
option.EnableFilter();
option.EnableValidator();
option.EnableTryItOutByDefault();
});
}
// Middleware pipeline order is critical:
// 1. CorrelationIdMiddleware - Must be first to track all requests
// Middleware Pipeline - Order is important!
// 1. CorrelationIdMiddleware: Generate correlation ID for request tracking (must be first)
app.UseMiddleware<CorrelationIdMiddleware>();
// 2. RequestResponseLoggingMiddleware - Optional, enable only when needed
// WARNING: This can generate large log files. Enable only for specific environments.
// Configure in appsettings.json: "Logging:EnableRequestLogging" and "Logging:EnableResponseLogging"
app.UseMiddleware<RequestResponseLoggingMiddleware>();
app.UseHttpsRedirection();
// 3. Authentication (built-in)
app.UseAuthentication();
// 4. Authorization (built-in)
app.UseAuthorization();
// 6. ExceptionHandlingMiddleware - Must be last to catch all exceptions
// 2. ExceptionHandlingMiddleware: Global exception handler (must be early to catch all exceptions)
app.UseMiddleware<ExceptionHandlingMiddleware>();
// 3. Routing & Static Files
app.UseRouting();
app.UseStaticFiles();
// 4. CORS (must be before UseAuthentication/UseAuthorization)
app.UseCors("CorsPolicy");
// 5. Authentication & Authorization
app.UseAuthentication();
app.UseAuthorization();
// 6. RequestResponseLoggingMiddleware: Log request/response (after routing and auth to avoid interfering with response)
app.UseMiddleware<RequestResponseLoggingMiddleware>();
// 7. Map Controllers
app.MapControllers();
try
{
Log.Information("Starting MyNewProjectName.WebAPI");
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application terminated unexpectedly");
throw;
}
finally
{
Log.CloseAndFlush();
}
app.Run();

View File

@@ -0,0 +1,334 @@
# Huong Dan Dat Ten Git Branch Trong Du An
> **Tham khao:** [Git Branch Naming Conventions - Codiga](https://codiga.io/blog/git-branch-naming-conventions)
---
## Muc Luc
1. [Nguyen Tac Chung](#1-nguyen-tac-chung)
2. [Cau Truc Ten Branch](#2-cau-truc-ten-branch)
3. [Cac Loai Branch (Branch Types)](#3-cac-loai-branch-branch-types)
4. [Bang Mau Ten Branch Theo Chuc Nang](#4-bang-mau-ten-branch-theo-chuc-nang)
5. [Quy Tac Dat Ten (Good Practices)](#5-quy-tac-dat-ten-good-practices)
6. [Mo Hinh Git Flow](#6-mo-hinh-git-flow)
7. [Vi Du Thuc Te Trong Du An](#7-vi-du-thuc-te-trong-du-an)
8. [Checklist Truoc Khi Tao Branch](#8-checklist-truoc-khi-tao-branch)
---
## 1. Nguyen Tac Chung
Theo bai viet tu Codiga, mot quy uoc dat ten branch tot giup:
| # | Loi ich | Mo ta |
|---|---------|-------|
| 1 | **Truy vet tac gia** | Biet ai da tao branch (developer nao) |
| 2 | **Lien ket voi issue tracker** | De dang trace branch voi task/ticket tren JIRA, Trello, GitHub Issues... |
| 3 | **Hieu muc dich branch** | Nhanh chong biet branch la bugfix, feature, hay hotfix |
| 4 | **To chuc workflow** | Giu cho quy trinh lam viec co trat tu va hieu qua |
---
## 2. Cau Truc Ten Branch
### Format chung
```
<type>/<ticket-id>-<short-description>
```
Trong do:
| Thanh phan | Bat buoc | Mo ta | Vi du |
|-----------|----------|-------|-------|
| `type` | Co | Loai branch (feature, bugfix, hotfix...) | `feature` |
| `ticket-id` | Co (neu co) | Ma ticket/issue tu issue tracker | `PROJ-1234` |
| `short-description` | Co | Mo ta ngan 3-6 tu, phan cach bang dau `-` | `add-user-authentication` |
### Vi du day du
```
feature/PROJ-1234-add-user-authentication
bugfix/PROJ-5678-fix-login-redirect
hotfix/PROJ-9012-patch-security-vulnerability
```
### Format mo rong (co ten tac gia)
Neu team co nhieu nguoi lam chung mot ticket, them ten tac gia:
```
<author>/<type>/<ticket-id>-<short-description>
```
Vi du:
```
julien/feature/1234-new-dashboard
david/feature/1234-new-dashboard
```
Dieu nay giup phan biet ro rang code cua tung developer cho cung mot task.
---
## 3. Cac Loai Branch (Branch Types)
### Branch chinh (Long-lived branches)
| Branch | Muc dich | Duoc merge tu | Ghi chu |
|--------|---------|---------------|---------|
| `main` (hoac `master`) | Code production, luon o trang thai stable | `release`, `hotfix` | Khong bao gio commit truc tiep |
| `develop` | Code moi nhat cho phien ban tiep theo | `feature`, `bugfix` | Nhanh tich hop chinh |
| `staging` | Moi truong test truoc khi len production | `develop` | Tuy chon, tuy du an |
### Branch tam thoi (Short-lived branches)
| Prefix | Muc dich | Tao tu | Merge vao | Vi du |
|--------|---------|--------|----------|-------|
| `feature/` | Tinh nang moi | `develop` | `develop` | `feature/PROJ-101-add-login-page` |
| `bugfix/` | Sua loi trong qua trinh phat trien | `develop` | `develop` | `bugfix/PROJ-202-fix-null-reference` |
| `hotfix/` | Sua loi khan cap tren production | `main` | `main` va `develop` | `hotfix/PROJ-303-fix-payment-crash` |
| `release/` | Chuan bi phien ban moi | `develop` | `main` va `develop` | `release/v1.2.0` |
| `chore/` | Cong viec bao tri, refactor, CI/CD | `develop` | `develop` | `chore/update-dependencies` |
| `docs/` | Cap nhat tai lieu | `develop` | `develop` | `docs/update-api-documentation` |
| `test/` | Viet test hoac cai thien test | `develop` | `develop` | `test/add-unit-tests-user-service` |
| `refactor/` | Tai cau truc code, khong thay doi chuc nang | `develop` | `develop` | `refactor/clean-up-user-repository` |
---
## 4. Bang Mau Ten Branch Theo Chuc Nang
### Authentication & Authorization
```
feature/PROJ-101-add-jwt-authentication
feature/PROJ-102-implement-refresh-token
feature/PROJ-103-add-role-based-access
bugfix/PROJ-104-fix-token-expiration
hotfix/PROJ-105-patch-auth-bypass
```
### CRUD Entity
```
feature/PROJ-201-create-product-entity
feature/PROJ-202-add-product-api-endpoints
feature/PROJ-203-implement-product-search
bugfix/PROJ-204-fix-product-update-validation
```
### Infrastructure & DevOps
```
chore/PROJ-301-setup-docker-compose
chore/PROJ-302-configure-ci-cd-pipeline
chore/PROJ-303-add-redis-caching
chore/PROJ-304-setup-logging-serilog
```
### Database & Migration
```
feature/PROJ-401-add-migration-user-table
feature/PROJ-402-seed-initial-data
bugfix/PROJ-403-fix-migration-conflict
```
### Documentation
```
docs/PROJ-501-update-readme
docs/PROJ-502-add-api-swagger-docs
docs/PROJ-503-create-deployment-guide
```
---
## 5. Quy Tac Dat Ten (Good Practices)
### Nen lam
| Quy tac | Chi tiet | Vi du |
|---------|---------|-------|
| **Dung ten mo ta** | Ten branch phai phan anh ro noi dung thay doi | `feature/PROJ-101-add-user-authentication` |
| **Giu ngan gon** | Chi 3-6 tu khoa, phan cach bang dau `-` | `bugfix/PROJ-202-fix-null-ref` |
| **Viet thuong toan bo** | Khong viet hoa | `feature/add-login` |
| **Dung dau `-` phan cach tu** | Khong dung dau cach, underscore, hoac camelCase | `fix-login-redirect` |
| **Bat dau bang type prefix** | Luon co prefix xac dinh loai branch | `feature/`, `bugfix/`, `hotfix/` |
| **Lien ket ticket ID** | Giup trace nguon goc thay doi | `PROJ-1234-...` |
### Khong nen lam
| Quy tac | Vi du sai | Vi du dung |
|---------|----------|-----------|
| **Khong dung ky tu dac biet** | `feature/add@user#auth` | `feature/add-user-auth` |
| **Khong dung dau cach** | `feature/add user auth` | `feature/add-user-auth` |
| **Khong viet hoa** | `Feature/Add-User-Auth` | `feature/add-user-auth` |
| **Khong dat ten chung chung** | `feature/new-stuff` | `feature/PROJ-101-add-payment-gateway` |
| **Khong dat ten qua dai** | `feature/PROJ-101-add-new-user-authentication-with-jwt-and-refresh-token-support-for-all-roles` | `feature/PROJ-101-add-jwt-auth` |
| **Khong dung so thuong** | `feature/123` | `feature/PROJ-123-add-login` |
| **Khong commit truc tiep vao main/develop** | — | Luon tao branch rieng |
---
## 6. Mo Hinh Git Flow
### So do tong quat
```
main (production)
|
|--- hotfix/PROJ-xxx-fix-critical-bug
| |
| v
| (merge vao main VA develop)
|
|--- release/v1.2.0
| |
| v
| (merge vao main VA develop)
|
develop (integration)
|
|--- feature/PROJ-xxx-new-feature
| |
| v
| (merge vao develop qua Pull Request)
|
|--- bugfix/PROJ-xxx-fix-bug
| |
| v
| (merge vao develop qua Pull Request)
|
|--- chore/update-packages
|
v
(merge vao develop qua Pull Request)
```
### Quy trinh lam viec
1. **Tao branch** tu `develop` (hoac `main` cho hotfix)
2. **Commit** thuong xuyen voi message ro rang
3. **Push** branch len remote
4. **Tao Pull Request** (PR) de review code
5. **Review & Approve** boi it nhat 1 thanh vien khac
6. **Merge** vao branch dich (squash merge hoac merge commit)
7. **Xoa branch** sau khi merge thanh cong
### Lenh Git mau
```bash
# Tao branch feature moi tu develop
git checkout develop
git pull origin develop
git checkout -b feature/PROJ-101-add-login-page
# Lam viec va commit
git add .
git commit -m "feat(PROJ-101): add login page UI"
# Push len remote
git push origin feature/PROJ-101-add-login-page
# Sau khi merge PR, xoa branch local
git checkout develop
git pull origin develop
git branch -d feature/PROJ-101-add-login-page
```
---
## 7. Vi Du Thuc Te Trong Du An
### Ap dung cho du an Clean Architecture (MyNewProjectName)
#### Sprint 1: Khoi tao du an
```bash
# Cau hinh co ban
chore/PROJ-001-setup-clean-architecture
chore/PROJ-002-configure-dependency-injection
chore/PROJ-003-setup-ef-core-database
chore/PROJ-004-add-serilog-logging
chore/PROJ-005-setup-docker-compose
```
#### Sprint 2: Authentication
```bash
# Tinh nang xac thuc
feature/PROJ-010-add-user-entity
feature/PROJ-011-implement-jwt-authentication
feature/PROJ-012-add-refresh-token-flow
feature/PROJ-013-implement-role-authorization
bugfix/PROJ-014-fix-token-validation-error
```
#### Sprint 3: CRUD cho SampleEntity
```bash
# Tinh nang CRUD
feature/PROJ-020-add-sample-entity-crud
feature/PROJ-021-add-pagination-support
feature/PROJ-022-implement-search-filter
bugfix/PROJ-023-fix-sample-delete-cascade
```
#### Hotfix khan cap
```bash
# Sua loi tren production
hotfix/PROJ-099-fix-sql-injection-vulnerability
hotfix/PROJ-100-patch-cors-configuration
```
### Quy uoc Commit Message (di kem voi branch)
De dong bo voi quy uoc branch, nen dung [Conventional Commits](https://www.conventionalcommits.org/):
```
<type>(<scope>): <description>
```
| Type | Muc dich | Vi du |
|------|---------|-------|
| `feat` | Tinh nang moi | `feat(PROJ-101): add login page` |
| `fix` | Sua loi | `fix(PROJ-202): resolve null reference in UserService` |
| `chore` | Bao tri | `chore: update NuGet packages` |
| `docs` | Tai lieu | `docs: update API documentation` |
| `refactor` | Tai cau truc | `refactor: simplify UserRepository queries` |
| `test` | Them/sua test | `test: add unit tests for AuthService` |
| `style` | Format code | `style: apply editorconfig rules` |
| `ci` | CI/CD | `ci: add GitHub Actions workflow` |
---
## 8. Checklist Truoc Khi Tao Branch
- [ ] Ten branch co bat dau bang type prefix khong? (`feature/`, `bugfix/`, `hotfix/`...)
- [ ] Ten branch co chua ticket/issue ID khong? (`PROJ-1234`)
- [ ] Mo ta co ngan gon va ro rang khong? (3-6 tu)
- [ ] Chi dung chu thuong, so, dau `-` va dau `/`?
- [ ] Khong co ky tu dac biet, dau cach, hoac chu viet hoa?
- [ ] Branch duoc tao tu dung branch nguon? (`develop` hoac `main`)
- [ ] Da pull code moi nhat tu branch nguon truoc khi tao?
---
## Tom Tat Nhanh
```
Format: <type>/<ticket-id>-<short-description>
Vi du: feature/PROJ-101-add-user-authentication
Type: feature | bugfix | hotfix | release | chore | docs | test | refactor
Chu y: - Viet thuong toan bo
- Dung dau `-` phan cach tu
- Giu ngan gon (3-6 tu)
- Khong ky tu dac biet
- Lien ket ticket ID
- Xoa branch sau khi merge
```

View File

@@ -0,0 +1,468 @@
# Huong Dan Viet Git Commit Message Trong Du An
> **Tham khao:** [Conventional Commits](https://www.conventionalcommits.org/)
---
## Muc Luc
1. [Nguyen Tac Chung](#1-nguyen-tac-chung)
2. [Cau Truc Commit Message](#2-cau-truc-commit-message)
3. [Cac Loai Type](#3-cac-loai-type)
4. [Scope - Pham Vi Thay Doi](#4-scope---pham-vi-thay-doi)
5. [Quy Tac Viet Description](#5-quy-tac-viet-description)
6. [Commit Message Voi Body Va Footer](#6-commit-message-voi-body-va-footer)
7. [Bang Vi Du Day Du](#7-bang-vi-du-day-du)
8. [Vi Du Thuc Te Trong Du An](#8-vi-du-thuc-te-trong-du-an)
9. [Nhung Loi Thuong Gap](#9-nhung-loi-thuong-gap)
10. [Checklist Truoc Khi Commit](#10-checklist-truoc-khi-commit)
---
## 1. Nguyen Tac Chung
Viet commit message chuan giup:
| # | Loi ich | Mo ta |
|---|---------|-------|
| 1 | **Doc lich su de dang** | Nhin vao git log biet ngay thay doi gi |
| 2 | **Tu dong tao changelog** | Cac tool co the tu dong tao changelog tu commit message |
| 3 | **Lien ket voi issue tracker** | De dang trace commit voi task/ticket |
| 4 | **Review code hieu qua** | Nguoi review hieu nhanh muc dich cua commit |
| 5 | **Tu dong versioning** | Xac dinh phien ban tu dong (semantic versioning) dua tren type |
---
## 2. Cau Truc Commit Message
### Format chung
```
<type>(<scope>): <description>
```
Trong do:
| Thanh phan | Bat buoc | Mo ta | Vi du |
|-----------|----------|-------|-------|
| `type` | Co | Loai thay doi (feat, fix, chore...) | `feat` |
| `scope` | Khong | Pham vi/module bi anh huong | `auth`, `api`, `user` |
| `description` | Co | Mo ta ngan, duoi 50 ky tu, viet hoa dau cau, khong dau cham cuoi | `add Google login` |
### Format day du (voi body va footer)
```
<type>(<scope>): <description>
<body>
<footer>
```
### Vi du nhanh
```
feat(auth): add Google login
fix(api): resolve 404 error
docs(readme): update install guide
chore: update dependencies
```
---
## 3. Cac Loai Type
### Type chinh (thuong dung)
| Type | Muc dich | Anh huong version | Vi du |
|------|---------|-------------------|-------|
| `feat` | Them tinh nang moi | MINOR (1.x.0) | `feat(auth): add Google OAuth` |
| `fix` | Sua loi | PATCH (1.0.x) | `fix(api): resolve 404 error` |
| `docs` | Cap nhat tai lieu | Khong | `docs(readme): update install guide` |
| `style` | Doi format/UI khong anh huong logic | Khong | `style: apply prettier formatting` |
| `refactor` | Tai cau truc code, khong thay doi chuc nang | Khong | `refactor: simplify UserService logic` |
| `perf` | Toi uu hieu nang | PATCH (1.0.x) | `perf: optimize database queries` |
| `test` | Them hoac sua test | Khong | `test: add unit tests for AuthService` |
| `chore` | Thay doi nho, bao tri, CI/CD | Khong | `chore: update dependencies` |
### Type bo sung (it dung hon)
| Type | Muc dich | Vi du |
|------|---------|-------|
| `build` | Thay doi build system hoac dependencies | `build: upgrade to .NET 8` |
| `ci` | Thay doi CI/CD pipeline | `ci: add GitHub Actions workflow` |
| `revert` | Hoan tac commit truoc do | `revert: revert feat(auth): add Google login` |
---
## 4. Scope - Pham Vi Thay Doi
Scope la phan **tuy chon** nam trong dau ngoac `()`, xac dinh module/file/module cu the bi anh huong.
### Danh sach scope khuyen nghi cho du an Clean Architecture
| Scope | Layer/Module | Vi du |
|-------|-------------|-------|
| `domain` | MyNewProjectName.Domain | `feat(domain): add Order entity` |
| `app` | MyNewProjectName.Application | `feat(app): add CreateUserCommand` |
| `infra` | MyNewProjectName.Infrastructure | `feat(infra): configure EF Core DbContext` |
| `api` | MyNewProjectName.WebAPI | `fix(api): resolve CORS issue` |
| `admin` | MyNewProjectName.AdminAPI | `feat(admin): add dashboard endpoint` |
| `contract` | MyNewProjectName.Contracts | `feat(contract): add UserDto` |
| `test` | MyNewProjectName.UnitTest | `test(test): add UserService tests` |
| `auth` | Module xac thuc | `feat(auth): implement JWT refresh token` |
| `cache` | Module caching/Redis | `feat(cache): add Redis caching layer` |
| `db` | Database/Migration | `feat(db): add migration for User table` |
| `docker` | Docker/Container | `chore(docker): update docker-compose` |
| `deps` | Dependencies/NuGet | `chore(deps): update MediatR to v12` |
### Quy tac scope
- Viet thuong toan bo
- Ngan gon, 1-2 tu
- Phai nhat quan trong toan du an
- Co the bo qua neu thay doi anh huong nhieu module
---
## 5. Quy Tac Viet Description
### Nen lam
| Quy tac | Chi tiet | Vi du |
|---------|---------|-------|
| **Viet hoa chu dau** | Chu dau tien cua description viet hoa | `feat: Add login page` |
| **Duoi 50 ky tu** | Giu description ngan gon | `fix: Resolve null reference in UserService` |
| **Dung dong tu menh lenh** | Bat dau bang dong tu (add, fix, update, remove...) | `feat: Add user authentication` |
| **Khong dau cham cuoi** | Khong ket thuc bang dau `.` | `docs: Update README` |
| **Mo ta "lam gi"** | Tap trung vao ket qua, khong phai qua trinh | `fix: Resolve 404 on login redirect` |
### Khong nen lam
| Quy tac | Vi du sai | Vi du dung |
|---------|----------|-----------|
| **Khong viet chung chung** | `fix: Fix bug` | `fix(auth): Resolve token expiration error` |
| **Khong qua dai** | `feat: Add new user authentication with JWT and refresh token and role-based access control` | `feat(auth): Add JWT authentication` |
| **Khong dung qua khu** | `feat: Added login page` | `feat: Add login page` |
| **Khong ghi ten file** | `fix: Fix UserService.cs` | `fix(app): Resolve null ref in user creation` |
| **Khong dung tieng Viet trong type** | `feat: Them trang dang nhap` | `feat: Add login page` |
### Danh sach dong tu khuyen dung
| Dong tu | Khi nao dung | Vi du |
|---------|-------------|-------|
| `add` | Them moi | `feat: Add payment gateway` |
| `remove` | Xoa bo | `refactor: Remove unused imports` |
| `update` | Cap nhat | `docs: Update API documentation` |
| `fix` | Sua loi | `fix: Fix null reference exception` |
| `resolve` | Giai quyet | `fix: Resolve race condition in checkout` |
| `implement` | Hien thuc | `feat: Implement search filter` |
| `refactor` | Tai cau truc | `refactor: Refactor UserRepository` |
| `optimize` | Toi uu | `perf: Optimize query performance` |
| `configure` | Cau hinh | `chore: Configure Serilog logging` |
| `migrate` | Di chuyen/migration | `feat: Migrate user table schema` |
| `replace` | Thay the | `refactor: Replace raw SQL with EF Core` |
| `rename` | Doi ten | `refactor: Rename UserDto to UserResponse` |
| `move` | Di chuyen | `refactor: Move validators to shared folder` |
| `simplify` | Don gian hoa | `refactor: Simplify error handling logic` |
| `extract` | Tach ra | `refactor: Extract email service interface` |
---
## 6. Commit Message Voi Body Va Footer
Khi commit phuc tap, can giai thich them, su dung body va footer:
### Format
```
<type>(<scope>): <description>
<-- dong trong bat buoc
<body>
<-- dong trong bat buoc
<footer>
```
### Vi du 1: Commit co body
```
feat(auth): Add JWT authentication
Implement JWT-based authentication using IdentityServer.
Include access token and refresh token flow.
Configure token expiration to 15 minutes for access token
and 7 days for refresh token.
```
### Vi du 2: Commit co body va footer (lien ket ticket)
```
fix(api): Resolve 500 error on user creation
The API was returning 500 when creating a user with an existing email.
Added proper validation check before inserting into database.
Return 409 Conflict instead of 500 Internal Server Error.
Resolves: PROJ-1234
```
### Vi du 3: Breaking change
```
feat(api)!: Change response format for all endpoints
BREAKING CHANGE: All API responses now follow the new standard format:
{
"success": true,
"data": {},
"message": "",
"errors": []
}
Previous format with flat response body is no longer supported.
Clients must update to handle the new wrapper format.
Resolves: PROJ-5678
```
### Footer keywords
| Keyword | Muc dich | Vi du |
|---------|---------|-------|
| `Resolves:` | Dong issue/ticket | `Resolves: PROJ-1234` |
| `Closes:` | Dong issue tren GitHub | `Closes: #123` |
| `Related:` | Lien quan den issue khac | `Related: PROJ-5678` |
| `BREAKING CHANGE:` | Thay doi khong tuong thich nguoc | `BREAKING CHANGE: API response format changed` |
| `Co-authored-by:` | Dong tac gia | `Co-authored-by: Name <email>` |
| `Reviewed-by:` | Nguoi review | `Reviewed-by: Name <email>` |
---
## 7. Bang Vi Du Day Du
### feat - Them tinh nang moi
```bash
feat(auth): Add Google OAuth login
feat(api): Add pagination support for product list
feat(domain): Add Order entity with value objects
feat(app): Add CreateUserCommand with validation
feat(infra): Add Redis caching for product queries
feat(admin): Add dashboard statistics endpoint
feat(contract): Add OrderResponseDto
```
### fix - Sua loi
```bash
fix(api): Resolve 404 error on login redirect
fix(auth): Fix token expiration calculation
fix(infra): Resolve database connection timeout
fix(app): Fix null reference in GetUserQuery
fix(domain): Fix value object equality comparison
```
### docs - Tai lieu
```bash
docs(readme): Update installation guide
docs(api): Add Swagger annotations for OrderController
docs: Add contributing guidelines
docs: Update environment variables documentation
```
### style - Format code
```bash
style: Apply EditorConfig formatting rules
style(api): Fix indentation in controllers
style: Remove trailing whitespace
```
### refactor - Tai cau truc
```bash
refactor(app): Simplify UserService error handling
refactor(infra): Extract IEmailService interface
refactor: Move validation logic to domain layer
refactor(api): Replace manual mapping with AutoMapper
```
### perf - Toi uu hieu nang
```bash
perf(infra): Optimize database queries with projection
perf(api): Add response compression middleware
perf(cache): Reduce Redis round trips with pipeline
```
### test - Them/sua test
```bash
test(app): Add unit tests for CreateUserCommand
test(domain): Add tests for Order entity validation
test(infra): Add integration tests for UserRepository
test: Increase code coverage to 80%
```
### chore - Bao tri
```bash
chore: Update NuGet packages
chore(deps): Upgrade to .NET 8
chore(docker): Update docker-compose configuration
chore(ci): Add GitHub Actions build workflow
chore: Update .gitignore
```
### build - Build system
```bash
build: Upgrade to .NET 8 SDK
build: Add Directory.Build.props for shared config
build: Configure multi-stage Docker build
```
### ci - CI/CD
```bash
ci: Add GitHub Actions workflow for PR checks
ci: Configure automatic deployment to staging
ci: Add SonarQube code analysis step
```
---
## 8. Vi Du Thuc Te Trong Du An
### Luong lam viec mot feature hoan chinh
Gia su lam task PROJ-101: Them tinh nang dang nhap
```bash
# 1. Tao branch
git checkout -b feature/PROJ-101-add-login
# 2. Them entity va domain logic
git commit -m "feat(domain): Add User entity with email and password"
# 3. Them command/query
git commit -m "feat(app): Add LoginCommand with FluentValidation"
# 4. Them infrastructure
git commit -m "feat(infra): Implement UserRepository with EF Core"
git commit -m "feat(infra): Add password hashing service"
# 5. Them API endpoint
git commit -m "feat(api): Add AuthController with login endpoint"
# 6. Them test
git commit -m "test(app): Add unit tests for LoginCommand handler"
# 7. Cap nhat tai lieu
git commit -m "docs(api): Add Swagger docs for auth endpoints"
# 8. Push va tao PR
git push origin feature/PROJ-101-add-login
```
### Luong sua loi
```bash
# 1. Tao branch
git checkout -b bugfix/PROJ-202-fix-login-error
# 2. Sua loi
git commit -m "fix(auth): Resolve incorrect password validation logic"
# 3. Them test cho truong hop loi
git commit -m "test(auth): Add test for invalid password scenario"
# 4. Push va tao PR
git push origin bugfix/PROJ-202-fix-login-error
```
### Luong hotfix khan cap
```bash
# 1. Tao branch tu main
git checkout main
git checkout -b hotfix/PROJ-303-fix-sql-injection
# 2. Sua loi
git commit -m "fix(infra): Sanitize SQL parameters to prevent injection
The raw SQL query in SearchRepository was concatenating user input
directly into the query string. Replaced with parameterized query
using EF Core's FromSqlInterpolated method.
Resolves: PROJ-303"
# 3. Push va tao PR vao main
git push origin hotfix/PROJ-303-fix-sql-injection
```
---
## 9. Nhung Loi Thuong Gap
| # | Loi sai | Vi du sai | Vi du dung |
|---|--------|----------|-----------|
| 1 | **Message qua chung chung** | `fix: Fix bug` | `fix(auth): Resolve token expiration error` |
| 2 | **Khong co type** | `Add login page` | `feat: Add login page` |
| 3 | **Dung qua khu** | `feat: Added new feature` | `feat: Add new feature` |
| 4 | **Qua nhieu thay doi trong 1 commit** | `feat: Add login, register, forgot password` | Tach thanh 3 commit rieng |
| 5 | **Commit file khong lien quan** | Commit ca file config lan feature | Chi commit file lien quan |
| 6 | **Message tieng Viet** | `feat: Them trang dang nhap` | `feat: Add login page` |
| 7 | **Dau cham cuoi** | `feat: Add login page.` | `feat: Add login page` |
| 8 | **Khong co scope khi can thiet** | `fix: Fix null reference` | `fix(app): Resolve null ref in GetUserQuery` |
| 9 | **Type sai** | `feat: Fix bug` | `fix: Resolve login error` |
| 10 | **Description qua dai** | 100+ ky tu tren 1 dong | Giu duoi 50 ky tu, dung body cho chi tiet |
---
## 10. Checklist Truoc Khi Commit
- [ ] Commit message co dung format `<type>(<scope>): <description>` khong?
- [ ] Type co chinh xac khong? (feat, fix, docs, style, refactor, perf, test, chore)
- [ ] Scope co phan anh dung module bi anh huong khong?
- [ ] Description co duoi 50 ky tu khong?
- [ ] Description co bat dau bang dong tu menh lenh khong? (add, fix, update...)
- [ ] Description co viet hoa chu dau khong?
- [ ] Khong co dau cham cuoi trong description?
- [ ] Moi commit chi chua 1 thay doi logic duy nhat?
- [ ] Commit co lien ket ticket ID khong? (trong scope hoac footer)
- [ ] Neu la breaking change, da danh dau `!` va them `BREAKING CHANGE:` trong footer?
---
## Tom Tat Nhanh
```
Format: <type>(<scope>): <description>
Type: feat | fix | docs | style | refactor | perf | test | chore | build | ci | revert
Scope: domain | app | infra | api | admin | contract | test | auth | cache | db | docker | deps
Vi du:
feat(auth): Add Google OAuth login # scope = "auth" (module authentication)
fix(api): Resolve 404 error # scope = "api" (API endpoints)
docs(readme): Update install guide # scope = "readme" (file/module cu the)
feat: Them tinh nang moi (VD: feat(login): Add Google OAuth).
fix: Sua loi (VD: fix(api): Resolve 404 error).
docs: Cap nhat tai lieu.
style: Doi format/UI khong anh huong logic.
refactor: Tai cau truc code.
perf: Toi uu hieu nang.
chore: Thay doi nho (VD: chore: Update dependencies).
Quy tac:
- Type va scope viet thuong
- Description viet hoa chu dau, duoi 50 ky tu
- Dung dong tu menh lenh (add, fix, update, remove, implement...)
- Khong dau cham cuoi
- 1 commit = 1 thay doi logic
- Lien ket ticket ID khi co the
```

394
base/.agent/rules/Redis.md Normal file
View File

@@ -0,0 +1,394 @@
---
trigger: always_on
---
# Hướng Dẫn Đặt Tên Redis Key Trong Dự Án
> **Tham khảo:** [Redis Namespace and Other Keys to Developing with Redis](https://redis.io/blog/5-key-takeaways-for-developing-with-redis/)
---
## Mục Lục
1. [Nguyên Tắc Chung](#1-nguyên-tắc-chung)
2. [Quy Ước Đặt Tên Key (Naming Convention)](#2-quy-ước-đặt-tên-key-naming-convention)
3. [Cấu Trúc Key Theo Namespace](#3-cấu-trúc-key-theo-namespace)
4. [Bảng Mẫu Key Theo Chức Năng](#4-bảng-mẫu-key-theo-chức-năng)
5. [Quy Tắc Về Độ Dài Key](#5-quy-tắc-về-độ-dài-key)
6. [Chọn Đúng Data Structure](#6-chọn-đúng-data-structure)
7. [Quản Lý Key: SCAN thay vì KEYS](#7-quản-lý-key-scan-thay-vì-keys)
8. [Chiến Lược TTL & Expiration](#8-chiến-lược-ttl--expiration)
9. [Cache Invalidation](#9-cache-invalidation)
10. [Ví Dụ Thực Tế Trong Dự Án](#10-ví-dụ-thực-tế-trong-dự-án)
---
## 1. Nguyên Tắc Chung
Theo bài viết từ Redis, có **5 điểm quan trọng** khi phát triển với Redis:
| # | Nguyên tắc | Mô tả |
|---|-----------|-------|
| 1 | **Namespace cho key** | Sử dụng dấu `:` để phân tách các phần của tên key, giúp dễ quản lý và tìm kiếm |
| 2 | **Giữ key ngắn gọn** | Key name cũng chiếm bộ nhớ — key dài 12 ký tự tốn thêm ~15% RAM so với key 6 ký tự (trên 1 triệu key) |
| 3 | **Dùng đúng data structure** | Hash, List, Set, Sorted Set — mỗi loại phù hợp với một use case khác nhau |
| 4 | **Dùng SCAN, không dùng KEYS** | Lệnh `KEYS` có thể block server, `SCAN` an toàn hơn cho production |
| 5 | **Sử dụng Lua Scripts** | Xử lý logic phía server để giảm latency và tối ưu hiệu suất |
---
## 2. Quy Ước Đặt Tên Key (Naming Convention)
### Format chung
```
{project}:{service}:{entity}:{identifier}
```
- **Dấu phân cách:** Luôn dùng dấu hai chấm `:` (colon) — đây là **convention chuẩn** của Redis
- **Chữ thường:** Tất cả các phần của key đều viết **lowercase**
- **Không dấu cách, không ký tự đặc biệt:** Chỉ dùng chữ cái, số, dấu `:` và dấu `-` hoặc `_`
### Đúng
```
myapp:user:profile:12345
myapp:order:detail:ORD-001
myapp:cache:product:list:page:1
```
### Sai
```
MyApp_User_Profile_12345 # Dùng underscore thay vì colon, viết hoa
user profile 12345 # Có dấu cách
MYAPP:USER:PROFILE:12345 # Viết hoa toàn bộ (lãng phí bộ nhớ)
```
---
## 3. Cấu Trúc Key Theo Namespace
Áp dụng cho dự án **Clean Architecture**, cấu trúc key nên phản ánh rõ layer và module:
```
{app}:{layer}:{entity}:{action/scope}:{identifier}
```
### Các prefix theo layer
| Prefix | Ý nghĩa | Ví dụ |
|--------|---------|-------|
| `app:cache` | Cache dữ liệu | `app:cache:product:list` |
| `app:session` | Quản lý session | `app:session:user:abc123` |
| `app:rate` | Rate limiting | `app:rate:api:login:192.168.1.1` |
| `app:lock` | Distributed lock | `app:lock:order:process:ORD-001` |
| `app:queue` | Message queue | `app:queue:email:pending` |
| `app:temp` | Dữ liệu tạm thời | `app:temp:otp:user:12345` |
| `app:pub` | Pub/Sub channels | `app:pub:notifications:user:12345` |
| `app:counter` | Bộ đếm | `app:counter:visit:page:home` |
---
## 4. Bảng Mẫu Key Theo Chức Năng
### Authentication & Authorization
| Key Pattern | Data Type | TTL | Mô tả |
|------------|-----------|-----|--------|
| `app:session:{sessionId}` | Hash | 30 phút | Thông tin session người dùng |
| `app:token:refresh:{userId}` | String | 7 ngày | Refresh token |
| `app:token:blacklist:{jti}` | String | Thời gian còn lại của token | JWT bị thu hồi |
| `app:temp:otp:{userId}` | String | 5 phút | Mã OTP xác thực |
| `app:rate:login:{ip}` | String (counter) | 15 phút | Giới hạn số lần đăng nhập |
### Cache Dữ Liệu (CRUD)
| Key Pattern | Data Type | TTL | Mô tả |
|------------|-----------|-----|--------|
| `app:cache:{entity}:detail:{id}` | Hash/String | 10 phút | Cache chi tiết 1 entity |
| `app:cache:{entity}:list:{hash}` | String (JSON) | 5 phút | Cache danh sách có phân trang/filter |
| `app:cache:{entity}:count` | String | 5 phút | Cache tổng số record |
| `app:cache:{entity}:ids:all` | Set | 10 phút | Tập hợp tất cả ID của entity |
> **`{hash}`** là hash MD5/SHA256 của query parameters (page, filter, sort) để tạo key unique cho mỗi truy vấn khác nhau.
### 🔔 Real-time & Pub/Sub
| Key Pattern | Data Type | TTL | Mô tả |
|------------|-----------|-----|--------|
| `app:pub:notification:{userId}` | Channel | — | Kênh thông báo realtime |
| `app:queue:email:pending` | List | — | Hàng đợi gửi email |
| `app:counter:online:users` | String | — | Đếm user đang online |
### 🔒 Distributed Locking
| Key Pattern | Data Type | TTL | Mô tả |
|------------|-----------|-----|--------|
| `app:lock:{resource}:{id}` | String | 30 giây | Lock tài nguyên để tránh race condition |
---
## 5. Quy Tắc Về Độ Dài Key
Theo bài viết từ Redis:
> *"Storing 1,000,000 keys, each set with a 32-character value, will consume about **96MB** when using 6-character key names, and **111MB** with 12-character names. This overhead of more than **15%** becomes quite significant as your number of keys grows."*
### Hướng dẫn cân bằng
| Quy tắc | Chi tiết |
|---------|---------|
| **Tối đa 50 ký tự** | Giữ tổng chiều dài key không quá 50 ký tự |
| **Viết tắt hợp lý** | `usr` thay vì `user`, `prod` thay vì `product` — nhưng phải có **bảng chú giải** |
| **Không lạm dụng viết tắt** | Key phải đọc được, tránh như `a:b:c:d:1` |
| **Ưu tiên rõ ràng nếu < 10K keys** | Nếu dataset nhỏ, ưu tiên key dễ đọc hơn key ngắn |
### Bảng viết tắt chuẩn (nếu cần tối ưu)
| Viết tắt | Đầy đủ |
|----------|--------|
| `usr` | user |
| `prod` | product |
| `ord` | order |
| `sess` | session |
| `notif` | notification |
| `cfg` | config |
| `inv` | inventory |
| `txn` | transaction |
---
## 6. Chọn Đúng Data Structure
Theo bài viết, việc chọn đúng cấu trúc dữ liệu giúp **tối ưu bộ nhớ và hiệu suất**:
| Data Structure | Khi nào dùng | Ví dụ trong dự án |
|---------------|-------------|-------------------|
| **String** | Giá trị đơn giản, counter, cache JSON | `app:cache:product:detail:123` → JSON string |
| **Hash** | Object có nhiều field, profile user | `app:session:abc123``{userId, role, name, exp}` |
| **List** | Queue, danh sách có thứ tự, cho phép trùng | `app:queue:email:pending` → FIFO queue |
| **Set** | Tập hợp unique, kiểm tra membership | `app:cache:user:ids:all` → tập hợp user IDs |
| **Sorted Set** | Leaderboard, ranking, timeline | `app:rank:score:board:game1` → ranking theo điểm |
| **Bitmap** | Track true/false cho lượng lớn, analytics | `app:analytics:daily:login:2026-02-23` → bit per user |
### 💡 Tips quan trọng:
- **Hash thay vì nhiều String**: Nhóm dữ liệu liên quan vào 1 Hash thay vì tạo nhiều key String riêng lẻ → tiết kiệm bộ nhớ đáng kể
- **List thay vì Set**: Nếu không cần kiểm tra uniqueness, List nhanh hơn và tốn ít RAM hơn
- **Tránh Sorted Set nếu không cần ranking**: Sorted Set tốn nhiều bộ nhớ và phức tạp nhất
---
## 7. Quản Lý Key: SCAN thay vì KEYS
### ⚠️ KHÔNG BAO GIỜ dùng `KEYS` trong production!
Lệnh `KEYS *` sẽ:
- **Block toàn bộ Redis server** cho đến khi hoàn thành
- **Tiêu tốn RAM** nguy hiểm
- Gây **downtime** nếu dataset lớn
### Dùng `SCAN` để duyệt key an toàn
```bash
# Cú pháp
SCAN cursor [MATCH pattern] [COUNT count]
# Ví dụ: Tìm tất cả cache key của product
SCAN 0 MATCH "app:cache:product:*" COUNT 100
# Dùng HSCAN cho Hash
HSCAN app:session:abc123 0 MATCH "*"
# Dùng SSCAN cho Set
SSCAN app:cache:user:ids:all 0 COUNT 50
```
### Trong code C# (StackExchange.Redis):
```csharp
// Đúng: Dùng SCAN
var server = redis.GetServer(endpoint);
var keys = server.Keys(pattern: "app:cache:product:*", pageSize: 100);
// Sai: Dùng KEYS (block server)
// var keys = server.Keys(pattern: "app:cache:product:*", pageSize: int.MaxValue);
```
---
## 8. Chiến Lược TTL & Expiration
| Loại dữ liệu | TTL khuyến nghị | Lý do |
|--------------|----------------|-------|
| **Cache API response** | 5 15 phút | Đảm bảo data tương đối fresh |
| **Session** | 30 phút 2 giờ | Theo session timeout của app |
| **OTP / Verification** | 3 10 phút | Bảo mật |
| **Refresh Token** | 7 30 ngày | Theo chính sách auth |
| **Rate Limit Counter** | 1 60 phút | Theo window rate limit |
| **Distributed Lock** | 10 60 giây | Tránh deadlock |
| **Analytics / Counter** | Không expire hoặc 24h | Tùy yêu cầu business |
### ⚡ Luôn đặt TTL cho mọi cache key!
```csharp
// Luôn set expiration khi SET
await db.StringSetAsync("app:cache:product:detail:123", jsonData, TimeSpan.FromMinutes(10));
// Hoặc set TTL riêng
await db.KeyExpireAsync("app:cache:product:detail:123", TimeSpan.FromMinutes(10));
```
> ⚠️ **Cảnh báo:** Key không có TTL sẽ tồn tại mãi mãi → nguy cơ memory leak!
---
## 9. Cache Invalidation
Khi dữ liệu thay đổi trong database chính (SQL, MongoDB...), cần **xóa cache Redis tương ứng**:
### Pattern: Tag-based Invalidation
```
# Khi tạo cache, thêm key vào một Set quản lý
SADD app:tags:product app:cache:product:detail:123
SADD app:tags:product app:cache:product:list:abc
SADD app:tags:product app:cache:product:count
# Khi invalidate, duyệt Set và xóa tất cả
SMEMBERS app:tags:product → lấy tất cả key liên quan
DEL app:cache:product:detail:123 app:cache:product:list:abc ...
DEL app:tags:product
```
### Trong code C#:
```csharp
public async Task InvalidateCacheByTagAsync(string tag)
{
var db = _redis.GetDatabase();
var tagKey = $"app:tags:{tag}";
// Lấy tất cả cache key thuộc tag này
var members = await db.SetMembersAsync(tagKey);
if (members.Length > 0)
{
// Xóa tất cả cache key
var keys = members.Select(m => (RedisKey)m.ToString()).ToArray();
await db.KeyDeleteAsync(keys);
}
// Xóa luôn tag set
await db.KeyDeleteAsync(tagKey);
}
```
---
## 10. Ví Dụ Thực Tế Trong Dự Án
Áp dụng quy ước cho dự án **MyNewProjectName** (Clean Architecture):
### Entity: `SampleEntity`
```
# Cache chi tiết
app:cache:sample:detail:{id}
# Cache danh sách (hash = MD5 của query params)
app:cache:sample:list:{queryHash}
# Cache count
app:cache:sample:count
# Tag để invalidation
app:tags:sample → Set chứa tất cả key cache liên quan
# Lock khi cập nhật
app:lock:sample:update:{id}
```
### Authentication Flow
```
# Session sau khi login
app:session:{sessionId} → Hash { userId, role, loginAt, ip }
# Refresh token
app:token:refresh:{userId} → "eyJhbGciOi..."
# OTP xác thực email
app:temp:otp:{userId} → "123456" (TTL: 5 phút)
# Blacklist JWT đã revoke
app:token:blacklist:{jti} → "1" (TTL: thời gian còn lại của token)
# Rate limit login
app:rate:login:{ip} → counter (TTL: 15 phút, max: 5 lần)
```
### Constant class trong C#
```csharp
/// <summary>
/// Định nghĩa tất cả Redis key patterns sử dụng trong dự án.
/// Sử dụng dấu ':' làm namespace separator theo convention chuẩn Redis.
/// </summary>
public static class RedisKeyPatterns
{
private const string Prefix = "app";
// ── Cache ──────────────────────────────────
public static string CacheDetail(string entity, string id)
=> $"{Prefix}:cache:{entity}:detail:{id}";
public static string CacheList(string entity, string queryHash)
=> $"{Prefix}:cache:{entity}:list:{queryHash}";
public static string CacheCount(string entity)
=> $"{Prefix}:cache:{entity}:count";
// ── Tags (cho cache invalidation) ──────────
public static string Tag(string entity)
=> $"{Prefix}:tags:{entity}";
// ── Session ────────────────────────────────
public static string Session(string sessionId)
=> $"{Prefix}:session:{sessionId}";
// ── Token ──────────────────────────────────
public static string RefreshToken(string userId)
=> $"{Prefix}:token:refresh:{userId}";
public static string BlacklistToken(string jti)
=> $"{Prefix}:token:blacklist:{jti}";
// ── Temporary ──────────────────────────────
public static string Otp(string userId)
=> $"{Prefix}:temp:otp:{userId}";
// ── Rate Limiting ──────────────────────────
public static string RateLimit(string action, string identifier)
=> $"{Prefix}:rate:{action}:{identifier}";
// ── Distributed Lock ───────────────────────
public static string Lock(string resource, string id)
=> $"{Prefix}:lock:{resource}:{id}";
}
```
---
## 📋 Checklist Trước Khi Tạo Key Mới
- [ ] Key có sử dụng namespace với dấu `:` không?
- [ ] Key có phản ánh đúng layer/module không?
- [ ] Key có ngắn gọn nhưng vẫn dễ hiểu không? (< 50 ký tự)
- [ ] Đã chọn đúng data structure (String/Hash/List/Set/Sorted Set)?
- [ ] Đã đặt TTL phù hợp cho key?
- [ ] Đã có chiến lược invalidation khi data thay đổi?
- [ ] Đã thêm key pattern vào class `RedisKeyPatterns`?
- [ ] Không dùng `KEYS` command trong code production?
---
> **💡 Ghi nhớ:** *"Luôn namespace key bằng dấu `:`, giữ key ngắn gọn, chọn đúng data structure, dùng SCAN thay vì KEYS, và luôn đặt TTL!"*

View File

@@ -0,0 +1,126 @@
---
name: GenerateGitHubActions
description: Hướng dẫn tạo CI/CD pipeline tự động hóa Build, Test, Docker Build & Deploy bằng GitHub Actions.
---
# GenerateGitHubActions Skill
Khi user yêu cầu tạo CI/CD Pipelines (ví dụ: GitHub Actions, hoặc tương đương cho GitLab CI / Azure DevOps), bạn cần sinh ra file luồng tự động hóa theo các bước chuẩn sau đây.
## Mục đích
- Tự động hóa quá trình kiểm tra mã nguồn (CI) và triển khai (CD).
- Đảm bảo code push lên nhánh `main` luôn hoạt động đúng đắn và sẵn sàng lên production.
## Hướng dẫn sinh cấu hình (GitHub Actions)
Tạo file luồng công việc Workflow cho GitHub Actions.
### 1. Đường dẫn và tên file
- **Đường dẫn**: `.github/workflows/ci-cd.yml`
- (Hoặc theo định dạng của platform tương ứng: `.gitlab-ci.yml` cho GitLab, `azure-pipelines.yml` cho Azure DevOps).
### 2. Các bước cấu hình bắt buộc trong file YAML
Workflow cần trải qua các luồng chính sau (mẫu dưới dây cho GitHub Actions):
#### Phân đoạn 1: Build & Test (CI)
- **Triggers**: Lắng nghe sự kiện `push` hoặc `pull_request` vào nhánh `main`.
- **Setup môi trường**:
- Checkout mã nguồn (ví dụ dùng `actions/checkout@v4`).
- Cài đặt .NET SDK tương ứng với dự án (ví dụ `actions/setup-dotnet@v4` cho .NET 8.0). **Lưu ý: Bật tính năng cache Nuget để tăng tốc độ build.**
- **Run Tests**:
- Chạy khối lệnh `dotnet restore`, `dotnet build --no-restore`.
- Quan trọng nhất: Chạy `dotnet test --no-build --verbosity normal`. (Chỉ khi Test Xanh (Passed) thì các bước sau mới được chạy tiếp).
#### Phân đoạn 2: Docker Build & Push (Bắt đầu CD)
- **Cần điều kiện**: Chỉ chạy khi Job Build & Test thành công (`needs: build`).
- **Đăng nhập Container Registry**:
- Login vào Docker Hub hoặc Azure Container Registry (ACR) sử dụng System Secrets (ví dụ `DOCKER_USERNAME``DOCKER_PASSWORD`).
- **Build & Push Image**:
- Build Image từ Dockerfile (chú ý chỉ đường dẫn thư mục gốc nơi chứa dự án chính để `docker build` có thể truy cập qua các tầng thư mục Clean Architecture).
- Gắn tag cho Image (ví dụ: `latest` hoặc theo Commit SHA/phiên bản).
- Push Image lên Registry.
#### Phân đoạn 3: Deploy to Server (CD - Webhook / SSH)
- Dùng thư viện `appleboy/ssh-action` (hoặc tương tự) để SSH vào Server đích.
- Yêu cầu server pull file Image mới nhất từ Registry.
- **Quan trọng:** Ưu tiên sử dụng `docker compose` để deploy (pull và up) nếu cấu trúc dự án của user có file `docker-compose.yml`, giúp khởi động lại toàn bộ stack (API, DB, Redis...) thay vì chỉ chạy `docker run` độc lập.
## Mẫu File Mặc Định (`ci-cd.yml`)
Dưới đây là khung mẫu bạn cần căn cứ để thiết kế khi sinh file cho user:
```yaml
name: CI/CD Pipeline
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0.x'
cache: true # Bật cache Nuget, lần sau build nhanh gấp đôi
cache-dependency-path: '**/packages.lock.json'
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
docker-build-push:
needs: build-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKER_USERNAME }}/chi-tiet-ten-project-lowercase:latest
deploy:
needs: docker-build-push
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy via SSH
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
cd /path/to/your/project/on/server # Trỏ tới thư mục chứa docker-compose.yml
docker compose pull # Kéo image mới nhất về (dựa theo file compose)
docker compose up -d --build # Khởi động lại các service có sự thay đổi
```
## Lưu ý cho AI Agent
- Khi User yêu cầu sinh pipeline, hãy yêu cầu User xác nhận về tên tài khoản Docker Hub, Server Credentials và nhắc họ cấu hình đầy đủ `Secrets` trên GitHub sau khi sinh file.
- **Hãy tự động thay thế chuỗi tên project (`chi-tiet-ten-project-lowercase` trong mẫu) bằng tên thật của Project / Repository mà User đang thao tác. Chuyển tất cả về chữ thường (lowercase) khi đặt tên Docker Image để tránh lỗi định dạng của Docker.**
- **Nếu user có dùng `docker-compose`, hãy ưu tiên sinh lệnh `docker compose up -d` thay vì `docker run` thuần.**

View File

@@ -0,0 +1,34 @@
---
name: GenerateCQRSFeature
description: Hướng dẫn tạo một feature theo chuẩn CQRS sử dụng MediatR (bao gồm Entity, Command/Query, Handler, và Controller).
---
# GenerateCQRSFeature Skill
Khi user yêu cầu tạo một feature theo luồng **CQRS** với đầu vào bao gồm **Tên Feature** (VD: `Order`) và **Action** (VD: `Create`), bạn **BẮT BUỘC** phải thực hiện các bước sau để sinh ra code và file tương ứng:
## 1. Đầu ra thư mục và file (Outputs)
Dựa trên {FeatureName} và {Action}, hãy tạo các file sau (nếu action thuộc loại Read/Get thì đổi 'Commands' thành 'Queries'):
1. **Domain Entity**
- **Đường dẫn**: `Domain/Entities/{FeatureName}.cs`
- **Nội dung**: Lớp Entity cơ bản định nghĩa các thuộc tính.
2. **Command / Query**
- **Đường dẫn**: `Application/Features/{FeatureName}s/Commands/{Action}{FeatureName}/{Action}{FeatureName}Command.cs`
- **Nội dung**: Input model kế thừa từ `IRequest<TResponse>` của MediatR.
3. **Command / Query Handler**
- **Đường dẫn**: `Application/Features/{FeatureName}s/Commands/{Action}{FeatureName}/{Action}{FeatureName}CommandHandler.cs`
- **Nội dung**: Xử lý logic nghiệp vụ, implements `IRequestHandler<{Action}{FeatureName}Command, TResponse>`. Inject Repository hoặc service cần thiết vào đây.
4. **WebAPI Controller**
- **Đường dẫn**: `WebAPI/Controllers/{FeatureName}sController.cs` (Lưu ý thêm số nhiều cho tên Controller nếu cần thiết).
- **Nội dung**: REST API endpoints. **Yêu cầu:** Nhận DI `IMediator` qua constructor để điều hướng Request (ví dụ gọi `await _mediator.Send(command)`).
## 2. Cấu hình Dependency Injection (KHÔNG CẦN LÀM)
⚠️ **Lưu ý:** Như nguyên tắc thiết lập project, luồng CQRS **KHÔNG CẦN** update file cấu hình Dependency Injection (DI configuration).
Thư viện MediatR đã tự động quét (Auto-register) tất cả các lớp Handler kế thừa từ `IRequestHandler`, vì thế bạn **TUYỆT ĐỐI BỎ QUA** việc cập nhật các file config DI cho phần Handler này.

View File

@@ -0,0 +1,40 @@
---
name: GenerateNTierFeature
description: Hướng dẫn tạo một feature theo chuẩn N-Tier (bao gồm Entity, Interface, Service, Controller và cấu hình Dependency Injection).
---
# GenerateNTierFeature Skill
Khi user yêu cầu tạo một feature theo luồng **N-Tier** với đầu vào là **Tên Feature** (VD: `Category`), bạn **BẮT BUỘC** phải thực hiện các bước sau để sinh ra code và file tương ứng:
## 1. Đầu ra thư mục và file (Outputs)
Hãy tạo các file sau với nội dung phù hợp cho {FeatureName}:
1. **Domain Entity**
- **Đường dẫn**: `Domain/Entities/{FeatureName}.cs`
- **Nội dung**: Lớp Entity cơ bản định nghĩa các thuộc tính.
2. **Service Interface**
- **Đường dẫn**: `Application/Interfaces/I{FeatureName}Service.cs`
- **Nội dung**: Các interface định nghĩa hợp đồng hàm cho {FeatureName}.
3. **Service Implementation**
- **Đường dẫn**: `Application/Services/{FeatureName}Service.cs`
- **Nội dung**: Lớp kế thừa từ `I{FeatureName}Service`. **Yêu cầu:** Nhận Dependency Injection (DI) thông qua constructor (ví dụ: `IRepository<{FeatureName}>`).
4. **WebAPI Controller**
- **Đường dẫn**: `WebAPI/Controllers/{FeatureName}sController.cs` (Lưu ý thêm số nhiều cho tên Controller).
- **Nội dung**: Lớp Controller. **Yêu cầu:** Phải nhận DI `I{FeatureName}Service` thông qua constructor và định nghĩa các endpoint tương ứng.
## 2. Cấu hình Dependency Injection (BƯỚC BẮT BUỘC THỰC HIỆN)
⚠️ **Quan trọng:** Không giống như CQRS với MediatR ở trên, luồng N-Tier **đòi hỏi** bạn phải chèn thủ công service mới tạo vào Container IoC.
Bạn **BẮT BUỘC** phải sử dụng tool để mở file cấu hình DI của project (có thể là `DependencyInjection.cs` hoặc `ServiceCollectionExtensions.cs` tùy cấu trúc thư mục) và chèn đoạn mã sau vào hàm cấu hình service liên quan:
```csharp
services.AddScoped<I{FeatureName}Service, {FeatureName}Service>();
```
Hãy đảm bảo bạn dùng công cụ sửa file chính xác (`replace_file_content` hoặc `multi_replace_file_content`) để không làm hỏng cú pháp của file DI.

View File

@@ -0,0 +1,49 @@
---
name: GenerateArchitectureTest
description: Hướng dẫn tạo Architecture Test sử dụng NetArchTest.Rules để bảo vệ kiến trúc Clean Architecture.
---
# GenerateArchitectureTest Skill
Khi user yêu cầu kiểm tra hoặc khởi tạo **Architecture Test**, bạn cần sử dụng thư viện `NetArchTest.Rules` để sinh luận kiểm điểm nhằm bảo vệ chặt chẽ cấu trúc "Clean Architecture" của dự án.
## Mục đích
- Ngăn chặn Dev code ẩu, import sai library/module giữa các tầng (layer).
- Nếu vi phạm (VD: import Entity Framework vào tầng Domain), bài test này sẽ báo ĐỎ ngay lập tức lúc build code. Bằng chứng thép giúp kiến trúc được bảo vệ tuyệt đối!
## Hướng dẫn viết Test Rules
Bạn cần viết các test method (dùng `[Fact]` với xUnit hoặc NUnit) sử dụng Fluent API của `NetArchTest.Rules`. Dưới đây là các luật phổ biến bắt buộc:
1. **Domain Layer Rules (Luật tầng Domain):**
- Domain không được phụ thuộc vào bất cứ thứ gì từ Infrastructure, Application hay WebAPI.
```csharp
[Fact]
public void DomainLayer_ShouldNot_HaveDependencyOn_OtherLayers()
{
var result = Types.InAssembly(DomainAssembly)
.ShouldNot()
.HaveDependencyOnAny(
"MyNewProjectName.Application",
"MyNewProjectName.Infrastructure",
"MyNewProjectName.WebAPI"
)
.GetResult();
Assert.True(result.IsSuccessful);
}
```
2. **Application Layer Rules (Luật tầng Application):**
- Application Layer chỉ được phép giao tiếp với Domain, KHÔNG ĐƯỢC có dependency vào `Infrastructure` hoặc `WebAPI`.
3. **Controller Rules (Luật đặt tên/vị trí API):**
- Controller bắt buộc phải kế thừa class BaseAPIController, và có hậu tố là "Controller".
- Không được phép truy vấn Database trực tiếp từ Controller (ngăn không cho Inject `DbContext` hay `IRepository` vào Controller, kiểm duyệt dependencies của Constructor).
4. **Handler Rules (CQRS):**
- Các Handler xử lý logic phải implements interface `IRequestHandler` và kết thúc bằng `CommandHandler` hoặc `QueryHandler`. Nó chỉ nằm ở Application Layer.
## Định dạng file đầu ra
- **Đường dẫn:** Chứa trong test project như `tests/MyNewProjectName.ArchitectureTests/`.
- **Tên file:** Thường đặt tên theo phạm vi test như `LayerTests.cs`, `DesignConventionTests.cs`, `NamingRulesTests.cs`, v.v.

View File

@@ -0,0 +1,34 @@
---
name: GenerateIntegrationTest
description: Hướng dẫn tạo Integration Test (người thật việc thật) sử dụng WebApplicationFactory và Testcontainers.
---
# GenerateIntegrationTest Skill
Khi user yêu cầu tạo **Integration Test** để kiểm tra API endpoint từ đầu đến cuối, bạn cần sinh ra code theo hướng dẫn sau:
## Mục đích
- Test xem các mảnh ghép lắp vào ghép với nhau có chạy đúng không.
- Flow: Client gọi API -> Middleware -> Controller -> CQRS Handler/Service -> Ghi/Đọc Database thật.
## Hướng dẫn thực hiện
1. **Setup WebApplicationFactory:**
- Tự động setup `WebApplicationFactory<Program>` (tạo một test server ngay trong RAM của .NET).
- Override cấu hình ứng dụng nếu cần thiết (ví dụ thay đổi ConnectionString trỏ sang test container).
2. **Setup Testcontainers (Real Database):**
- Sử dụng thư viện `Testcontainers` (hoặc cấu hình tương tự) để tự động spin up một Docker container chứa Database thật (ví dụ: PostgreSQL, SQL Server).
- Đảm bảo database container này được start trước khi chạy test và tự động xóa (dispose) sau khi test xong. Tránh dùng SQLite in-memory vì hay bị lỗi vặt và không tương đương với database production.
3. **Viết kịch bản Test gọi API (Arrange - Act - Assert):**
- Tạo đối tượng `HttpClient` từ `WebApplicationFactory.CreateClient()`.
- **Arrange:** Chuẩn bị payload data dạng JSON objects hoặc tạo trước data base records nếu là API GET/PUT/DELETE.
- **Act:** Gọi thẳng vào API endpoint tương ứng bằng code. VD: `await client.PostAsJsonAsync("/api/v1/samples", payload);`.
- **Assert:** Kiểm tra kết quả trả về:
- Check HTTP Status: có phải `200 OK` hay `400 Bad Request` không.
- Deserialize response body để check chính xác object.
- (Tùy chọn) Query thẳng vào database container xem bản ghi đã được sinh ra/cập nhật thật chưa.
## Định dạng file đầu ra
- **Đường dẫn:** Chứa trong project test tương ứng như `tests/MyNewProjectName.IntegrationTests/Controllers/...`.
- **Tên file:** `[ControllerName]Tests.cs` (ví dụ: `OrdersControllerTests.cs`).

View File

@@ -0,0 +1,32 @@
---
name: GenerateUnitTest
description: Hướng dẫn tạo Unit Test cô lập (tốc độ cao) sử dụng Mocking (Moq hoặc NSubstitute).
---
# GenerateUnitTest Skill
Khi user yêu cầu tạo **Unit Test** cho một class/method, bạn cần tuân thủ các nguyên tắc sau để sinh ra code test:
## Mục đích
- Chỉ test duy nhất 1 class/method.
- Bỏ qua hoàn toàn Database thật, Redis hay HTTP.
- Tốc độ chạy cực nhanh.
## 1. Với CQRS Handlers hoặc Services
- **Nhận diện Dependencies:** Tự động nhận diện các Interface (ví dụ: `IRepository`, `IUnitOfWork`, `ILogger`, `IMediator`) được inject vào constructor.
- **Tạo Mock Object:** Sử dụng thư viện Mocking (như `Moq` hoặc `NSubstitute`) để tạo instance giả của các Interface này.
- **Kịch bản Test (Arrange - Act - Assert):**
- **Arrange:** Cấp data giả (Mock data) cho các hàm của Interface.
- **Act:** Gọi hàm thực thi (ví dụ `Handle()` của CQRS hoặc các method của Service).
- **Assert:** Kiểm tra kết quả trả về, HOẶC verify xem một method của Mock object có được gọi đúng số lần không (ví dụ kiểm tra xem `_repository.AddAsync()` có được gọi không), HOẶC kiểm tra xem nó có ném ra `ValidationException` khi input sai không.
## 2. Với Domain Entities
- **Mục tiêu:** Kiểm tra các logic kinh doanh, tính toán nội bộ của Entity.
- **Kịch bản:**
- Khởi tạo Entity với các trạng thái cụ thể.
- Gọi method thay đổi trạng thái hoặc tính toán (ví dụ: `Order.CalculateTotal()`).
- Kiểm tra xem giá trị biến đổi có đúng với quy tắc nghiệp vụ không (ví dụ: `Total` phải bằng `Price * Quantity`).
## Định dạng file đầu ra
- **Đường dẫn:** Đặt trong project `tests/MyNewProjectName.UnitTests/...` (tương ứng với thư mục gốc của class bị test).
- **Tên file:** `[ClassName]Tests.cs` (ví dụ: `CreateOrderCommandHandlerTests.cs`).

View File

@@ -0,0 +1 @@
/home/visssoft/.agents/skills/ui-ux-pro-max

View File

@@ -0,0 +1,19 @@
Tôi cần 1 landing page cho Sale Party bán sản phẩm bảo hiểm UVL-IC
Dưới đây là script ghi lại nhu cầu:
Hỏi: Website chỉ được dùng cho Sale phải không?
Đáp: Đúng vậy
Hỏi: Có phải là mỗi sale sẽ được cung cấp 1 tài khoản riêng và đăng nhập vào hệ thống theo tài khoản được cung cấp?
Đáp: Đúng như vậy. Sale đang có tài khoản này. Tài khoản được kiểm tra Authencated bởi 1 API đang có. Tài liệu API Spec tôi sẽ gửi sau
Hỏi: Hãy mô tả thêm yêu cầu cho Sale sau khi đã đăng nhập vào hệ thống?
Đáp: Sau khi Sale đăng nhập vào hệ thống. Họ có nhu cầu trình diễn bảng minh họa bảo hiểm cho khách hàng xem được. Tất nhiên họ sẽ phải nhập các thông tin khách hàng vào hệ thống. Họ có thể nhập dữ liệu này trực tiếp hoặc quét Căn cước của khách hàng trực tiếp vào hệ thống. Có cách nào lưu những thông tin này và sử dụng cho lần sau được không nhỉ. Sau khi có những thông tin này. Website cần đưa ra được bảng minh họa bảo hiểm theo sản phẩm được lựa chọn. Tôi nghĩ lần này sẽ triển khai 2 loại sản phẩm là UVL-IC và UVL-EL. Sau đó nếu khách hàng đồng ý. Sale sẽ submit các thông tin này về hệ thống ghi nhận bảo hiểm. Tôi đang cân nhắc đưa luồng thanh toán trực tiếp vào hệ thống. Nếu không đủ thời gian phát triển. Thì có thể thực hiện vấn đề này sau được không?
Hỏi: Bạn cứ nêu ra các nhu cầu mong muốn. Tôi sẽ ghi nhận và trao đổi tiếp. Tôi có 1 câu hỏi tiếp theo: Vậy bảng minh họa bảo hiểm có phải là 1 file PDF phải không? Có API nào kết xuất ra file này dựa theo thông tin khách hàng nhập vào
Đáp: Đúng rồi. Hiện tại đã có API như thế. Tôi sẽ cung cấp API Spec cho các bạn sau. Vậy là tôi phải cung cấp thông tin 2 API phải không?
Hỏi: Tới hiện tại là như vậy. Không biết là bạn có nhu cầu theo dõi toàn hệ thống hay không? Kiểu như 1 trang admin để theo dõi hoạt động của các Sale chẳng hạn
Đáp: Hiện tại thì không cần
Hỏi: Sale thường trình diễn bảng minh họa bảo hiểm trên máy tính cá nhân? Hay Ipad hoặc điện thoại. Bạn có thể cung cấp thêm về thông tin này được không?
Đáp: Tôi không có thống kê chính xác. Có thể là đa phần sẽ sử dụng máy tính xách tay. Một phần dùng điện thoại. Liệu có thể đáp ứng tất cả các nhu cầu này hay không?
Hỏi: Tất nhiên là được rồi. Và tôi có câu hỏi cuối như sau: Các bạn có thay đổi thông tin về sản phẩm này thường xuyên hay không?
Đáp: Có lẽ là không. Một sản phẩm mới ra sẽ cần khoảng 6 tháng để phát triển. Mà cũng chưa chắc. Có thể khi đó sản phẩm hiện tại không còn bán nữa chẳng hạn. Trước mắt tôi cần bán được 2 sản phẩm này đã
Hỏi: Vâng cảm ơn bạn về cuộc trò chuyện.
Đáp: Cảm ơn

View File

@@ -0,0 +1,48 @@
# Tài liệu Kiến trúc C4 - Mức 4: Code Documentation (Frontend Store)
Mô tả chi tiết cấu trúc quản lý dữ liệu (State Management) tại Frontend để phục vụ luồng nghiệp vụ phức tạp.
## 1. Global State (Zustand/Context)
Để đảm bảo dữ liệu khách hàng được lưu giữ khi chuyển giữa các trang (Chọn sản phẩm -> Quay lại sửa thông tin -> Xem PDF), sử dụng Global Store.
### `useCustomerStore`
- **State**:
- `personalInfo`: { name, cccd, dob, gender, ... }
- `ocrStatus`: 'idle' | 'scanning' | 'success' | 'error'
- `selectedProduct`: 'UVL-IC' | 'UVL-EL' | null
- `illustrationUrl`: string (Link PDF)
- **Actions**:
- `updateInfo(field, value)`
- `setOCRData(data)`
- `resetSession()`
## 2. Sequence Diagram: Luồng OCR & Tạo PDF
```mermaid
sequenceDiagram
participant User as Sales Agent
participant FE as Frontend (Store)
participant BFF as Backend BFF
participant OCR as OCR Service
participant PDF as PDF Engine
User->>FE: Upload ảnh CCCD
FE->>BFF: POST /api/ocr (image)
BFF->>OCR: Extract Info
OCR-->>BFF: {name, cccd, dob...}
BFF-->>FE: Return JSON
FE->>FE: Update Store (personalInfo)
FE-->>User: Hiển thị Form (đã điền sẵn)
User->>FE: Chọn SP & Nhấn "Xem Minh Họa"
FE->>BFF: POST /api/illustration {info, product}
BFF->>PDF: Generate Request
PDF-->>BFF: PDF Stream/URL
BFF-->>FE: Return URL
FE-->>User: Hiển thị PDF Viewer
```
---
*Biên soạn bởi: Agent mbl-landing-page-architecture*

View File

@@ -0,0 +1,61 @@
# Tài liệu Kiến trúc C4 - Mức 3: Component Diagram (Landing Page)
Chi tiết hóa các thành phần logic trong **Sales Landing App****BFF Service**.
## 1. Frontend Components (Sales Landing App)
### 1.1. Modules
- **Auth Module**: Form đăng nhập, xử lý Token.
- **Customer Module**: Form nhập liệu, Component Camera/Upload cho OCR.
- **Product Module**: Giao diện chọn sản phẩm (Card chọn UVL-IC/UVL-EL).
- **Illustration Module**: Hiển thị PDF (PDF Viewer) và nút tải về/Submit.
### 1.2. Shared
- **UI Kit**: Button, Input, Modal (theo Design System).
- **API Client**: Axios wrapper để gọi BFF.
## 2. Backend Components (BFF Service)
### 2.1. Controllers
- **AuthController**: Xác thực với Auth API.
- **OCRController**: Proxy ảnh sang OCR Service.
- **IllustrationController**: Tổng hợp dữ liệu -> Gọi PDF Engine -> Trả về URL/File.
- **SubmissionController**: Gửi dữ liệu cuối cùng về Core.
### 2.2. Services
- **ClientService**: Lưu/Lấy thông tin khách hàng từ Database.
## 3. Sơ đồ Component
```mermaid
C4Component
title Component Diagram - Landing Page System
Container_Boundary(frontend, "Sales Landing App") {
Component(c_auth, "Auth Module", "React", "Login Screen")
Component(c_cust, "Customer Module", "React", "Form & OCR Upload")
Component(c_pdf, "Illustration Module", "React", "PDF View & Submit")
}
Container_Boundary(backend, "BFF Service") {
Component(ctl_ocr, "OCR Controller", "Node.js", "Handle Upload & Proxy")
Component(ctl_ill, "Illustration Controller", "Node.js", "Coordinate PDF Gen")
Component(svc_client, "Client Service", "Node.js", "Manage Client Data DB")
}
ContainerDb(db, "PostgreSQL", "Database", "Store Drafts")
System_Ext(ocr_ext, "External OCR", "Service")
System_Ext(pdf_ext, "External PDF Gen", "Service")
Rel(c_auth, ctl_ocr, "Authenticated Req")
Rel(c_cust, ctl_ocr, "Upload Image")
Rel(c_cust, ctl_ill, "Request PDF")
Rel(ctl_ocr, ocr_ext, "Proxy")
Rel(ctl_ill, pdf_ext, "Proxy")
Rel(ctl_ill, svc_client, "Save Info")
Rel(svc_client, db, "Persist")
```
---
*Biên soạn bởi: Agent mbl-landing-page-architecture*

View File

@@ -0,0 +1,54 @@
# Tài liệu Kiến trúc C4 - Mức 2: Container Diagram (Landing Page)
Mô tả các thành phần triển khai của hệ thống Landing Page.
## 1. Danh sách Container
### 1.1. Sales Landing App (Frontend)
- **Công nghệ**: React, Vite, TailwindCSS.
- **Vai trò**: Cung cấp giao diện tương tác (Responsive).
- **Chức năng**: Form nhập liệu, Camera capture (cho OCR), PDF Viewer, điều hướng sản phẩm.
### 1.2. Backend For Frontend (BFF)
- **Công nghệ**: Node.js / Express (hoặc Serverless Functions).
- **Vai trò**: Trung gian bảo mật và điều phối API.
- **Chức năng**:
- Ẩn các Token/Secret khi gọi OCR và PDF API.
- Quản lý session người dùng.
- Chuẩn hóa dữ liệu trước khi gửi đi.
### 1.3. Local Storage / Caching DB
- **Công nghệ**: PostgreSQL (hoặc Redis/LocalStorage tùy yêu cầu lưu lâu dài). *Dựa trên yêu cầu lưu lại khách hàng lần sau, sẽ dùng PostgreSQL.*
- **Vai trò**: Lưu nháp thông tin khách hàng và lịch sử minh họa tạm thời.
## 2. Sơ đồ Container
```mermaid
C4Container
title Container Diagram - Landing Page Sales Party
Person(sales_agent, "Nhân viên kinh doanh", "Sử dụng thiết bị cá nhân.")
System_Boundary(landing_zone, "Landing Page System") {
Container(web_app, "Sales Landing App", "React/Vite", "Giao diện người dùng Mobile-first.")
Container(bff_api, "BFF Service", "Node.js", "Xử lý logic trung gian và bảo mật.")
ContainerDb(local_db, "Client Cache DB", "PostgreSQL", "Lưu thông tin khách hàng nháp.")
}
System_Ext(auth_api, "Auth API", "Xác thực")
System_Ext(ocr_service, "OCR Service", "Xử lý ảnh")
System_Ext(pdf_engine, "PDF Engine", "Tạo PDF")
System_Ext(core_system, "Core System", "Lưu hợp đồng")
Rel(sales_agent, web_app, "Tương tác", "HTTPS")
Rel(web_app, bff_api, "API Call", "JSON/HTTPS")
Rel(bff_api, local_db, "Đọc/Ghi dữ liệu KH", "SQL")
Rel(bff_api, auth_api, "Proxy xác thực", "REST")
Rel(bff_api, ocr_service, "Gọi OCR", "REST")
Rel(bff_api, pdf_engine, "Gọi tạo PDF", "REST")
Rel(bff_api, core_system, "Submit hồ sơ", "REST")
```
---
*Biên soạn bởi: Agent mbl-landing-page-architecture*

View File

@@ -0,0 +1,58 @@
# Tài liệu Kiến trúc C4 - Mức 1: System Context (Landing Page)
Tài liệu này mô tả bối cảnh hệ thống của **Landing Page dành cho Sales**, tập trung vào việc hỗ trợ nhân viên kinh doanh trình diễn và khởi tạo bảo hiểm.
## 1. Tổng quan hệ thống
**Landing Page Sales Party** là ứng dụng web giúp nhân viên kinh doanh đăng nhập, quản lý khách hàng (OCR), và tạo bảng minh họa (PDF) cho các sản phẩm bảo hiểm (UVL-IC, UVL-EL) trên đa thiết bị.
## 2. Personas (Tác nhân)
### Nhân viên kinh doanh (Sales Agent)
- **Loại**: Người dùng duy nhất.
- **Mô tả**: Sử dụng Laptop hoặc Mobile để tư vấn trực tiếp cho khách hàng.
- **Mục tiêu**: Nhập liệu nhanh (OCR), cho xem bảng minh họa tức thì, chốt hợp đồng tại chỗ.
- **Tính năng chính**: Login, Scan CCCD, Chọn sản phẩm, Xem/Tải PDF, Submit hồ sơ.
## 3. Các hệ thống bên ngoài (External Systems)
### API Authentication (Existing)
- **Vai trò**: Xác thực tài khoản của Sales Agent.
- **Giao thức**: REST API / OIDC.
### Dịch vụ OCR (OCR Service)
- **Vai trò**: Trích xuất thông tin khách hàng từ ảnh CCCD.
- **Giao thức**: REST API.
### API Illustration PDF (PDF Engine)
- **Vai trò**: Tính toán quyền lợi và kết xuất file PDF minh họa.
- **Giao thức**: REST API.
### Hệ thống Core Bảo hiểm (Core System)
- **Vai trò**: Tiếp nhận hồ sơ yêu cầu bảo hiểm (khi Sales submit).
- **Giao thức**: REST/SOAP API.
## 4. Sơ đồ System Context (C4 Context Diagram)
```mermaid
C4Context
title System Context Diagram - Landing Page Sales Party
Person(sales_agent, "Nhân viên kinh doanh", "Sử dụng Landing Page để tư vấn và chốt đơn.")
System(landing_system, "Landing Page System", "Cho phép nhập liệu KH, xem minh họa và nộp hồ sơ.")
System_Ext(auth_api, "Auth API", "Hệ thống xác thực tài khoản Sales.")
System_Ext(ocr_service, "OCR Service", "Trích xuất dữ liệu CCCD.")
System_Ext(pdf_engine, "PDF Generator API", "Tạo file PDF bản minh họa.")
System_Ext(core_system, "Core Insurance System", "Ghi nhận hợp đồng.")
Rel(sales_agent, landing_system, "Truy cập, nhập liệu, xem PDF", "HTTPS")
Rel(landing_system, auth_api, "Xác thực người dùng", "REST/JSON")
Rel(landing_system, ocr_service, "Gửi ảnh CCCD", "REST/JSON")
Rel(landing_system, pdf_engine, "Gửi data để lấy PDF", "REST/JSON")
Rel(landing_system, core_system, "Submit hồ sơ", "REST/JSON")
```
---
*Biên soạn bởi: Agent mbl-landing-page-architecture*

View File

@@ -0,0 +1,24 @@
# Hệ thống Tài liệu Kiến trúc C4 - Landing Page Sales Party
Tài liệu này tổng hợp kiến trúc hệ thống cho dự án **Landing Page dành cho Sales**, giúp đội ngũ phát triển nắm bắt cấu trúc từ tổng quan đến chi tiết.
## Mục lục Tài liệu
### [Level 1: System Context](./C4_Context.md)
- **Mô tả**: Bức tranh tổng thể về tương tác giữa Sales Agent và hệ thống Landing Page cùng các đối tác (OCR, Auth, Core).
- **Trọng tâm**: Xác định ranh giới hệ thống và các điểm tích hợp.
### [Level 2: Container Diagram](./C4_Container.md)
- **Mô tả**: Phân rã hệ thống thành Web App (Frontend), BFF (Backend), và Database.
- **Công nghệ**: React, Node.js, PostgreSQL.
### [Level 3: Component Diagram](./C4_Component.md)
- **Mô tả**: Cấu trúc nội bộ của Frontend (Modules) và Backend BFF (Controllers/Services).
- **Trọng tâm**: Logic xử lý OCR và tạo bảng minh họa.
### [Level 4: Code Documentation](./C4_Code.md)
- **Mô tả**: Chi tiết luồng xử lý dữ liệu (Sequence Diagram) và quản lý State tại Frontend.
- **Trọng tâm**: Cơ chế đồng bộ dữ liệu giữa Form nhập liệu và PDF Viewer.
---
*Được cập nhật mới nhất bởi: Agent mbl-landing-page-architecture*

View File

@@ -1,4 +1,4 @@
# PowerShell script to rename the project from iYHCT360 to a new name
# PowerShell script to rename the project from MyNewProjectName to a new name
# Usage: .\rename-project.ps1 -NewName "NewProjectName"
param(
@@ -6,7 +6,7 @@ param(
[string]$NewName
)
$OldName = "iYHCT360"
$OldName = "MyNewProjectName"
Write-Host "🔄 Renaming project from '$OldName' to '$NewName'..." -ForegroundColor Cyan

View File

@@ -1,6 +1,6 @@
#!/bin/bash
# Script to rename the project from iYHCT360 to a new name
# Script to rename the project from MyNewProjectName to a new name
# Usage: ./rename-project.sh NewProjectName
set -e
@@ -11,7 +11,7 @@ if [ -z "$1" ]; then
exit 1
fi
OLD_NAME="iYHCT360"
OLD_NAME="MyNewProjectName"
NEW_NAME="$1"
echo "🔄 Renaming project from '$OLD_NAME' to '$NEW_NAME'..."

3
rename-project/rename.md Normal file
View File

@@ -0,0 +1,3 @@
Window: .\rename-project.ps1 -NewName "TenDuAnThucTeCuaBan"
Linux: ./rename-project.sh "TenDuAnThucTeCuaBan"