create-dwy 0.15.0 → 0.16.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 (51) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/bin/index.js +5 -3
  3. package/package.json +1 -1
  4. package/src/skills-install.js +48 -7
  5. package/src/sync-all.js +6 -18
  6. package/templates/ai-tools/CLAUDE.md +27 -0
  7. package/templates/ai-tools/rules/Android/dwy-android-pitfalls.md +52 -0
  8. package/templates/ai-tools/rules/Python/dwy-python-backend.md +16 -63
  9. package/templates/ai-tools/rules/Python/dwy-python-base-lib.md +89 -0
  10. package/templates/ai-tools/rules/Python/dwy-python-pitfalls.md +49 -0
  11. package/templates/ai-tools/rules/Vue/dwy-vue-base-lib.md +127 -0
  12. package/templates/ai-tools/rules/Vue/dwy-vue-core.md +22 -86
  13. package/templates/ai-tools/rules/Vue/dwy-vue-pitfalls.md +66 -0
  14. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dependency-freshness.md +59 -0
  15. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-development.md +47 -2
  16. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-acceptance/SKILL.md +3 -2
  17. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +51 -9
  18. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-commercial/SKILL.md +58 -0
  19. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-competitor/SKILL.md +2 -1
  20. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-explore/SKILL.md +83 -0
  21. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-mvp/SKILL.md +2 -1
  22. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prd/SKILL.md +1 -0
  23. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/SKILL.md +14 -9
  24. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +65 -13
  25. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +30 -11
  26. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-release/SKILL.md +3 -2
  27. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-design/SKILL.md +17 -10
  28. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +42 -10
  29. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-launch/SKILL.md +22 -10
  30. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-prd/SKILL.md +13 -4
  31. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-ship/SKILL.md +13 -4
  32. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tasks/SKILL.md +12 -2
  33. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tdd-dev/SKILL.md +15 -6
  34. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-validate/SKILL.md +2 -1
  35. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-version/SKILL.md +1 -0
  36. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-ideate/SKILL.md +0 -21
  37. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-0to1/SKILL.md +0 -113
  38. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-0to1/templates/STATE.md +0 -56
  39. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dev-qa/SKILL.md +0 -117
  40. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-market-analysis/SKILL.md +0 -92
  41. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-coarse/SKILL.md +0 -72
  42. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-coarse/templates/prd-coarse.md +0 -61
  43. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-detailed/SKILL.md +0 -76
  44. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-detailed/templates/prd-detailed.md +0 -119
  45. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prototype/SKILL.md +0 -104
  46. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-spike/SKILL.md +0 -81
  47. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-dev/SKILL.md +0 -212
  48. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-dev/templates/impl-plan.md +0 -115
  49. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tech-feasibility/SKILL.md +0 -105
  50. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-version-plan/SKILL.md +0 -64
  51. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-version-plan/templates/version-plan.md +0 -72
package/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # create-dwy
2
2
 
3
+ ## 0.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - `dwy` 开头自动检测更新全局外部 skill:未装 / 清单变更(cli 加了 skill 或改了 tag)才联网重装,已装且清单一致零开销;失败不阻塞 sync。用户无需再另记 `dwy skills install`。
8
+ - 新增 `dwy-commercial` 商业分析 skill(立项阶段 validate 之后、poc 之前):商业模式 / 变现路径 / 定价 / 单位经济(CAC/LTV/毛利/回本)/ GTM 获客 + 商业可行性结论;startup 模式为硬闸门、builder 模式跳过。包装 pm-skills 的 `business-model` / `monetization-strategy` / `pricing-strategy` / `startup-canvas` / `porters-five-forces` + `gtm-strategy`。
9
+ - `dwy skills install` 降级为隐藏的强制刷新兜底(向后兼容保留,日常无需手动);`printHelp` 只露 `dwy` 主入口,`dwy sync` 为别名。
10
+
3
11
  ## 0.15.0
4
12
 
5
13
  ### Minor Changes
package/bin/index.js CHANGED
@@ -15,14 +15,16 @@ function printHelp() {
15
15
  `dwy ${pkg.version}`,
16
16
  '',
17
17
  'Usage:',
18
- ' dwy 运行交互式同步',
19
- ' dwy sync 运行交互式同步',
20
- ' dwy skills install 安装/更新全局外部 skill 到 ~/.dwy/skills/',
18
+ ' dwy 运行交互式同步(自动更新全局外部 skill)',
21
19
  ' dwy --help 显示本说明',
22
20
  ' dwy -h 显示本说明',
23
21
  ' dwy --version 显示当前版本',
24
22
  ' dwy -V 显示当前版本',
25
23
  '',
24
+ '以下为隐藏命令(向后兼容保留,日常无需手动调用):',
25
+ ' dwy sync 同上的别名',
26
+ ' dwy skills install 强制刷新全局外部 skill',
27
+ '',
26
28
  ]
27
29
  console.log(lines.join('\n'))
28
30
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dwy",
3
- "version": "0.15.0",
3
+ "version": "0.16.0",
4
4
  "description": "项目脚手架与多平台 AI 配置同步工具",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs-extra'
2
2
  import path from 'path'
3
3
  import os from 'node:os'
4
+ import { createHash } from 'node:crypto'
4
5
  import { getDwyHomeDir, runGit, chalk } from './utils.js'
5
6
 
6
7
  function logAction(label) {
@@ -29,6 +30,12 @@ const SKILL_SOURCES = {
29
30
  'test-scenarios': 'pm-execution/skills/test-scenarios',
30
31
  'release-notes': 'pm-execution/skills/release-notes',
31
32
  'gtm-strategy': 'pm-go-to-market/skills/gtm-strategy',
33
+ // 商业分析(dwy-commercial 依赖):商业模式 / 变现 / 定价 / 创业画布 / 赛道五力
34
+ 'business-model': 'pm-product-strategy/skills/business-model',
35
+ 'monetization-strategy': 'pm-product-strategy/skills/monetization-strategy',
36
+ 'pricing-strategy': 'pm-product-strategy/skills/pricing-strategy',
37
+ 'startup-canvas': 'pm-product-strategy/skills/startup-canvas',
38
+ 'porters-five-forces': 'pm-product-strategy/skills/porters-five-forces',
32
39
  'intended-vs-implemented': 'pm-ai-shipping/skills/intended-vs-implemented',
33
40
  'north-star-metric': 'pm-marketing-growth/skills/north-star-metric',
34
41
  },
@@ -47,6 +54,13 @@ const SKILL_SOURCES = {
47
54
  },
48
55
  }
49
56
 
57
+ // 计算 SKILL_SOURCES 清单的内容指纹,用于检测「cli 加了 skill / 改了 tag」→ 需重装。
58
+ // SKILL_SOURCES 是源码内固定字面量,JSON.stringify 稳定(插入顺序固定)。
59
+ // 取 sha256 前 12 位,足以区分任意清单变更,无碰撞风险。
60
+ function computeManifestHash() {
61
+ return createHash('sha256').update(JSON.stringify(SKILL_SOURCES)).digest('hex').slice(0, 12)
62
+ }
63
+
50
64
  // 全局外部 skill 安装目录:~/.dwy/skills/
51
65
  function getGlobalSkillsDir() {
52
66
  return path.join(getDwyHomeDir(), 'skills')
@@ -92,22 +106,49 @@ export async function installSkills() {
92
106
  }
93
107
  }
94
108
 
95
- // 写版本清单,供查看与后续更新比对
96
- await fs.writeJson(path.join(targetDir, 'VERSIONS.json'), versions, { spaces: 2 })
109
+ // 写版本清单 + 清单指纹,供查看与后续更新比对。
110
+ // manifest_hash 变化(cli 加了 skill / 改了 tag)→ 下次 ensureSkillsInstalled 触发重装。
111
+ await fs.writeJson(
112
+ path.join(targetDir, 'VERSIONS.json'),
113
+ { manifest_hash: computeManifestHash(), versions },
114
+ { spaces: 2 },
115
+ )
97
116
  console.log(chalk.green(`\n✓ 外部 skill 已安装到 ${targetDir}`))
98
117
  } finally {
99
118
  await fs.remove(tmpRoot)
100
119
  }
101
120
  }
102
121
 
103
- // 全局 skill 是否已就绪(供 sync 自检:缺则自动装)。
122
+ // 全局 skill 是否已就绪(VERSIONS.json 存在)。
104
123
  export async function skillsInstalled() {
105
124
  return fs.pathExists(path.join(getGlobalSkillsDir(), 'VERSIONS.json'))
106
125
  }
107
126
 
127
+ // 确保 ~/.dwy/skills/ 与 cli 当前 SKILL_SOURCES 清单一致:未装 / 清单变更 → 自动重装。
128
+ // hash 一致 → 零开销跳过;失败 → warn 一行不阻塞主流程(离线/网络故障不挡 sync)。
129
+ // 返回 true = 本次执行了安装,false = 跳过。
108
130
  export async function ensureSkillsInstalled() {
109
- if (await skillsInstalled()) return false
110
- console.log(chalk.gray('未检测到全局外部 skill,自动安装...'))
111
- await installSkills()
112
- return true
131
+ const versionsFile = path.join(getGlobalSkillsDir(), 'VERSIONS.json')
132
+ // 已装:读 manifest_hash 比对,一致即跳过,不联网
133
+ if (await fs.pathExists(versionsFile)) {
134
+ let stored
135
+ try {
136
+ stored = await fs.readJson(versionsFile)
137
+ } catch {
138
+ stored = {}
139
+ }
140
+ // 兼容老格式(无 manifest_hash)→ 视为不一致,触发一次重装补上指纹
141
+ if (stored.manifest_hash === computeManifestHash()) return false
142
+ console.log(chalk.gray('全局外部 skill 清单已更新,重新拉取...'))
143
+ } else {
144
+ console.log(chalk.gray('未检测到全局外部 skill,自动安装...'))
145
+ }
146
+ try {
147
+ await installSkills()
148
+ return true
149
+ } catch (error) {
150
+ // 离线/网络故障:不阻塞主流程,下次再来
151
+ console.log(chalk.yellow(`外部 skill 自动更新失败:${error.message}`))
152
+ return false
153
+ }
113
154
  }
package/src/sync-all.js CHANGED
@@ -26,13 +26,6 @@ import { scanExistingCursorRules, syncCursor } from './sync-cursor.js'
26
26
  import { syncOpenCode } from './sync-opencode.js'
27
27
  import { ensureSkillsInstalled } from './skills-install.js'
28
28
 
29
- // 产品 0→1 流程里依赖全局外部 skill 的包装型原子 skill。
30
- // 同步选中其中任一个时,触发全局外部 skill 自检安装。
31
- const WRAPPER_SKILL_NAMES = new Set([
32
- 'dwy-ideate', 'dwy-competitor', 'dwy-validate', 'dwy-mvp', 'dwy-prd',
33
- 'dwy-version', 'dwy-tasks', 'dwy-tdd-dev', 'dwy-acceptance', 'dwy-release',
34
- ])
35
-
36
29
  const PLATFORM_OPTIONS = [
37
30
  { value: 'claude', label: 'Claude Code', hint: '支持 rules / skills / commands / hooks' },
38
31
  { value: 'codex', label: 'Codex', hint: '支持 rules / skills / hooks' },
@@ -567,6 +560,12 @@ export async function syncAll({
567
560
 
568
561
  const projectDir = projectDirOverride || process.cwd()
569
562
 
563
+ // 开头无条件自检全局外部 skill:未装 / 清单变更(cli 加了 skill 或改了 tag)则自动更新,
564
+ // 已装且清单一致则零开销跳过。失败不阻塞 sync(离线/网络故障,下次再来)。
565
+ // 这样用户跑一次 `dwy` 就把 skill 更新到位,无需另记 `dwy skills install`。
566
+ console.log(chalk.gray('检查全局外部 skill 更新...'))
567
+ await ensureSkillsInstalled()
568
+
570
569
  console.log(chalk.blue('\nScanning available Claude Code/Codex configuration...\n'))
571
570
  const scans = {
572
571
  skills: await scanSkills(sourceDir),
@@ -698,17 +697,6 @@ export async function syncAll({
698
697
  }
699
698
  await saveSyncState(projectDir, syncState)
700
699
 
701
- // 产品 0→1 流程的包装型原子 skill 依赖全局外部 skill(~/.dwy/skills/)。
702
- // 若本次同步选中了这些 skill 且全局未安装,自动装一次(缺则装、有则跳过)。
703
- if (selected.skills?.some((s) => WRAPPER_SKILL_NAMES.has(s.name))) {
704
- try {
705
- await ensureSkillsInstalled()
706
- } catch (error) {
707
- console.log(chalk.yellow(`\n外部 skill 自动安装失败:${error.message}`))
708
- console.log(chalk.gray('可稍后手动运行 `dwy skills install` 重试。'))
709
- }
710
- }
711
-
712
700
  if (!syncedAnySupportedPlatform) {
713
701
  console.log(chalk.yellow('未选择已接入平台,本次未执行同步。'))
714
702
  }
@@ -54,6 +54,21 @@
54
54
  - 如果你写了 200 行但可以写成 50 行,重写它。
55
55
  - 问自己:"一个资深工程师会说这过于复杂吗?"如果是,简化。
56
56
 
57
+ ### 写代码前走决策阶梯
58
+
59
+ 写任何新代码前,从上往下逐级问,命中即停:
60
+
61
+ 1. 这任务真需要存在吗?(YAGNI——能不做就不做)
62
+ 2. 代码库里已有现成的吗?
63
+ 3. 标准库覆盖吗?
64
+ 4. 平台 / 框架原生特性有吗?
65
+ 5. 已安装的依赖能做吗?
66
+ 6. 能一行解决吗?
67
+ 7. 以上都不行 → 才写最小代码。
68
+
69
+ 刻意的简化用注释标注:跳过了什么、何时该加回。
70
+ 红线——以下永不简化掉:输入校验、错误处理、安全、可访问性。先理解问题,再谈优化。
71
+
57
72
  ### 3. 精准改动
58
73
 
59
74
  只动必须动的代码。只清理自己造成的混乱。
@@ -181,3 +196,15 @@
181
196
  - 后端用 dict / map / any 接收 body
182
197
  - 分页 `page_size` 无上限
183
198
  - 图片只校验文件大小不校验像素
199
+
200
+ ## 自成长项目规则(project-rules)
201
+
202
+ 写项目要自成长:同一类问题反复踩坑,主动沉淀成可复用规则,下次自动生效,不靠人事后总结。
203
+
204
+ - **触发**:写代码 / 排查问题时,同一类问题踩坑 **≥2 次**(重复的报错根因、踩同一个坑、被反复纠正同一处写法)。
205
+ - **动作**:把教训沉淀成一条可复用规则(写清「现象 / 根因 / 怎么避免」),追加到 `project-rules.md`;已有同类规则补充进去,不重复立条。
206
+ - **落盘位置**(按当前工具自动选):
207
+ - **Claude Code** → 项目 `.claude/rules/project-rules.md`(放进去即自动加载,无需注册)。
208
+ - **Codex** → 项目根 `AGENTS.md`,写在 `<!-- DWY-RULES:START/END -->` 托管块**之外**的 `## [自成长] 项目规则` 节下。托管块内会被 `dwy codex sync` 整块覆盖,自成长内容**必落块外**。
209
+ - **方式**:自动沉淀,事后输出一行告知「已沉淀 <规则> 到 project-rules」,不打断当前任务、不逐条问用户。
210
+ - **边界**:只沉淀**可复用、跨任务**的教训;一次性 / 本任务特有的问题不立规则,避免噪音堆积。
@@ -0,0 +1,52 @@
1
+ ---
2
+ description: Android 开发避坑清单(Gradle / 签名 / Compose / 生命周期 / 真机部署踩坑),按分类持续追加
3
+ paths:
4
+ - "**/*.java"
5
+ - "**/*.kt"
6
+ - "**/*.kts"
7
+ - "**/*.gradle"
8
+ - "**/AndroidManifest.xml"
9
+ - "**/res/**/*.xml"
10
+ ---
11
+
12
+ # Android 开发坑清单
13
+
14
+ > 动手前先对照本清单避开已知坑;踩到新坑随手按四栏格式(现象 / 根因 / 正确做法 / 适用)追加到对应分类,没有合适分类就新建一个。
15
+ >
16
+ > 通用开发规范见 `dwy-android-core` rule,本文件只记**非显而易见的实战坑**。
17
+
18
+ ## 分类索引
19
+
20
+ - 工程与构建(Gradle / AGP / 依赖冲突)
21
+ - 签名与真机部署
22
+ - Compose / UI(重组、状态、生命周期)
23
+ - 生命周期 / 并发(协程、ViewModel、内存泄漏)
24
+ - 权限 / Manifest / Capability
25
+ - 排查方法论
26
+ - 其他分类遇到再加
27
+
28
+ ---
29
+
30
+ ## 工程与构建
31
+
32
+ <!-- 待填:踩到坑按四栏格式追加 -->
33
+
34
+ ## 签名与真机部署
35
+
36
+ <!-- 待填 -->
37
+
38
+ ## Compose / UI
39
+
40
+ <!-- 待填 -->
41
+
42
+ ## 生命周期 / 并发
43
+
44
+ <!-- 待填 -->
45
+
46
+ ## 权限 / Manifest / Capability
47
+
48
+ <!-- 待填 -->
49
+
50
+ ## 排查方法论
51
+
52
+ <!-- 待填 -->
@@ -10,52 +10,6 @@ paths:
10
10
 
11
11
  ---
12
12
 
13
- ## 零、优先使用 `dwyeapi` 基础框架库(强制)
14
-
15
- `dwyeapi` 是团队内部 PyPI 基础设施包,已封装后端项目所需的通用能力。**凡是 dwyeapi 已提供的能力,禁止自行实现,也禁止引入其他库重复造轮子。**
16
-
17
- ### 必须复用 dwyeapi 的能力域
18
-
19
- | 能力域 | 内容 |
20
- |--------|------|
21
- | 配置管理 | 应用 Settings 基类、运行环境识别(dev / prod) |
22
- | 异常体系 | 业务异常基类与全局异常处理器注册 |
23
- | 统一响应 | `{ code, message, data, timestamp }` 包装 |
24
- | 分页 | 分页查询参数模型与分页响应封装 |
25
- | 数据库 | ORM 声明式基类、时间戳 Mixin、异步引擎与会话工厂 |
26
- | 依赖注入 | FastAPI `get_db` 工厂 |
27
- | 安全 | 密码哈希、JWT 创建与解码 |
28
- | 缓存 | 共享异步 Redis 客户端 |
29
- | 时间 | 业务时间统一入口(Asia/Shanghai) |
30
- | 脱敏 | PII 数据脱敏函数 |
31
- | 日志 | 全局日志配置与门面 |
32
- | 健康检查 | 健康检查路由工厂 |
33
- | 异步任务 | 耗时任务队列与上下文 |
34
- | 邮件 | 邮件验证码可插拔通道 |
35
-
36
- ### 查阅方式(关键)
37
-
38
- **在编写任何后端代码前,以及调用任何 dwyeapi 提供的 API 之前,必须先用 `Skill` 工具加载 `dwy-eapi` skill 查阅当前最新接口。** 本文件**只规定约束与边界**,**不固化** dwyeapi 的具体模块路径、类名、函数签名、参数 —— 这些会随版本演进,固化在 rules 里会与实际包不同步。
39
-
40
- ```
41
- Skill 工具调用:skill="dwy-eapi"
42
- ```
43
-
44
- ### 强制规则
45
-
46
- - 涉及上表任一能力域,**先查 skill 拿到当前 API,再写代码**
47
- - **禁止**自造异常基类,必须复用 dwyeapi 异常体系
48
- - **禁止**手写 bcrypt / PyJWT 调用,必须走 dwyeapi 安全模块
49
- - **禁止**自定义统一响应包装函数,必须用 dwyeapi 响应模块
50
- - **禁止**自定义分页模型,必须用 dwyeapi 分页模块
51
- - **禁止**自定义 `DeclarativeBase`,模型必须继承 dwyeapi 提供的基类
52
- - **禁止**在路由函数中直接创建数据库会话,必须用 dwyeapi 依赖工厂
53
- - **禁止**自建 Redis 连接池,必须用 dwyeapi 共享客户端
54
- - **禁止**在业务代码中直接使用 `datetime.now()`,必须用 dwyeapi 时间模块
55
- - **禁止**自行 `logging.basicConfig`,必须用 dwyeapi 日志模块
56
-
57
- ---
58
-
59
13
  ## 一、项目结构与业务聚合(强制)
60
14
 
61
15
  ### 核心原则
@@ -103,7 +57,7 @@ app/
103
57
  - 测试镜像功能聚合结构:`tests/users/test_router.py` / `tests/users/test_service.py`(详见第九节)
104
58
 
105
59
  ### 例外
106
- - 通用基础设施代码(如 dwyeapi 内部)可保持技术层结构
60
+ - 通用基础设施代码(如基础框架库内部)可保持技术层结构
107
61
 
108
62
  ---
109
63
 
@@ -167,11 +121,11 @@ async def create_user(
167
121
 
168
122
  ### 统一响应格式
169
123
 
170
- 所有成功 / 分页 / 错误响应统一走 dwyeapi 响应与异常模块(具体 API 与字段结构查 `dwy-eapi` skill),**禁止**自造响应包装函数,**禁止**在 router 中手动构造 `{"success": true, ...}` 等非标准格式。
124
+ 所有成功 / 分页 / 错误响应统一走项目级响应与异常模块,**禁止**自造响应包装函数,**禁止**在 router 中手动构造 `{"success": true, ...}` 等非标准格式。
171
125
 
172
126
  ### 分页
173
127
 
174
- 分页参数模型与响应包装统一走 dwyeapi(具体 API 查 `dwy-eapi` skill),**禁止**自定义。
128
+ 分页参数模型与响应包装统一走项目级分页模块,**禁止**自定义。
175
129
 
176
130
  ---
177
131
 
@@ -234,7 +188,7 @@ class UserResponse(UserBase):
234
188
 
235
189
  - 数据库会话、当前用户、配置等**必须**通过 `Depends()` 注入
236
190
  - **禁止**在路由函数中直接创建数据库会话或实例化 service
237
- - `get_db` 依赖必须用 dwyeapi 提供的工厂生成(具体 API 查 `dwy-eapi` skill)
191
+ - `get_db` 依赖必须用项目级工厂生成,**禁止**每个路由各自创建
238
192
 
239
193
  ```python
240
194
  # 反例:路由中直接创建会话
@@ -258,8 +212,8 @@ async def get_user(
258
212
 
259
213
  ### 强制规则
260
214
 
261
- - **必须**复用 dwyeapi 异常体系与全局处理器(具体异常类与注册方法查 `dwy-eapi` skill),**禁止**自造异常基类
262
- - 项目级扩展异常必须继承 dwyeapi 异常基类
215
+ - **必须**复用项目级异常体系与全局处理器,**禁止**自造异常基类
216
+ - 项目级扩展异常必须继承统一异常基类
263
217
  - **service 层**抛业务异常,**禁止**抛 `HTTPException`(service 不感知 HTTP)
264
218
  - **router 层**不写 `try / except`,由全局 handler 统一处理
265
219
  - **禁止**裸 `except:` 或 `except Exception: pass`
@@ -273,7 +227,7 @@ async def get_user(
273
227
  ### 强制规则
274
228
 
275
229
  - 必须使用 **SQLAlchemy 2.0** 声明式映射 + async
276
- - 模型继承 dwyeapi 提供的声明式基类与时间戳 Mixin(具体类名查 `dwy-eapi` skill),**禁止**自定义 `DeclarativeBase`
230
+ - 模型继承项目级统一声明式基类与时间戳 Mixin,**禁止**每个模型各自定义 `DeclarativeBase`
277
231
  - 数据库迁移使用 **Alembic**,**禁止**手动执行 DDL
278
232
  - **禁止**在路由层直接写 SQL 查询
279
233
  - **禁止**使用 SQLAlchemy 1.x 风格的 `session.query(...)`
@@ -283,7 +237,7 @@ from sqlalchemy.orm import Mapped, mapped_column
283
237
  from sqlalchemy import String
284
238
  import uuid as uuid_lib
285
239
 
286
- class User(BaseFromDwyeapi, TimestampMixinFromDwyeapi): # 实际基类名以 skill 为准
240
+ class User(Base, TimestampMixin): # 项目级统一基类与时间戳 Mixin
287
241
  __tablename__ = "users"
288
242
 
289
243
  id: Mapped[int] = mapped_column(primary_key=True)
@@ -328,10 +282,10 @@ user = result.scalar_one_or_none()
328
282
 
329
283
  | 规则 | 说明 |
330
284
  |------|------|
331
- | 存储算法 | **只允许 bcrypt**,必须走 dwyeapi 安全模块,**禁止**手写 bcrypt 调用或使用 MD5 / SHA / 明文 |
285
+ | 存储算法 | **只允许 bcrypt**,必须走项目级统一安全模块,**禁止**散落手写 bcrypt 调用或使用 MD5 / SHA / 明文 |
332
286
  | 接口返回 | **永远不返回**密码字段,Response Schema 中不包含 |
333
287
  | 密码强度 | **长度 ≥ 8 字符**,且**必须**包含 **大写字母、小写字母、数字、特殊符号** 中的至少 **3 类** |
334
- | 密码长度上限 | Schema 必须 `max_length=72`(bcrypt 输入硬上限,超过会被静默截断),除非 dwyeapi 已做 SHA-256 预哈希 |
288
+ | 密码长度上限 | Schema 必须 `max_length=72`(bcrypt 输入硬上限,超过会被静默截断),除非安全模块已做 SHA-256 预哈希 |
335
289
  | 登录错误 | 返回"用户名或密码错误",**不区分**是用户名不存在还是密码错误 |
336
290
  | 强度校验位置 | Pydantic Schema 层用 `field_validator` 校验复杂度,**禁止**只校长度 |
337
291
 
@@ -361,12 +315,12 @@ class UserCreate(BaseModel):
361
315
  return v
362
316
  ```
363
317
 
364
- > 该 validator 建议下沉到 dwyeapi 提供统一的 `PasswordStr` 类型,避免每个项目重复实现。
318
+ > 该 validator 建议下沉为项目级统一的 `PasswordStr` 类型,避免每个模块重复实现。
365
319
 
366
320
  ### 7.3 接口认证与授权
367
321
 
368
322
  - 所有 API **必须** JWT 认证(白名单除外:`/health` / `/auth/login` / `/auth/register`)
369
- - JWT 创建与解码**必须**走 dwyeapi 安全模块,**禁止**直接调用 PyJWT
323
+ - JWT 创建与解码**必须**走项目级统一安全模块,**禁止**散落直接调用 PyJWT
370
324
  - Token 黑名单存 Redis(登出后失效,TTL = 剩余有效期)
371
325
  - Secret Key 必须从环境变量读取,**禁止**硬编码
372
326
  - Secret Key 长度与生成方式(按字节算,不按字符算):
@@ -389,7 +343,7 @@ class UserCreate(BaseModel):
389
343
 
390
344
  - **禁止**直接返回 ORM 对象(可能暴露 password、内部 ID 等)
391
345
  - 列表接口**必须**分页,**禁止**返回全量数据
392
- - PII 字段(手机号 / 邮箱 / 身份证 / 银行卡 / 姓名 / 地址 / IP / 车牌)必须使用 dwyeapi 脱敏函数处理
346
+ - PII 字段(手机号 / 邮箱 / 身份证 / 银行卡 / 姓名 / 地址 / IP / 车牌)必须使用项目级统一脱敏函数处理
393
347
 
394
348
  ### 7.5 错误处理安全
395
349
 
@@ -449,7 +403,7 @@ file_path = Path("/uploads") / safe_name
449
403
 
450
404
  ### 7.8 审计日志
451
405
 
452
- 通过 dwyeapi 日志模块获取审计 logger,记录以下操作:
406
+ 通过项目级统一日志模块获取审计 logger,记录以下操作:
453
407
 
454
408
  | 操作 | 记录内容 |
455
409
  |------|---------|
@@ -510,8 +464,7 @@ AI 编写或审查后端代码时,**必须**检查以下违规模式,按严
510
464
 
511
465
  | 检查项 | 违规模式 | 严重程度 |
512
466
  |--------|---------|---------|
513
- | 重复造轮子 | 自造异常基类 / 自封装统一响应 / 手写 bcrypt 或 JWT 调用 / 自建 Redis 连接池 等 dwyeapi 已提供能力 | 高 |
514
- | 未查 skill | 调用 dwyeapi 能力域却未先查 `dwy-eapi` skill 拿当前 API | 高 |
467
+ | 重复造轮子 | 自造异常基类 / 自封装统一响应 / 散落手写 bcrypt 或 JWT 调用 / 自建 Redis 连接池 等本应走项目级统一模块的能力 | 高 |
515
468
  | 技术层散落 | 顶级出现 `routers/` / `services/` / `models/` / `schemas/` 等技术层目录把不同功能文件混放,未按功能聚合 | 高 |
516
469
  | 测试与源码混放 | 测试文件放在源码目录中(如 `app/users/test_router.py`),未独立到测试目录 | 高 |
517
470
  | 密码明文 | `password` 字段出现在 Response Schema | **致命 → STOP** |
@@ -528,5 +481,5 @@ AI 编写或审查后端代码时,**必须**检查以下违规模式,按严
528
481
  | service 抛 HTTPException | service 层直接 `raise HTTPException` | 高 |
529
482
  | 路由中开会话 | 路由函数中 `async with session_factory()` | 高 |
530
483
  | 旧式查询 | `session.query(...)` 而非 `select(...)` | 高 |
531
- | `datetime.now()` 直用 | 业务代码直接 `datetime.now()` 而非走 dwyeapi 时间模块 | 高 |
484
+ | `datetime.now()` 直用 | 业务代码直接 `datetime.now()` 而非走项目级统一时间模块 | 高 |
532
485
  | 物理删除业务数据 | 对业务数据执行物理删除 | 中 |
@@ -0,0 +1,89 @@
1
+ ---
2
+ description: 团队内部 dwyeapi 基础框架库强制复用规范(配置 / 异常 / 响应 / 分页 / ORM / 安全 / 缓存 / 时间 / 脱敏 / 日志:禁重复造轮子)
3
+ paths:
4
+ - "**/*.py"
5
+ ---
6
+
7
+ # Python 后端 dwyeapi 基础库强制复用规范
8
+
9
+ `dwy-python-backend` rule 的团队内补充。通用 FastAPI / SQLAlchemy / 安全编码规范见 `dwy-python-backend`;本文件只管「凡 dwyeapi 已提供的能力,禁止自行实现,也禁止引入其他库重复造轮子」。
10
+
11
+ `dwyeapi` 是团队内部 PyPI 基础设施包,已封装后端项目所需的通用能力。
12
+
13
+ ---
14
+
15
+ ## 一、必须复用 dwyeapi 的能力域
16
+
17
+ | 能力域 | 内容 |
18
+ |--------|------|
19
+ | 配置管理 | 应用 Settings 基类、运行环境识别(dev / prod) |
20
+ | 异常体系 | 业务异常基类与全局异常处理器注册 |
21
+ | 统一响应 | `{ code, message, data, timestamp }` 包装 |
22
+ | 分页 | 分页查询参数模型与分页响应封装 |
23
+ | 数据库 | ORM 声明式基类、时间戳 Mixin、异步引擎与会话工厂 |
24
+ | 依赖注入 | FastAPI `get_db` 工厂 |
25
+ | 安全 | 密码哈希、JWT 创建与解码 |
26
+ | 缓存 | 共享异步 Redis 客户端 |
27
+ | 时间 | 业务时间统一入口(Asia/Shanghai) |
28
+ | 脱敏 | PII 数据脱敏函数 |
29
+ | 日志 | 全局日志配置与门面 |
30
+ | 健康检查 | 健康检查路由工厂 |
31
+ | 异步任务 | 耗时任务队列与上下文 |
32
+ | 邮件 | 邮件验证码可插拔通道 |
33
+
34
+ ### 查阅方式(关键)
35
+
36
+ **在编写任何后端代码前,以及调用任何 dwyeapi 提供的 API 之前,必须先用 `Skill` 工具加载 `dwy-eapi` skill 查阅当前最新接口。** 本文件**只规定约束与边界**,**不固化** dwyeapi 的具体模块路径、类名、函数签名、参数 —— 这些会随版本演进,固化在 rules 里会与实际包不同步。
37
+
38
+ ```
39
+ Skill 工具调用:skill="dwy-eapi"
40
+ ```
41
+
42
+ ---
43
+
44
+ ## 二、强制规则
45
+
46
+ - 涉及上表任一能力域,**先查 skill 拿到当前 API,再写代码**
47
+ - **禁止**自造异常基类,必须复用 dwyeapi 异常体系
48
+ - **禁止**手写 bcrypt / PyJWT 调用,必须走 dwyeapi 安全模块
49
+ - **禁止**自定义统一响应包装函数,必须用 dwyeapi 响应模块
50
+ - **禁止**自定义分页模型,必须用 dwyeapi 分页模块
51
+ - **禁止**自定义 `DeclarativeBase`,模型必须继承 dwyeapi 提供的基类
52
+ - **禁止**在路由函数中直接创建数据库会话,必须用 dwyeapi 依赖工厂
53
+ - **禁止**自建 Redis 连接池,必须用 dwyeapi 共享客户端
54
+ - **禁止**在业务代码中直接使用 `datetime.now()`,必须用 dwyeapi 时间模块
55
+ - **禁止**自行 `logging.basicConfig`,必须用 dwyeapi 日志模块
56
+
57
+ ---
58
+
59
+ ## 三、各能力域 dwyeapi 落点
60
+
61
+ 通用规范(路由 / Schema / ORM / 安全编码本身的要求)见 `dwy-python-backend` 对应章节;本节只说明这些规范在团队项目里**必须落到哪个 dwyeapi 模块**。
62
+
63
+ | 能力 | 团队项目落点 | 对应通用规范 |
64
+ |------|-------------|-------------|
65
+ | 统一响应 / 异常 | dwyeapi 响应模块 + 异常体系(禁自造) | backend 二、五 |
66
+ | 分页 | dwyeapi 分页模块(禁自定义) | backend 二 |
67
+ | ORM 基类 / 时间戳 | 继承 dwyeapi 声明式基类与 Mixin | backend 六 |
68
+ | `get_db` 依赖 | dwyeapi 依赖工厂 | backend 四 |
69
+ | 密码哈希 | dwyeapi 安全模块(bcrypt,禁手写) | backend 7.2 |
70
+ | JWT 创建 / 解码 | dwyeapi 安全模块(禁直接 PyJWT) | backend 7.3 |
71
+ | PII 脱敏 | dwyeapi 脱敏函数 | backend 7.4 |
72
+ | 审计日志 | dwyeapi 日志模块获取审计 logger | backend 7.8 |
73
+ | 业务时间 | dwyeapi 时间模块(禁裸 `datetime.now()`) | backend 六 / 七 |
74
+ | Redis 缓存 | dwyeapi 共享客户端(禁自建连接池) | backend 八 |
75
+
76
+ > 密码复杂度校验建议下沉到 dwyeapi 提供统一的 `PasswordStr` 类型,避免每个项目重复实现。
77
+
78
+ ---
79
+
80
+ ## 四、违规检测清单(dwyeapi 相关)
81
+
82
+ 通用后端违规清单见 `dwy-python-backend` 第十节;涉及 dwyeapi 必须额外检查:
83
+
84
+ | 检查项 | 违规模式 | 严重程度 |
85
+ |--------|---------|---------|
86
+ | 重复造轮子 | 自造异常基类 / 自封装统一响应 / 手写 bcrypt 或 JWT 调用 / 自建 Redis 连接池 等 dwyeapi 已提供能力 | 高 |
87
+ | 未查 skill | 调用 dwyeapi 能力域却未先查 `dwy-eapi` skill 拿当前 API | 高 |
88
+ | 密码弱存储 | 使用 MD5 / SHA 而非 dwyeapi 安全模块的 bcrypt | **致命 → STOP** |
89
+ | `datetime.now()` 直用 | 业务代码直接 `datetime.now()` 而非走 dwyeapi 时间模块 | 高 |
@@ -0,0 +1,49 @@
1
+ ---
2
+ description: Python 后端开发避坑清单(FastAPI / SQLAlchemy / async / 依赖 / 部署踩坑),按分类持续追加
3
+ paths:
4
+ - "**/*.py"
5
+ - "**/pyproject.toml"
6
+ - "**/uv.lock"
7
+ ---
8
+
9
+ # Python 开发坑清单
10
+
11
+ > 动手前先对照本清单避开已知坑;踩到新坑随手按四栏格式(现象 / 根因 / 正确做法 / 适用)追加到对应分类,没有合适分类就新建一个。
12
+ >
13
+ > 通用开发规范见 `dwy-python-core` / `dwy-python-backend` rule,本文件只记**非显而易见的实战坑**。
14
+
15
+ ## 分类索引
16
+
17
+ - 依赖与环境(uv / venv / 版本)
18
+ - async / 并发(事件循环、阻塞调用、连接池)
19
+ - FastAPI / Pydantic(路由、Schema、校验、序列化)
20
+ - SQLAlchemy / ORM(异步 session、懒加载、事务)
21
+ - 部署与运行时(打包、环境变量、进程模型)
22
+ - 排查方法论
23
+ - 其他分类遇到再加
24
+
25
+ ---
26
+
27
+ ## 依赖与环境
28
+
29
+ <!-- 待填:踩到坑按四栏格式追加 -->
30
+
31
+ ## async / 并发
32
+
33
+ <!-- 待填 -->
34
+
35
+ ## FastAPI / Pydantic
36
+
37
+ <!-- 待填 -->
38
+
39
+ ## SQLAlchemy / ORM
40
+
41
+ <!-- 待填 -->
42
+
43
+ ## 部署与运行时
44
+
45
+ <!-- 待填 -->
46
+
47
+ ## 排查方法论
48
+
49
+ <!-- 待填 -->