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.
- package/CHANGELOG.md +8 -0
- package/bin/index.js +5 -3
- package/package.json +1 -1
- package/src/skills-install.js +48 -7
- package/src/sync-all.js +6 -18
- package/templates/ai-tools/CLAUDE.md +27 -0
- package/templates/ai-tools/rules/Android/dwy-android-pitfalls.md +52 -0
- package/templates/ai-tools/rules/Python/dwy-python-backend.md +16 -63
- package/templates/ai-tools/rules/Python/dwy-python-base-lib.md +89 -0
- package/templates/ai-tools/rules/Python/dwy-python-pitfalls.md +49 -0
- package/templates/ai-tools/rules/Vue/dwy-vue-base-lib.md +127 -0
- package/templates/ai-tools/rules/Vue/dwy-vue-core.md +22 -86
- package/templates/ai-tools/rules/Vue/dwy-vue-pitfalls.md +66 -0
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dependency-freshness.md +59 -0
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-development.md +47 -2
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-acceptance/SKILL.md +3 -2
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +51 -9
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-commercial/SKILL.md +58 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-competitor/SKILL.md +2 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-explore/SKILL.md +83 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-mvp/SKILL.md +2 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prd/SKILL.md +1 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/SKILL.md +14 -9
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +65 -13
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +30 -11
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-release/SKILL.md +3 -2
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-design/SKILL.md +17 -10
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +42 -10
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-launch/SKILL.md +22 -10
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-prd/SKILL.md +13 -4
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-ship/SKILL.md +13 -4
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tasks/SKILL.md +12 -2
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tdd-dev/SKILL.md +15 -6
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-validate/SKILL.md +2 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-version/SKILL.md +1 -0
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-ideate/SKILL.md +0 -21
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-0to1/SKILL.md +0 -113
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-0to1/templates/STATE.md +0 -56
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dev-qa/SKILL.md +0 -117
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-market-analysis/SKILL.md +0 -92
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-coarse/SKILL.md +0 -72
- 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
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prd-detailed/SKILL.md +0 -76
- 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
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prototype/SKILL.md +0 -104
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-spike/SKILL.md +0 -81
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-dev/SKILL.md +0 -212
- 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
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tech-feasibility/SKILL.md +0 -105
- package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-version-plan/SKILL.md +0 -64
- 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
package/src/skills-install.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
await
|
|
112
|
-
|
|
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
|
-
-
|
|
60
|
+
- 通用基础设施代码(如基础框架库内部)可保持技术层结构
|
|
107
61
|
|
|
108
62
|
---
|
|
109
63
|
|
|
@@ -167,11 +121,11 @@ async def create_user(
|
|
|
167
121
|
|
|
168
122
|
### 统一响应格式
|
|
169
123
|
|
|
170
|
-
所有成功 / 分页 /
|
|
124
|
+
所有成功 / 分页 / 错误响应统一走项目级响应与异常模块,**禁止**自造响应包装函数,**禁止**在 router 中手动构造 `{"success": true, ...}` 等非标准格式。
|
|
171
125
|
|
|
172
126
|
### 分页
|
|
173
127
|
|
|
174
|
-
|
|
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`
|
|
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
|
-
-
|
|
262
|
-
-
|
|
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
|
-
-
|
|
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(
|
|
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
|
|
285
|
+
| 存储算法 | **只允许 bcrypt**,必须走项目级统一安全模块,**禁止**散落手写 bcrypt 调用或使用 MD5 / SHA / 明文 |
|
|
332
286
|
| 接口返回 | **永远不返回**密码字段,Response Schema 中不包含 |
|
|
333
287
|
| 密码强度 | **长度 ≥ 8 字符**,且**必须**包含 **大写字母、小写字母、数字、特殊符号** 中的至少 **3 类** |
|
|
334
|
-
| 密码长度上限 | Schema 必须 `max_length=72`(bcrypt
|
|
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
|
|
318
|
+
> 该 validator 建议下沉为项目级统一的 `PasswordStr` 类型,避免每个模块重复实现。
|
|
365
319
|
|
|
366
320
|
### 7.3 接口认证与授权
|
|
367
321
|
|
|
368
322
|
- 所有 API **必须** JWT 认证(白名单除外:`/health` / `/auth/login` / `/auth/register`)
|
|
369
|
-
- JWT
|
|
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 /
|
|
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
|
-
|
|
406
|
+
通过项目级统一日志模块获取审计 logger,记录以下操作:
|
|
453
407
|
|
|
454
408
|
| 操作 | 记录内容 |
|
|
455
409
|
|------|---------|
|
|
@@ -510,8 +464,7 @@ AI 编写或审查后端代码时,**必须**检查以下违规模式,按严
|
|
|
510
464
|
|
|
511
465
|
| 检查项 | 违规模式 | 严重程度 |
|
|
512
466
|
|--------|---------|---------|
|
|
513
|
-
| 重复造轮子 | 自造异常基类 / 自封装统一响应 /
|
|
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()`
|
|
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
|
+
<!-- 待填 -->
|