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.
- package/README.md +19 -0
- package/bin/cli.js +38 -0
- package/package.json +48 -0
- package/src/commands/add.js +129 -0
- package/src/commands/init-check.js +19 -0
- package/src/commands/init-context.js +37 -0
- package/src/commands/init-rules.js +42 -0
- package/src/commands/init-workflow.js +36 -0
- package/src/commands/init.js +722 -0
- package/src/utils/config.js +167 -0
- package/src/utils/file.js +53 -0
- package/templates/GETTING-STARTED.md +196 -0
- package/templates/content/context/AGENTS.md.template +462 -0
- package/templates/content/rules/01-evnict-kit-general-rules.md +303 -0
- package/templates/content/rules/02-evnict-kit-security-rules.md +423 -0
- package/templates/content/rules/03-evnict-kit-backend-conventions.md +383 -0
- package/templates/content/rules/04-evnict-kit-frontend-conventions.md +402 -0
- package/templates/content/rules/05-evnict-kit-project-conventions.md +228 -0
- package/templates/content/skills/evnict-kit-brainstorm/SKILL.md +140 -0
- package/templates/content/skills/evnict-kit-bug-fix/SKILL.md +108 -0
- package/templates/content/skills/evnict-kit-checkpoint/SKILL.md +156 -0
- package/templates/content/skills/evnict-kit-code-review/SKILL.md +158 -0
- package/templates/content/skills/evnict-kit-coordinate/SKILL.md +274 -0
- package/templates/content/skills/evnict-kit-create-api/SKILL.md +281 -0
- package/templates/content/skills/evnict-kit-create-component/SKILL.md +263 -0
- package/templates/content/skills/evnict-kit-create-page/SKILL.md +247 -0
- package/templates/content/skills/evnict-kit-database-migration/SKILL.md +164 -0
- package/templates/content/skills/evnict-kit-doc-postmortem/SKILL.md +93 -0
- package/templates/content/skills/evnict-kit-finish-branch/SKILL.md +87 -0
- package/templates/content/skills/evnict-kit-fix-attt/SKILL.md +129 -0
- package/templates/content/skills/evnict-kit-fix-business-logic/SKILL.md +89 -0
- package/templates/content/skills/evnict-kit-git-worktrees/SKILL.md +104 -0
- package/templates/content/skills/evnict-kit-merge-checklist/SKILL.md +108 -0
- package/templates/content/skills/evnict-kit-onboard/SKILL.md +143 -0
- package/templates/content/skills/evnict-kit-prompt-standard/SKILL.md +103 -0
- package/templates/content/skills/evnict-kit-receiving-review/SKILL.md +89 -0
- package/templates/content/skills/evnict-kit-security-audit/SKILL.md +190 -0
- package/templates/content/skills/evnict-kit-spec/SKILL.md +237 -0
- package/templates/content/skills/evnict-kit-tdd/SKILL.md +413 -0
- package/templates/content/skills/evnict-kit-wiki/SKILL.md +412 -0
- package/templates/content/workflows/evnict-kit-archive-wiki.md +100 -0
- package/templates/content/workflows/evnict-kit-attt.md +100 -0
- package/templates/content/workflows/evnict-kit-bug-fix.md +107 -0
- package/templates/content/workflows/evnict-kit-feature-large.md +393 -0
- package/templates/content/workflows/evnict-kit-feature-small.md +86 -0
- package/templates/content/workflows/evnict-kit-handoff.md +243 -0
- package/templates/content/workflows/evnict-kit-implement.md +247 -0
- package/templates/content/workflows/evnict-kit-init-check.md +76 -0
- package/templates/content/workflows/evnict-kit-init-context.md +58 -0
- package/templates/content/workflows/evnict-kit-init-rules.md +114 -0
- package/templates/content/workflows/evnict-kit-init-wiki.md +80 -0
- package/templates/content/workflows/evnict-kit-plan.md +308 -0
- package/templates/content/workflows/evnict-kit-review.md +53 -0
- package/templates/content/workflows/evnict-kit-spec-archive.md +53 -0
- package/templates/content/workflows/evnict-kit-wiki-archive-feature.md +164 -0
- package/templates/content/workflows/evnict-kit-wiki-query.md +91 -0
- package/templates/content/workflows/evnict-kit-wiki-scan-project.md +272 -0
- package/templates/context/AGENT.md.template +9 -0
- package/templates/context/AGENTS.md.template +462 -0
- package/templates/context/CLAUDE.md.template +301 -0
- package/templates/context/copilot-instructions.md.template +60 -0
- package/templates/context/cursorrules.template +114 -0
- package/templates/instruct/Instruct-Agent-AI.be.md +96 -0
- package/templates/instruct/Instruct-Agent-AI.fe.md +79 -0
- package/templates/rules/antigravity/01-evnict-kit-general-rules.md +303 -0
- package/templates/rules/antigravity/02-evnict-kit-security-rules.md +423 -0
- package/templates/rules/antigravity/03-evnict-kit-backend-conventions.md +383 -0
- package/templates/rules/antigravity/04-evnict-kit-frontend-conventions.md +402 -0
- package/templates/rules/antigravity/05-evnict-kit-project-conventions.md +228 -0
- package/templates/rules/claude/README.md +8 -0
- package/templates/rules/cursor/01-evnict-kit-general-rules.mdc +46 -0
- package/templates/rules/cursor/02-evnict-kit-security-rules.mdc +46 -0
- package/templates/rules/cursor/03-evnict-kit-backend-conventions.mdc +50 -0
- package/templates/rules/cursor/04-evnict-kit-frontend-conventions.mdc +43 -0
- package/templates/rules/cursor/05-evnict-kit-project-conventions.mdc +63 -0
- package/templates/rules/cursor/README.md +7 -0
- package/templates/skills/evnict-kit-brainstorm/SKILL.md +140 -0
- package/templates/skills/evnict-kit-bug-fix/SKILL.md +108 -0
- package/templates/skills/evnict-kit-checkpoint/SKILL.md +156 -0
- package/templates/skills/evnict-kit-code-review/SKILL.md +158 -0
- package/templates/skills/evnict-kit-coordinate/SKILL.md +274 -0
- package/templates/skills/evnict-kit-create-api/SKILL.md +281 -0
- package/templates/skills/evnict-kit-create-component/SKILL.md +263 -0
- package/templates/skills/evnict-kit-create-page/SKILL.md +247 -0
- package/templates/skills/evnict-kit-database-migration/SKILL.md +164 -0
- package/templates/skills/evnict-kit-doc-postmortem/SKILL.md +93 -0
- package/templates/skills/evnict-kit-finish-branch/SKILL.md +87 -0
- package/templates/skills/evnict-kit-fix-attt/SKILL.md +129 -0
- package/templates/skills/evnict-kit-fix-business-logic/SKILL.md +89 -0
- package/templates/skills/evnict-kit-git-worktrees/SKILL.md +104 -0
- package/templates/skills/evnict-kit-merge-checklist/SKILL.md +108 -0
- package/templates/skills/evnict-kit-onboard/SKILL.md +143 -0
- package/templates/skills/evnict-kit-prompt-standard/SKILL.md +103 -0
- package/templates/skills/evnict-kit-receiving-review/SKILL.md +89 -0
- package/templates/skills/evnict-kit-security-audit/SKILL.md +190 -0
- package/templates/skills/evnict-kit-spec/SKILL.md +237 -0
- package/templates/skills/evnict-kit-tdd/SKILL.md +413 -0
- package/templates/skills/evnict-kit-wiki/SKILL.md +412 -0
- package/templates/wiki/README.md +35 -0
- package/templates/wiki/config.example.yaml +17 -0
- package/templates/wiki/package.json +17 -0
- package/templates/wiki/raw/notes/.gitkeep +1 -0
- package/templates/wiki/scripts/ingest.js +66 -0
- package/templates/workflows/antigravity/evnict-kit-archive-wiki.md +100 -0
- package/templates/workflows/antigravity/evnict-kit-attt.md +100 -0
- package/templates/workflows/antigravity/evnict-kit-bug-fix.md +107 -0
- package/templates/workflows/antigravity/evnict-kit-feature-large.md +393 -0
- package/templates/workflows/antigravity/evnict-kit-feature-small.md +86 -0
- package/templates/workflows/antigravity/evnict-kit-handoff.md +243 -0
- package/templates/workflows/antigravity/evnict-kit-implement.md +247 -0
- package/templates/workflows/antigravity/evnict-kit-init-check.md +76 -0
- package/templates/workflows/antigravity/evnict-kit-init-context.md +58 -0
- package/templates/workflows/antigravity/evnict-kit-init-rules.md +114 -0
- package/templates/workflows/antigravity/evnict-kit-init-wiki.md +80 -0
- package/templates/workflows/antigravity/evnict-kit-plan.md +308 -0
- package/templates/workflows/antigravity/evnict-kit-review.md +53 -0
- package/templates/workflows/antigravity/evnict-kit-spec-archive.md +53 -0
- package/templates/workflows/antigravity/evnict-kit-wiki-archive-feature.md +164 -0
- package/templates/workflows/antigravity/evnict-kit-wiki-query.md +91 -0
- package/templates/workflows/antigravity/evnict-kit-wiki-scan-project.md +272 -0
- package/templates/workflows/claude/README.md +6 -0
- package/templates/workflows/claude/evnict-kit-archive-wiki.md +98 -0
- package/templates/workflows/claude/evnict-kit-attt.md +98 -0
- package/templates/workflows/claude/evnict-kit-bug-fix.md +105 -0
- package/templates/workflows/claude/evnict-kit-feature-large.md +391 -0
- package/templates/workflows/claude/evnict-kit-feature-small.md +84 -0
- package/templates/workflows/claude/evnict-kit-handoff.md +240 -0
- package/templates/workflows/claude/evnict-kit-implement.md +245 -0
- package/templates/workflows/claude/evnict-kit-init-check.md +74 -0
- package/templates/workflows/claude/evnict-kit-init-context.md +56 -0
- package/templates/workflows/claude/evnict-kit-init-rules.md +112 -0
- package/templates/workflows/claude/evnict-kit-init-wiki.md +78 -0
- package/templates/workflows/claude/evnict-kit-plan.md +305 -0
- package/templates/workflows/claude/evnict-kit-review.md +51 -0
- package/templates/workflows/claude/evnict-kit-spec-archive.md +51 -0
- package/templates/workflows/claude/evnict-kit-wiki-archive-feature.md +162 -0
- package/templates/workflows/claude/evnict-kit-wiki-query.md +89 -0
- 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
|