evnict-kit 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (138) hide show
  1. package/README.md +19 -0
  2. package/bin/cli.js +38 -0
  3. package/package.json +48 -0
  4. package/src/commands/add.js +129 -0
  5. package/src/commands/init-check.js +19 -0
  6. package/src/commands/init-context.js +37 -0
  7. package/src/commands/init-rules.js +42 -0
  8. package/src/commands/init-workflow.js +36 -0
  9. package/src/commands/init.js +722 -0
  10. package/src/utils/config.js +167 -0
  11. package/src/utils/file.js +53 -0
  12. package/templates/GETTING-STARTED.md +196 -0
  13. package/templates/content/context/AGENTS.md.template +462 -0
  14. package/templates/content/rules/01-evnict-kit-general-rules.md +303 -0
  15. package/templates/content/rules/02-evnict-kit-security-rules.md +423 -0
  16. package/templates/content/rules/03-evnict-kit-backend-conventions.md +383 -0
  17. package/templates/content/rules/04-evnict-kit-frontend-conventions.md +402 -0
  18. package/templates/content/rules/05-evnict-kit-project-conventions.md +228 -0
  19. package/templates/content/skills/evnict-kit-brainstorm/SKILL.md +140 -0
  20. package/templates/content/skills/evnict-kit-bug-fix/SKILL.md +108 -0
  21. package/templates/content/skills/evnict-kit-checkpoint/SKILL.md +156 -0
  22. package/templates/content/skills/evnict-kit-code-review/SKILL.md +158 -0
  23. package/templates/content/skills/evnict-kit-coordinate/SKILL.md +274 -0
  24. package/templates/content/skills/evnict-kit-create-api/SKILL.md +281 -0
  25. package/templates/content/skills/evnict-kit-create-component/SKILL.md +263 -0
  26. package/templates/content/skills/evnict-kit-create-page/SKILL.md +247 -0
  27. package/templates/content/skills/evnict-kit-database-migration/SKILL.md +164 -0
  28. package/templates/content/skills/evnict-kit-doc-postmortem/SKILL.md +93 -0
  29. package/templates/content/skills/evnict-kit-finish-branch/SKILL.md +87 -0
  30. package/templates/content/skills/evnict-kit-fix-attt/SKILL.md +129 -0
  31. package/templates/content/skills/evnict-kit-fix-business-logic/SKILL.md +89 -0
  32. package/templates/content/skills/evnict-kit-git-worktrees/SKILL.md +104 -0
  33. package/templates/content/skills/evnict-kit-merge-checklist/SKILL.md +108 -0
  34. package/templates/content/skills/evnict-kit-onboard/SKILL.md +143 -0
  35. package/templates/content/skills/evnict-kit-prompt-standard/SKILL.md +103 -0
  36. package/templates/content/skills/evnict-kit-receiving-review/SKILL.md +89 -0
  37. package/templates/content/skills/evnict-kit-security-audit/SKILL.md +190 -0
  38. package/templates/content/skills/evnict-kit-spec/SKILL.md +237 -0
  39. package/templates/content/skills/evnict-kit-tdd/SKILL.md +413 -0
  40. package/templates/content/skills/evnict-kit-wiki/SKILL.md +412 -0
  41. package/templates/content/workflows/evnict-kit-archive-wiki.md +100 -0
  42. package/templates/content/workflows/evnict-kit-attt.md +100 -0
  43. package/templates/content/workflows/evnict-kit-bug-fix.md +107 -0
  44. package/templates/content/workflows/evnict-kit-feature-large.md +393 -0
  45. package/templates/content/workflows/evnict-kit-feature-small.md +86 -0
  46. package/templates/content/workflows/evnict-kit-handoff.md +243 -0
  47. package/templates/content/workflows/evnict-kit-implement.md +247 -0
  48. package/templates/content/workflows/evnict-kit-init-check.md +76 -0
  49. package/templates/content/workflows/evnict-kit-init-context.md +58 -0
  50. package/templates/content/workflows/evnict-kit-init-rules.md +114 -0
  51. package/templates/content/workflows/evnict-kit-init-wiki.md +80 -0
  52. package/templates/content/workflows/evnict-kit-plan.md +308 -0
  53. package/templates/content/workflows/evnict-kit-review.md +53 -0
  54. package/templates/content/workflows/evnict-kit-spec-archive.md +53 -0
  55. package/templates/content/workflows/evnict-kit-wiki-archive-feature.md +164 -0
  56. package/templates/content/workflows/evnict-kit-wiki-query.md +91 -0
  57. package/templates/content/workflows/evnict-kit-wiki-scan-project.md +272 -0
  58. package/templates/context/AGENT.md.template +9 -0
  59. package/templates/context/AGENTS.md.template +462 -0
  60. package/templates/context/CLAUDE.md.template +301 -0
  61. package/templates/context/copilot-instructions.md.template +60 -0
  62. package/templates/context/cursorrules.template +114 -0
  63. package/templates/instruct/Instruct-Agent-AI.be.md +96 -0
  64. package/templates/instruct/Instruct-Agent-AI.fe.md +79 -0
  65. package/templates/rules/antigravity/01-evnict-kit-general-rules.md +303 -0
  66. package/templates/rules/antigravity/02-evnict-kit-security-rules.md +423 -0
  67. package/templates/rules/antigravity/03-evnict-kit-backend-conventions.md +383 -0
  68. package/templates/rules/antigravity/04-evnict-kit-frontend-conventions.md +402 -0
  69. package/templates/rules/antigravity/05-evnict-kit-project-conventions.md +228 -0
  70. package/templates/rules/claude/README.md +8 -0
  71. package/templates/rules/cursor/01-evnict-kit-general-rules.mdc +46 -0
  72. package/templates/rules/cursor/02-evnict-kit-security-rules.mdc +46 -0
  73. package/templates/rules/cursor/03-evnict-kit-backend-conventions.mdc +50 -0
  74. package/templates/rules/cursor/04-evnict-kit-frontend-conventions.mdc +43 -0
  75. package/templates/rules/cursor/05-evnict-kit-project-conventions.mdc +63 -0
  76. package/templates/rules/cursor/README.md +7 -0
  77. package/templates/skills/evnict-kit-brainstorm/SKILL.md +140 -0
  78. package/templates/skills/evnict-kit-bug-fix/SKILL.md +108 -0
  79. package/templates/skills/evnict-kit-checkpoint/SKILL.md +156 -0
  80. package/templates/skills/evnict-kit-code-review/SKILL.md +158 -0
  81. package/templates/skills/evnict-kit-coordinate/SKILL.md +274 -0
  82. package/templates/skills/evnict-kit-create-api/SKILL.md +281 -0
  83. package/templates/skills/evnict-kit-create-component/SKILL.md +263 -0
  84. package/templates/skills/evnict-kit-create-page/SKILL.md +247 -0
  85. package/templates/skills/evnict-kit-database-migration/SKILL.md +164 -0
  86. package/templates/skills/evnict-kit-doc-postmortem/SKILL.md +93 -0
  87. package/templates/skills/evnict-kit-finish-branch/SKILL.md +87 -0
  88. package/templates/skills/evnict-kit-fix-attt/SKILL.md +129 -0
  89. package/templates/skills/evnict-kit-fix-business-logic/SKILL.md +89 -0
  90. package/templates/skills/evnict-kit-git-worktrees/SKILL.md +104 -0
  91. package/templates/skills/evnict-kit-merge-checklist/SKILL.md +108 -0
  92. package/templates/skills/evnict-kit-onboard/SKILL.md +143 -0
  93. package/templates/skills/evnict-kit-prompt-standard/SKILL.md +103 -0
  94. package/templates/skills/evnict-kit-receiving-review/SKILL.md +89 -0
  95. package/templates/skills/evnict-kit-security-audit/SKILL.md +190 -0
  96. package/templates/skills/evnict-kit-spec/SKILL.md +237 -0
  97. package/templates/skills/evnict-kit-tdd/SKILL.md +413 -0
  98. package/templates/skills/evnict-kit-wiki/SKILL.md +412 -0
  99. package/templates/wiki/README.md +35 -0
  100. package/templates/wiki/config.example.yaml +17 -0
  101. package/templates/wiki/package.json +17 -0
  102. package/templates/wiki/raw/notes/.gitkeep +1 -0
  103. package/templates/wiki/scripts/ingest.js +66 -0
  104. package/templates/workflows/antigravity/evnict-kit-archive-wiki.md +100 -0
  105. package/templates/workflows/antigravity/evnict-kit-attt.md +100 -0
  106. package/templates/workflows/antigravity/evnict-kit-bug-fix.md +107 -0
  107. package/templates/workflows/antigravity/evnict-kit-feature-large.md +393 -0
  108. package/templates/workflows/antigravity/evnict-kit-feature-small.md +86 -0
  109. package/templates/workflows/antigravity/evnict-kit-handoff.md +243 -0
  110. package/templates/workflows/antigravity/evnict-kit-implement.md +247 -0
  111. package/templates/workflows/antigravity/evnict-kit-init-check.md +76 -0
  112. package/templates/workflows/antigravity/evnict-kit-init-context.md +58 -0
  113. package/templates/workflows/antigravity/evnict-kit-init-rules.md +114 -0
  114. package/templates/workflows/antigravity/evnict-kit-init-wiki.md +80 -0
  115. package/templates/workflows/antigravity/evnict-kit-plan.md +308 -0
  116. package/templates/workflows/antigravity/evnict-kit-review.md +53 -0
  117. package/templates/workflows/antigravity/evnict-kit-spec-archive.md +53 -0
  118. package/templates/workflows/antigravity/evnict-kit-wiki-archive-feature.md +164 -0
  119. package/templates/workflows/antigravity/evnict-kit-wiki-query.md +91 -0
  120. package/templates/workflows/antigravity/evnict-kit-wiki-scan-project.md +272 -0
  121. package/templates/workflows/claude/README.md +6 -0
  122. package/templates/workflows/claude/evnict-kit-archive-wiki.md +98 -0
  123. package/templates/workflows/claude/evnict-kit-attt.md +98 -0
  124. package/templates/workflows/claude/evnict-kit-bug-fix.md +105 -0
  125. package/templates/workflows/claude/evnict-kit-feature-large.md +391 -0
  126. package/templates/workflows/claude/evnict-kit-feature-small.md +84 -0
  127. package/templates/workflows/claude/evnict-kit-handoff.md +240 -0
  128. package/templates/workflows/claude/evnict-kit-implement.md +245 -0
  129. package/templates/workflows/claude/evnict-kit-init-check.md +74 -0
  130. package/templates/workflows/claude/evnict-kit-init-context.md +56 -0
  131. package/templates/workflows/claude/evnict-kit-init-rules.md +112 -0
  132. package/templates/workflows/claude/evnict-kit-init-wiki.md +78 -0
  133. package/templates/workflows/claude/evnict-kit-plan.md +305 -0
  134. package/templates/workflows/claude/evnict-kit-review.md +51 -0
  135. package/templates/workflows/claude/evnict-kit-spec-archive.md +51 -0
  136. package/templates/workflows/claude/evnict-kit-wiki-archive-feature.md +162 -0
  137. package/templates/workflows/claude/evnict-kit-wiki-query.md +89 -0
  138. package/templates/workflows/claude/evnict-kit-wiki-scan-project.md +270 -0
@@ -0,0 +1,274 @@
1
+ ---
2
+ name: evnict-kit-coordinate
3
+ description: FE↔BE coordination protocol — handoff, API contract format, status sync. Đảm bảo 2 agent (BE, FE) phối hợp đúng khi cùng làm 1 feature.
4
+ compatibility: All tech stacks
5
+ ---
6
+
7
+ # evnict-kit-coordinate — FE↔BE Coordination
8
+
9
+ ## Khi nào dùng
10
+ - Feature cần cả Backend và Frontend
11
+ - BE agent xong API → FE agent cần biết để implement
12
+ - Cần sync status giữa 2 project/agent
13
+
14
+ ## Input Parameters
15
+ - `action` (bắt buộc): handoff | publish-contract | read-contract | update-status | check-status
16
+ - `feature` (bắt buộc): Feature slug
17
+
18
+ ---
19
+
20
+ ## Workflow Steps
21
+
22
+ ### Action: HANDOFF — Bàn giao feature chi tiết
23
+ Hành động đặc biệt được kích hoạt bởi lệnh `/evnict-kit:handoff`.
24
+
25
+ > **CHI TIẾT:** Xem command `evnict-kit-handoff` để biết template handoff 5 sections đầy đủ.
26
+
27
+ #### Bước 1: Rà soát và Publish Contract
28
+ Chạy nội dung tương đương `PUBLISH-CONTRACT` nếu chưa chạy. Xác minh `contracts/{feature}-api.yaml` đã có đầy đủ.
29
+
30
+ #### Bước 2: Append Entry vào Handoff Log (5 SECTIONS)
31
+ **KHÔNG** tạo file riêng cho mỗi feature. Thay vào đó, APPEND entry vào `.evnict/handoff/handoff.md`.
32
+
33
+ Nếu file chưa tồn tại → tạo mới với header:
34
+ ```markdown
35
+ # Agent Handoff Log
36
+ > File này dùng để trao đổi giữa BE Agent và FE Agent.
37
+ > Mỗi issue ghi theo format bên dưới.
38
+
39
+ ---
40
+ ```
41
+
42
+ Mỗi entry PHẢI có 5 sections:
43
+
44
+ | Section | Nội dung |
45
+ |---------|----------|
46
+ | 1. Tổng quan | Tasks hoàn thành, files, tests, commits |
47
+ | 2. API Contract | Endpoints, DTO→TypeScript interface, Validation rules sync, Request/Response format |
48
+ | 3. Yêu cầu FE | FE tasks, UI requirements, Dropdowns cần load |
49
+ | 4. Lưu ý | ResponseData wrapper, Auth, Date format, Audit fields |
50
+ | 5. Hướng dẫn | Step-by-step cho FE Agent |
51
+
52
+ Entry format:
53
+ ```markdown
54
+ ### [{YYYY-MM-DD}] BE→FE: {Feature Name} — Handoff
55
+ - **Trạng thái:** 🔴 Chờ xử lý
56
+ - **Mô tả:** {tóm tắt}
57
+ - **API liên quan:** {endpoints}
58
+ - **Kết quả xử lý:** _(FE Agent điền sau khi implement xong)_
59
+ ```
60
+
61
+ #### Bước 3: DTO → TypeScript Interface
62
+ Agent PHẢI đọc Java DTO file và generate TypeScript interface:
63
+ ```typescript
64
+ // Auto-generated từ {EntityName}DTO.java
65
+ export interface {EntityName} {
66
+ ID: number;
67
+ FIELD_NAME: string; // @JsonProperty("FIELD_NAME")
68
+ NULLABLE_FIELD?: string; // nullable → optional (?)
69
+ // ... map tất cả fields
70
+ }
71
+ ```
72
+
73
+ **Quy tắc mapping:**
74
+ | Java Type | TypeScript Type | Note |
75
+ |-----------|----------------|------|
76
+ | Long, Integer, int | number | |
77
+ | String | string | |
78
+ | Date, LocalDate | string | format: yyyy-MM-dd |
79
+ | BigDecimal | number | |
80
+ | Boolean | boolean | |
81
+ | Nullable field | optional (?) | thêm `?` |
82
+
83
+ #### Bước 4: Validation Rules Sync Table
84
+ Trích validation từ BE code → tạo sync table:
85
+ ```markdown
86
+ | Field | Rule | Error Message (tiếng Việt) |
87
+ |-------|------|---------------------------|
88
+ | {FIELD} | Required | "Vui lòng nhập {field}" |
89
+ | {FIELD} | MaxLength(N) | "Không quá N ký tự" |
90
+ ```
91
+ FE PHẢI implement cùng validation rules để đảm bảo UX nhất quán.
92
+
93
+ #### Bước 5: Update BE Status
94
+ Cập nhật `.evnict/handoff/be-status.md`:
95
+ ```markdown
96
+ # BE Status
97
+ status: done
98
+ feature: {feature-name}
99
+ completed_tasks: ["task-01", "task-02", ...]
100
+ api_contract: handoff/contracts/{feature}-api.yaml
101
+ handoff_entry: handoff/handoff.md (entry [{date}] BE→FE: {feature})
102
+ last_updated: {timestamp}
103
+ ```
104
+
105
+ #### Khi Agent bên kia xử lý xong
106
+ Update status entry trong `handoff.md`:
107
+ - Đổi `🔴 Chờ xử lý` → `🟢 Đã xử lý`
108
+ - Điền **Kết quả xử lý:** {mô tả kết quả}
109
+
110
+ ---
111
+
112
+ ### Action: PUBLISH-CONTRACT — BE agent ghi API contract
113
+
114
+ #### Bước 1: Tạo contract file
115
+ Sau khi BE hoàn thành API, tạo file:
116
+ `.evnict/handoff/contracts/{feature}-api.yaml`
117
+
118
+ ```yaml
119
+ feature: {feature-name}
120
+ generated: {YYYY-MM-DD HH:mm}
121
+ status: ready
122
+ base_url: /api/{module}
123
+
124
+ endpoints:
125
+ - method: GET
126
+ path: /api/{module}
127
+ description: Danh sách (phân trang)
128
+ auth: required
129
+ params:
130
+ keyword: { type: string, required: false }
131
+ page: { type: integer, default: 0 }
132
+ size: { type: integer, default: 20 }
133
+ response:
134
+ 200:
135
+ schema: ResponseData
136
+ data:
137
+ content: [{ id: number, name: string, ... }]
138
+ totalElements: number
139
+ totalPages: number
140
+
141
+ - method: POST
142
+ path: /api/{module}
143
+ description: Tạo mới
144
+ auth: required
145
+ body:
146
+ name: { type: string, required: true, maxLength: 200 }
147
+ phone: { type: string, pattern: "^0[0-9]{9}$" }
148
+ response:
149
+ 200:
150
+ schema: ResponseData
151
+ data: { id: number, name: string, ... }
152
+ 400:
153
+ schema: ResponseData
154
+ error: { message: string }
155
+ ```
156
+
157
+ #### Bước 2: Update BE status
158
+ Cập nhật `.evnict/handoff/be-status.md`:
159
+ ```markdown
160
+ # BE Status
161
+ status: done
162
+ feature: {feature-name}
163
+ completed_tasks: ["task-01", "task-02", "task-03", "task-04"]
164
+ api_contract: handoff/contracts/{feature}-api.yaml
165
+ last_updated: {timestamp}
166
+ notes: {any important notes for FE agent}
167
+ ```
168
+
169
+ ---
170
+
171
+ ### Action: READ-CONTRACT — FE agent đọc contract
172
+
173
+ #### Bước 1: Check BE status
174
+ ```bash
175
+ cat .evnict/handoff/be-status.md
176
+ # → status: done? → Đọc contract
177
+ # → status: in-progress? → CHƯA sẵn sàng, chờ
178
+ ```
179
+
180
+ #### Bước 2: Đọc contract
181
+ ```bash
182
+ cat .evnict/handoff/contracts/{feature}-api.yaml
183
+ ```
184
+
185
+ #### Bước 3: Generate FE service từ contract
186
+ Dựa vào contract → tạo Angular service:
187
+ ```typescript
188
+ // Auto-generated from contract
189
+ @Injectable({ providedIn: 'root' })
190
+ export class {Module}Service {
191
+ private baseUrl = `${environment.apiUrl}/api/{module}`;
192
+
193
+ constructor(private http: HttpClient) {}
194
+
195
+ // Từ GET endpoint trong contract
196
+ search(keyword: string, page: number, size: number): Observable<ResponseData> {
197
+ const params = new HttpParams()
198
+ .set('keyword', keyword)
199
+ .set('page', page.toString())
200
+ .set('size', size.toString());
201
+ return this.http.get<ResponseData>(this.baseUrl, { params });
202
+ }
203
+
204
+ // Từ POST endpoint trong contract
205
+ create(dto: {Module}DTO): Observable<ResponseData> {
206
+ return this.http.post<ResponseData>(this.baseUrl, dto);
207
+ }
208
+ }
209
+ ```
210
+
211
+ ---
212
+
213
+ ### Action: UPDATE-STATUS — Cập nhật status
214
+
215
+ #### BE Status format
216
+ ```markdown
217
+ # BE Status
218
+ status: idle | in-progress | done | blocked
219
+ feature: {feature-name}
220
+ current_task: task-{N}
221
+ completed_tasks: ["task-01", "task-02"]
222
+ blocked_reason: {reason if blocked}
223
+ last_updated: {timestamp}
224
+ ```
225
+
226
+ #### FE Status format
227
+ ```markdown
228
+ # FE Status
229
+ status: idle | waiting-be | in-progress | done | blocked
230
+ feature: {feature-name}
231
+ current_task: task-{N}
232
+ completed_tasks: ["task-05", "task-06"]
233
+ waiting_for: {what FE is waiting for}
234
+ last_updated: {timestamp}
235
+ ```
236
+
237
+ ---
238
+
239
+ ### Action: CHECK-STATUS — Kiểm tra status
240
+
241
+ Đọc cả 2 file status → tóm tắt:
242
+ ```markdown
243
+ ## 📊 Feature: {feature-name}
244
+
245
+ ### Backend
246
+ - Status: {status}
247
+ - Progress: {completed}/{total} tasks
248
+ - API Contract: {ready/not-ready}
249
+
250
+ ### Frontend
251
+ - Status: {status}
252
+ - Progress: {completed}/{total} tasks
253
+ - Waiting for: {if any}
254
+
255
+ ### Next Action
256
+ - {Recommendation based on status}
257
+ ```
258
+
259
+ ---
260
+
261
+ ## Error Handling
262
+
263
+ ### DỪNG khi:
264
+ - FE agent cố implement mà BE chưa done → Phải chờ hoặc dùng mock
265
+ - Contract file không tồn tại → BE chưa publish
266
+ - Contract format lỗi → Nhắc fix trước
267
+
268
+ ---
269
+
270
+ ## Tiêu chí hoàn thành
271
+ - [ ] Contract file tạo đúng format YAML
272
+ - [ ] Status files cập nhật đúng
273
+ - [ ] FE agent đọc được contract
274
+ - [ ] Status sync giữa BE ↔ FE chính xác
@@ -0,0 +1,281 @@
1
+ ---
2
+ name: evnict-kit-create-api
3
+ description: Tạo API endpoint chuẩn EVNICT — DTO→Controller→Service→Repository→Test. Code examples Spring Boot + JOOQ + Oracle.
4
+ compatibility: Java Spring Boot, JOOQ, Oracle
5
+ ---
6
+
7
+ # evnict-kit-create-api — Tạo API Endpoint
8
+
9
+ ## Khi nào dùng
10
+ - Tạo API endpoint mới (CRUD hoặc custom)
11
+ - Thêm endpoint vào module có sẵn
12
+ - Tạo module mới với đầy đủ layers
13
+
14
+ ## Input Parameters
15
+ - `module` (bắt buộc): Tên module (VD: customer, order, don-vi)
16
+ - `action` (bắt buộc): Loại endpoint (list, get, create, update, delete, search, custom)
17
+ - `description` (bắt buộc): Mô tả API
18
+ - `fields` (optional): Danh sách fields cho DTO
19
+
20
+ ---
21
+
22
+ ## Workflow Steps
23
+
24
+ ### Bước 1: Xác định cấu trúc
25
+ Đọc conventions từ `.agent/rules/03-evnict-kit-backend-conventions.md` và `05-evnict-kit-project-conventions.md`.
26
+
27
+ Xác định:
28
+ - Package path: `com.evn.{project}.{module}`
29
+ - Existing module? → Thêm vào module có sẵn
30
+ - New module? → Tạo đầy đủ package structure
31
+
32
+ ### Bước 2: Tạo DTO
33
+ ```java
34
+ // File: src/main/java/com/evn/{project}/{module}/dto/{Module}DTO.java
35
+ public class CustomerDTO {
36
+ private Long id;
37
+
38
+ @NotBlank(message = "Tên không được trống")
39
+ @Size(max = 200, message = "Tên không quá 200 ký tự")
40
+ private String name;
41
+
42
+ @Pattern(regexp = "^0[0-9]{9}$", message = "Số điện thoại không hợp lệ")
43
+ private String phone;
44
+
45
+ @NotNull(message = "Mã đơn vị bắt buộc")
46
+ private Long donViId;
47
+
48
+ // Getters, Setters (hoặc @Data nếu dùng Lombok)
49
+ }
50
+ ```
51
+
52
+ **Quy tắc DTO:**
53
+ - KHÔNG expose internal IDs, password hashes, audit fields (trừ khi cần hiển thị)
54
+ - Mỗi endpoint CÓ THỂ có DTO riêng (CreateDTO, UpdateDTO, ResponseDTO)
55
+ - Validation annotations trên DTO, KHÔNG trên Entity
56
+
57
+ ### Bước 3: Tạo Repository
58
+ ```java
59
+ // File: src/main/java/com/evn/{project}/{module}/repository/{Module}Repository.java
60
+ @Repository
61
+ @RequiredArgsConstructor
62
+ public class CustomerRepository {
63
+ private final DSLContext dsl;
64
+
65
+ // LIST — paginated
66
+ public Page<CustomerDTO> search(String keyword, int page, int size, String sortBy) {
67
+ Condition condition = DSL.trueCondition();
68
+ if (StringUtils.isNotBlank(keyword)) {
69
+ condition = condition.and(
70
+ CUSTOMER.NAME.containsIgnoreCase(keyword)
71
+ .or(CUSTOMER.PHONE.containsIgnoreCase(keyword))
72
+ );
73
+ }
74
+
75
+ int total = dsl.fetchCount(CUSTOMER, condition);
76
+ List<CustomerDTO> data = dsl.selectFrom(CUSTOMER)
77
+ .where(condition)
78
+ .orderBy(getSortField(sortBy))
79
+ .offset(page * size)
80
+ .limit(size)
81
+ .fetchInto(CustomerDTO.class);
82
+
83
+ return new Page<>(data, total, page, size);
84
+ }
85
+
86
+ // GET by ID
87
+ public CustomerDTO findById(Long id) {
88
+ return dsl.selectFrom(CUSTOMER)
89
+ .where(CUSTOMER.ID.eq(id))
90
+ .fetchOneInto(CustomerDTO.class);
91
+ }
92
+
93
+ // CREATE
94
+ public CustomerDTO create(CustomerDTO dto) {
95
+ CustomerRecord record = dsl.newRecord(CUSTOMER);
96
+ record.setName(dto.getName());
97
+ record.setPhone(dto.getPhone());
98
+ record.setDonViId(dto.getDonViId());
99
+ record.setCreatedDate(LocalDateTime.now());
100
+ record.store();
101
+ return record.into(CustomerDTO.class);
102
+ }
103
+
104
+ // UPDATE
105
+ public int update(Long id, CustomerDTO dto) {
106
+ return dsl.update(CUSTOMER)
107
+ .set(CUSTOMER.NAME, dto.getName())
108
+ .set(CUSTOMER.PHONE, dto.getPhone())
109
+ .set(CUSTOMER.UPDATED_DATE, LocalDateTime.now())
110
+ .where(CUSTOMER.ID.eq(id))
111
+ .execute();
112
+ }
113
+
114
+ // DELETE (soft delete)
115
+ public int delete(Long id) {
116
+ return dsl.update(CUSTOMER)
117
+ .set(CUSTOMER.IS_DELETED, 1)
118
+ .where(CUSTOMER.ID.eq(id))
119
+ .execute();
120
+ }
121
+
122
+ // Sort field whitelist
123
+ private SortField<?> getSortField(String sortBy) {
124
+ return switch (sortBy) {
125
+ case "name" -> CUSTOMER.NAME.asc();
126
+ case "createdDate" -> CUSTOMER.CREATED_DATE.desc();
127
+ default -> CUSTOMER.ID.desc();
128
+ };
129
+ }
130
+ }
131
+ ```
132
+
133
+ ### Bước 4: Tạo Service
134
+ ```java
135
+ // File: src/main/java/com/evn/{project}/{module}/service/{Module}Service.java
136
+ @Service
137
+ @RequiredArgsConstructor
138
+ @Slf4j
139
+ public class CustomerService {
140
+ private final CustomerRepository customerRepository;
141
+
142
+ public Page<CustomerDTO> search(String keyword, int page, int size, String sortBy) {
143
+ return customerRepository.search(keyword, page, size, sortBy);
144
+ }
145
+
146
+ public CustomerDTO getById(Long id) {
147
+ CustomerDTO dto = customerRepository.findById(id);
148
+ if (dto == null) {
149
+ throw new BusinessException("Không tìm thấy khách hàng với ID: " + id);
150
+ }
151
+ return dto;
152
+ }
153
+
154
+ @Transactional
155
+ public CustomerDTO create(CustomerDTO dto) {
156
+ // Business validation
157
+ validateBusinessRules(dto);
158
+ return customerRepository.create(dto);
159
+ }
160
+
161
+ @Transactional
162
+ public void update(Long id, CustomerDTO dto) {
163
+ // Check exists
164
+ getById(id);
165
+ // Business validation
166
+ validateBusinessRules(dto);
167
+ customerRepository.update(id, dto);
168
+ }
169
+
170
+ @Transactional
171
+ public void delete(Long id) {
172
+ // Check exists
173
+ getById(id);
174
+ customerRepository.delete(id);
175
+ }
176
+
177
+ private void validateBusinessRules(CustomerDTO dto) {
178
+ // Add business-specific validations here
179
+ }
180
+ }
181
+ ```
182
+
183
+ ### Bước 5: Tạo Controller
184
+ ```java
185
+ // File: src/main/java/com/evn/{project}/{module}/controller/{Module}Controller.java
186
+ @RestController
187
+ @RequestMapping("/api/{module}")
188
+ @RequiredArgsConstructor
189
+ @Slf4j
190
+ public class CustomerController {
191
+
192
+ private final CustomerService customerService;
193
+
194
+ @GetMapping
195
+ public ResponseEntity<ResponseData> search(
196
+ @RequestParam(defaultValue = "") String keyword,
197
+ @RequestParam(defaultValue = "0") @Min(0) int page,
198
+ @RequestParam(defaultValue = "20") @Min(1) @Max(100) int size,
199
+ @RequestParam(defaultValue = "id") String sortBy) {
200
+ return ResponseEntity.ok(ResponseData.ok(
201
+ customerService.search(keyword, page, size, sortBy)
202
+ ));
203
+ }
204
+
205
+ @GetMapping("/{id}")
206
+ public ResponseEntity<ResponseData> getById(@PathVariable Long id) {
207
+ return ResponseEntity.ok(ResponseData.ok(customerService.getById(id)));
208
+ }
209
+
210
+ @PostMapping
211
+ public ResponseEntity<ResponseData> create(@Valid @RequestBody CustomerDTO dto) {
212
+ return ResponseEntity.ok(ResponseData.ok(customerService.create(dto)));
213
+ }
214
+
215
+ @PutMapping("/{id}")
216
+ public ResponseEntity<ResponseData> update(
217
+ @PathVariable Long id,
218
+ @Valid @RequestBody CustomerDTO dto) {
219
+ customerService.update(id, dto);
220
+ return ResponseEntity.ok(ResponseData.ok("Cập nhật thành công"));
221
+ }
222
+
223
+ @DeleteMapping("/{id}")
224
+ public ResponseEntity<ResponseData> delete(@PathVariable Long id) {
225
+ customerService.delete(id);
226
+ return ResponseEntity.ok(ResponseData.ok("Xóa thành công"));
227
+ }
228
+ }
229
+ ```
230
+
231
+ ### Bước 6: Viết Tests (TDD)
232
+ Dùng skill `evnict-kit-tdd` → viết test cho:
233
+ 1. Service layer: business logic, edge cases, validation
234
+ 2. Controller layer: request/response, validation errors
235
+ 3. Repository layer: query correctness (nếu có DB test)
236
+
237
+ ### Bước 7: Verify
238
+ ```bash
239
+ ./mvnw test # All tests pass
240
+ ./mvnw spotless:check # Lint
241
+ ./mvnw compile # Build
242
+ ```
243
+
244
+ ### Bước 8: Security check
245
+ - [ ] Endpoint có authentication filter?
246
+ - [ ] Roles/permissions đã khai báo?
247
+ - [ ] Input validation đầy đủ?
248
+ - [ ] Không expose sensitive fields trong DTO?
249
+ - [ ] SQL injection safe? (JOOQ type-safe)
250
+
251
+ ---
252
+
253
+ ## File Checklist (cho CRUD endpoint mới)
254
+ - [ ] `{Module}DTO.java` — với validation annotations
255
+ - [ ] `{Module}Repository.java` — JOOQ queries
256
+ - [ ] `{Module}Service.java` — business logic + @Transactional
257
+ - [ ] `{Module}Controller.java` — REST endpoints + @Valid
258
+ - [ ] `{Module}ServiceTest.java` — unit tests
259
+ - [ ] `{Module}ControllerTest.java` — MockMvc tests
260
+ - [ ] Security config updated (nếu cần whitelist)
261
+ - [ ] Migration script (nếu table mới)
262
+
263
+ ---
264
+
265
+ ## Error Handling
266
+
267
+ ### DỪNG khi:
268
+ - Table chưa tồn tại trong DB → Tạo migration trước (dùng skill `evnict-kit-database-migration`)
269
+ - Module structure không rõ → Đọc conventions hoặc hỏi user
270
+ - Conflict với endpoint hiện có → Báo user
271
+
272
+ ---
273
+
274
+ ## Tiêu chí hoàn thành
275
+ - [ ] DTO tạo đúng với validation
276
+ - [ ] Repository dùng JOOQ type-safe
277
+ - [ ] Service có business logic + @Transactional
278
+ - [ ] Controller return ResponseData
279
+ - [ ] Tests viết và PASS
280
+ - [ ] Security check PASS
281
+ - [ ] Build + lint PASS