@deepstorm/cli 0.1.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 (148) hide show
  1. package/README.md +72 -0
  2. package/dist/agents/reef-inspect-figma.md +77 -0
  3. package/dist/agents/reef-review-backend.md.tmpl +112 -0
  4. package/dist/agents/reef-review-frontend.md.tmpl +78 -0
  5. package/dist/agents/reef-review-infra.md +47 -0
  6. package/dist/agents/reef-review-security.md.tmpl +80 -0
  7. package/dist/agents/reef-scope-analysis.md +64 -0
  8. package/dist/build-registry.js +375 -0
  9. package/dist/cli.js +8581 -0
  10. package/dist/config-schema.json +133 -0
  11. package/dist/env-examples/context7.env-example +19 -0
  12. package/dist/env-examples/feishu-wiki.env-example +16 -0
  13. package/dist/env-examples/figma.env-example +16 -0
  14. package/dist/env-examples/github.env-example +20 -0
  15. package/dist/env-examples/jira.env-example +20 -0
  16. package/dist/hooks/mcp-hook.sh +77 -0
  17. package/dist/hooks/reef-auto-format.sh.tmpl +72 -0
  18. package/dist/hooks/reef-block-dangerous.sh +70 -0
  19. package/dist/hooks/reef-hooks.json +72 -0
  20. package/dist/hooks/reef-intent-detect.sh +129 -0
  21. package/dist/hooks/reef-protect-files.sh +55 -0
  22. package/dist/hooks/reef-run-tests.sh +84 -0
  23. package/dist/hooks/reef-scope-check.sh +386 -0
  24. package/dist/hooks/reef-scope-ci.sh +28 -0
  25. package/dist/hooks/reef-scope-gate.sh +115 -0
  26. package/dist/hooks/reef-scope-pre-commit.sh.tmpl +28 -0
  27. package/dist/hooks/reef-scope-setup.sh +204 -0
  28. package/dist/hooks/reef-scope-split.sh +203 -0
  29. package/dist/hooks/sweep-hooks.json +14 -0
  30. package/dist/hooks/sweep-mcp-hook.sh +77 -0
  31. package/dist/hooks/tide-hooks.json +14 -0
  32. package/dist/hooks/tide-session-preload.sh +17 -0
  33. package/dist/mcp/code-hosting/github.json +20 -0
  34. package/dist/mcp/design-tools/figma.json +19 -0
  35. package/dist/mcp/docs-reference/context7.json +28 -0
  36. package/dist/mcp/e2e-testing/playwright.json +13 -0
  37. package/dist/mcp/knowledge-base/feishu-wiki.json +19 -0
  38. package/dist/mcp/project-management/jira.json +27 -0
  39. package/dist/mcp-skills/deepflow-mcp-feishu-wiki-read/SKILL.md +65 -0
  40. package/dist/mcp-skills/deepflow-mcp-feishu-wiki-write/SKILL.md +63 -0
  41. package/dist/mcp-skills/deepflow-mcp-figma-read/SKILL.md +98 -0
  42. package/dist/mcp-skills/deepflow-mcp-github-read/SKILL.md +62 -0
  43. package/dist/mcp-skills/deepflow-mcp-github-write/SKILL.md +63 -0
  44. package/dist/mcp-skills/deepflow-mcp-jira-read/SKILL.md +80 -0
  45. package/dist/mcp-skills/deepflow-mcp-jira-write/SKILL.md +74 -0
  46. package/dist/mcp-skills/deepflow-mcp-playwright-read/SKILL.md +79 -0
  47. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +65 -0
  48. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +63 -0
  49. package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +98 -0
  50. package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +62 -0
  51. package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +63 -0
  52. package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +80 -0
  53. package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +74 -0
  54. package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +79 -0
  55. package/dist/registry.json +818 -0
  56. package/dist/skills/atoll-ops/SKILL.md +46 -0
  57. package/dist/skills/reef-commit/SKILL.md +127 -0
  58. package/dist/skills/reef-gen-backend/SKILL.md.tmpl +87 -0
  59. package/dist/skills/reef-gen-backend/variants/java/steps.md +28 -0
  60. package/dist/skills/reef-gen-backend/variants/python/steps.md +70 -0
  61. package/dist/skills/reef-gen-frontend/SKILL.md.tmpl +83 -0
  62. package/dist/skills/reef-gen-frontend/variants/angular/steps.md +30 -0
  63. package/dist/skills/reef-harden/EXAMPLES.md +89 -0
  64. package/dist/skills/reef-harden/SKILL.md +136 -0
  65. package/dist/skills/reef-pr/SKILL.md +97 -0
  66. package/dist/skills/reef-review/SKILL.md.tmpl +107 -0
  67. package/dist/skills/reef-scope/SKILL.md +134 -0
  68. package/dist/skills/reef-start/SKILL.md.tmpl +562 -0
  69. package/dist/skills/reef-start/references/jira-start-subagent.md +60 -0
  70. package/dist/skills/reef-style-backend/SKILL.md.tmpl +134 -0
  71. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/chat-client.md +96 -0
  72. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/rag.md +94 -0
  73. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +62 -0
  74. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/tool-calling.md +68 -0
  75. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +220 -0
  76. package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +148 -0
  77. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +131 -0
  78. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/quick-reference.md +103 -0
  79. package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +119 -0
  80. package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +101 -0
  81. package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +181 -0
  82. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/controller.md +95 -0
  83. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +121 -0
  84. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/infrastructure.md +179 -0
  85. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +202 -0
  86. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +107 -0
  87. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +83 -0
  88. package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +150 -0
  89. package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +197 -0
  90. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/examples/user-repository-test.md +69 -0
  91. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +101 -0
  92. package/dist/skills/reef-style-backend/fragments/java/test/junit5/examples/user-service-test.md +61 -0
  93. package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +100 -0
  94. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/examples/user-controller-test.md +61 -0
  95. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +85 -0
  96. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/examples/user-service-integration-test.md +56 -0
  97. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +83 -0
  98. package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +77 -0
  99. package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +164 -0
  100. package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +139 -0
  101. package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +177 -0
  102. package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +101 -0
  103. package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +135 -0
  104. package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +111 -0
  105. package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +83 -0
  106. package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +207 -0
  107. package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +91 -0
  108. package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +227 -0
  109. package/dist/skills/reef-style-backend/variants/java/examples/contributor-pattern.md +97 -0
  110. package/dist/skills/reef-style-backend/variants/java/quick-reference.md +117 -0
  111. package/dist/skills/reef-style-backend/variants/python/examples/crud-router.md +71 -0
  112. package/dist/skills/reef-style-backend/variants/python/examples/pydantic-schema.md +45 -0
  113. package/dist/skills/reef-style-backend/variants/python/examples/pytest-fixture.md +60 -0
  114. package/dist/skills/reef-style-backend/variants/python/examples/sqlalchemy-model.md +31 -0
  115. package/dist/skills/reef-style-backend/variants/python/quick-reference.md +111 -0
  116. package/dist/skills/reef-style-frontend/SKILL.md.tmpl +70 -0
  117. package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +79 -0
  118. package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +150 -0
  119. package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +141 -0
  120. package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +112 -0
  121. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +134 -0
  122. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/quick-reference.md +66 -0
  123. package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +252 -0
  124. package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +56 -0
  125. package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +100 -0
  126. package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +119 -0
  127. package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +95 -0
  128. package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +100 -0
  129. package/dist/skills/reef-testcase/SKILL.md +138 -0
  130. package/dist/skills/reef-testcase/references/coverage-dimensions.md +99 -0
  131. package/dist/skills/reef-testcase/references/test-case-template.md +63 -0
  132. package/dist/skills/sweep-init/SKILL.md +286 -0
  133. package/dist/skills/sweep-init/scripts/flow-selector.mjs +305 -0
  134. package/dist/skills/sweep-plan/SKILL.md.tmpl +312 -0
  135. package/dist/skills/sweep-plan/references/test-flow-template.md +46 -0
  136. package/dist/skills/sweep-run/SKILL.md +437 -0
  137. package/dist/skills/sweep-run/scripts/env-manager.mjs +208 -0
  138. package/dist/skills/sweep-run/scripts/flow-parser.mjs +329 -0
  139. package/dist/skills/sweep-run/scripts/flow-selector.mjs +483 -0
  140. package/dist/skills/sweep-run/scripts/mcp-manager.mjs +208 -0
  141. package/dist/skills/sweep-run/scripts/spec-compiler.mjs +303 -0
  142. package/dist/skills/tide-discuss/SKILL.md.tmpl +449 -0
  143. package/dist/skills/tide-discuss/references/checklists.md +88 -0
  144. package/dist/skills/tide-discuss/references/data-format.md +237 -0
  145. package/dist/skills/tide-discuss/references/prd-template.md +134 -0
  146. package/dist/skills/tide-discuss/references/publish-flow.md +167 -0
  147. package/dist/skills/tide-discuss/references/role-prompts.md +105 -0
  148. package/package.json +38 -0
@@ -0,0 +1,148 @@
1
+ # API 规范
2
+
3
+ ## 速查
4
+
5
+ | 场景 | 决策 |
6
+ | --- | --- |
7
+ | 资源路径 | 英文复数 kebab-case:`/api/v1/user-roles`,不是 `/api/v1/userRoles` 或 `/api/v1/user_role` |
8
+ | 自定义动作 | AIP-136 冒号语法:`@PostMapping("/{id}:publish")` |
9
+ | 查询参数 | `?status=active&page=0&size=20&sort=name,asc` |
10
+ | 统一响应体 | `ApiResponse<T>` / `PageResponse<T>` / `ErrorResponse` |
11
+ | 版本策略 | URL path 前缀 `/api/v1/`;非破坏性变更不升级,破坏性变更 +1 |
12
+ | 错误响应 | AIP-193 兼容 `ErrorResponse`:`{ "code": "USER_001", "message": "...", "detail": {} }` |
13
+ | 分页 | Spring `Pageable` → `PageResponse` |
14
+ | OpenAPI | `@Operation(summary = "...")` + `@ApiResponse` + `@Tag(name = "users")` |
15
+
16
+ ## 核心规范
17
+
18
+ ### 资源命名
19
+
20
+ - **资源用复数名词**:`/users`、`/orders`、`/line-items`
21
+ - **嵌套资源**:`/apps/{appId}/users/{userId}`
22
+ - **自定义方法用冒号**:`/users/{id}:activate`(AIP-136)
23
+ - **查询参数用 snake_case**:`created_before`、`sort_by`
24
+
25
+ ### 统一响应体
26
+
27
+ 使用泛型包装所有响应:
28
+
29
+ ```java
30
+ // ✅ 单资源响应
31
+ @GetMapping("/{id}")
32
+ public ResponseEntity<ApiResponse<UserResponse>> getUser(@PathVariable Long id) {
33
+ UserResponse user = userService.getUser(id);
34
+ return ResponseEntity.ok(ApiResponse.success(user));
35
+ }
36
+
37
+ // ✅ 分页响应
38
+ @GetMapping
39
+ public ResponseEntity<PageResponse<UserResponse>> listUsers(Pageable pageable) {
40
+ Page<UserResponse> page = userService.listUsers(pageable);
41
+ return ResponseEntity.ok(PageResponse.from(page));
42
+ }
43
+
44
+ // ❌ 坏:直接返回实体或裸 List
45
+ @GetMapping
46
+ public List<UserResponse> listUsers() { ... } // ← 缺少分页和包装
47
+ ```
48
+
49
+ 响应体结构:
50
+
51
+ ```java
52
+ // ApiResponse — 单资源或操作响应
53
+ public record ApiResponse<T>(boolean success, T data, String message) {
54
+ public static <T> ApiResponse<T> success(T data) {
55
+ return new ApiResponse<>(true, data, null);
56
+ }
57
+ public static <T> ApiResponse<T> success(String message) {
58
+ return new ApiResponse<>(true, null, message);
59
+ }
60
+ }
61
+
62
+ // PageResponse — 分页响应
63
+ public record PageResponse<T>(
64
+ List<T> content,
65
+ int page,
66
+ int size,
67
+ long totalElements,
68
+ int totalPages
69
+ ) {
70
+ public static <T> PageResponse<T> from(Page<T> page) {
71
+ return new PageResponse<>(
72
+ page.getContent(),
73
+ page.getNumber(),
74
+ page.getSize(),
75
+ page.getTotalElements(),
76
+ page.getTotalPages()
77
+ );
78
+ }
79
+ }
80
+
81
+ // ErrorResponse — 错误响应
82
+ public record ErrorResponse(String code, String message, Map<String, Object> detail) {
83
+ public static ErrorResponse of(String code, String message) {
84
+ return new ErrorResponse(code, message, Map.of());
85
+ }
86
+ }
87
+ ```
88
+
89
+ ### OpenAPI 文档
90
+
91
+ ```java
92
+ @RestController
93
+ @RequestMapping("/api/v1/users")
94
+ @Tag(name = "users", description = "用户管理")
95
+ public class UserController {
96
+
97
+ @Operation(summary = "获取用户列表")
98
+ @ApiResponse(responseCode = "200", description = "成功返回用户分页列表")
99
+ @GetMapping
100
+ public ResponseEntity<PageResponse<UserResponse>> listUsers(Pageable pageable) {
101
+ // ...
102
+ }
103
+
104
+ @Operation(summary = "创建用户")
105
+ @ApiResponse(responseCode = "201", description = "创建成功")
106
+ @PostMapping
107
+ public ResponseEntity<ApiResponse<UserResponse>> createUser(
108
+ @Valid @RequestBody CreateUserRequest request
109
+ ) {
110
+ // ...
111
+ }
112
+ }
113
+ ```
114
+
115
+ **规范:**
116
+ - 每个 Controller 类加 `@Tag(name = "...", description = "...")`
117
+ - 每个接口方法加 `@Operation(summary = "...")`
118
+ - 非 200 响应(400/403/404)加 `@ApiResponse(responseCode = "4XX", description = "...")`
119
+ - 避免在 `application.yml` 中暴露 OpenAPI 端点到生产环境
120
+
121
+ ### 版本策略
122
+
123
+ - **非破坏性变更**(新增字段、新增可选参数):停留在当前版本
124
+ - **破坏性变更**(重命名字段、删除字段、改变类型):创建新版本 `/api/v2/...`
125
+ - **废弃端点**:保留旧版本,加 `@Deprecated` 注解,同时在类或方法 Javadoc 中注明废弃原因和迁移目标版本
126
+ - **过渡期**:旧版本至少维护 2 个发布周期
127
+
128
+ ### 分页约定
129
+
130
+ - 列表接口**必须**支持分页
131
+ - 入参:Spring 自动解析 `?page=0&size=20&sort=name,asc`
132
+ - `page` 从 0 开始
133
+ - `size` 默认 20,最大值 200
134
+ - 响应:`PageResponse<T>` 包含 `content`、`page`、`size`、`totalElements`、`totalPages`
135
+
136
+ ### 错误码命名
137
+
138
+ ```
139
+ {MODULE}_{NNN}
140
+
141
+ MODULE: 2-4 个大写字母标识模块
142
+ 示例:USER_001 / ORDER_002 / AUTH_003 / APP_004
143
+ ```
144
+
145
+ - `_001`-`_099`:输入验证错误
146
+ - `_100`-`_199`:资源状态错误(不存在、已存在、冲突)
147
+ - `_200`-`_299`:权限错误
148
+ - `_300`-`_399`:系统内部错误
@@ -0,0 +1,131 @@
1
+ # 数据库迁移(Liquibase)
2
+
3
+ ## ChangeSet 格式
4
+
5
+ - ID:`YYYYMMDDHHMM-NNN`(如 `202405231200-001`)
6
+ - 添加新变更:在 `changelogs/` 目录创建新 XML 文件,无需修改 master changelog(`<includeAll>` 自动扫描)
7
+ - 文件按时间命名排序以控制执行顺序
8
+
9
+ ## 属性替换
10
+
11
+ 主 changelog 定义了数据库类型变量以支持多数据库(H2 开发/测试,MySQL/PG/MSSQL 生产):
12
+
13
+ | 变量 | 用途 |
14
+ |------|------|
15
+ | `${boolean.type}` | 布尔类型 |
16
+ | `${string.type}(255)` | 字符串 VARCHAR/NVARCHAR |
17
+ | `${date.time.type}` | 日期时间 TIMESTAMP/DATETIME/DATETIME2 |
18
+ | `${clob.type}` | 大文本 CLOB/LONGTEXT/TEXT/NVARCHAR(MAX) |
19
+ | `${enum.ordinal.type}` | 枚举序号类型 |
20
+
21
+ 枚举类型在不同数据库中处理方式不同:H2/MySQL 用 `ENUM(...)` 原生枚举,PG/MSSQL 用 `${string.type}(255)`。枚举属性命名规则:`${<module>.<enum-name>.type}`。
22
+
23
+ ## 完整示例
24
+
25
+ ### 建表(序列 + 表 + 外键,省略 XML namespace)
26
+
27
+ ```xml
28
+ <!-- 序列(H2/PG/MSSQL 用序列,MySQL 用序列表模拟) -->
29
+ <changeSet author="lgong" id="202604062320-004" dbms="h2,postgresql,mssql">
30
+ <createSequence sequenceName="my_entity_seq" startValue="1" incrementBy="50" />
31
+ </changeSet>
32
+ <changeSet author="lgong" id="202604062320-004" dbms="mysql">
33
+ <createTable tableName="my_entity_seq">
34
+ <column name="next_val" type="BIGINT" />
35
+ </createTable>
36
+ <insert tableName="my_entity_seq"><column name="next_val" value="1" /></insert>
37
+ </changeSet>
38
+
39
+ <changeSet author="developer" id="202604062320-005">
40
+ <createTable tableName="my_entity">
41
+ <column name="id" type="BIGINT"><constraints nullable="false" primaryKey="true" primaryKeyName="myEntityPK" /></column>
42
+ <column name="created_at" type="${date.time.type}" />
43
+ <column name="name" type="${string.type}(255)"><constraints nullable="false" /></column>
44
+ <column name="enabled" type="${boolean.type}"><constraints nullable="false" /></column>
45
+ <column name="dtype" type="${string.type}(31)"><constraints nullable="false" /></column>
46
+ </createTable>
47
+ </changeSet>
48
+
49
+ <changeSet author="developer" id="202604062320-006">
50
+ <addColumn tableName="my_entity"><column name="app_id" type="BIGINT" /></addColumn>
51
+ </changeSet>
52
+ <changeSet author="developer" id="202604062320-007">
53
+ <addForeignKeyConstraint constraintName="FKmyentity_app"
54
+ baseTableName="my_entity" baseColumnNames="app_id"
55
+ referencedTableName="application" referencedColumnNames="id" />
56
+ </changeSet>
57
+ ```
58
+
59
+ ### 新增字段 / 修改字段类型
60
+
61
+ ```xml
62
+ <changeSet author="developer" id="202605111000-01">
63
+ <addColumn tableName="my_entity"><column name="new_field" type="${string.type}(255)" /></addColumn>
64
+ </changeSet>
65
+ <!-- 可空→非空需先填充数据 -->
66
+ <changeSet author="developer" id="202605111000-02">
67
+ <addNotNullConstraint tableName="my_entity" columnName="new_field"
68
+ columnDataType="${string.type}(255)" defaultNullValue="默认值" />
69
+ </changeSet>
70
+ <changeSet author="developer" id="202604281406">
71
+ <modifyDataType tableName="my_entity" columnName="choices" newDataType="${string.list.type}" />
72
+ </changeSet>
73
+ ```
74
+
75
+ ### 索引 / 唯一约束 / 外键
76
+
77
+ ```xml
78
+ <changeSet author="developer" id="202605081601">
79
+ <createIndex indexName="IDXm64b01r9rj9aco9it9puvg9tb" tableName="my_entity">
80
+ <column name="app_id" />
81
+ </createIndex>
82
+ </changeSet>
83
+
84
+ <changeSet author="developer" id="202604062320-028" dbms="h2,mysql,postgresql">
85
+ <addUniqueConstraint constraintName="UC_MYENTITYSLUG_COL"
86
+ tableName="my_entity" columnNames="slug" />
87
+ </changeSet>
88
+
89
+ <changeSet author="developer" id="202604211125-10">
90
+ <addForeignKeyConstraint constraintName="FKhoumjjymjhbu0oiwh6ynyio6u"
91
+ baseTableName="my_entity" baseColumnNames="app_id"
92
+ referencedTableName="application" referencedColumnNames="id" />
93
+ </changeSet>
94
+ ```
95
+
96
+ ### 删除操作 / 自定义 SQL
97
+
98
+ ```xml
99
+ <changeSet author="developer" id="202604211125-03">
100
+ <dropColumn tableName="my_entity" columnName="old_column" />
101
+ </changeSet>
102
+ <changeSet author="developer" id="202604211125-02">
103
+ <dropUniqueConstraint constraintName="UC_APPLICATIONNAVIGATION_MENU_ID_COL" tableName="application" />
104
+ </changeSet>
105
+ <changeSet author="developer" id="202605081101">
106
+ <dropForeignKeyConstraint constraintName="FKjr9mly7obkthhxdr0hk29r88t" baseTableName="my_entity" />
107
+ </changeSet>
108
+ <changeSet author="developer" id="202605210012-03">
109
+ <sql>UPDATE form_revision SET revisions_order = (SELECT COUNT(*) FROM (SELECT id, form_id FROM form_revision) AS fr2 WHERE fr2.form_id = form_revision.form_id AND fr2.id &lt; form_revision.id)</sql>
110
+ </changeSet>
111
+ ```
112
+
113
+ ## SQL Server 特殊处理
114
+
115
+ 可空列的唯一约束使用过滤索引:
116
+
117
+ ```xml
118
+ <changeSet author="developer" id="..." dbms="mssql">
119
+ <createIndex indexName="UC_TABLENAME_COL" tableName="table" unique="true">
120
+ <column name="column_name" />
121
+ </createIndex>
122
+ <modifySql><append value=" WHERE column_name IS NOT NULL" /></modifySql>
123
+ </changeSet>
124
+ ```
125
+
126
+ ## 最佳实践
127
+
128
+ - 新增字段必须设为可空(默认),若需非空则先填充数据再 `addNotNullConstraint`
129
+ - 序列命名 `<table_name>_seq`,`startValue=1, incrementBy=50`(匹配 Hibernate 默认序列优化)
130
+ - 同一 table 的多次 `addColumn` 合并到一个 changeset
131
+ - 先 `dropForeignKeyConstraint` 再 `createIndex` 是常见模式
@@ -0,0 +1,103 @@
1
+ # Liquibase 数据库迁移规范
2
+
3
+ ## 概述
4
+
5
+ 使用 Liquibase 进行数据库版本管理,所有 Schema 变更通过 changelog 文件管理。
6
+
7
+ ## 文件结构
8
+
9
+ ```
10
+ src/main/resources/db/changelog/
11
+ db.changelog-master.yaml // 主入口文件
12
+ v1.0.0/
13
+ v1.0.0-001-create-users.yaml
14
+ v1.0.0-002-create-orders.yaml
15
+ v1.0.0-003-add-email-index.yaml
16
+ v1.1.0/
17
+ v1.1.0-001-add-profile-table.yaml
18
+ ```
19
+
20
+ ## 主入口文件
21
+
22
+ ```yaml
23
+ # db.changelog-master.yaml
24
+ databaseChangeLog:
25
+ - include:
26
+ file: db/changelog/v1.0.0/v1.0.0-001-create-users.yaml
27
+ - include:
28
+ file: db/changelog/v1.0.0/v1.0.0-002-create-orders.yaml
29
+ - include:
30
+ file: db/changelog/v1.1.0/v1.1.0-001-add-profile-table.yaml
31
+ ```
32
+
33
+ ## Changeset 示例
34
+
35
+ ```yaml
36
+ # v1.0.0-001-create-users.yaml
37
+ databaseChangeLog:
38
+ - changeSet:
39
+ id: v1.0.0-001
40
+ author: developer
41
+ changes:
42
+ - createTable:
43
+ tableName: users
44
+ columns:
45
+ - column:
46
+ name: id
47
+ type: BIGINT
48
+ autoIncrement: true
49
+ constraints:
50
+ primaryKey: true
51
+ nullable: false
52
+ - column:
53
+ name: name
54
+ type: VARCHAR(100)
55
+ constraints:
56
+ nullable: false
57
+ - column:
58
+ name: email
59
+ type: VARCHAR(255)
60
+ constraints:
61
+ unique: true
62
+ nullable: false
63
+ - column:
64
+ name: created_at
65
+ type: TIMESTAMP
66
+ defaultValueComputed: CURRENT_TIMESTAMP
67
+ ```
68
+
69
+ ## changeset 命名
70
+
71
+ ```
72
+ 格式: {version}-{seq}-{description}
73
+ 示例: v1.0.0-003-add-email-index
74
+ ```
75
+
76
+ ## 回滚
77
+
78
+ ```yaml
79
+ # 每个 changeset 应可回滚
80
+ - changeSet:
81
+ id: v1.2.0-001
82
+ author: developer
83
+ changes:
84
+ - addColumn:
85
+ tableName: users
86
+ columns:
87
+ - column:
88
+ name: phone
89
+ type: VARCHAR(20)
90
+ rollback:
91
+ - dropColumn:
92
+ tableName: users
93
+ columnName: phone
94
+ ```
95
+
96
+ ## 最佳实践
97
+
98
+ - ✅ 每个 changeset 有唯一 ID
99
+ - ✅ 每个 changeset 只做单一变更(创建表、加索引、加字段)
100
+ - ✅ 变更不可修改已发布的 changeset(追加新 changeset)
101
+ - ✅ 生产环境使用 `context` 标签控制不同环境数据
102
+ - ❌ 不在 changeset 中使用存储过程
103
+ - ❌ 不修改已合并到主干的 changeset
@@ -0,0 +1,119 @@
1
+ # 依赖管理规范
2
+
3
+ ## 速查
4
+
5
+ | 场景 | 决策 |
6
+ | --- | --- |
7
+ | 声明依赖 | 全部在 `gradle/libs.versions.toml` 的 `[libraries]` 中声明 |
8
+ | 版本声明 | 全部在 `gradle/libs.versions.toml` 的 `[versions]` 中声明 |
9
+ | 引入新依赖 | 先查 version catalog 是否已有,无则按模块 + 版本号格式添加 |
10
+ | 临时排除 | 在 catalog 中使用 `exclude` 而非 `build.gradle.kts` 中排除 |
11
+ | 版本升级 | 同步升级相关依赖(如 Liquibase + Spring Boot 一起升级) |
12
+ | CVE 检查 | 配合 Dependabot / Renovate + Gradle Versions Plugin |
13
+ | 许可限制 | 禁止引入 AGPL / 需商业许可的依赖 |
14
+
15
+ ## 核心规范
16
+
17
+ ### Gradle Version Catalog 结构
18
+
19
+ ```toml
20
+ # gradle/libs.versions.toml
21
+ [versions]
22
+ spring-boot = "3.5.0"
23
+ spring-dependency-management = "1.1.7"
24
+ mapstruct = "1.6.3"
25
+ liquibase = "4.31.0"
26
+ testcontainers = "1.20.6"
27
+
28
+ [libraries]
29
+ spring-boot-starter-web = { module = "org.springframework.boot:spring-boot-starter-web" }
30
+ spring-boot-starter-data-jpa = { module = "org.springframework.boot:spring-boot-starter-data-jpa" }
31
+ mapstruct = { module = "org.mapstruct:mapstruct", version.ref = "mapstruct" }
32
+ mapstruct-processor = { module = "org.mapstruct:mapstruct-processor", version.ref = "mapstruct" }
33
+
34
+ [bundles]
35
+ spring-web = ["spring-boot-starter-web", "spring-boot-starter-validation"]
36
+
37
+ [plugins]
38
+ spring-boot = { id = "org.springframework.boot", version.ref = "spring-boot" }
39
+ ```
40
+
41
+ **规范:**
42
+ - 所有依赖的**版本号**必须声明在 `[versions]` 中,不得在 `[libraries]` 中使用 `version = "1.0.0"` 字面量
43
+ - `[libraries]` 中使用 `version.ref` 引用版本
44
+ - 同一生态的依赖使用同一个版本变量(如所有 Spring Boot starter 共享 `spring-boot` 版本)
45
+ - 多个 artifact 共享版本的依赖组(如 mapstruct + mapstruct-processor)用同一 `version.ref`
46
+ - 关联依赖使用 `[bundles]` 分组
47
+
48
+ ### 禁止硬编码版本号
49
+
50
+ ```kotlin
51
+ // ✅ 好:build.gradle.kts
52
+ dependencies {
53
+ implementation(libs.spring.boot.starter.web)
54
+ implementation(libs.mapstruct)
55
+ annotationProcessor(libs.mapstruct.processor)
56
+ }
57
+
58
+ // ❌ 坏:build.gradle.kts — 硬编码版本号
59
+ dependencies {
60
+ implementation("org.springframework.boot:spring-boot-starter-web:3.5.0") // ← 硬编码
61
+ implementation("org.mapstruct:mapstruct:1.6.3") // ← 硬编码
62
+ }
63
+ ```
64
+
65
+ ### 版本一致性规则
66
+
67
+ 升级一个依赖时,检查同一生态链的其他依赖是否需要同步升级:
68
+
69
+ | 升级场景 | 需同步升级的关联依赖 |
70
+ |---------|-------------------|
71
+ | Spring Boot 升级 | Spring Cloud、spring-dependency-management、相关 starter |
72
+ | Liquibase 升级 | 验证与 Spring Boot 版本兼容性 |
73
+ | MapStruct 升级 | mapstruct-processor 必须保持同一版本 |
74
+ | Hibernate 升级 | Spring Boot 提供的 Hibernate 版本(随 Boot 版本) |
75
+ | Testcontainers 升级 | 验证与 JUnit 5 兼容性 |
76
+
77
+ ### Gradle Versions Plugin
78
+
79
+ ```kotlin
80
+ // build.gradle.kts
81
+ plugins {
82
+ id("com.github.ben-manes.versions") version "0.52.0"
83
+ }
84
+ ```
85
+
86
+ ```bash
87
+ # 检查可用升级
88
+ ./gradlew dependencyUpdates -Drevision=release
89
+
90
+ # 输出示例:
91
+ # The following dependencies have newer versions:
92
+ # com.google.guava:guava [32.1.3 -> 33.4.0]
93
+ ```
94
+
95
+ **规范:**
96
+ - 每个 Major 版本升级前先在本地验证兼容性
97
+ - 升级依赖必须作为独立 PR/commit,附 changelog 摘要
98
+ - Minor/Patch 升级可随功能 PR 一起提交
99
+
100
+ ### 已知 CVE 管理
101
+
102
+ ```bash
103
+ # 配合 Gradle Versions Plugin 使用 OWASP Dependency Check(可选)
104
+ ./gradlew dependencyCheckAnalyze
105
+ ```
106
+
107
+ **规范:**
108
+ - Dependabot 或 Renovate 的 CVE PR 在 7 天内处理
109
+ - 紧急 CVE(CVSS >= 7.0):1 天内升级并验证
110
+ - 无法立即升级时,记录在安全看板中,加 `@SuppressWarnings("CVE-XXXX")` 并附缓解说明
111
+
112
+ ### 禁止使用的依赖
113
+
114
+ | 依赖 | 禁止原因 | 替代方案 |
115
+ |------|---------|---------|
116
+ | Apache Commons Lang 2 | 有已知 CVE,已被 lang3 取代 | `org.apache.commons:commons-lang3` |
117
+ | Log4j 1.x | EOL,有已知 CVE | Spring Boot 内置 Logback 或 Log4j 2 |
118
+ | Guava 旧版 < 30.0 | 已知 CVE | 最低 32.x |
119
+ | Joda-Time | 已被 java.time 取代 | `java.time.*` / `ThreeTen-Extra`(如需要) |
@@ -0,0 +1,101 @@
1
+ # 错误码枚举示例
2
+
3
+ ## 完整 ErrorCode 枚举
4
+
5
+ ```java
6
+ package com.example.app.common;
7
+
8
+ /**
9
+ * 全局错误码枚举。
10
+ *
11
+ * 编码规则:{MODULE}_{NNN}
12
+ * MODULE = 2-4 个大写字母
13
+ * NNN = 三位数字
14
+ * 001-099 输入验证
15
+ * 100-199 资源状态
16
+ * 200-299 权限
17
+ * 300-399 系统内部
18
+ */
19
+ public enum ErrorCode {
20
+
21
+ // ── User ──────────────────────────────────────────────
22
+ USER_001("USER_001", "用户名已存在"),
23
+ USER_002("USER_002", "邮箱格式无效"),
24
+ USER_003("USER_003", "手机号格式无效"),
25
+ USER_004("USER_004", "密码不符合安全策略"),
26
+
27
+ USER_100("USER_100", "用户不存在"),
28
+ USER_101("USER_101", "用户已禁用"),
29
+ USER_102("USER_102", "用户邮箱未验证"),
30
+ USER_103("USER_103", "用户已删除"),
31
+
32
+ USER_200("USER_200", "无权操作该用户"),
33
+ USER_201("USER_201", "不能操作自身账号"),
34
+
35
+ // ── Order / App ──────────────────────────────────────
36
+ APP_001("APP_001", "应用名称为空"),
37
+ APP_002("APP_002", "应用名称超长"),
38
+ APP_100("APP_100", "应用不存在"),
39
+ APP_101("APP_101", "应用已下架"),
40
+ APP_200("APP_200", "无权操作该应用"),
41
+
42
+ // ── Auth ──────────────────────────────────────────────
43
+ AUTH_001("AUTH_001", "Token 已过期"),
44
+ AUTH_002("AUTH_002", "Token 无效"),
45
+ AUTH_003("AUTH_003", "Token 签名验证失败"),
46
+ AUTH_004("AUTH_004", "Refresh Token 无效"),
47
+ AUTH_201("AUTH_201", "无权限访问"),
48
+ AUTH_202("AUTH_202", "角色权限不足"),
49
+ AUTH_203("AUTH_203", "需要 Root Tenant 权限"),
50
+
51
+ // ── Validating ────────────────────────────────────────
52
+ VALIDATION_001("VALIDATION_001", "参数校验失败"),
53
+ VALIDATION_002("VALIDATION_002", "请求体格式无效"),
54
+
55
+ // ── Generic ───────────────────────────────────────────
56
+ GENERIC_001("GENERIC_001", "系统内部错误"),
57
+ GENERIC_002("GENERIC_002", "服务暂时不可用"),
58
+ GENERIC_404("GENERIC_404", "接口不存在"),
59
+ GENERIC_429("GENERIC_429", "请求频率过高");
60
+
61
+ private final String code;
62
+ private final String defaultMessage;
63
+
64
+ ErrorCode(String code, String defaultMessage) {
65
+ this.code = code;
66
+ this.defaultMessage = defaultMessage;
67
+ }
68
+
69
+ public String code() { return code; }
70
+ public String defaultMessage() { return defaultMessage; }
71
+
72
+ @Override
73
+ public String toString() { return code + ": " + defaultMessage; }
74
+ }
75
+ ```
76
+
77
+ ## 异常 + 错误码对照表
78
+
79
+ | 异常类 | HTTP | 错误码示例 | 典型场景 |
80
+ |--------|------|-----------|---------|
81
+ | `ResourceNotFoundException` | 404 | `USER_100` | 用户不存在 |
82
+ | `AlreadyExistsException` | 409 | `USER_001` | 用户名重复 |
83
+ | `InvalidArgumentException` | 400 | `ORDER_001` | 金额无效 |
84
+ | `PermissionDeniedException` | 403 | `AUTH_201` | 无权操作 |
85
+ | `FailedPreconditionException` | 400 | `ORDER_101` | 订单状态不允许 |
86
+
87
+ ## 映射逻辑
88
+
89
+ ```java
90
+ // 推荐:在子类构造时注入 ErrorCode,GlobalExceptionHandler 从异常中读取
91
+ public class ResourceNotFoundException extends BusinessException {
92
+ public ResourceNotFoundException(ErrorCode errorCode, Object... args) {
93
+ super(errorCode.code(), HttpStatus.NOT_FOUND,
94
+ String.format(errorCode.defaultMessage(), args));
95
+ }
96
+ }
97
+
98
+ // Service 使用
99
+ throw new ResourceNotFoundException(ErrorCode.USER_100);
100
+ throw new ResourceNotFoundException(ErrorCode.USER_100, userId);
101
+ ```