Backend (.NET 10): - Domain: BaseEntity/AuditableEntity, ContractType/Phase/ApprovalDecision enums, User/Role (Identity<Guid>), AppRoles (12 const) - Application: IApplicationDbContext/ICurrentUser/IDateTime/IJwtTokenService, custom exceptions, ValidationBehavior (MediatR pipeline), Auth CQRS (Login/Refresh/Me), DependencyInjection - Infrastructure: ApplicationDbContext (IdentityDbContext), AuditingInterceptor (auto audit + soft delete), DbInitializer (seed 12 role + admin), DesignTimeDbContextFactory, JwtTokenService, DateTimeService, DI - Api: CurrentUserService, GlobalExceptionMiddleware (ProblemDetails), AuthController, Program.cs rewrite (Serilog + JWT + CORS + Swagger), appsettings + launchSettings (port 5443) - Migration Init applied to SolutionErp_Dev LocalDB Frontend (React 19 + Vite 8 + Tailwind 4): - fe-admin (:8082 blue) + fe-user (:8080 emerald) - shared structure, khac menu + brand color - Tailwind 4 via @tailwindcss/vite plugin, theme brand colors - AuthContext (localStorage token), ProtectedRoute, Layout (sidebar + header) - UI kit: Button/Input/Label (CVA + Tailwind) - LoginPage voi toast error, DashboardPage/InboxPage placeholder - Axios interceptor: auto Bearer + 401 redirect - TanStack Query client, React Router 7, Sonner toast Package downgrades (do .NET 10 / TS 6 compat): - MediatR 14 -> 12.4.1 (v14 breaking changes) - Swashbuckle 10 -> 6.9.0 (v10 khong tuong thich OpenApi 2) - Removed Microsoft.AspNetCore.OpenApi (conflict voi Swashbuckle) E2E verified: POST /api/auth/login qua Vite proxy ca 2 FE -> JWT + user info Credentials seed: admin@solutionerp.local / Admin@123456 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.3 KiB
9.3 KiB
Migration To-dos — Atomic Roadmap
Mỗi item là 1 task atomic (~2-8h work). Tick
[x]khi xong. Link session log nếu có.
Phase 0 — Draft Scaffold (T1)
- Tạo cấu trúc thư mục
SOLUTION_ERP/ - Scaffold .NET 10 solution
SolutionErp.slnx - Scaffold 4 project:
SolutionErp.{Domain, Application, Infrastructure, Api} - Wire Clean Arch references (Api → App/Infra, Infra → App, App → Domain)
- Install NuGet base: MediatR, FluentValidation, AutoMapper, EF Core SqlServer, Identity, JWT, Swagger, Serilog
- Scaffold 2 React + Vite apps
fe-admin+fe-uservới TS template - Config vite.config.ts: port, strictPort, proxy
/api, alias@ - Pin Node
>=20trong package.json +.nvmrccho CI - Parse 8 form →
docs/forms-spec.md - Parse quy trình →
docs/workflow-contract.md - Viết
docs/{CLAUDE,STATUS,PROJECT-MAP}.md - Viết
.gitignore,README.md,global.json,docker-compose.yml - Tạo placeholder skill folders:
contract-workflow,form-engine,permission-matrix git init+ commit đầu (25dad7f)- Push Gitea remote (chờ URL từ user)
Phase 1 — Alpha Core (T2-4)
Foundation (đã xong Session 2)
Domain/Common/BaseEntity.cs(Id Guid, CreatedAt, UpdatedAt, CreatedBy, UpdatedBy)Domain/Common/AuditableEntity.cs(IsDeleted, DeletedAt, DeletedBy)Domain/Contracts/Enums:ContractType,ContractPhase(9 state),ApprovalDecisionDomain/Identity/User.cs(IdentityUser + FullName + RefreshToken + IsActive)Domain/Identity/Role.cs(IdentityRole + Description)Domain/Identity/AppRoles.cs— 12 role constantsApplication/Common/Interfaces/: IApplicationDbContext, ICurrentUser, IDateTime, IJwtTokenServiceApplication/Common/Exceptions/*Application/Common/Behaviors/ValidationBehavior.csApplication/DependencyInjection.cs— MediatR + FluentValidationInfrastructure/Persistence/ApplicationDbContext.cs : IdentityDbContextInfrastructure/Persistence/Interceptors/AuditingInterceptor.csInfrastructure/Persistence/DbInitializer.cs— seed 12 role + adminInfrastructure/Persistence/DesignTimeDbContextFactory.csInfrastructure/Identity/{JwtSettings, JwtTokenService}.csInfrastructure/Services/DateTimeService.csInfrastructure/DependencyInjection.csApi/Services/CurrentUserService.csApi/Middleware/GlobalExceptionMiddleware.csApi/Controllers/AuthController.cs(login, refresh, me, logout)Api/Program.cs(Serilog, JWT, CORS, Swagger, middleware)Api/appsettings.{json, Development.json}+launchSettings.json(port 5443)- Migration 1
Init+ apply toSolutionErp_DevLocalDB - FE: Vite config (Tailwind 4 + proxy + alias)
- FE:
src/{index.css, lib/api.ts, lib/cn.ts, types/auth.ts}cho 2 app - FE:
src/contexts/AuthContext.tsx,components/{ProtectedRoute, Layout}.tsx - FE:
components/ui/{Button, Input, Label}.tsx - FE:
pages/LoginPage.tsx,pages/DashboardPage.tsx(admin) +pages/InboxPage.tsx(user) - FE:
App.tsxvới Router + AuthProvider + Toaster - FE:
main.tsxvới QueryClient (TanStack Query) - E2E verified: login qua Vite proxy cả 2 app → JWT + user info
Phase 1 đợt 2 — CRUD master + Permission Matrix (sắp tới)
Domain/Entities/Supplier(Code, Name, TaxCode, Phone, Email, Address, Type enum: NCC/NTP/TĐ/ĐVDV)Domain/Entities/Project(Code, Name, StartDate, EndDate, ManagerUserId)Domain/Entities/Department(Code, Name, ManagerUserId)- EF
IEntityTypeConfiguration<T>cho mỗi entity - CQRS CRUD: Create/Update/Delete/GetById/List (với paging) cho 3 entity
Api/Controllers/{SuppliersController, ProjectsController, DepartmentsController}- Migration 2:
AddMasterData Domain/Entities/MenuItem(Key PascalCase, Label, ParentKey, Order, Icon)Domain/Entities/Permission(RoleId, MenuKey, CanRead/Create/Update/Delete)- Seed default menu tree + permission admin có full access
Application/Permissions/Queries/GetMyMenuTreeQuery— resolve per-user, cacheApi/Controllers/{MenusController, RolesController, PermissionsController}- Migration 3:
AddPermissions Domain/Entities/Contractskeleton (Id, Type, SupplierId, ProjectId, Phase=DangChon, DraftData JSON)- Contract CRUD draft only (không workflow Phase 3)
- FE:
<PermissionGuard menuKey="Suppliers" action="Update">+usePermission()hook - FE Admin: 3 trang CRUD Supplier/Project/Department với table + modal + search/sort
- FE Admin: Permission Matrix grid page (role × menu × CRUD checkbox)
- FE User: trang "HĐ của tôi" list + filter
- Route guard theo role admin-only
- Update
SolutionErp.slnxnếu thêm project mới
Exit criteria Phase 1
- Admin login → tạo NCC/Project → tạo role "Nhân viên CCM" → gán permission menu "Contracts.Read"
- User CCM login → thấy menu Contracts, không thấy menu Admin
- Tạo Contract draft → list hiển thị, không bị 403 sai
Phase 2 — Form Engine (T5-6)
- Khảo sát: OpenXml vs Aspose.Words — chọn 1 (Aspose có license phí; OpenXml free nhưng verbose)
- Convert 3 file
.doc→.docx(COM automation PowerShell hoặc LibreOffice headless) - Parse chi tiết field của 5 template HĐ — mỗi form thành JSON spec
Domain/Entities/ContractTemplate(Id, FormCode, Name, TemplateFile path, FieldSpec JSON)Application/Forms/Services/IFormRenderer— input: template + data dict → output: byte[] (.docx)- Implement
DocxRenderer(OpenXml-based replace placeholder) - Implement
XlsxRenderercho FO-002.07 (dùng EPPlus/ClosedXML) Api/Controllers/FormsController— GET /templates, POST /render- FE user: form builder — chọn template → dynamic form → preview → export
- FE admin: upload template mới, edit field mapping
- Lưu
ContractClause(FO-002.04) dạng rich text, admin edit qua TipTap/TinyMCE - Import/export template (để backup)
- Test: 1 HĐ Giao khoán filled → export .docx mở bằng Word y hệt mẫu
Phase 3 — Workflow State Machine (T7-9)
Domain/Entities/ContractApproval+ContractComment+ContractAttachmentDomain/Entities/Contractupdate: thêmPhase,SlaDeadline,BypassProcurementAndCCMDomain/Services/IContractWorkflowService.TransitionAsync(...)— state guard + role guard + side effectsInfrastructure/Services/ContractCodeGenerator(implement RG-001) với locking cho seqInfrastructure/HostedServices/SlaExpiryJob— check mỗi 15min, auto-approve quá hạnInfrastructure/Services/NotificationService— email (MailKit) + in-app (SignalR optional)- MediatR behavior:
AuditBehavior— log mọi command - API:
POST /api/contracts/{id}/transitionsbody:{targetPhase, comment} - FE user Inbox: list "HĐ chờ tôi xử lý" (query by current phase + user role)
- FE Contract detail page: timeline 9 phase, approval panel, comment thread
- Upload attachment (scan có chữ ký đối tác)
- Notification UI: badge count, dropdown, click → detail
- E2E test: happy path end-to-end 1 HĐ qua 9 phase
- E2E test: reject → quay về DangSoanThao
- E2E test: SLA expired → auto-approve + log
Phase 4 — Reporting + Polish (T10-11)
- Dashboard admin: số HĐ theo phase, top NCC, top dự án, tổng giá trị theo tháng
- Excel export theo bộ lọc (dùng EPPlus)
- Report: HĐ quá hạn SLA bao nhiêu lần theo phase/role
- UX polish: skeleton loader, empty state, error boundary có recovery button
- Accessibility: keyboard nav, focus trap modal, aria labels
- Dark mode (optional, nếu rảnh)
- Performance: index DB cho query hot (SupplierId, ProjectId, Phase)
- Tài liệu user guide: quy trình tạo HĐ + duyệt
- UAT với 5-10 HĐ dữ liệu thật từ bộ phận Cung ứng
Phase 5 — Production (T12-13)
docs/guides/cicd.md— CI/CD runbook- Gitea Actions workflow
.gitea/workflows/deploy.yml— build .NET + 2 FE, deploy IIS qua SSH/WinRM - Pin Node 20.x trong workflow, test CI sớm (không để surprise cuối dự án)
scripts/deploy-iis.ps1— stop app pool, xcopy, start app pool- Windows Server setup: IIS + URL Rewrite + ARR (reverse proxy FE → IIS)
- SQL Server prod: backup plan daily + weekly full
- HTTPS certificate (Let's Encrypt qua win-acme hoặc mua cert)
appsettings.Production.json+ user secrets- Security audit: owasp top 10 check
- Rate limiting middleware (AspNetCoreRateLimit hoặc built-in)
- Health check endpoint
/healthcho IIS probe - Error tracking: Serilog → file rolling daily, retention 30 ngày
- Runbook: restart app, rollback migration, restore backup
- UAT production 1 tuần với 2-3 user thật
- Go-live checklist: backup, rollback plan, on-call contact
Post-launch (Phase 6+ — future)
- E-signature integration (VNPT CA hoặc FPT CA)
- Tích hợp Bravo / SAP ERP import NCC
- Mobile app (React Native?) cho BOD duyệt ngoài giờ
- AI: gợi ý điền form dựa HĐ cũ, OCR scan HĐ đối tác
- Multi-tenant nếu có công ty thứ 2