create-dwy 0.5.0 → 0.6.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 (156) hide show
  1. package/bin/index.js +1 -1
  2. package/package.json +2 -2
  3. package/src/create.js +61 -37
  4. package/src/sync.js +213 -53
  5. package/templates/claude-global/CLAUDE.md +92 -0
  6. package/templates/claude-global/rules/dwy-android-core.md +610 -0
  7. package/templates/claude-global/rules/dwy-api-design.md +110 -0
  8. package/templates/claude-global/rules/dwy-db-migration.md +46 -0
  9. package/templates/claude-global/rules/dwy-flutter-core.md +564 -0
  10. package/templates/claude-global/rules/dwy-flutter-network.md +222 -0
  11. package/templates/claude-global/rules/dwy-flutter-router.md +97 -0
  12. package/templates/claude-global/rules/dwy-flutter-testing.md +94 -0
  13. package/templates/claude-global/rules/dwy-git-commit-convention.md +82 -0
  14. package/templates/claude-global/rules/dwy-git-security.md +53 -0
  15. package/templates/claude-global/rules/dwy-payload-limits.md +178 -0
  16. package/templates/claude-global/rules/{prefer-open-source.md → dwy-prefer-open-source.md} +4 -0
  17. package/templates/claude-global/rules/dwy-project-structure.md +181 -0
  18. package/templates/claude-global/rules/{python-code-style.md → dwy-python-core.md} +314 -357
  19. package/templates/claude-global/rules/dwy-python-fastapi.md +272 -0
  20. package/templates/claude-global/rules/dwy-python-orm.md +61 -0
  21. package/templates/claude-global/rules/{backend-security.md → dwy-python-security.md} +116 -79
  22. package/templates/claude-global/rules/dwy-python-testing.md +53 -0
  23. package/templates/claude-global/rules/{server-security.md → dwy-server-security.md} +1 -2
  24. package/templates/claude-global/rules/dwy-tdd-development.md +70 -0
  25. package/templates/claude-global/rules/dwy-tech-stack.md +60 -0
  26. package/templates/claude-global/rules/dwy-vue-api.md +146 -0
  27. package/templates/claude-global/rules/{vue-code-style.md → dwy-vue-core.md} +361 -152
  28. package/templates/claude-global/rules/dwy-vue-pinia.md +75 -0
  29. package/templates/claude-global/rules/dwy-vue-testing.md +102 -0
  30. package/templates/claude-global/settings.json +20 -0
  31. package/templates/claude-global/skills/dwy-deploy-audit/SKILL.md +481 -0
  32. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-data.md +117 -0
  33. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +74 -0
  34. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-runtime.md +184 -0
  35. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_base.sh +39 -0
  36. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_capacity.sh +278 -0
  37. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_db.sh +122 -0
  38. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_docker.sh +249 -0
  39. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_env.sh +64 -0
  40. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_https.sh +60 -0
  41. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_logs.sh +209 -0
  42. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +92 -0
  43. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_resilience.sh +157 -0
  44. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_secrets.sh +327 -0
  45. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_services.sh +77 -0
  46. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_ssh.sh +56 -0
  47. package/templates/claude-global/skills/dwy-deploy-audit/scripts/run_all.sh +105 -0
  48. package/templates/claude-global/skills/dwy-docker/SKILL.md +187 -0
  49. package/templates/claude-global/skills/dwy-docker/references/ask-templates.md +142 -0
  50. package/templates/claude-global/skills/dwy-docker/references/templates.md +479 -0
  51. package/templates/claude-global/skills/dwy-docker/references/version-rules.md +76 -0
  52. package/templates/claude-global/skills/dwy-docker/scripts/query_dockerhub.py +204 -0
  53. package/templates/claude-global/skills/dwy-docker/scripts/scan_images.sh +185 -0
  54. package/templates/claude-global/skills/dwy-eapi/SKILL.md +433 -0
  55. package/templates/claude-global/skills/dwy-eapi/references/tasks-integration-guide.md +387 -0
  56. package/templates/claude-global/skills/dwy-ekit/SKILL.md +85 -0
  57. package/templates/claude-global/skills/dwy-ekit/references/data.md +135 -0
  58. package/templates/claude-global/skills/dwy-ekit/references/form.md +68 -0
  59. package/templates/claude-global/skills/dwy-ekit/references/request.md +183 -0
  60. package/templates/claude-global/skills/dwy-ekit/references/ui.md +109 -0
  61. package/templates/claude-global/skills/{dwy-frontend-eui → dwy-eui}/SKILL.md +129 -15
  62. package/templates/claude-global/skills/dwy-eui/references/eui-design-guide.md +257 -0
  63. package/templates/claude-global/skills/dwy-eui/references/eui-integration-guide.md +302 -0
  64. package/templates/claude-global/skills/dwy-eui/references/eui-landing-design-guide.md +437 -0
  65. package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +214 -0
  66. package/templates/claude-global/skills/dwy-mirror-source/SKILL.md +275 -0
  67. package/templates/claude-global/skills/dwy-mirror-source/references/dockerfile-snippets.md +228 -0
  68. package/templates/claude-global/skills/dwy-mirror-source/references/mirror-providers.md +150 -0
  69. package/templates/claude-global/skills/dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc +0 -0
  70. package/templates/claude-global/skills/dwy-mirror-source/scripts/apply_mirrors.py +666 -0
  71. package/templates/claude-global/skills/dwy-mirror-source/scripts/check_mirrors.py +868 -0
  72. package/templates/claude-global/skills/dwy-pentest/SKILL.md +344 -0
  73. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__init__.py +1 -0
  74. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc +0 -0
  75. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc +0 -0
  76. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc +0 -0
  77. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc +0 -0
  78. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc +0 -0
  79. package/templates/claude-global/skills/dwy-pentest/scripts/lib/config.py +109 -0
  80. package/templates/claude-global/skills/dwy-pentest/scripts/lib/executor.py +157 -0
  81. package/templates/claude-global/skills/dwy-pentest/scripts/lib/severity.py +96 -0
  82. package/templates/claude-global/skills/dwy-pentest/scripts/lib/validator.py +113 -0
  83. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_auth.py +289 -0
  84. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_recon.py +217 -0
  85. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_report.py +343 -0
  86. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_scan.py +205 -0
  87. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_ssl.py +208 -0
  88. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_vuln.py +205 -0
  89. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_web.py +330 -0
  90. package/templates/claude-global/skills/dwy-pentest/scripts/pyproject.toml +14 -0
  91. package/templates/claude-global/skills/dwy-pentest/scripts/setup_tools.sh +97 -0
  92. package/templates/claude-global/skills/dwy-pentest/scripts/uv.lock +415 -0
  93. package/templates/claude-global/skills/dwy-publish/SKILL.md +135 -0
  94. package/templates/claude-global/skills/dwy-vue-core/SKILL.md +890 -0
  95. package/templates/project/backend/.dwy.hbs +5 -0
  96. package/templates/project/backend/.python-version +1 -0
  97. package/templates/project/backend/CLAUDE.md.hbs +40 -0
  98. package/templates/project/{web/backend → backend}/alembic/script.py.mako +2 -0
  99. package/templates/project/{web/backend → backend}/alembic.ini.hbs +0 -1
  100. package/templates/project/backend/deploy.sh.hbs +18 -0
  101. package/templates/project/{web → backend}/docker-compose.prod.yml.hbs +1 -1
  102. package/templates/project/{web/backend → backend}/pyproject.toml.hbs +4 -5
  103. package/templates/project/{web/backend → backend}/src/app/config.py.hbs +1 -1
  104. package/templates/project/backend/src/app/database.py +10 -0
  105. package/templates/project/backend/src/app/dependencies.py +5 -0
  106. package/templates/project/{web/backend → backend}/src/app/main.py.hbs +3 -3
  107. package/templates/project/backend/src/app/models/base.py +5 -0
  108. package/templates/project/web/CLAUDE.md.hbs +29 -36
  109. package/templates/project/web/{frontend → apps/web}/index.html.hbs +1 -0
  110. package/templates/project/web/{frontend → apps/web}/package.json.hbs +4 -3
  111. package/templates/project/web/apps/web/src/App.vue +9 -0
  112. package/templates/project/web/apps/web/src/api/client.ts +5 -0
  113. package/templates/project/web/apps/web/src/composables/.gitkeep +0 -0
  114. package/templates/project/web/apps/web/src/layouts/DefaultLayout.vue +8 -0
  115. package/templates/project/web/{frontend → apps/web}/src/router/index.ts +1 -1
  116. package/templates/project/web/{frontend → apps/web}/src/stores/auth.ts +4 -2
  117. package/templates/project/web/apps/web/src/style.css +2 -0
  118. package/templates/project/web/apps/web/src/types/router.d.ts +8 -0
  119. package/templates/project/web/apps/web/src/views/HomeView.vue +8 -0
  120. package/templates/project/web/apps/web/tsconfig.app.json +18 -0
  121. package/templates/project/web/apps/web/tsconfig.json +6 -0
  122. package/templates/project/web/{frontend → apps/web}/vite.config.ts.hbs +7 -1
  123. package/templates/project/web/package.json.hbs +16 -0
  124. package/templates/project/web/packages/.gitkeep +0 -0
  125. package/templates/project/web/pnpm-workspace.yaml +3 -0
  126. package/templates/claude-global/rules/agent-team-execution.md +0 -138
  127. package/templates/claude-global/rules/agent-team-patterns.md +0 -157
  128. package/templates/claude-global/rules/git-security.md +0 -144
  129. package/templates/claude-global/skills/dwy-auto-sync/SKILL.md +0 -52
  130. package/templates/claude-global/skills/dwy-backend-base/SKILL.md +0 -279
  131. package/templates/claude-global/skills/dwy-frontend-core/SKILL.md +0 -181
  132. package/templates/project/web/.claude/commands/deploy.md +0 -4
  133. package/templates/project/web/backend/src/app/models/base.py +0 -5
  134. package/templates/project/web/deploy.sh.hbs +0 -113
  135. package/templates/project/web/frontend/src/App.vue +0 -3
  136. package/templates/project/web/frontend/src/api/client.ts +0 -13
  137. package/templates/project/web/frontend/src/style.css +0 -1
  138. package/templates/project/web/frontend/src/views/HomeView.vue +0 -5
  139. package/templates/project/web/frontend/tsconfig.app.json +0 -14
  140. package/templates/project/web/frontend/tsconfig.json +0 -4
  141. /package/templates/{project/web/backend → claude-global/skills/dwy-pentest/scripts}/.python-version +0 -0
  142. /package/templates/project/{web/backend → backend}/Dockerfile +0 -0
  143. /package/templates/project/{web/backend → backend}/alembic/env.py.hbs +0 -0
  144. /package/templates/{claude-global/skills → project/backend/alembic/versions}/.gitkeep +0 -0
  145. /package/templates/project/{web → backend}/docker-compose.dev.yml.hbs +0 -0
  146. /package/templates/project/{web/backend → backend}/src/app/__init__.py +0 -0
  147. /package/templates/project/{web/backend → backend}/src/app/models/__init__.py +0 -0
  148. /package/templates/project/{web/backend → backend}/src/app/routers/__init__.py +0 -0
  149. /package/templates/project/{web/backend → backend}/src/app/routers/health.py +0 -0
  150. /package/templates/project/{web/backend → backend}/src/app/schemas/__init__.py +0 -0
  151. /package/templates/project/{web/backend → backend}/src/app/services/__init__.py +0 -0
  152. /package/templates/project/{web/backend → backend}/tests/__init__.py +0 -0
  153. /package/templates/project/{web/backend → backend}/tests/conftest.py.hbs +0 -0
  154. /package/templates/project/{web/backend/alembic/versions → mobile}/.gitkeep +0 -0
  155. /package/templates/project/web/{frontend → apps/web}/src/components/.gitkeep +0 -0
  156. /package/templates/project/web/{frontend → apps/web}/src/main.ts +0 -0
@@ -0,0 +1,610 @@
1
+ ---
2
+ description: Android (Java + Kotlin + XML) 代码注释与基础风格规范
3
+ paths:
4
+ - "**/*.java"
5
+ - "**/*.kt"
6
+ - "**/*.kts"
7
+ - "**/AndroidManifest.xml"
8
+ - "**/res/**/*.xml"
9
+ ---
10
+
11
+ # Android 代码注释与基础风格规范
12
+
13
+ 适用于 **Android 原生项目**(Java + Kotlin 混编,XML 布局)。本规范**重点是代码注释**,兼带最小必要的命名 / 简约 / 反模式约束。
14
+
15
+ 不限制构建工具、UI 框架、架构模式、SDK 版本、依赖库选型 —— 与老项目兼容。
16
+
17
+ ## 一、命名规范
18
+
19
+ | 类型 | 风格 | 示例 |
20
+ |---|---|---|
21
+ | 包 | `lowercase` | `com.example.user` |
22
+ | 类 / 接口 / 枚举 | `PascalCase` | `LoginActivity`、`UserAdapter` |
23
+ | 方法 / 函数 | `camelCase` | `loadUserById()` |
24
+ | 变量 / 参数 | `camelCase` | `userCount` |
25
+ | 常量 (`static final` / `const val`) | `UPPER_SNAKE_CASE` | `MAX_RETRY_COUNT` |
26
+ | Activity | `XxxActivity` | `LoginActivity` |
27
+ | Fragment | `XxxFragment` | `HomeFragment` |
28
+ | Adapter / ViewHolder | `XxxAdapter` / `XxxViewHolder` | `OrderAdapter` |
29
+ | Dialog | `XxxDialog` | `ConfirmDialog` |
30
+ | 布尔变量 / 方法 | `is*` / `has*` / `can*` / `should*` | `isLoading`、`hasNext` |
31
+
32
+ ### 资源命名
33
+
34
+ | 资源类型 | 规则 | 示例 |
35
+ |---|---|---|
36
+ | 布局文件 | `<类型>_<功能>.xml` 全小写下划线 | `activity_login.xml`、`fragment_home.xml`、`item_order.xml`、`dialog_confirm.xml` |
37
+ | View id | `<控件类型>_<功能>` | `tv_username`、`et_password`、`btn_submit`、`rv_orders`、`iv_avatar` |
38
+ | drawable | `<用途>_<状态>` | `bg_button_primary`、`ic_arrow_right`、`shape_card_corner` |
39
+ | color | `<语义>` | `color_primary`、`color_text_secondary` |
40
+ | string | `<场景>_<语义>` | `login_button_submit`、`home_title` |
41
+ | dimen | `<语义>` | `padding_normal`、`text_size_title` |
42
+
43
+ ### 禁止的写法
44
+
45
+ ```kotlin
46
+ // ❌ 单字母变量(循环索引除外)
47
+ val d = getData()
48
+ // ✅
49
+ val userData = getData()
50
+
51
+ // ❌ 匈牙利命名
52
+ val strName = "Alice"
53
+ val lstUsers = mutableListOf<User>()
54
+ // ✅
55
+ val name = "Alice"
56
+ val users = mutableListOf<User>()
57
+
58
+ // ❌ 布尔变量不带语义前缀
59
+ val active = true
60
+ // ✅
61
+ val isActive = true
62
+ ```
63
+
64
+ ### 禁止魔法字符串与硬编码
65
+
66
+ - 同一字面量在文件内出现 **2 次及以上**必须提取为常量
67
+ - **用户可见文案必须走 `strings.xml`**,便于国际化与统一审校
68
+ - 颜色走 `colors.xml`,尺寸走 `dimens.xml`
69
+
70
+ ```kotlin
71
+ // ❌ 散落
72
+ intent.putExtra("user_id", 123)
73
+ val id = intent.getLongExtra("user_id", 0)
74
+
75
+ // ✅ 常量化
76
+ class UserActivity : AppCompatActivity() {
77
+ companion object {
78
+ private const val EXTRA_USER_ID = "user_id"
79
+
80
+ fun newIntent(context: Context, userId: Long) =
81
+ Intent(context, UserActivity::class.java).putExtra(EXTRA_USER_ID, userId)
82
+ }
83
+ }
84
+ ```
85
+
86
+ ```xml
87
+ <!-- ❌ 裸文案 / 裸数字 -->
88
+ <TextView android:text="登录" android:textSize="16sp" />
89
+
90
+ <!-- ✅ 走资源 -->
91
+ <TextView android:text="@string/login_button_submit" android:textSize="@dimen/text_size_normal" />
92
+ ```
93
+
94
+ ## 二、代码注释(本文件重点)
95
+
96
+ ### 总则
97
+ - 注释解释 **为什么(why)**,不复述 **做了什么(what)**
98
+ - 全部使用**中文**,**禁止**中英混用
99
+ - 公开 API 注释**必须**有,**禁止**简单的"获取 / 设置 / 处理"敷衍式注释
100
+ - 代码改动后**必须**同步更新注释,留下过时注释 = 违规
101
+
102
+ ### 2.1 文件 / 类级注释(强制)
103
+
104
+ 每个公开类必须有类级 Javadoc / KDoc,放在类声明上方,说明:
105
+ - 该类的**职责**
106
+ - 与谁配合工作(被谁调用、依赖谁)
107
+ - 关键的使用约束 / 生命周期 / 线程要求(如果有)
108
+
109
+ ```kotlin
110
+ /**
111
+ * 登录页。
112
+ *
113
+ * 负责账号密码 / 验证码登录入口,登录成功后跳转 [HomeActivity] 并清空回退栈。
114
+ *
115
+ * 入口:[newIntent] 静态构造,禁止外部直接 new Intent。
116
+ */
117
+ class LoginActivity : AppCompatActivity() { ... }
118
+ ```
119
+
120
+ ```kotlin
121
+ /**
122
+ * 订单列表 Adapter。
123
+ *
124
+ * 配合 [RecyclerView] + [DiffUtil] 增量刷新,UI 线程使用,**非线程安全**。
125
+ *
126
+ * @property onItemClick 列表项点击回调,参数为订单 ID
127
+ */
128
+ class OrderAdapter(
129
+ private val onItemClick: (orderId: Long) -> Unit,
130
+ ) : ListAdapter<Order, OrderViewHolder>(DIFF_CALLBACK) { ... }
131
+ ```
132
+
133
+ ```java
134
+ /**
135
+ * 用户头像加载工具。
136
+ *
137
+ * <p>封装默认占位图、圆角、错误图,所有头像加载必须走本类。
138
+ */
139
+ public final class AvatarLoader {
140
+ private AvatarLoader() {}
141
+ ...
142
+ }
143
+ ```
144
+
145
+ ### 2.2 公开方法 / 函数注释(强制)
146
+
147
+ 所有 `public` / Kotlin 默认可见性的方法 / 函数(含顶层函数)必须有 Javadoc / KDoc,包含:
148
+ - 一句话功能描述
149
+ - `@param` 每个参数的语义、取值范围、可空性
150
+ - `@return` 返回值语义,空集合 / null 含义
151
+ - `@throws` 可能抛出的异常 + 触发条件
152
+ - 涉及线程 / 主线程要求时**必须**注明
153
+
154
+ ```kotlin
155
+ /**
156
+ * 根据 ID 查询用户。
157
+ *
158
+ * 内部走内存缓存 → DB → 网络的三级回源,网络超时 5 秒。
159
+ * 必须在后台线程调用,**禁止主线程**。
160
+ *
161
+ * @param userId 用户唯一标识,必须 > 0
162
+ * @return 匹配的用户对象;不存在时抛 [NotFoundException],不返回 null
163
+ * @throws NotFoundException 用户不存在
164
+ * @throws IOException 网络请求失败且本地无缓存
165
+ */
166
+ fun getUserById(userId: Long): User { ... }
167
+ ```
168
+
169
+ ```java
170
+ /**
171
+ * 启动登录页。
172
+ *
173
+ * <p>调用方所在 Activity / Fragment 不需要预先清栈,本方法内部已带 {@code FLAG_ACTIVITY_CLEAR_TOP}。
174
+ *
175
+ * @param context 启动上下文,Activity / Application 均可
176
+ * @param redirectUri 登录成功后跳转的目标 URI,可为 null;为 null 时跳首页
177
+ */
178
+ public static void start(@NonNull Context context, @Nullable String redirectUri) { ... }
179
+ ```
180
+
181
+ ### 2.3 Activity / Fragment 注释模板(强制)
182
+
183
+ 页面类**必须**在类级注释中注明:**入口方式、关键参数、返回结果(如有)**。
184
+
185
+ ```kotlin
186
+ /**
187
+ * 订单详情页。
188
+ *
189
+ * 入口:[newIntent],必须传入 [EXTRA_ORDER_ID]。
190
+ * 返回:取消订单成功时 setResult [RESULT_ORDER_CANCELED],携带 [EXTRA_ORDER_ID]。
191
+ */
192
+ class OrderDetailActivity : AppCompatActivity() {
193
+
194
+ companion object {
195
+ private const val EXTRA_ORDER_ID = "order_id"
196
+ const val RESULT_ORDER_CANCELED = 1001
197
+
198
+ /**
199
+ * 构造启动订单详情页的 Intent。
200
+ *
201
+ * @param context 启动上下文
202
+ * @param orderId 订单 ID,必须 > 0
203
+ */
204
+ fun newIntent(context: Context, orderId: Long): Intent =
205
+ Intent(context, OrderDetailActivity::class.java)
206
+ .putExtra(EXTRA_ORDER_ID, orderId)
207
+ }
208
+ }
209
+ ```
210
+
211
+ ### 2.4 回调 / 监听器注释(强制)
212
+
213
+ 回调参数必须注明每个参数的语义,以及回调发生的**线程**。
214
+
215
+ ```kotlin
216
+ /**
217
+ * 文件下载回调。所有方法在**主线程**触发。
218
+ */
219
+ interface DownloadListener {
220
+ /**
221
+ * 下载进度更新。
222
+ *
223
+ * @param downloaded 已下载字节数
224
+ * @param total 文件总字节数;-1 表示未知大小
225
+ */
226
+ fun onProgress(downloaded: Long, total: Long)
227
+
228
+ /** 下载完成,[file] 为最终落盘路径。 */
229
+ fun onSuccess(file: File)
230
+
231
+ /** 下载失败,[error] 为失败原因,UI 层负责展示提示。 */
232
+ fun onError(error: Throwable)
233
+ }
234
+ ```
235
+
236
+ ### 2.5 复杂方法 / 业务规则注释(强制)
237
+
238
+ 涉及**业务规则、边界条件、为什么这样写**时必须有注释。简单 getter / setter / 一目了然的方法可省。
239
+
240
+ ```kotlin
241
+ // ❌ 无用注释,只复述代码
242
+ // 获取用户
243
+ val user = userRepo.getById(userId)
244
+
245
+ // ✅ 解释原因
246
+ // 此处必须用本地缓存,因为退到后台超过 30 分钟会被系统回收,
247
+ // 网络回源会触发整页重绘,体验差
248
+ val user = userCache.getById(userId)
249
+ ```
250
+
251
+ ```kotlin
252
+ fun computeDiscount(order: Order): BigDecimal {
253
+ // 业务规则(2024-12 起):VIP 用户首单 8 折,后续 9 折,叠加优惠券时取较低者
254
+ // 详见需求 PRD-2401-折扣调整
255
+ if (order.isFirstOrder && order.user.isVip) {
256
+ return order.amount.multiply(BigDecimal("0.8"))
257
+ }
258
+ ...
259
+ }
260
+ ```
261
+
262
+ ### 2.6 字段 / 属性注释(强制)
263
+
264
+ 公开字段、`data class` 字段、配置常量必须逐个注释。
265
+
266
+ ```kotlin
267
+ /**
268
+ * 用户信息。
269
+ *
270
+ * @property id 用户 ID,服务端分配,> 0
271
+ * @property name 昵称,1-32 字符
272
+ * @property avatarUrl 头像地址,空字符串表示无头像(不为 null)
273
+ * @property vipLevel 会员等级,0=普通,1=VIP,2=SVIP
274
+ */
275
+ data class User(
276
+ val id: Long,
277
+ val name: String,
278
+ val avatarUrl: String,
279
+ val vipLevel: Int,
280
+ )
281
+ ```
282
+
283
+ ```java
284
+ public final class Constants {
285
+ /** 登录态本地缓存的 SP key,迁移版本时不允许复用此 key,会读到旧数据。 */
286
+ public static final String SP_KEY_LOGIN_TOKEN = "login_token_v2";
287
+
288
+ /** 网络请求超时,单位毫秒。短于 3000 易在弱网误判,长于 15000 用户感知卡顿。 */
289
+ public static final int NETWORK_TIMEOUT_MS = 8000;
290
+ }
291
+ ```
292
+
293
+ ### 2.7 XML 资源注释(强制)
294
+
295
+ `strings.xml` / `colors.xml` / `dimens.xml` 中超过一屏的资源**必须**分组,每组用 `<!-- -->` 注释说明用途。
296
+
297
+ ```xml
298
+ <!-- res/values/strings.xml -->
299
+ <resources>
300
+ <!-- 通用 -->
301
+ <string name="app_name">我的应用</string>
302
+ <string name="confirm">确定</string>
303
+ <string name="cancel">取消</string>
304
+
305
+ <!-- 登录 / 注册 -->
306
+ <string name="login_title">登录</string>
307
+ <string name="login_hint_username">请输入手机号</string>
308
+ <string name="login_hint_password">请输入密码</string>
309
+ <string name="login_button_submit">登录</string>
310
+
311
+ <!-- 错误提示 -->
312
+ <string name="error_network_unavailable">网络不可用,请检查后重试</string>
313
+ <string name="error_server_busy">服务器繁忙,请稍后再试</string>
314
+ </resources>
315
+ ```
316
+
317
+ 布局文件中重要节点(如自定义复杂组件)用 `<!-- -->` 标注职责:
318
+
319
+ ```xml
320
+ <!-- 顶部状态条:展示订单状态色块 + 状态文案,根据 OrderStatus 切换 -->
321
+ <LinearLayout
322
+ android:id="@+id/ll_status_bar"
323
+ ... />
324
+ ```
325
+
326
+ ### 2.8 注释规范汇总
327
+
328
+ | 场景 | 风格 | 必填 |
329
+ |---|---|---|
330
+ | 单行可描述清楚 | 单行 KDoc/Javadoc:`/** xxx。 */` | ✓ |
331
+ | 有参数 / 返回值 / 异常 | 多行,含 `@param` / `@return` / `@throws` | ✓ |
332
+ | Activity / Fragment | 必须注明入口、参数 key、返回结果 | ✓ |
333
+ | 回调接口 | 必须注明触发线程 + 每个参数语义 | ✓ |
334
+ | `data class` 字段 | Kotlin `@property`,Java 字段上 `/** */` | ✓ |
335
+ | 复杂业务规则 | 行内注释解释 why + 关联 PRD / 需求号 | ✓ |
336
+ | 重写方法 | `@inheritDoc` 或重新写明覆写差异 | ✓ |
337
+ | 简单 getter / setter / 一目了然 | 可省 | — |
338
+ | 测试方法 | 测试名本身就是描述,可省 | — |
339
+
340
+ ### 2.9 行内注释禁止事项
341
+
342
+ ```kotlin
343
+ // ❌ 复述代码
344
+ // i 自增
345
+ i++
346
+
347
+ // ❌ 注释掉的代码块(用 git 历史,不要留垃圾)
348
+ // val old = legacyService.fetch()
349
+ // ...
350
+ val data = newService.fetch()
351
+
352
+ // ❌ TODO 不带责任人 / 时间 / 关联 issue
353
+ // TODO: 这里有点问题
354
+
355
+ // ✅ TODO 必须带责任人 + 日期 + 上下文
356
+ // TODO(zhang, 2026-05-30): 兼容企业微信扫码,关联 issue #1234
357
+ ```
358
+
359
+ ## 三、可空性
360
+
361
+ ### Kotlin
362
+ - 默认所有类型不可空,可空显式 `?`
363
+ - **禁止** `!!` 出现在生产代码,除非附注释说明为什么一定不为 null
364
+
365
+ ```kotlin
366
+ // ❌ !! 滥用
367
+ val name = user!!.profile!!.nickname!!
368
+
369
+ // ✅ 安全调用 + 兜底
370
+ val name = user?.profile?.nickname ?: getString(R.string.user_anonymous)
371
+ ```
372
+
373
+ ### Java
374
+ - 公开 API 参数 / 字段 / 返回值用 `@Nullable` / `@NonNull`(`androidx.annotation.*`) 标注
375
+ - 集合返回值优先返回空集合而非 `null`
376
+
377
+ ```java
378
+ // ❌ 没有可空标注
379
+ public User findById(long id) { ... }
380
+
381
+ // ✅
382
+ @Nullable
383
+ public User findById(long id) { ... }
384
+
385
+ @NonNull
386
+ public List<Order> findOrders(long userId) {
387
+ return repository.queryByUser(userId);
388
+ }
389
+ ```
390
+
391
+ ### 禁止 `Object` / `Any` 当懒标注
392
+
393
+ - Java **禁止**用 `Object` 当 API 入参 / 返回值(反射框架边界除外)
394
+ - Kotlin **禁止**用 `Any` / `Any?`(JSON tree、反射桥接除外)
395
+ - 必须使用时**附注释说明原因**
396
+
397
+ ## 四、函数与类设计
398
+
399
+ ### 强制规则
400
+ - 公开方法 / 函数参数 ≤ **3 个**,超过则封装为 DTO / `data class`
401
+ - Kotlin 优先**命名参数 + 默认参数**代替方法重载
402
+ - 函数体不超过 **50 行**,超过则拆分
403
+ - 单文件不超过 **500 行**,超过则拆分模块
404
+ - 嵌套不超过 **3 层**,使用 early return / `?.let` / `return@xxx` 降低嵌套
405
+ - 类字段 / Kotlin 主构造参数 ≤ **7 个**,超过则按职责拆分
406
+
407
+ ```kotlin
408
+ // ❌ 多个布尔参数 + 重载
409
+ fun share(toWechat: Boolean, toQQ: Boolean, withImage: Boolean)
410
+
411
+ // ✅ 命名参数 + 枚举
412
+ enum class ShareChannel { WECHAT, QQ, WEIBO }
413
+ fun share(channel: ShareChannel, withImage: Boolean = false)
414
+ ```
415
+
416
+ ```kotlin
417
+ // ❌ 深层嵌套
418
+ fun process(user: User?) {
419
+ if (user != null) {
420
+ if (user.isActive) {
421
+ if (user.orders.isNotEmpty()) {
422
+ user.orders.forEach { ... }
423
+ }
424
+ }
425
+ }
426
+ }
427
+
428
+ // ✅ 提前返回
429
+ fun process(user: User?) {
430
+ if (user == null || !user.isActive) {
431
+ return
432
+ }
433
+ if (user.orders.isEmpty()) {
434
+ return
435
+ }
436
+ user.orders.forEach { ... }
437
+ }
438
+ ```
439
+
440
+ ### 控制流大括号
441
+
442
+ `if` / `else` / `else if` / `for` / `while` / `do-while` **即使只有一条语句**,必须使用 `{}` 包裹。
443
+
444
+ **理由**:
445
+ - 后续添加第二行时不会因为漏 `{}` 静默脱出条件体(经典回归 bug)
446
+ - IDE 自动 reformat 与跨工具 merge 不会错位
447
+ - Java 与 Kotlin 视觉一致,混编项目无歧义
448
+
449
+ **例外**:Kotlin **表达式形式**的 `if`(用作赋值右值、`return` 表达式、单表达式函数体)允许单行无 `{}`:
450
+
451
+ ```kotlin
452
+ val name = if (user.isVip) user.vipName else user.nickname // ✅ 表达式 if
453
+ return if (cond) doA() else doB() // ✅ 表达式 if
454
+ fun grade(score: Int) = if (score >= 60) "PASS" else "FAIL" // ✅ 单表达式函数
455
+ ```
456
+
457
+ **反例**:
458
+
459
+ ```kotlin
460
+ // ❌ 单行 if 语句无大括号 — 后续加一行就出 bug
461
+ if (!initialized.compareAndSet(false, true)) return
462
+
463
+ // ❌ 单行 for / while
464
+ for (item in list) item.refresh()
465
+ while (queue.isNotEmpty()) queue.poll().run()
466
+ ```
467
+
468
+ ```java
469
+ // ❌ Java 同样禁止
470
+ if (user == null) return;
471
+ for (Order o : orders) o.cancel();
472
+ ```
473
+
474
+ **正例**:
475
+
476
+ ```kotlin
477
+ // ✅ if 语句必须 {}
478
+ if (!initialized.compareAndSet(false, true)) {
479
+ return
480
+ }
481
+
482
+ for (item in list) {
483
+ item.refresh()
484
+ }
485
+
486
+ while (queue.isNotEmpty()) {
487
+ queue.poll().run()
488
+ }
489
+ ```
490
+
491
+ ```java
492
+ // ✅ Java 同样
493
+ if (user == null) {
494
+ return;
495
+ }
496
+
497
+ for (Order o : orders) {
498
+ o.cancel();
499
+ }
500
+ ```
501
+
502
+ ## 五、Context 与内存泄漏
503
+
504
+ - **禁止**长生命周期对象(单例、静态字段、长任务)持有 Activity / Fragment / View 引用
505
+ - 单例需要 Context 时**必须** `applicationContext`
506
+ - Handler 内部类必须 `static` + `WeakReference`(Java)
507
+ - Bitmap / Cursor / InputStream 必须 `use { ... }`(Kotlin) / try-with-resources(Java) 释放
508
+ - Fragment 中持有的 View 引用在 `onDestroyView` 必须置 null
509
+ - 监听器 / 广播注册成对(`onStart/onStop` 或 `onResume/onPause`),**禁止**在 `onCreate` 注册、`onDestroy` 解注册(横屏切换重建会泄漏)
510
+
511
+ ```kotlin
512
+ // ❌ 持有 Activity Context
513
+ object UserManager {
514
+ var context: Context? = null // Activity 销毁后泄漏
515
+ }
516
+
517
+ // ✅ ApplicationContext
518
+ object UserManager {
519
+ private lateinit var appContext: Context
520
+ fun init(context: Context) { appContext = context.applicationContext }
521
+ }
522
+ ```
523
+
524
+ ## 六、错误处理
525
+
526
+ ### 强制规则
527
+ - UI 层**必须** try/catch 捕获预期异常并展示用户友好提示;**禁止**让异常崩溃到 `Thread.UncaughtExceptionHandler`
528
+ - **禁止** `catch (Exception e) {}` 空捕获
529
+ - **禁止** `catch (Throwable t)`(会吞 OOM / StackOverflow)
530
+ - **禁止** `e.printStackTrace()`,改用项目自有日志工具
531
+
532
+ ## 七、代码简约原则
533
+
534
+ ### 核心思想
535
+
536
+ 代码只写**必要的逻辑**,不写"以防万一"的冗余。信任内部代码与框架保证,只在系统边界(用户输入、网络响应、IPC)做校验。
537
+
538
+ ```kotlin
539
+ // ❌ 不必要的 fallback — user.name 类型已是非空 String
540
+ val name = user.name ?: ""
541
+
542
+ // ✅
543
+ val name = user.name
544
+
545
+ // ❌ 不必要的异常兜底 — 掩盖 bug
546
+ val result = try {
547
+ calculate(x)
548
+ } catch (e: Exception) {
549
+ 0
550
+ }
551
+
552
+ // ✅ 让异常暴露,或只捕预期类型
553
+ val result = calculate(x)
554
+ ```
555
+
556
+ ### 判断标准
557
+
558
+ 写每一行防御代码前问自己:**这个情况在当前上下文下真的会发生吗?**
559
+
560
+ - **会** → 写防御,加注释说明触发条件
561
+ - **不会** → 不写,信任上游保证
562
+ - **不确定** → 查看调用链确认,不要"以防万一"
563
+
564
+ ## 八、常见反模式(禁止)
565
+
566
+ | 反模式 | 正确做法 |
567
+ |---|---|
568
+ | Kotlin `!!` 非空断言 | `?:` / `requireNotNull(x) { "..." }` 并附错误信息 |
569
+ | `catch (Exception e) {}` 空块 | 只捕获预期具体异常,或重新抛 |
570
+ | `catch (Throwable t)` | 只捕预期类型,OOM / StackOverflow 不该兜 |
571
+ | `e.printStackTrace()` | 项目自有日志工具 |
572
+ | Java `==` 比较对象 | `Objects.equals(a, b)` / `a.equals(b)` |
573
+ | Kotlin `===` 滥用 | 仅在确实需要引用相等时使用 |
574
+ | 暴露内部可变集合 | 返回不可变副本 |
575
+ | 静态可变状态 / 单例存 UI 数据 | 局部状态 / 持久化存储 |
576
+ | Kotlin 顶层 `var` | `val`;需可变用类封装 + 同步原语 |
577
+ | 内部类持有外部 Activity | `static` 内部类 + `WeakReference` |
578
+ | 拼接 SQL 字符串 | 参数化查询 |
579
+ | 硬编码 dp / sp 值 | `@dimen/` |
580
+ | 硬编码颜色值 `#FF0000` | `@color/color_primary` |
581
+ | 用户可见文案硬编码 | `@string/...` |
582
+ | 单行 `if` / `for` / `while` / `do-while` 无 `{}`(Kotlin 表达式 `if` 除外) | 即使只一行也写 `{}`;详见 §四「控制流大括号」 |
583
+
584
+ ## 九、代码自检(写代码时强制执行)
585
+
586
+ **每次生成或修改 Android 代码后,必须逐条验证。任一未通过 → STOP,立即修正。**
587
+
588
+ | # | 检查项 | 违规即 STOP |
589
+ |---|--------|------------|
590
+ | 1 | 公开类有类级 Javadoc / KDoc(中文),说明职责与协作关系 | ✓ |
591
+ | 2 | 公开方法 / 函数有 Javadoc / KDoc,含 `@param` / `@return` / `@throws` | ✓ |
592
+ | 3 | Activity / Fragment 类注释包含入口、参数 key、返回结果 | ✓ |
593
+ | 4 | 回调接口注明每个参数语义 + 触发线程 | ✓ |
594
+ | 5 | `data class` / 公开字段 / 配置常量逐个 `@property` / 字段注释 | ✓ |
595
+ | 6 | 复杂业务逻辑有"why"行内注释,关联 PRD / 需求号 | ✓ |
596
+ | 7 | 注释全部中文,无中英混用,无敷衍式"获取 / 设置 / 处理" | ✓ |
597
+ | 8 | 注释与代码同步更新,无过时注释 | ✓ |
598
+ | 9 | 公开方法参数 + 返回值类型完整,无 `Object` / `Any` 懒标注 | ✓ |
599
+ | 10 | Kotlin 无 `!!`(必须使用时附注释说明) | ✓ |
600
+ | 11 | Java 公开 API 用 `@Nullable` / `@NonNull` 标注 | ✓ |
601
+ | 12 | 单例 / 长生命周期对象用 `applicationContext`,不持有 Activity | ✓ |
602
+ | 13 | 用户可见文案走 `strings.xml`,颜色走 `colors.xml`,尺寸走 `dimens.xml` | ✓ |
603
+ | 14 | 同一文件内字符串字面量 ≥ 2 次必须提取为常量 | ✓ |
604
+ | 15 | 无空 `catch` 块、无 `catch (Throwable)`、无 `e.printStackTrace()` | ✓ |
605
+ | 16 | 嵌套不超过 3 层,函数体不超过 50 行,单文件不超过 500 行 | ✓ |
606
+ | 17 | 资源命名遵守:`activity_*` / `fragment_*` / `item_*` / `tv_*` / `et_*` / `btn_*` 等约定 | ✓ |
607
+ | 18 | 无注释掉的代码块(垃圾代码),TODO 必须带责任人 + 日期 + 上下文 | ✓ |
608
+ | 19 | `if` / `else` / `else if` / `for` / `while` / `do-while` 语句即使单行也带 `{}`(Kotlin 表达式 `if` 除外) | ✓ |
609
+
610
+ **不执行自检就提交代码 = 违规。**