create-dwy 0.16.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/bin/index.js +0 -0
  3. package/package.json +3 -2
  4. package/src/searchable-select.js +419 -0
  5. package/src/sync-all.js +11 -7
  6. package/src/sync.js +38 -32
  7. package/templates/ai-tools/CHANGELOG.md +76 -0
  8. package/templates/ai-tools/VERSION +1 -0
  9. package/templates/ai-tools/hook-manifests/hooks.json +7 -0
  10. package/templates/ai-tools/hooks/Git/pre-git-commit-ai-signature-check.sh +57 -0
  11. package/templates/ai-tools/rules/Android/dwy-android-core.md +215 -4
  12. package/templates/ai-tools/rules/Docker/dwy-docker.md +52 -1
  13. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +50 -1
  14. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-design.md +139 -0
  15. package/templates/ai-tools/rules/HarmonyOS/dwy-harmony-pitfalls.md +72 -0
  16. package/templates/ai-tools/rules/iOS/dwy-ios-pitfalls.md +7 -0
  17. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit.md +9 -6
  18. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/SKILL.md +511 -0
  19. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-data.md +126 -0
  20. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-network.md +88 -0
  21. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-runtime.md +195 -0
  22. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/security-doc-search.md +179 -0
  23. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_base.sh +39 -0
  24. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_capacity.sh +278 -0
  25. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_db.sh +147 -0
  26. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_docker.sh +230 -0
  27. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_env.sh +64 -0
  28. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_https.sh +60 -0
  29. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_logs.sh +209 -0
  30. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_nginx.sh +100 -0
  31. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_resilience.sh +157 -0
  32. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_secrets.sh +265 -0
  33. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_services.sh +94 -0
  34. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_ssh.sh +56 -0
  35. package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/run_all.sh +105 -0
  36. package/templates/ai-tools/skills//345/205/203/345/267/245/345/205/267/dwy-shared/SKILL.md +22 -1
  37. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/SKILL.md +1 -1
  38. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/references/checks-runtime.md +2 -2
  39. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_docker.sh +3 -3
  40. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_secrets.sh +1 -1
  41. package/templates/ai-tools/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-first/SKILL.md +176 -0
  42. package/templates/ai-tools/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-deploy-first/references/chapter-01-mirrors.md +112 -0
@@ -0,0 +1,76 @@
1
+ # ai-tools 内部版本
2
+
3
+ 本目录(`dwy-cli/templates/ai-tools`)rules / skills / hooks / commands / CLAUDE.md 等模板资产的**内部版本**,与 `create-dwy` npm 版本**解耦**。
4
+
5
+ - 当前版本:见同目录 `VERSION`(单一事实源)
6
+ - 用途:模板变更可追溯;发 `create-dwy` 时可对照本 CHANGELOG 摘发布说明;本地/多仓对照「模板包」新旧
7
+
8
+ ## 维护约定(强制)
9
+
10
+ **每次**修改本目录下任意内容(含新增 / 删除 / 重命名)时,同一变更必须:
11
+
12
+ 1. 按下方规则 bump `VERSION`
13
+ 2. 在本文件**顶部**(当前版本段之上)追加新版本段
14
+ 3. 条目写清路径与行为变化,不写空话
15
+
16
+ 禁止:改了模板却不 bump、不写 CHANGELOG。
17
+
18
+ ### 版本号(SemVer,0.x 阶段)
19
+
20
+ | 级别 | 何时 |
21
+ | ---- | ---- |
22
+ | **MAJOR**(`x.0.0`) | 破坏性:同步目录结构变更、托管块语义不兼容、删除已被广泛依赖的 skill/rule 且无迁移说明 |
23
+ | **MINOR**(`0.y.0`) | 新增 skill / rule / hook / command;既有规范**新增强制约束**或大段能力 |
24
+ | **PATCH**(`0.y.z`) | 文案修正、正反例补充、链接修复、不改变强制语义的小改 |
25
+
26
+ 0.x 阶段允许不稳定;仍用 minor / patch 区分「新增能力」与「修正」。
27
+
28
+ ### 版本段格式
29
+
30
+ ```markdown
31
+ ## x.y.z — YYYY-MM-DD
32
+
33
+ ### Added
34
+ - …
35
+
36
+ ### Changed
37
+ - …
38
+
39
+ ### Fixed
40
+ - …
41
+
42
+ ### Removed
43
+ - …
44
+ ```
45
+
46
+ 无某类变更可省略对应小节。日期用实际合并/提交日(`YYYY-MM-DD`)。
47
+
48
+ ### 与 create-dwy 的关系
49
+
50
+ - 改本目录 ≠ 自动发 npm;发布 `create-dwy` 时另走 CLI 发版流程,并在 `dwy-cli/CHANGELOG.md` 摘要用户可感变化
51
+ - 本文件是模板侧明细;CLI CHANGELOG 可只写要点并指向 ai-tools 版本号(如 `ai-tools 0.1.0`)
52
+
53
+ ---
54
+
55
+ ## 0.2.0 — 2026-07-23
56
+
57
+ ### Added
58
+
59
+ - `skills/运维发布/dwy-deploy-first/`:通用「首次部署」skill(分章)
60
+ - 第 1 章:镜像源强制配置 — apt → 阿里云;uv/pip → 阿里云 PyPI
61
+ - 禁止默认 `astral.sh/uv/install.sh`;与 `dwy-mirror-source`(本机工具源)、`dwy-docker`(工程规范)边界划清
62
+ - `references/chapter-01-mirrors.md`:通用 Dockerfile 片段(Debian/Ubuntu/Alpine + uv/pip)
63
+
64
+ ---
65
+
66
+ ## 0.1.0 — 2026-07-20
67
+
68
+ ### Added
69
+
70
+ - 建立 ai-tools 内部版本:`VERSION` + 本 `CHANGELOG.md` 及维护约定
71
+ - `skills/元工具/dwy-shared/SKILL.md`:入库流程增加步骤 6.5,改模板必须同批 bump `VERSION` + `CHANGELOG.md`
72
+ - `rules/Android/dwy-android-core.md`:新增「数据实体类封装(强制)」
73
+ - **Java**:字段 `private`,提供 `getXxx`/`setXxx` 与 `toString`;禁止 public 实例字段
74
+ - **Kotlin**:优先 `data class` + `val`(自带 `toString`);禁止 `@JvmField`;非 data class 必须手写 `toString`
75
+ - 不强制 `equals`/`hashCode`
76
+ - 联动 §2.6 注释、§八反模式、§九自检项 #22
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -12,5 +12,12 @@
12
12
  "matcher": "Bash",
13
13
  "platforms": ["claude", "codex"],
14
14
  "timeout": 30
15
+ },
16
+ {
17
+ "name": "pre-git-commit-ai-signature-check.sh",
18
+ "event": "PreToolUse",
19
+ "matcher": "Bash",
20
+ "platforms": ["claude", "codex"],
21
+ "timeout": 30
15
22
  }
16
23
  ]
@@ -0,0 +1,57 @@
1
+ #!/bin/bash
2
+ # Claude Code PreToolUse Hook
3
+ # 拦截 git commit message 中的 AI 署名 / 生成声明
4
+ #
5
+ # 背景:AI agent 常在 commit message 中附加 Co-Authored-By: Claude / Generated with Claude Code 等署名。
6
+ # 本 hook 检出此类模式后硬拦截(exit 2),要求人工确认或清理后再提交。
7
+ #
8
+ # 覆盖模式(不区分大小写):
9
+ # Co-Authored-By: <ai-name> <noreply@...>
10
+ # Generated with Claude Code
11
+ # Generated with ...
12
+ # noreply@anthropic.com / noreply@openai.com
13
+ # Claude / ChatGPT / GPT / Copilot / Cursor / AI / LLM (在签名行上下文)
14
+
15
+ # 从 stdin 读 JSON 取待执行命令(Claude Code 与 Codex 同样通过 stdin 传 .tool_input.command)
16
+ CMD=$(python3 -c 'import sys,json; print(json.load(sys.stdin).get("tool_input",{}).get("command",""))')
17
+
18
+ # 只在含 git commit 的命令上检查
19
+ printf '%s' "$CMD" | grep -q 'git commit' || exit 0
20
+
21
+ # AI 署名匹配模式
22
+ AI_PATTERNS=(
23
+ # Co-Authored-By trailer(任意 AI 名称 + 含 < 的邮箱)
24
+ "Co-Authored-By:"
25
+ # 生成声明
26
+ "Generated with"
27
+ # 常见 AI 署名邮箱
28
+ "noreply@anthropic\.com"
29
+ "noreply@openai\.com"
30
+ # AI 产品名(在签名行上下文,前面有 Co-Authored-By 或单独出现在行尾)
31
+ "Co-Authored-By:.*[Cc]laude"
32
+ "Co-Authored-By:.*[Cc]hatGPT"
33
+ "Co-Authored-By:.*GPT"
34
+ "Co-Authored-By:.*[Cc]opilot"
35
+ "Co-Authored-By:.*[Cc]ursor"
36
+ )
37
+
38
+ REGEX=$(IFS='|'; echo "${AI_PATTERNS[*]}")
39
+
40
+ HIT=$(printf '%s' "$CMD" | grep -inE "$REGEX" 2>/dev/null)
41
+
42
+ if [ -n "$HIT" ]; then
43
+ echo "========================================"
44
+ echo " WARNING: AI 署名 detected"
45
+ echo "========================================"
46
+ echo ""
47
+ echo "$HIT"
48
+ echo ""
49
+ echo "commit message 中禁止出现 Co-Authored-By / Generated with 等 AI 署名。"
50
+ echo "请清理 AI 署名后再提交。"
51
+ echo ""
52
+ echo "如果要强制提交,请手动执行 git commit(绕过 hook)。"
53
+ echo "========================================"
54
+ exit 2 # 暂停等待人工审批
55
+ fi
56
+
57
+ exit 0
@@ -336,7 +336,7 @@ fun computeDiscount(order: Order): BigDecimal {
336
336
 
337
337
  ### 2.6 字段 / 属性注释(强制)
338
338
 
339
- 公开字段、`data class` 字段、配置常量必须逐个注释。
339
+ 公开属性、`data class` 构造参数、配置常量必须逐个注释。**数据实体**除注释外还须遵守 §四「数据实体类封装」:Java 注释写在 **private 字段**上,Kotlin 用 `@property`。
340
340
 
341
341
  ```kotlin
342
342
  /**
@@ -356,6 +356,26 @@ data class User(
356
356
  ```
357
357
 
358
358
  ```java
359
+ /**
360
+ * 用户信息实体。字段私有,经 getter / setter 访问。
361
+ */
362
+ public class User {
363
+ /** 用户 ID,服务端分配,> 0 */
364
+ private long id;
365
+ /** 昵称,1-32 字符 */
366
+ private String name;
367
+
368
+ public long getId() {
369
+ return id;
370
+ }
371
+
372
+ public void setId(long id) {
373
+ this.id = id;
374
+ }
375
+
376
+ // ... 其余 get/set、toString 见 §四「数据实体类封装」
377
+ }
378
+
359
379
  public final class Constants {
360
380
  /** 登录态本地缓存的 SP key,迁移版本时不允许复用此 key,会读到旧数据。 */
361
381
  public static final String SP_KEY_LOGIN_TOKEN = "login_token_v2";
@@ -407,7 +427,7 @@ public final class Constants {
407
427
  | 有参数 / 返回值 / 异常 | 多行,含 `@param` / `@return` / `@throws` | ✓ |
408
428
  | Activity / Fragment | 必须注明入口、参数 key、返回结果 | ✓ |
409
429
  | 回调接口 | 必须注明触发线程 + 每个参数语义 | ✓ |
410
- | `data class` 字段 | Kotlin `@property`,Java 字段上 `/** */` | ✓ |
430
+ | `data class` / Java 实体字段 | Kotlin `@property`;Java **private 字段**上 `/** */` + get/set | ✓ |
411
431
  | 复杂业务规则 | 行内注释解释 why + 关联 PRD / 需求号 | ✓ |
412
432
  | 重写方法 | `@inheritDoc` 或重新写明覆写差异 | ✓ |
413
433
  | Gradle 脚本(Groovy) | 解释 why:锁版本/exclude 原因、变体用途、自定义 task/方法语义(详见 §2.10) | ✓ |
@@ -533,7 +553,7 @@ public List<Order> findOrders(long userId) {
533
553
 
534
554
  ### 强制规则
535
555
 
536
- - 公开方法 / 函数参数 ≤ **3 个**,超过则封装为 DTO / `data class`
556
+ - 公开方法 / 函数参数 ≤ **3 个**,超过则封装为 DTO / `data class`(封装出的 DTO **必须**遵守下文「数据实体类封装」)
537
557
  - Kotlin 优先**命名参数 + 默认参数**代替方法重载
538
558
  - 函数体不超过 **50 行**,超过则拆分
539
559
  - 单文件不超过 **500 行**,超过则拆分模块
@@ -636,6 +656,146 @@ for (Order o : orders) {
636
656
  }
637
657
  ```
638
658
 
659
+ ### 数据实体类封装(强制)
660
+
661
+ **动机**:Java 一旦暴露 `public` 字段,调用方直接依赖字段名与访问方式;后续改为 `private` + getter 会破坏源码 / 二进制兼容。实体从一开始就封装,避免后期改结构成本。Kotlin 编译属性本就是 private 字段 + 访问器,规则与 Java 分写,禁止用 `@JvmField` 退化成 public 字段。
662
+
663
+ #### 适用范围
664
+
665
+ | 覆盖 | 不覆盖 |
666
+ | ---- | ------ |
667
+ | Model / Entity / DTO / VO / Bean 及同类数据载体 | Activity / Fragment / Adapter / ViewHolder / Repository / ViewModel 等非数据载体 |
668
+ | 含功能包内私有、`internal` 实体 | `enum`;仅含常量的 `object` / `Constants` 类 |
669
+ | 网络响应体、跨层传递的数据对象 | 纯 UI 状态若仅为页面内部临时结构且不跨层,可豁免(跨层则按实体处理) |
670
+
671
+ #### 共同要求
672
+
673
+ 1. 字段 / 属性**不得**以 public 字段形式对外暴露(禁止 Java `public` 实例字段、禁止 Kotlin `@JvmField`)
674
+ 2. 必须提供读取能力:Java `getXxx()`,Kotlin property 访问器
675
+ 3. 必须有有意义的 `toString()`(能区分关键业务字段);禁止依赖 `Object` 默认 `ClassName@hash`
676
+ 4. **不强制** `equals` / `hashCode`(集合去重 / Map key 场景由调用方按需补)
677
+
678
+ ---
679
+
680
+ #### Java 规则
681
+
682
+ | 项 | 要求 |
683
+ | -- | ---- |
684
+ | 字段 | 必须 `private`;禁止 `public` / 包可见实例字段当实体 API |
685
+ | 访问器 | 提供 `getXxx()` / `setXxx()`;不可变实体可用构造注入 + 仅 getter |
686
+ | toString | `@Override public String toString()`,包含关键业务字段 |
687
+ | 注释 | 类级说明职责;每个 private 字段上 `/** */`(见 §2.6) |
688
+
689
+ ```java
690
+ /**
691
+ * 用户信息实体。
692
+ *
693
+ * <p>字段私有 + getter/setter,避免 public 字段导致后续无法安全改结构。
694
+ */
695
+ public class User {
696
+ /** 用户 ID,服务端分配,> 0 */
697
+ private long id;
698
+ /** 昵称,1-32 字符 */
699
+ private String name;
700
+
701
+ public long getId() {
702
+ return id;
703
+ }
704
+
705
+ public void setId(long id) {
706
+ this.id = id;
707
+ }
708
+
709
+ public String getName() {
710
+ return name;
711
+ }
712
+
713
+ public void setName(String name) {
714
+ this.name = name;
715
+ }
716
+
717
+ @Override
718
+ public String toString() {
719
+ return "User{id=" + id + ", name='" + name + "'}";
720
+ }
721
+ }
722
+ ```
723
+
724
+ ```java
725
+ // ❌ public 字段 — 调用方直接 .id,后期改 private 会大面积破坏
726
+ public class User {
727
+ public long id;
728
+ public String name;
729
+ }
730
+
731
+ // ❌ 有 private 字段但缺 toString / 缺访问器
732
+ public class User {
733
+ private long id;
734
+ // 无 getId,外部无法读;无 toString,日志只能看到 @hash
735
+ }
736
+ ```
737
+
738
+ ---
739
+
740
+ #### Kotlin 规则
741
+
742
+ | 项 | 要求 |
743
+ | -- | ---- |
744
+ | 优先写法 | **`data class` + `val`**(编译为 private 字段 + getter;`data class` 自带 `toString`) |
745
+ | 可变字段 | 确需可变用 `var`;仅允许类内改写时用 `private set` |
746
+ | 禁止 | `@JvmField`;Java 风格 public 字段;为 `data class` 再手写冗余 get/set(Java 互操作强制命名除外) |
747
+ | 非 data class | property 或 private 字段 + 访问器,**必须** `override fun toString()` |
748
+ | 注释 | 构造属性用 `@property`,与 §2.6 一致 |
749
+
750
+ ```kotlin
751
+ /**
752
+ * 用户信息实体。
753
+ *
754
+ * @property id 用户 ID,服务端分配,> 0
755
+ * @property name 昵称,1-32 字符
756
+ */
757
+ data class User(
758
+ val id: Long,
759
+ val name: String,
760
+ )
761
+ // data class 已生成 toString / componentN / copy;JVM 侧字段为 private + getter
762
+ ```
763
+
764
+ ```kotlin
765
+ /**
766
+ * 可编辑的草稿实体。
767
+ *
768
+ * 非 data class 时必须手写 toString;title 仅允许类内改写。
769
+ */
770
+ class Draft {
771
+ var title: String = ""
772
+ private set
773
+
774
+ /**
775
+ * 更新草稿标题。
776
+ *
777
+ * @param value 新标题,调用方已做长度校验
778
+ */
779
+ fun updateTitle(value: String) {
780
+ title = value
781
+ }
782
+
783
+ override fun toString(): String = "Draft(title=$title)"
784
+ }
785
+ ```
786
+
787
+ ```kotlin
788
+ // ❌ @JvmField 把属性暴露成 public 字段,与 Java public 字段同样难改
789
+ class User {
790
+ @JvmField
791
+ var id: Long = 0
792
+ }
793
+
794
+ // ❌ 普通 class 实体无 toString — 日志只有类名@hash
795
+ class User(val id: Long, val name: String)
796
+ // ✅ 改为 data class User(...) 或 override fun toString()
797
+ ```
798
+
639
799
  ### 包组织(按功能聚合)
640
800
 
641
801
  **按功能聚合(package-by-feature),不按技术分层(package-by-layer)。**
@@ -743,6 +903,51 @@ val result = calculate(x)
743
903
 
744
904
  ## 八、常见反模式(禁止)
745
905
 
906
+ ### 字符串空判断(强制)
907
+
908
+ 判断字符串是否为空、是否有有效内容时,**禁止**用 `length()` 比较,必须用语义明确的 `isEmpty()` / `isNotEmpty()`(Kotlin 意图为「非空白」时优先 `isNotBlank()`)。
909
+
910
+ **强制规则**:
911
+
912
+ - **禁止** `s.length() > 0`、`s.length() == 0`、`s.length() != 0`、`s.trim().length() > 0` 等 `length` 判空写法
913
+ - **必须**用 `isEmpty()` / `!isEmpty()`(Java) 或 `isEmpty()` / `isNotEmpty()`(Kotlin)
914
+ - 需先去首尾空白再判是否有内容:Java `!s.trim().isEmpty()`;Kotlin `s.trim().isNotEmpty()` 或等价的 `s.isNotBlank()`
915
+ - 可空字符串仍先做 null 判断,再判空:`s != null && !s.trim().isEmpty()`(与 §三可空性一致)
916
+
917
+ ```java
918
+ // ❌ 用 length 判「是否有有效内容」——语义弱、易被误读成「长度阈值」
919
+ public String resolveVideoType() {
920
+ if (videoType != null && videoType.trim().length() > 0) {
921
+ return videoType.trim();
922
+ }
923
+ if (type != null && type.trim().length() > 0) {
924
+ return type.trim();
925
+ }
926
+ return null;
927
+ }
928
+
929
+ // ✅ 用 isEmpty 表达「非空」
930
+ public String resolveVideoType() {
931
+ if (videoType != null && !videoType.trim().isEmpty()) {
932
+ return videoType.trim();
933
+ }
934
+ if (type != null && !type.trim().isEmpty()) {
935
+ return type.trim();
936
+ }
937
+ return null;
938
+ }
939
+ ```
940
+
941
+ ```kotlin
942
+ // ❌
943
+ if (videoType != null && videoType.trim().length > 0) { ... }
944
+
945
+ // ✅
946
+ if (!videoType.isNullOrBlank()) { ... }
947
+ // 或需保留 trim 后的值时:
948
+ val trimmed = videoType?.trim().orEmpty()
949
+ if (trimmed.isNotEmpty()) { ... }
950
+ ```
746
951
 
747
952
  | 反模式 | 正确做法 |
748
953
  | ----------------------------------------------------------------- | ------------------------------------------- |
@@ -761,6 +966,9 @@ val result = calculate(x)
761
966
  | 硬编码颜色值 `#FF0000` | `@color/color_primary` |
762
967
  | 用户可见文案硬编码 | `@string/...` |
763
968
  | 单行 `if` / `for` / `while` / `do-while` 无 `{}`(Kotlin 表达式 `if` 除外) | 即使只一行也写 `{}`;详见 §四「控制流大括号」 |
969
+ | Java 实体 `public` 实例字段 | `private` 字段 + `getXxx`/`setXxx` + `toString` |
970
+ | Kotlin 实体 `@JvmField` / 无 `toString` 的普通 class 实体 | 优先 `data class` + `val`;或手写访问器 + `toString` |
971
+ | `s.length() > 0` / `s.trim().length() > 0` 等用 length 判空 / 非空 | Java `!s.isEmpty()` / `!s.trim().isEmpty()`;Kotlin `s.isNotEmpty()` / `s.isNotBlank()`;详见本节「字符串空判断」 |
764
972
 
765
973
 
766
974
  ## 九、代码自检(写代码时强制执行)
@@ -774,7 +982,7 @@ val result = calculate(x)
774
982
  | 2 | 方法 / 函数(不分可见性)有 Javadoc / KDoc,含 `@param` / `@return` / `@throws`;一目了然的简单 getter / setter、测试方法可省 | ✓ |
775
983
  | 3 | Activity / Fragment 类注释包含入口、参数 key、返回结果 | ✓ |
776
984
  | 4 | 回调接口注明每个参数语义 + 触发线程 | ✓ |
777
- | 5 | `data class` / 公开字段 / 配置常量逐个 `@property` / 字段注释 | ✓ |
985
+ | 5 | `data class` 属性 / Java 实体 private 字段 / 配置常量逐个 `@property` 或字段注释 | ✓ |
778
986
  | 6 | 复杂业务逻辑有"why"行内注释,关联 PRD / 需求号 | ✓ |
779
987
  | 7 | 注释全部中文,无中英混用,无敷衍式"获取 / 设置 / 处理" | ✓ |
780
988
  | 8 | 注释与代码同步更新,无过时注释 | ✓ |
@@ -791,6 +999,9 @@ val result = calculate(x)
791
999
  | 19 | `if` / `else` / `else if` / `for` / `while` / `do-while` 语句即使单行也带 `{}`(Kotlin 表达式 `if` 除外) | ✓ |
792
1000
  | 20 | 新增类按**功能聚合**放对应功能包(`login/` / `order/`),不按 `activities/` / `models/` 分层横切;仅多功能共享才放 `common/` | ✓ |
793
1001
  | 21 | Gradle 脚本(Groovy / kts):锁版本 / `exclude` / 自定义 task / 变体 / 关键魔法值有 why 注释,无复述式、无注释掉的配置块 | ✓ |
1002
+ | 22 | 数据实体(Model/DTO/VO/Bean 等):Java 字段 `private` + get/set + `toString`;Kotlin 优先 `data class`+`val`(自带 toString),禁止 `@JvmField`,非 data class 必须手写 `toString` | ✓ |
1003
+ | 23 | 字符串空判断用 `isEmpty` / `isNotEmpty` / `isNotBlank`,禁止 `length() > 0` / `== 0` / `!= 0` 及 `trim().length() > 0` 判空;详见 §八「字符串空判断」 | ✓ |
794
1004
 
795
1005
 
796
1006
  **不执行自检就提交代码 = 违规。**
1007
+
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: Docker 通用规则(镜像固定版本、容器内存上限、日志大小限制)
2
+ description: Docker 通用规则(镜像固定版本、容器内存上限、日志大小限制、配置变更重启姿势)
3
3
  ---
4
4
 
5
5
  # Docker 通用规则
@@ -69,3 +69,54 @@ services:
69
69
  max-size: "10m"
70
70
  max-file: "3"
71
71
  ```
72
+
73
+ ## 三、配置变更重启姿势(env_file / environment / .env)
74
+
75
+ ### 根因
76
+
77
+ `docker compose restart` 只重启**已存在的容器进程**,**不会重读** `env_file` / `environment` / `.env`。env 变量只在**容器创建时**注入容器配置;restart 沿用旧容器的旧环境,新 env 永远进不去。
78
+
79
+ ### 现象(真实踩坑)
80
+
81
+ 预览机改了 `.env` 的 `DOLPHINDB_FACTOR__PORT=18560`,用 `docker compose restart` 加载 → preview 容器一直拿不到新值,因子 provider 静默回退到主库 8848,功能"看起来通了"实际走错库。当时报"已验证"是错的——只验了隧道连通,没验 app 实际配置(`printenv`)。
82
+
83
+ release 环境没踩坑:deploy-bg 是 build 后 `up -d`,镜像变更触发容器重建,重建时重读了 env_file。
84
+
85
+ ### 强制规则
86
+
87
+ **改了 env_file / environment / `.env` 后,必须重建容器,不能只 restart:**
88
+
89
+ ```bash
90
+ # 正确:重建容器,重读 env
91
+ docker compose up -d --force-recreate backend
92
+
93
+ # 或更精准:只重建受影响服务,不动依赖
94
+ docker compose up -d --force-recreate --no-deps backend
95
+
96
+ # 错误:restart 不重读 env,新配置永远不生效
97
+ docker compose restart backend
98
+ ```
99
+
100
+ ### 判断容器是否重建的速查
101
+
102
+ | 操作 | 容器是否重建 | env 是否重读 |
103
+ |------|------------|------------|
104
+ | `docker compose restart` | 否(仅重启进程) | **否** |
105
+ | `docker compose up -d`(镜像无变化) | 否 | 否 |
106
+ | `docker compose up -d`(镜像有变化) | 是 | 是 |
107
+ | `docker compose up -d --force-recreate` | 是 | 是 |
108
+ | `docker compose up -d --env-file .env.prod`(换了 env 文件) | 是 | 是 |
109
+
110
+ ### 验证清单(改 env 后必跑)
111
+
112
+ ```bash
113
+ # 1. 确认容器内实际生效的 env(不是宿主机的 .env)
114
+ docker compose exec backend printenv | grep DOLPHINDB_FACTOR__
115
+
116
+ # 2. 不要只验连通性,要验 app 实际读到的配置
117
+ # 连通=隧道通,不代表 app 配置对
118
+ ```
119
+
120
+ ### 一句话
121
+
122
+ > 配置改了别 restart,`up -d --force-recreate`。验证别只看连通,`printenv` 看容器内真值。
@@ -8,7 +8,13 @@ paths:
8
8
 
9
9
  # ArkUI / 工程 / 运行时 避坑
10
10
 
11
- `dwy-arkts-strict.md` 管语言语法层;本文件管 **ArkUI 组件、V2 状态管理、资源(图标)、工程构建、运行时容错**。
11
+ | 文件 | 层 |
12
+ |---|---|
13
+ | `dwy-arkts-strict.md` | 语言语法(arkts-no-*) |
14
+ | **本文件** | 组件 API、V2 状态、资源、工程构建、设备/运行时 |
15
+ | `dwy-arkts-ui-design.md` | UI 设计与交互(弹层关闭、加载三态等事前约束) |
16
+ | `dwy-harmony-pitfalls.md` | 四栏踩坑归档 |
17
+
12
18
  均为真机/DevEco 构建实测踩坑(编译期或运行期暴露,命令行 tsc 查不出)。
13
19
 
14
20
  ## 1. sys.symbol 图标名 ≠ SF Symbols 名,必须查鸿蒙清单逐个映射
@@ -191,3 +197,46 @@ $NODE $HVIGOR --mode module -p module=entry@default -p product=default -p pageTy
191
197
  见 `dwy-arkts-strict.md` 第 9 条(cryptoFramework 签名规格 / 公钥 DER 格式 / TCPSocket 局域网)。
192
198
  补充:手撸 HTTP/2 的 HPACK 全字面量是否被对端(如 grpc-netty)接受、华为 IAP 需 AGC 后台
193
199
  配商品 + 真机华为账号、asset/Preferences 在不同设备的可用性,均真机才能定论。
200
+
201
+ ## 11. assembleHap 未配 signingConfigs → unsigned.hap
202
+
203
+ - **现象**:`hvigorw assembleHap` 成功但日志 `Will skip sign 'hos_hap'`,产物 `entry-default-unsigned.hap`;真机拒装。
204
+ - **根因**:`build-profile.json5` 的 `signingConfigs` 为空。
205
+ - **正确做法**:DevEco → Project Structure → Signing Configs 自动签名写回工程;**模拟器**可装 unsigned;**真机**必须签名。
206
+ - **适用**:CLI / CI 打包。
207
+
208
+ ## 12. 本地模拟器(HVD)命令行:`-hvd` + hdc `127.0.0.1:5555`
209
+
210
+ - **现象**:不知如何脱离 IDE 起模拟器、装 HAP。
211
+ - **正确做法**:
212
+ - 启动:`/Applications/DevEco-Studio.app/Contents/tools/emulator/Emulator -hvd "Pura 90"`(名见 `~/.Huawei/Emulator/deployed/*.ini`)。
213
+ - 设备:`hdc list targets` → 常见 `127.0.0.1:5555`。
214
+ - 安装:`hdc -t 127.0.0.1:5555 install entry/.../entry-default-unsigned.hap`。
215
+ - 启动:`hdc -t 127.0.0.1:5555 shell aa start -a EntryAbility -b <bundleName>`。
216
+ - **适用**:无 GUI / 脚本化调试。
217
+
218
+ ## 13. 模拟器验不了局域网 TCP / mDNS / 设备发现
219
+
220
+ - **现象**:模拟器里 app 连不上局域网电视、ADB、mDNS 服务。
221
+ - **根因**:HVD 网络多为 NAT,**不是**与电脑同 Wi-Fi 的 peer;模拟器「代理设置」只影响 HTTP 出网,**不能**桥接局域网。
222
+ - **正确做法**:依赖局域网原始 TCP / mDNS 的链路(ADB 5555、插件 28100、无线调试配对)必须 **鸿蒙真机 + 与目标设备同 Wi-Fi**。模拟器只做 UI / 导航 / 无网逻辑。
223
+ - **适用**:任何局域网设备调试类 app。
224
+
225
+ ## 14. ScanKit `generateBarcode` 在模拟器常失败
226
+
227
+ - **现象**:二维码区域一直 spinner / 空白,业务 payload(如配对码数字)已生成。
228
+ - **根因**:`@kit.ScanKit` 的 `generateBarcode.createBarcode` 在 HVD 或部分镜像不可用。
229
+ - **正确做法**:
230
+ 1. 调用时显式 `backgroundColor` / `pixelMapColor` / `margin`。
231
+ 2. `catch` 后 **纯逻辑矩阵兜底**(自研或库 → `image.createPixelMap` RGBA),保证有图。
232
+ 3. UI:仅生成中 spinner;失败出文案,禁止永久 loading(见 `dwy-arkts-ui-design.md`)。
233
+ - **适用**:任何依赖出码展示的页面。
234
+
235
+ ## 文件职责(与本目录其它规则)
236
+
237
+ | 文件 | 管什么 |
238
+ |---|---|
239
+ | `dwy-arkts-strict.md` | 语言:禁用语法、arkts-no-* |
240
+ | `dwy-arkts-ui-build.md`(本文件) | 组件 API、状态、资源、构建、运行时/设备坑 |
241
+ | `dwy-arkts-ui-design.md` | **UI 设计规范**(弹层/顶栏/加载态/图标语义,事前约束) |
242
+ | `dwy-harmony-pitfalls.md` | 踩坑四栏归档(现象/根因/做法/适用),新坑先记这里再回写规范 |