@teamix-evo/skills 0.2.0 → 0.3.0

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 (26) hide show
  1. package/README.md +18 -8
  2. package/_template/SKILL.md.hbs +14 -1
  3. package/manifest.json +59 -2
  4. package/package.json +6 -2
  5. package/skills/teamix-evo-coding-conventions/SKILL.md +92 -0
  6. package/skills/teamix-evo-coding-conventions/api-layering.md +225 -0
  7. package/skills/teamix-evo-coding-conventions/checklist.md +173 -0
  8. package/skills/teamix-evo-coding-conventions/error-and-loading.md +269 -0
  9. package/skills/teamix-evo-coding-conventions/file-structure.md +273 -0
  10. package/skills/teamix-evo-coding-conventions/forms-and-validation.md +220 -0
  11. package/skills/teamix-evo-coding-conventions/reuse-first.md +122 -0
  12. package/skills/teamix-evo-coding-conventions/routing-and-codesplit.md +298 -0
  13. package/skills/teamix-evo-coding-conventions/testing.md +313 -0
  14. package/skills/teamix-evo-design-rules/SKILL.md +86 -0
  15. package/skills/teamix-evo-design-rules/boundaries.md +89 -0
  16. package/skills/teamix-evo-design-rules/checklist.md +108 -0
  17. package/skills/teamix-evo-design-rules/generation-flow.md +142 -0
  18. package/skills/teamix-evo-design-rules/prompts/page-design.md +148 -0
  19. package/skills/teamix-evo-design-rules-opentrek/SKILL.md +48 -0
  20. package/skills/teamix-evo-design-rules-opentrek/brand-rules.md +74 -0
  21. package/skills/teamix-evo-design-rules-uni-manager/SKILL.md +51 -0
  22. package/skills/teamix-evo-design-rules-uni-manager/ai-scenarios.md +51 -0
  23. package/skills/teamix-evo-design-rules-uni-manager/command-center.md +108 -0
  24. package/skills/teamix-evo-design-rules-uni-manager/danger-ops.md +87 -0
  25. package/skills/teamix-evo-manage/SKILL.md +80 -40
  26. package/skills/teamix-evo-ui-upgrade/SKILL.md +75 -0
package/README.md CHANGED
@@ -39,14 +39,24 @@ pnpm --filter @teamix-evo/skills scaffold:skill
39
39
 
40
40
  ## 消费态命令(CLI)
41
41
 
42
- 由 `teamix-evo skills` 子命令族管理:
42
+ 由 `teamix-evo skills` 子命令族管理(source-mirror 模型见 [ADR 0013](../../docs/adr/0013-skills-source-mirror.md)):
43
43
 
44
44
  ```bash
45
- teamix-evo skills add # 全量装入 manifest 内所有 skill
46
- teamix-evo skills add <name> # 增量:仅装入指定 skill(可多个)
47
- teamix-evo skills list # 列出全部 skill(默认:已装✓ / 未装○)
48
- teamix-evo skills ls # 别名
49
- teamix-evo skills list --installed # 仅看已装
50
- teamix-evo skills update # 升级到最新版(managed 策略保留你的自定义)
51
- teamix-evo skills uninstall # 卸载
45
+ teamix-evo skills add # 全量装入 manifest 内所有 skill
46
+ teamix-evo skills add <name...> # 增量:仅装入指定 skill(可多个)
47
+ teamix-evo skills list # 列出全部 skill(默认:已装✓ / 未装○)
48
+ teamix-evo skills update # 升级到最新版(managed 策略保留你的自定义)
49
+ teamix-evo skills sync [name...] # 源 → IDE 镜像(漂移恢复用)
50
+ teamix-evo skills doctor # 检测源 / 镜像漂移
51
+ teamix-evo skills uninstall # 卸载(源 + 镜像 + lock)
52
+ ```
53
+
54
+ ### 全局装机(`--scope global`)
55
+
56
+ ```bash
57
+ # 一行装到 ~/.qoder/skills/ 与 ~/.claude/skills/,元数据落到 ~/.teamix-evo-global/
58
+ npx teamix-evo@latest skills add teamix-evo-manage --scope global --ide claude,qoder -y
59
+
60
+ # 后续维护(update / uninstall 等)需 cd 到全局元数据根
61
+ cd ~/.teamix-evo-global && npx teamix-evo skills update
52
62
  ```
@@ -1,6 +1,10 @@
1
1
  ---
2
2
  name: {{name}}
3
- description: {{description}}
3
+ description: |
4
+ {{description}}
5
+ TRIGGER when: <列举正向触发场景 / 短语 / 文件路径模式;3-8 条;具体优于抽象>
6
+ SKIP: <显式列出**不该**自己上的相邻情形,至少 2 条;指明该走哪个 skill>
7
+ Coordinates with: <可选;声明同时激活的 sibling skill>
4
8
  ---
5
9
 
6
10
  # {{title}}
@@ -9,3 +13,12 @@ description: {{description}}
9
13
  <!-- teamix-evo:managed:end id="core" -->
10
14
 
11
15
  > 这部分内容由你(用户)自行编辑,CLI 升级时不会覆盖。
16
+
17
+ <!--
18
+ description 写法见 [ADR 0015](../../../docs/adr/0015-skill-description-trigger-contract.md):
19
+ - 第一行:一句话能力声明,≤ 100 字符,以动词开头
20
+ - TRIGGER when:正向枚举,具体优于抽象,中英文 + 文件 glob 三类信号都收
21
+ - SKIP:负向边界,**强制**字段,显式让位给相邻 skill
22
+ - Coordinates with:可选,声明协作而非抢活
23
+ manifest.json 的 description 必须与本字段完全一致(单源)。
24
+ -->
package/manifest.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://teamix-evo.dev/schema/skills-package/v1.json",
3
3
  "schemaVersion": 1,
4
4
  "package": "skills",
5
- "version": "0.1.0",
5
+ "version": "0.2.0",
6
6
  "engines": {
7
7
  "teamix-evo": ">=0.1.0"
8
8
  },
@@ -10,13 +10,70 @@
10
10
  {
11
11
  "id": "teamix-evo-manage",
12
12
  "name": "teamix-evo-manage",
13
- "description": "Manage the teamix-evo toolkit (init / update / uninstall) inside the current project. Auto-trigger when the user mentions teamix-evo, design system installation, or related lifecycle tasks.",
13
+ "description": "Manage the teamix-evo toolkit lifecycle inside the current project — install, initialize, update, list, or uninstall design / skills / ui / biz-ui / templates packages.\nTRIGGER when: user runs or asks about CLI commands `teamix-evo design init|update|list|list-variants|uninstall`, `teamix-evo skills add|update|list|uninstall|sync|doctor`, `teamix-evo ui init|add|list`, `teamix-evo biz-ui add|list-variants`, `teamix-evo templates add|list-variants`; phrases like \"init teamix-evo\"、\"set up the design system\"、\"装一下 teamix-evo\"、\"升级 teamix-evo\"、\"卸载 teamix-evo\"、\"看看装了哪些 teamix-evo 资源\"、\"加业务组件\"、\"加页面模板\"; user touches `.teamix-evo/config.json` or `.teamix-evo/manifest.json`.\nSKIP: any content task — generating components, pages, services, or reviewing screens; changes to `src/` files, design tokens, or business logic. Those go to teamix-evo-coding-conventions or teamix-evo-design-rules. SKIP if the user is mid-flow inside an already-initialized project asking to \"新增页面 / 加按钮 / 调接口\" — that's coding work, not lifecycle. SKIP placeholder→real UI migration (\"升级 UI\"、\"接入真组件\"、\"replace placeholders\") — that's owned by teamix-evo-ui-upgrade.\nCoordinates with: teamix-evo-ui-upgrade (hand off the placeholder migration loop after `ui add` is in place); otherwise lifecycle is the entry point and precedes content skills rather than co-triggering.",
14
14
  "version": "0.1.0",
15
15
  "source": "skills/teamix-evo-manage",
16
16
  "ides": ["qoder", "claude"],
17
17
  "updateStrategy": "managed",
18
18
  "managedRegions": ["core"],
19
19
  "template": false
20
+ },
21
+ {
22
+ "id": "teamix-evo-design-rules",
23
+ "name": "teamix-evo-design-rules",
24
+ "description": "Apply teamix-evo design system rules when AI is asked to generate or review a full UI screen / page (variant-neutral baseline).\nTRIGGER when: user asks to \"新建 / 优化 / 重构 一个页面\"、\"做一个列表页 / 详情页 / 表单页 / 仪表盘\"、\"create / review / refactor a page、screen、dashboard、template\"; intent involves layout structure, page-level information density, or multi-component composition; file write under `src/pages/**` or `src/templates/**`.\nSKIP: single-component edits like \"加个按钮\"、\"改 input 的 label\"、\"调 card 的 padding\"、\"add a tooltip\" — those go to teamix-evo-coding-conventions; pure tokens / theme override edits — those go to ESLint and `tokens.overrides.css`; pure code refactor with no visual change; teamix-evo lifecycle commands — those go to teamix-evo-manage.\nCoordinates with: teamix-evo-design-rules-<variant> (always layered when same-named variant is installed in `.teamix-evo/design/pack.lock.json`); teamix-evo-coding-conventions (run alongside when the screen also creates new files).",
25
+ "version": "0.6.0",
26
+ "source": "skills/teamix-evo-design-rules",
27
+ "ides": ["qoder", "claude"],
28
+ "updateStrategy": "managed",
29
+ "managedRegions": ["core"],
30
+ "template": false
31
+ },
32
+ {
33
+ "id": "teamix-evo-design-rules-opentrek",
34
+ "name": "teamix-evo-design-rules-opentrek",
35
+ "description": "Apply OpenTrek brand-specific design rules (tone / voice / brand color usage) on top of the variant-neutral teamix-evo-design-rules baseline.\nTRIGGER when: teamix-evo-design-rules is triggering AND the project's `.teamix-evo/design/pack.lock.json` records `variant: \"opentrek\"`; tasks where brand voice, copywriting tone, or OpenTrek-specific brand color usage is in scope; phrases like \"OpenTrek 风格\"、\"沉稳 / 克制 的文案\"、\"按 OpenTrek 调性写空态 / 标题 / 引导\".\nSKIP: project does not have OpenTrek variant installed; tasks belonging to other variants (`uni-manager` etc.); single-component edits with no copy / brand concern; pure structural / layout decisions with no brand surface.\nCoordinates with: teamix-evo-design-rules (mandatory — this is an overlay, never standalone).",
36
+ "version": "0.1.0",
37
+ "source": "skills/teamix-evo-design-rules-opentrek",
38
+ "variant": "opentrek",
39
+ "ides": ["qoder", "claude"],
40
+ "updateStrategy": "managed",
41
+ "managedRegions": ["core"],
42
+ "template": false
43
+ },
44
+ {
45
+ "id": "teamix-evo-design-rules-uni-manager",
46
+ "name": "teamix-evo-design-rules-uni-manager",
47
+ "description": "Apply cloud-management-specific design rules (AI scenarios A-G, danger ops, command center / topology patterns) on top of the variant-neutral teamix-evo-design-rules baseline.\nTRIGGER when: teamix-evo-design-rules is triggering AND the project's `.teamix-evo/design/pack.lock.json` records `variant: \"uni-manager\"`; tasks involving 云资源管理 / AI 副驾驶场景 / 高危删除 / 跨区域迁移 / 流量切换 / 指挥官大屏 / 拓扑感知 / 告警诊断; phrases like \"做一个云资源列表 / 大屏 / 命令面板 / 危险操作确认\"; file paths involving `cockpit / topology / cloud-resources / alarm`.\nSKIP: project does not have uni-manager variant installed; tasks belonging to other variants (`opentrek` etc.); pure non-cloud business pages even within a uni-manager project (defer to baseline rules only); single-component edits with no danger-op or scenario surface.\nCoordinates with: teamix-evo-design-rules (mandatory — this is an overlay, never standalone).",
48
+ "version": "0.1.0",
49
+ "source": "skills/teamix-evo-design-rules-uni-manager",
50
+ "variant": "uni-manager",
51
+ "ides": ["qoder", "claude"],
52
+ "updateStrategy": "managed",
53
+ "managedRegions": ["core"],
54
+ "template": false
55
+ },
56
+ {
57
+ "id": "teamix-evo-ui-upgrade",
58
+ "name": "teamix-evo-ui-upgrade",
59
+ "description": "Replace `_placeholder/` UI components with real `@teamix-evo/ui` registry entries in projects scaffolded by `npm create teamix-evo -- --preset console`.\nTRIGGER when: user asks to \"升级 UI\"、\"接入真组件\"、\"替换 placeholder\"、\"upgrade UI\"、\"replace placeholders\"、\"swap in real components\"、\"make the UI real\"; user opens / edits a file under `src/components/_placeholder/**`; project contains `.teamix-evo/create/pending-ui.json`; literal `@teamix-evo:placeholder` tag appears in a file the user is touching.\nSKIP: project has no `.teamix-evo/create/pending-ui.json` (placeholders already cleared or never installed); user wants to add a NEW component that isn't on the pending list (defer to teamix-evo-coding-conventions); user is editing component internals, not migrating; user is initializing / updating teamix-evo itself (defer to teamix-evo-manage).\nCoordinates with: teamix-evo-coding-conventions (touch only when migrating import sites — file placement and reuse rules apply); teamix-evo-manage (uses `teamix-evo ui add` under the hood — but this skill drives the migration loop).",
60
+ "version": "0.1.0",
61
+ "source": "skills/teamix-evo-ui-upgrade",
62
+ "ides": ["qoder", "claude"],
63
+ "updateStrategy": "managed",
64
+ "managedRegions": ["core"],
65
+ "template": false
66
+ },
67
+ {
68
+ "id": "teamix-evo-coding-conventions",
69
+ "name": "teamix-evo-coding-conventions",
70
+ "description": "Enforce teamix-evo coding conventions in consumer business apps — reuse-first against `@teamix-evo/ui` registry, API code under `src/services/`, `src/` layering and import boundaries.\nTRIGGER when: user asks to \"新增 / 重构 / 写一个\" 组件 / 页面 / 接口 / 钩子 / 工具 / hook; phrases like \"create a CRUD page\"、\"add an API call\"、\"extract a hook\"、\"refactor this fetch\"、\"调一下接口\"、\"加个组件\"、\"加个按钮\"; file write under `src/pages/**`、`src/components/**`、`src/services/**`、`src/hooks/**`; AI is about to write any new `.tsx` / `.ts` file in a teamix-evo-installed project.\nSKIP: pure visual / layout design questions about an already-existing screen with no code change (defer to teamix-evo-design-rules); teamix-evo lifecycle (init / update / uninstall) — those go to teamix-evo-manage; changes only to design tokens / styles / theme — those go to ESLint and `tokens.overrides.css`.\nCoordinates with: teamix-evo-design-rules (run alongside when the change also creates a UI screen — design-rules handles the visual side, this skill handles file placement and reuse).",
71
+ "version": "0.1.0",
72
+ "source": "skills/teamix-evo-coding-conventions",
73
+ "ides": ["qoder", "claude"],
74
+ "updateStrategy": "managed",
75
+ "managedRegions": ["core"],
76
+ "template": false
20
77
  }
21
78
  ]
22
79
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teamix-evo/skills",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Skills (AI IDE capabilities) for Teamix Evo",
5
5
  "type": "module",
6
6
  "files": [
@@ -12,7 +12,11 @@
12
12
  "devDependencies": {
13
13
  "@clack/prompts": "^0.8.0",
14
14
  "tsx": "^4.0.0",
15
- "@teamix-evo/registry": "0.2.0"
15
+ "@teamix-evo/registry": "0.3.0"
16
+ },
17
+ "publishConfig": {
18
+ "access": "public",
19
+ "registry": "https://registry.npmjs.org/"
16
20
  },
17
21
  "scripts": {
18
22
  "validate": "tsx scripts/validate-skills.ts",
@@ -0,0 +1,92 @@
1
+ ---
2
+ name: teamix-evo-coding-conventions
3
+ description: |
4
+ Enforce teamix-evo coding conventions in consumer business apps — reuse-first against `@teamix-evo/ui` registry, API code under `src/services/`, `src/` layering and import boundaries.
5
+ TRIGGER when: user asks to "新增 / 重构 / 写一个" 组件 / 页面 / 接口 / 钩子 / 工具 / hook; phrases like "create a CRUD page"、"add an API call"、"extract a hook"、"refactor this fetch"、"调一下接口"、"加个组件"、"加个按钮"; file write under `src/pages/**`、`src/components/**`、`src/services/**`、`src/hooks/**`; AI is about to write any new `.tsx` / `.ts` file in a teamix-evo-installed project.
6
+ SKIP: pure visual / layout design questions about an already-existing screen with no code change (defer to teamix-evo-design-rules); teamix-evo lifecycle (init / update / uninstall) — those go to teamix-evo-manage; changes only to design tokens / styles / theme — those go to ESLint and `tokens.overrides.css`.
7
+ Coordinates with: teamix-evo-design-rules (run alongside when the change also creates a UI screen — design-rules handles the visual side, this skill handles file placement and reuse).
8
+ ---
9
+
10
+ # teamix-evo-coding-conventions
11
+
12
+ This skill bundles the **AI-readable engineering conventions** for consumer business apps that consume `@teamix-evo/*` packages. It complements [`teamix-evo-design-rules`](../teamix-evo-design-rules/SKILL.md) — design-rules tells AI *what a screen should look like*; this skill tells AI *where the code should live and what to reuse before writing new code*.
13
+
14
+ <!-- teamix-evo:managed:start id="core" -->
15
+
16
+ ## When to use
17
+
18
+ Activate this skill whenever AI is about to **write or refactor code** inside a consumer app that has installed teamix-evo. Common signals:
19
+
20
+ - "新增一个 xxx 页面 / 列表页 / 详情页"
21
+ - "加一个 xxx 接口 / 调用 xxx API"
22
+ - "写一个 xxx 组件"
23
+ - "把这段重构一下"
24
+ - "create a CRUD page for orders"
25
+ - "add an API call to fetch users"
26
+ - Any time AI is about to create a new file under `src/pages/`、`src/components/`、`src/services/`、`src/hooks/`
27
+
28
+ If the task is purely about *visual design* of a screen (layout / colors / spacing), use [`teamix-evo-design-rules`](../teamix-evo-design-rules/SKILL.md) instead — or run both in tandem.
29
+
30
+ ## What this skill does
31
+
32
+ Before AI writes or commits code, it performs an **8-step gated flow**. Steps 1-4 are baseline (always run); steps 5-7 are topic gates (run when the task touches that topic); step 8 is the final self-review.
33
+
34
+ 1. **Reuse-first check** — read [`reuse-first.md`](reuse-first.md). Before creating any new component, query the `@teamix-evo/ui` registry (via MCP `list_components` / `find_components`) and grep the local project. Only write new code when no reuse path exists.
35
+ 2. **Layering check** — read [`api-layering.md`](api-layering.md). Any code that talks to a backend goes under `src/services/<domain>.ts`. Components never call `fetch` / `axios` directly. Data hooks live in `src/hooks/` and consume services.
36
+ 3. **Directory check** — read [`file-structure.md`](file-structure.md). Place the new file under the right top-level folder (pages / components / services / hooks / types / utils / lib / stores / contexts). Each folder has a single responsibility and an import boundary.
37
+ 4. **Forms gate** — if the task involves a form, read [`forms-and-validation.md`](forms-and-validation.md). `react-hook-form` + `zod`; schema lives at `src/services/<domain>.schema.ts`. Never wire forms with raw `useState`.
38
+ 5. **Error/loading gate** — if the task adds a page or data hook, read [`error-and-loading.md`](error-and-loading.md). Ensure global ErrorBoundary, page-level fallback, and three-state handling (`isPending` / `isError` / data) are in place; mutations report success/error via `toast`.
39
+ 6. **Routing gate** — if the task adds a route or page-entry, read [`routing-and-codesplit.md`](routing-and-codesplit.md). Pages use `React.lazy`; auth/role guards live in `src/routes/guards.tsx`; 404 / 403 / 500 fallbacks exist.
40
+ 7. **Testing gate** — read [`testing.md`](testing.md). Pure functions and zod schemas are **mandatory** to test; hooks and reusable components are recommended. Test files sit next to the source as `*.test.ts(x)`; `vitest` + `@testing-library/react` + `msw`.
41
+ 8. **Self-review** — run [`checklist.md`](checklist.md). Every item must pass before declaring the change done. Anything failing must be fixed or surfaced to the user.
42
+
43
+ ## Inputs the user provides
44
+
45
+ - Intent: "add a page", "add a service call", "refactor X", "extract a hook", etc.
46
+ - Optional: domain entity (`order`、`user`、`tenant`、`workflow`)
47
+ - Optional: existing file the change should land near
48
+
49
+ ## Outputs
50
+
51
+ - Code placed under the conventional path (`src/pages/<id>/`、`src/services/<domain>.ts`、…)
52
+ - Reuse decisions explicitly logged ("reused `Button` from `@teamix-evo/ui`" / "no match found, wrote a new `OrderCard` under `src/components/`")
53
+ - Pass / fail status against [`checklist.md`](checklist.md)
54
+
55
+ ## How to invoke (typical flow)
56
+
57
+ 1. Parse user intent → identify which artifact is being created (page / component / service / hook / util / form / route)
58
+ 2. Read [`reuse-first.md`](reuse-first.md) and run the reuse query (MCP first, then local grep)
59
+ 3. Read [`file-structure.md`](file-structure.md) to pick the destination directory
60
+ 4. If the change touches network / backend, read [`api-layering.md`](api-layering.md)
61
+ 5. If the change involves a form, read [`forms-and-validation.md`](forms-and-validation.md)
62
+ 6. If the change adds a page / data hook, read [`error-and-loading.md`](error-and-loading.md) for fallback / Skeleton / toast wiring
63
+ 7. If the change adds a route or page entry, read [`routing-and-codesplit.md`](routing-and-codesplit.md)
64
+ 8. Decide test coverage per [`testing.md`](testing.md); write `*.test.ts(x)` next to source
65
+ 9. Write the code; cite each reuse / new-write decision in the response
66
+ 10. Run through [`checklist.md`](checklist.md); list pass / fail explicitly
67
+
68
+ ## Files in this skill
69
+
70
+ | File | Purpose |
71
+ | --- | --- |
72
+ | [`reuse-first.md`](reuse-first.md) | Decision flow for reusing existing components / utilities before writing new ones |
73
+ | [`file-structure.md`](file-structure.md) | Top-level `src/` folder layout、ownership、import boundaries、naming、global-state tier (useState → Context → store) |
74
+ | [`api-layering.md`](api-layering.md) | Where API code lives (`src/services/`)、how data flows from service → hook → component |
75
+ | [`forms-and-validation.md`](forms-and-validation.md) | `react-hook-form` + `zod` patterns、schema location、submit/error wiring |
76
+ | [`error-and-loading.md`](error-and-loading.md) | Global / page ErrorBoundary、react-query three-state、Suspense、toast、reportError |
77
+ | [`routing-and-codesplit.md`](routing-and-codesplit.md) | `React.lazy` per page、guards、404/403/500 fallback、search-params state、Suspense placement |
78
+ | [`testing.md`](testing.md) | `vitest` + RTL + msw、co-located tests、what is mandatory vs recommended |
79
+ | [`checklist.md`](checklist.md) | Multi-section self-review before declaring done |
80
+
81
+ ## Relationship to other skills
82
+
83
+ - [`teamix-evo-design-rules`](../teamix-evo-design-rules/SKILL.md) — visual / interaction rules for screen generation. Run **alongside** this skill when the task includes UI; this skill never overrides design-rules on visual concerns.
84
+ - [`teamix-evo-manage`](../teamix-evo-manage/SKILL.md) — lifecycle (`init` / `update` / `uninstall` / `skills add`). Out of scope here.
85
+
86
+ ## Why these conventions
87
+
88
+ - **Single source for UI** — `@teamix-evo/ui` is a source-injected component library (89 entries). Re-implementing `Button`、`Dialog`、`DataTable` etc. inside the consumer app fragments the design system and breaks token discipline.
89
+ - **Stable seams for change** — keeping API calls in `src/services/` means a backend rename only edits one file; keeping components free of fetch logic means they stay easy to test and reuse.
90
+ - **Predictable file location** — when AI (or a new teammate) opens an unfamiliar consumer app, the directory shape is the same across all teamix-evo-bootstrapped projects.
91
+
92
+ <!-- teamix-evo:managed:end -->
@@ -0,0 +1,225 @@
1
+ # API 数据层规范
2
+
3
+ > **核心原则**:组件不直接 fetch。所有与后端通信的代码都落在 `src/services/`,通过 `src/hooks/` 暴露给组件。
4
+
5
+ ---
6
+
7
+ ## 三层结构
8
+
9
+ ```
10
+ ┌────────────────────────────────────┐
11
+ │ src/pages/, src/components/ │ 组件层:消费 hook,渲染 UI
12
+ │ - 不调用 fetch / axios │
13
+ │ - 不直接读 process.env │
14
+ │ - 不拼 URL │
15
+ └──────────────┬─────────────────────┘
16
+ │ 仅通过 hook
17
+
18
+ ┌────────────────────────────────────┐
19
+ │ src/hooks/use<Domain>*.ts │ Hook 层:状态管理 + 缓存(react-query 等)
20
+ │ - 包装请求生命周期(loading/error) │
21
+ │ - 处理缓存 / 重试 / 失效 │
22
+ │ - 不写具体的 URL / payload 拼装 │
23
+ └──────────────┬─────────────────────┘
24
+ │ 调用纯函数
25
+
26
+ ┌────────────────────────────────────┐
27
+ │ src/services/<domain>.ts │ Service 层:请求纯函数
28
+ │ - 输入参数 → 输出 Promise<T> │
29
+ │ - 拼 URL / 序列化 / 反序列化 │
30
+ │ - 不依赖 React │
31
+ └──────────────┬─────────────────────┘
32
+
33
+
34
+ ┌────────────────────────────────────┐
35
+ │ src/lib/http.ts │ 传输层:axios / fetch wrapper
36
+ │ - 鉴权 / baseURL / 错误归一化 │
37
+ │ - 全局 interceptor │
38
+ └────────────────────────────────────┘
39
+ ```
40
+
41
+ ---
42
+
43
+ ## §1 · `src/services/` 约定
44
+
45
+ ### 文件组织
46
+
47
+ 按**领域**(domain)拆,**不**按 HTTP 动词拆。
48
+
49
+ ```
50
+ src/services/
51
+ ├── order.ts # 订单领域:list / get / create / update / cancel
52
+ ├── user.ts # 用户领域
53
+ ├── tenant.ts # 租户领域
54
+ └── index.ts # 统一 re-export(可选)
55
+ ```
56
+
57
+ ❌ 不要按动词拆:`getOrders.ts` / `createOrder.ts` / `updateOrder.ts` —— 会爆文件数。
58
+
59
+ ### 函数签名约定
60
+
61
+ ```ts
62
+ // src/services/order.ts
63
+ import { http } from '@/lib/http';
64
+ import type { Order, OrderListParams, CreateOrderInput } from '@/types/order';
65
+
66
+ export async function listOrders(params: OrderListParams): Promise<Order[]> {
67
+ const { data } = await http.get('/api/orders', { params });
68
+ return data.list;
69
+ }
70
+
71
+ export async function getOrder(id: string): Promise<Order> {
72
+ const { data } = await http.get(`/api/orders/${id}`);
73
+ return data;
74
+ }
75
+
76
+ export async function createOrder(input: CreateOrderInput): Promise<Order> {
77
+ const { data } = await http.post('/api/orders', input);
78
+ return data;
79
+ }
80
+ ```
81
+
82
+ 要点:
83
+ - **纯函数**(不依赖 React 上下文,可以在 SSR / Node 测试中单独跑)
84
+ - **类型来自 `src/types/`**,不要在 service 文件里就地定义 domain 类型
85
+ - **错误不在 service 层处理** —— 抛出去给 hook / 全局 interceptor 处理(归一化在 `src/lib/http.ts`)
86
+ - **不写 console.log / 业务弹窗** —— 是数据通道,不是 UI 通道
87
+
88
+ ### 反模式
89
+
90
+ - ❌ `fetch('https://api.example.com/...')` 写死 host(应该用 `http` wrapper)
91
+ - ❌ service 函数返回 `{ loading, data, error }`(那是 hook 该做的)
92
+ - ❌ 在 service 里 `import { toast } from 'sonner'` 弹通知(归到 hook 或全局 interceptor)
93
+ - ❌ 一个 service 函数同时拼 URL、做业务计算、改全局 store
94
+
95
+ ---
96
+
97
+ ## §2 · `src/hooks/` 约定
98
+
99
+ ### 命名
100
+
101
+ - Query 类:`useOrderList`、`useOrder(id)`、`useUserProfile`
102
+ - Mutation 类:`useCreateOrder`、`useCancelOrder`、`useUpdateUser`
103
+
104
+ ### 实现(以 `@tanstack/react-query` 为例)
105
+
106
+ ```ts
107
+ // src/hooks/useOrderList.ts
108
+ import { useQuery } from '@tanstack/react-query';
109
+ import { listOrders } from '@/services/order';
110
+ import type { OrderListParams } from '@/types/order';
111
+
112
+ export function useOrderList(params: OrderListParams) {
113
+ return useQuery({
114
+ queryKey: ['orders', params],
115
+ queryFn: () => listOrders(params),
116
+ staleTime: 30_000,
117
+ });
118
+ }
119
+ ```
120
+
121
+ ```ts
122
+ // src/hooks/useCreateOrder.ts
123
+ import { useMutation, useQueryClient } from '@tanstack/react-query';
124
+ import { createOrder } from '@/services/order';
125
+
126
+ export function useCreateOrder() {
127
+ const qc = useQueryClient();
128
+ return useMutation({
129
+ mutationFn: createOrder,
130
+ onSuccess: () => qc.invalidateQueries({ queryKey: ['orders'] }),
131
+ });
132
+ }
133
+ ```
134
+
135
+ ### 反模式
136
+
137
+ - ❌ Hook 里再次拼 URL(违反"service 拼 URL"边界)
138
+ - ❌ 一个 hook 调多个 service 把数据缝合 —— 缝合应该在 service 层(返回组合好的 DTO)
139
+ - ❌ Hook 里写业务跳转 / 路由切换 —— 由组件接 `mutation.onSuccess` callback 处理
140
+
141
+ ---
142
+
143
+ ## §3 · `src/lib/http.ts` 约定
144
+
145
+ **全局只能有一份 http 实例**。提供:
146
+
147
+ - `baseURL`(从 env)
148
+ - 鉴权 header 注入
149
+ - 错误归一化(把后端 `{ code, message }` 变成 `Error` 抛出)
150
+ - 请求 ID / trace header
151
+ - (可选)mock 拦截
152
+
153
+ ```ts
154
+ // src/lib/http.ts
155
+ import axios from 'axios';
156
+
157
+ export const http = axios.create({
158
+ baseURL: import.meta.env.VITE_API_BASE,
159
+ timeout: 15_000,
160
+ });
161
+
162
+ http.interceptors.request.use((config) => {
163
+ const token = localStorage.getItem('token');
164
+ if (token) config.headers.Authorization = `Bearer ${token}`;
165
+ return config;
166
+ });
167
+
168
+ http.interceptors.response.use(
169
+ (res) => res,
170
+ (err) => {
171
+ const message = err.response?.data?.message ?? err.message;
172
+ return Promise.reject(new Error(message));
173
+ },
174
+ );
175
+ ```
176
+
177
+ ### 反模式
178
+
179
+ - ❌ 在 service 里再 `import axios` 直接用(应该用 `http`)
180
+ - ❌ 多份 `http` 实例(分包拆 fetch 应该用一个 instance + 不同 baseURL config)
181
+ - ❌ 把鉴权放在每个 service 里手动加 header
182
+
183
+ ---
184
+
185
+ ## §4 · 类型分层
186
+
187
+ ```
188
+ src/types/
189
+ ├── order.ts # Order、OrderStatus、OrderListParams、CreateOrderInput
190
+ ├── user.ts
191
+ └── api.ts # 通用 ApiResponse<T>、Pagination<T>
192
+ ```
193
+
194
+ - **Domain 类型**(如 `Order`)放 `src/types/<domain>.ts`,被 service / hook / component 共享
195
+ - **API 入参 / 出参类型**:与 domain 类型分开命名(`OrderListParams` vs `Order`)
196
+ - **不要**让组件 props 类型直接 `import` service 的内部类型 —— 走 `src/types/` 中转
197
+
198
+ ---
199
+
200
+ ## §5 · 何时可以打破
201
+
202
+ 某些场景**允许**绕过本规范,但需要在代码注释里说明原因:
203
+
204
+ | 场景 | 允许 |
205
+ | --- | --- |
206
+ | 静态资源 fetch(读 `public/` 下的 json) | 组件直接 `fetch` 可,不用进 service |
207
+ | 第三方 SDK(地图、支付)有自己的客户端 | 包装层放 `src/lib/<sdk>.ts`,不强制走 services |
208
+ | 一次性的诊断 / 调试代码 | 临时 fetch 可,但 PR 合并前必须清理或归位 |
209
+
210
+ ---
211
+
212
+ ## AI 必须输出的层级日志
213
+
214
+ 完成涉及网络 / 后端的改动时,AI 必须在响应里写明:
215
+
216
+ ```
217
+ ## 数据层改动
218
+
219
+ - src/types/order.ts: 新增 `OrderListParams`、`OrderStatus`
220
+ - src/services/order.ts: 新增 `listOrders`、`cancelOrder`(纯函数,使用 `@/lib/http`)
221
+ - src/hooks/useOrderList.ts: 包 `useQuery`,key=['orders', params]
222
+ - src/pages/orders/index.tsx: 仅消费 `useOrderList`,不直接 fetch
223
+ ```
224
+
225
+ 每一层都点到名,才算合规。