@nebula-skills/nebula-code-standards 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.
@@ -0,0 +1,842 @@
1
+ # 新 Nebula 项目初始化向导
2
+
3
+ > 用户说「初始化新 nebula 项目」「按 nebula 规范起一个项目」「新建业务模块(前后端)」时,AI 进入本向导。目标:**一条指令把前后端跑起来 + Swagger 能调通 + 主应用菜单能点开子应用**。
4
+ >
5
+ > 本向导是规则文档,**不依赖任何具体业务样板项目**。生成的代码 100% 来自规则推导。
6
+
7
+ ---
8
+
9
+ ## 一、技术栈基线(强制)
10
+
11
+ 生成的项目**必须**对齐这套版本,避免和现有生态不兼容:
12
+
13
+ | 技术 | 版本 | 备注 |
14
+ |------|------|------|
15
+ | JDK | `17` | 强制;用 nebula-boot 必须 JDK 17 |
16
+ | Gradle | `8.12`(Wrapper) | 必须通过 `gradlew` 调用,不依赖本机 Gradle |
17
+ | Spring Boot | `3.5.9` | 由 `nebula-boot-dependencies` BOM 管理 |
18
+ | Spring Cloud | `2025.0.0` | 由 BOM 管理 |
19
+ | MyBatis-Plus | `3.5.9` | 由 BOM 管理 |
20
+ | nebula-support | `0.1.22-SNAPSHOT` | 实际以最新发布版为准 |
21
+ | nebula-boot | `0.1.22-SNAPSHOT` | |
22
+ | nebula-auth | `0.1.8-SNAPSHOT` | |
23
+ | nebula-system | `0.2.47-SNAPSHOT` | |
24
+ | Vue | `3.5.x` | 前端 |
25
+ | Vite | `5.x` | |
26
+ | TypeScript | `5.5+` | |
27
+ | Node.js | `>= 22` | 强制 |
28
+ | pnpm | `>= 9` | 强制 |
29
+ | IceStark | `2.8.x` | 微前端 |
30
+
31
+ ### IntelliJ IDEA 必备配置
32
+
33
+ 向导结束时**必须**告知用户在 IDEA 中调整以下设置(否则编译会失败或行为异常):
34
+
35
+ 1. **Project SDK**:`File → Project Structure → Project → SDK = 17`
36
+ 2. **Project language level**:`17`
37
+ 3. **Gradle JVM**:`File → Settings → Build, Execution, Deployment → Build Tools → Gradle → Gradle JVM = 17`
38
+ 4. **Gradle build and run using**:选 `Gradle`(而非 IDEA)以保证 Wrapper 行为一致
39
+ 5. **Java Compiler target bytecode version**:`17`
40
+ 6. **Annotation Processing**:`File → Settings → Build, Execution, Deployment → Compiler → Annotation Processors → Enable annotation processing`(Lombok / MapStruct 必需)
41
+ 7. **File Encoding**:`Settings → Editor → File Encodings → 全选 UTF-8 + Transparent native-to-ascii conversion ✅`
42
+ 8. 必装插件:`Lombok` / `MapStruct Support` / `MyBatisX` / `Save Actions`(可选)
43
+
44
+ ---
45
+
46
+ ## 二、触发关键词
47
+
48
+ 以下任一关键词触发本向导:
49
+
50
+ - 「初始化 / 新建 / 起一个 nebula 项目 / 业务模块」
51
+ - 「按 nebula 规范创建新工程」
52
+ - 「一条指令把前后端跑起来」
53
+ - 用户指定项目名(如 `mes` / `scm` / `oa`),且当前不存在对应仓库
54
+
55
+ ---
56
+
57
+ ## 三、向导总览
58
+
59
+ ```
60
+ 阶段 1:需求摸底(AskUserQuestion 7 道题)
61
+
62
+ 阶段 2:依赖连通性检测(Bash nc + mysql/redis-cli)
63
+
64
+ 阶段 3:后端骨架生成(Gradle 四层 + Application + application.yml + Flyway + Demo CRUD + 3 个必备 SPI 空实现)
65
+
66
+ 阶段 4:前端骨架生成(独立仓库 / monorepo 内置二选一,参考 [[microapp-guide]])
67
+
68
+ 阶段 5:启动验证 + 配置修改指引(Gradle 命令 + MySQL/Redis 修改位置)
69
+
70
+ 阶段 6:收尾交付清单(git init / 启动命令 / IDEA 配置 / 后续 TODO)
71
+ ```
72
+
73
+ 每个阶段如遇异常(端口冲突、连接失败、文件已存在、Nexus 拉取失败),AI **必须** 用 AskUserQuestion 让用户决策,**禁止** 自行覆盖或跳过。
74
+
75
+ ---
76
+
77
+ ## 四、阶段 1:需求摸底
78
+
79
+ 按以下顺序发出 AskUserQuestion(一次发一题或合并发,但不能跳过):
80
+
81
+ ### Q1:项目范围
82
+ - 仅后端
83
+ - 仅前端
84
+ - 前后端一起(推荐)
85
+
86
+ ### Q2:项目 domain(小写英文)
87
+ - 用户输入示例:`mes` / `scm` / `oa`
88
+ - AI 自动派生(再让用户确认或自定义):
89
+
90
+ | 派生项 | 规则 | 示例(domain=mes) |
91
+ |--------|------|-------------------|
92
+ | 后端 group | `com.huida.{domain}` | `com.huida.mes` |
93
+ | 后端工件 | `nebula-{domain}-{api/core/starter/server}` | `nebula-mes-api/core/starter/server` |
94
+ | 后端端口(建议) | `80XX` 段,避开 `9010` / `8081` / `8080` | `8082` |
95
+ | 数据库名 | `nebula_{domain}` | `nebula_mes` |
96
+ | 表前缀 | `t_` | `t_demo` |
97
+ | 前端工件 | `nebula-{domain}-web` | `nebula-mes-web` |
98
+ | IceStark name | `nebula{PascalDomain}App` / `{domain}app` | `nebulaMesApp` / `mesapp` |
99
+ | 前端端口(建议) | `30XX` 段,避开 `3000/3001/3002` | `3004` |
100
+ | API 前缀 | `/nebula-{domain}` | `/nebula-mes` |
101
+
102
+ ### Q3:MySQL 配置
103
+ - host(默认 `localhost`)
104
+ - port(默认 `3306`)
105
+ - 用户名 / 密码
106
+ - 数据库不存在时:自动建 / 手动建 / 跳过
107
+
108
+ ### Q4:Redis 配置
109
+ - host / port / password / db index(默认 db=0)
110
+
111
+ ### Q5:其他中间件(多选)
112
+ - RocketMQ / Nacos / Elasticsearch / MongoDB
113
+
114
+ > 选了什么,对应 starter 才会引入;不选可后续按需添加。
115
+
116
+ ### Q6:示例 CRUD 资源名
117
+ - 默认 `demo`(生成 `t_demo` 表 + DemoController/Service/Mapper/VO)
118
+
119
+ ### Q7:前端形态(仅 Q1 含前端时)
120
+ - 独立仓库(**默认**,推荐业务域)
121
+ - monorepo 内置(仅平台公共域推荐)
122
+
123
+ ---
124
+
125
+ ## 五、阶段 2:依赖连通性检测
126
+
127
+ 按顺序执行(每项失败时给出 3 条排查建议 + AskUserQuestion 让用户选「修复重试 / 跳过 / 改配置」):
128
+
129
+ ### 5.1 内网 Nexus(最先检测,不通后面都没意义)
130
+
131
+ ```bash
132
+ curl -s -o /dev/null -w "%{http_code}" http://10.10.1.130:8081/repository/nebula-public/
133
+ ```
134
+
135
+ | 返回码 | 处理 |
136
+ |--------|------|
137
+ | `200` / `301` / `302` | ✅ 通 |
138
+ | `503` | ⚠️ Nexus 暂时不可用,等几分钟重试 |
139
+ | 连接超时 / `Connection refused` | ❌ **未连接公司内网或 VPN**,先解决网络再继续 |
140
+
141
+ **遇到任何拉不到 nebula 包的情况,第一句话必须是:**「请确认已连接公司内网或 VPN,可访问 `http://10.10.1.130:8081`,然后重试。」
142
+
143
+ ### 5.2 MySQL
144
+
145
+ ```bash
146
+ nc -z {host} {port}
147
+ mysql --protocol=tcp -h {host} -P {port} -u {user} -p{pw} -e 'SELECT 1' 2>&1
148
+ # 选了自动建库:
149
+ mysql --protocol=tcp -h {host} -P {port} -u {user} -p{pw} \
150
+ -e "CREATE DATABASE IF NOT EXISTS nebula_{domain} DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
151
+ ```
152
+
153
+ 失败排查建议:
154
+ 1. 端口不通 → 检查 mysql 是否启动、防火墙、安全组
155
+ 2. 账号失败 → 检查用户名密码、`mysql.user` 是否有当前 host 权限
156
+ 3. SSL/TLS 报错 → 在 URL 加 `useSSL=false`
157
+
158
+ ### 5.3 Redis
159
+
160
+ ```bash
161
+ nc -z {host} {port}
162
+ redis-cli -h {host} -p {port} [-a {pw}] -n {db} ping
163
+ ```
164
+
165
+ ### 5.4 RocketMQ / Nacos / ES / MongoDB(按 Q5 选择)
166
+
167
+ ```bash
168
+ nc -z {host} 9876 # RocketMQ NameServer
169
+ nc -z {host} 8848 && curl -sf "http://{host}:8848/nacos/v1/console/health/liveness" # Nacos
170
+ curl -sf "http://{host}:9200" | head -20 # Elasticsearch
171
+ nc -z {host} 27017 # MongoDB
172
+ ```
173
+
174
+ 任何一项失败 **必须** 询问用户,**不要** 默认跳过。
175
+
176
+ ---
177
+
178
+ ## 六、阶段 3:后端骨架生成
179
+
180
+ 仅当 Q1 含「后端」时执行。
181
+
182
+ ### 6.1 目录结构
183
+
184
+ ```
185
+ nebula-{domain}/
186
+ ├── settings.gradle
187
+ ├── build.gradle # 根 build.gradle
188
+ ├── gradle.properties # projectGroup / version / nebula 各版本
189
+ ├── gradlew / gradlew.bat # Gradle Wrapper
190
+ ├── gradle/wrapper/
191
+ │ ├── gradle-wrapper.jar
192
+ │ └── gradle-wrapper.properties
193
+ ├── .gitignore # 见 §六-3-.gitignore
194
+ ├── README.md # 启动文档
195
+ ├── nebula-{domain}-api/
196
+ │ ├── build.gradle
197
+ │ └── src/main/java/com/huida/{domain}/{domain}/
198
+ │ ├── constant/{Domain}Constants.java
199
+ │ ├── dto/ # 跨服务 DTO
200
+ │ └── provider/ # RPC 接口(FeignClient)
201
+ ├── nebula-{domain}-core/
202
+ │ ├── build.gradle
203
+ │ └── src/main/java/com/huida/{domain}/{domain}/
204
+ │ ├── controller/
205
+ │ ├── service/ + service/impl/
206
+ │ ├── mapper/ + mapper/ext/
207
+ │ ├── entity/
208
+ │ ├── vo/{param,resp}/
209
+ │ ├── convert/
210
+ │ ├── feign/local/ # LocalStub
211
+ │ └── infra/ # 必备 SPI 空实现(见 §六-4)
212
+ ├── nebula-{domain}-starter/
213
+ │ ├── build.gradle
214
+ │ └── src/main/
215
+ │ ├── java/com/huida/{domain}/autoconfigure/{Domain}AutoConfiguration.java
216
+ │ └── resources/META-INF/spring/
217
+ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports
218
+ └── nebula-{domain}-server/
219
+ ├── build.gradle
220
+ └── src/main/
221
+ ├── java/com/huida/{domain}/Nebula{Domain}Application.java
222
+ └── resources/
223
+ ├── application.yml
224
+ ├── application-dev.yml
225
+ ├── application-uat.yml
226
+ ├── application-prod.yml
227
+ ├── log4j2-dev.xml
228
+ ├── log4j2-uat.xml
229
+ ├── log4j2-prod.xml
230
+ ├── banner.txt # 见 §六-5
231
+ └── db/migration/V1.0.0__init.sql
232
+ ```
233
+
234
+ ### 6.2 关键文件模板
235
+
236
+ **`gradle.properties`**
237
+ ```properties
238
+ projectGroup=com.huida.{domain}
239
+ projectVersion=0.1.0-SNAPSHOT
240
+ nebulaSupportVersion=0.1.22-SNAPSHOT
241
+ nebulaBootVersion=0.1.22-SNAPSHOT
242
+ nebulaAuthVersion=0.1.8-SNAPSHOT
243
+ nebulaSystemVersion=0.2.47-SNAPSHOT
244
+
245
+ org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8
246
+ org.gradle.parallel=true
247
+ org.gradle.caching=true
248
+ ```
249
+
250
+ **根 `build.gradle`**(关键是 Nexus 仓库 + 子项目通用依赖)
251
+ ```gradle
252
+ plugins { id 'java-library' }
253
+
254
+ allprojects {
255
+ group = projectGroup
256
+ version = projectVersion
257
+ repositories {
258
+ // 公司内网 Nexus(必须连内网或 VPN 才能访问)
259
+ maven {
260
+ url 'http://10.10.1.130:8081/repository/nebula-public/'
261
+ allowInsecureProtocol = true
262
+ }
263
+ mavenCentral()
264
+ }
265
+ }
266
+
267
+ subprojects {
268
+ apply plugin: 'java-library'
269
+ java {
270
+ sourceCompatibility = JavaVersion.VERSION_17
271
+ targetCompatibility = JavaVersion.VERSION_17
272
+ }
273
+ tasks.withType(JavaCompile) {
274
+ options.encoding = 'UTF-8'
275
+ options.compilerArgs += ['-parameters']
276
+ }
277
+ dependencies {
278
+ api platform("com.huida.nebula:nebula-support-dependencies:${nebulaSupportVersion}")
279
+ api platform("com.huida.nebula.boot:nebula-boot-dependencies:${nebulaBootVersion}")
280
+ compileOnly 'org.projectlombok:lombok'
281
+ annotationProcessor 'org.projectlombok:lombok'
282
+ }
283
+ }
284
+ ```
285
+
286
+ **`settings.gradle`**
287
+ ```gradle
288
+ rootProject.name = 'nebula-{domain}'
289
+ include 'nebula-{domain}-api'
290
+ include 'nebula-{domain}-core'
291
+ include 'nebula-{domain}-starter'
292
+ include 'nebula-{domain}-server'
293
+ ```
294
+
295
+ **`nebula-{domain}-server/build.gradle`**
296
+ ```gradle
297
+ plugins {
298
+ id 'org.springframework.boot' version '3.5.9'
299
+ id 'io.spring.dependency-management' version '1.1.6'
300
+ }
301
+
302
+ dependencies {
303
+ api project(':nebula-{domain}-starter')
304
+ api "com.huida.nebula.boot:nebula-boot-starter:${nebulaBootVersion}"
305
+ api "com.huida.nebula.boot:nebula-boot-starter-security:${nebulaBootVersion}"
306
+ api "com.huida.nebula:nebula-auth-starter:${nebulaAuthVersion}"
307
+ api "com.huida.nebula:nebula-system-starter:${nebulaSystemVersion}"
308
+ }
309
+
310
+ bootJar { archiveFileName = "${project.name}.jar" }
311
+ ```
312
+
313
+ **`Nebula{Domain}Application.java`**
314
+ ```java
315
+ package com.huida.{domain};
316
+
317
+ import org.springframework.boot.SpringApplication;
318
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
319
+ import org.springframework.scheduling.annotation.EnableAsync;
320
+ import org.springframework.scheduling.annotation.EnableScheduling;
321
+
322
+ @SpringBootApplication(scanBasePackages = {"com.huida.{domain}", "com.huida.nebula"})
323
+ @EnableScheduling
324
+ @EnableAsync
325
+ public class Nebula{Domain}Application {
326
+ public static void main(String[] args) {
327
+ SpringApplication.run(Nebula{Domain}Application.class, args);
328
+ }
329
+ }
330
+ ```
331
+
332
+ **`application.yml`**
333
+ ```yaml
334
+ spring:
335
+ profiles:
336
+ active: dev
337
+ application:
338
+ name: nebula-{domain}
339
+
340
+ server:
341
+ port: ${PORT:{PORT}}
342
+
343
+ logging:
344
+ config: classpath:log4j2-${spring.profiles.active}.xml
345
+ ```
346
+
347
+ **`application-dev.yml`**
348
+ ```yaml
349
+ spring:
350
+ datasource:
351
+ url: jdbc:mysql://{mysql-host}:{mysql-port}/nebula_{domain}?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
352
+ username: {mysql-user}
353
+ password: {mysql-pw}
354
+ driver-class-name: com.mysql.cj.jdbc.Driver
355
+ data:
356
+ redis:
357
+ host: {redis-host}
358
+ port: {redis-port}
359
+ password: {redis-pw}
360
+ database: {redis-db}
361
+
362
+ nebula:
363
+ flyway:
364
+ enabled: true
365
+ tenant:
366
+ enabled: true
367
+ isolation-mode: FIELD
368
+ swagger:
369
+ enabled: true
370
+
371
+ springdoc:
372
+ swagger-ui:
373
+ path: /swagger-ui.html
374
+ ```
375
+
376
+ > log4j2-{dev/uat/prod}.xml 模板见 [framework-standards.md](framework-standards.md) §三-E。
377
+
378
+ ### 6.3 .gitignore(项目根目录,必须)
379
+
380
+ ```gitignore
381
+ # ---------------- Gradle ----------------
382
+ .gradle/
383
+ build/
384
+ !gradle/wrapper/gradle-wrapper.jar
385
+ gradle-app.setting
386
+ !**/src/main/**/build/
387
+ !**/src/test/**/build/
388
+
389
+ # ---------------- IDE: IntelliJ IDEA ----------------
390
+ .idea/
391
+ *.iml
392
+ *.iws
393
+ *.ipr
394
+ out/
395
+
396
+ # ---------------- IDE: VS Code ----------------
397
+ .vscode/
398
+ .vscode-server/
399
+
400
+ # ---------------- IDE: Eclipse ----------------
401
+ .classpath
402
+ .project
403
+ .settings/
404
+ .factorypath
405
+
406
+ # ---------------- OS ----------------
407
+ .DS_Store
408
+ Thumbs.db
409
+
410
+ # ---------------- Java ----------------
411
+ *.class
412
+ *.log
413
+ hs_err_pid*
414
+
415
+ # ---------------- Spring Boot ----------------
416
+ *.original
417
+
418
+ # ---------------- Local config / secrets ----------------
419
+ application-local.yml
420
+ application-local.yaml
421
+ application-local.properties
422
+ *.local.yml
423
+ .env.local
424
+
425
+ # ---------------- Logs / temp ----------------
426
+ logs/
427
+ *.log.gz
428
+ tmp/
429
+
430
+ # ---------------- Node (前端目录) ----------------
431
+ node_modules/
432
+ dist/
433
+ *.tsbuildinfo
434
+ .pnpm-store/
435
+ ```
436
+
437
+ ### 6.4 必备 SPI 空实现(`nebula-framework-task` 启动期依赖)
438
+
439
+ `nebula-framework-task` 模块定义了 3 个持久化 SPI,业务项目**必须**提供实现(哪怕空实现)才能让 task starter 起来。
440
+
441
+ 放在 `nebula-{domain}-core/src/main/java/com/huida/{domain}/{domain}/infra/`:
442
+
443
+ **`DefaultApiLogRepository.java`**
444
+ ```java
445
+ package com.huida.{domain}.{domain}.infra;
446
+
447
+ import com.huida.nebula.framework.task.apilog.ApiLogModel;
448
+ import com.huida.nebula.framework.task.apilog.ApiLogRepository;
449
+ import lombok.extern.slf4j.Slf4j;
450
+ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
451
+ import org.springframework.stereotype.Component;
452
+
453
+ /**
454
+ * API 日志默认占位实现。
455
+ * 业务方按需替换为真实存储(DB / ES / 日志文件)。
456
+ */
457
+ @Slf4j
458
+ @Component
459
+ @ConditionalOnMissingBean(ApiLogRepository.class)
460
+ public class DefaultApiLogRepository implements ApiLogRepository {
461
+ @Override
462
+ public void saveAsync(ApiLogModel logModel) {
463
+ log.debug("[ApiLog skipped] {}", logModel);
464
+ }
465
+ }
466
+ ```
467
+
468
+ **`DefaultTaskInstanceRepository.java`**
469
+ ```java
470
+ package com.huida.{domain}.{domain}.infra;
471
+
472
+ import com.huida.nebula.framework.task.core.executor.TaskInstanceRepository;
473
+ import com.huida.nebula.framework.task.core.model.TaskContext;
474
+ import lombok.extern.slf4j.Slf4j;
475
+ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
476
+ import org.springframework.stereotype.Component;
477
+
478
+ /**
479
+ * 任务实例持久化默认占位实现。
480
+ * 业务方启用任务调度时需实现真实持久化。
481
+ */
482
+ @Slf4j
483
+ @Component
484
+ @ConditionalOnMissingBean(TaskInstanceRepository.class)
485
+ public class DefaultTaskInstanceRepository implements TaskInstanceRepository {
486
+ @Override public void markRunning(Long instanceId) { log.debug("[task] running id={}", instanceId); }
487
+ @Override public void markSuccess(Long instanceId, TaskContext ctx) { log.debug("[task] success id={} ctx={}", instanceId, ctx); }
488
+ @Override public void markFailed(Long instanceId, String errorMsg) { log.debug("[task] failed id={} err={}", instanceId, errorMsg); }
489
+ @Override public void markCancelled(Long instanceId) { log.debug("[task] cancelled id={}", instanceId); }
490
+ @Override public void updateProgress(Long instanceId, int progress, long successCount, long failCount) {
491
+ log.debug("[task] progress id={} pct={} ok={} fail={}", instanceId, progress, successCount, failCount);
492
+ }
493
+ }
494
+ ```
495
+
496
+ **`DefaultTaskQueueService.java`**
497
+ ```java
498
+ package com.huida.{domain}.{domain}.infra;
499
+
500
+ import com.huida.nebula.framework.task.core.model.TaskMessage;
501
+ import com.huida.nebula.framework.task.queue.TaskQueueService;
502
+ import lombok.extern.slf4j.Slf4j;
503
+ import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
504
+ import org.springframework.stereotype.Component;
505
+
506
+ /**
507
+ * 任务队列默认占位实现:内存直投,方便本地启动。
508
+ * 生产环境应替换为 RocketMQ / Disruptor 实现。
509
+ */
510
+ @Slf4j
511
+ @Component
512
+ @ConditionalOnMissingBean(TaskQueueService.class)
513
+ public class DefaultTaskQueueService implements TaskQueueService {
514
+ @Override
515
+ public void publish(TaskMessage message) {
516
+ log.debug("[task-queue] publish {}", message);
517
+ // 本地占位:不真投递。生产需替换为 RocketMQ / 内存直投处理器
518
+ }
519
+ @Override
520
+ public String queueType() {
521
+ return "noop";
522
+ }
523
+ }
524
+ ```
525
+
526
+ > 这 3 个类**禁止** 写真实业务逻辑,只是为了让 starter 在缺少业务实现时能启动。日志级别保持 `debug`,避免污染 INFO。
527
+
528
+ ### 6.5 banner.txt(项目身份标识)
529
+
530
+ `nebula-{domain}-server/src/main/resources/banner.txt`,规则:
531
+
532
+ - **禁止** 出现任何其他业务项目名(含历史样板项目名)的字眼
533
+ - 显示当前项目名 + 版本 + Spring Boot 版本
534
+ - 标题用 `Nebula {DomainUpper}`(中文项目名可加在副标题)
535
+
536
+ 通用模板(按当前项目名替换):
537
+
538
+ ```
539
+ _ _ _ _ {Domain}
540
+ | \ | | ___| |__ _ _| | __ _ ________________
541
+ | \| |/ _ \ '_ \| | | | |/ _` | {业务中文名}
542
+ | |\ | __/ |_) | |_| | | (_| |
543
+ |_| \_|\___|_.__/ \__,_|_|\__,_| v${project.version}
544
+
545
+ :: Spring Boot :: (v${spring-boot.version})
546
+ :: Active Profile :: ${spring.profiles.active}
547
+ ```
548
+
549
+ AI 生成时**必须**把 `{Domain}` / `{业务中文名}` 替换为当前项目的对应值,不能保留任何样板字眼。
550
+
551
+ ### 6.6 Flyway 建表脚本
552
+
553
+ **`V1.0.0__init.sql`**(必须含 11 公共字段 + Demo 表)
554
+
555
+ ```sql
556
+ -- 示例资源表(11 个公共字段 + 业务字段)
557
+ CREATE TABLE IF NOT EXISTS t_{demoResource} (
558
+ id BIGINT NOT NULL COMMENT '主键',
559
+ tenant_id BIGINT DEFAULT NULL COMMENT '租户ID',
560
+ create_user_id BIGINT DEFAULT NULL COMMENT '创建人ID',
561
+ creator VARCHAR(128) DEFAULT NULL COMMENT '创建人',
562
+ create_time DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) COMMENT '创建时间',
563
+ modify_user_id BIGINT DEFAULT NULL COMMENT '更新人ID',
564
+ updater VARCHAR(128) DEFAULT NULL COMMENT '更新人',
565
+ modify_time DATETIME(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6) COMMENT '更新时间',
566
+ delete_flag INT NOT NULL DEFAULT 0 COMMENT '逻辑删除(0:正常 1:已删)',
567
+ remark VARCHAR(500) DEFAULT NULL COMMENT '备注',
568
+ custom_fields TEXT DEFAULT NULL COMMENT '扩展字段(JSON)',
569
+ -- 业务字段
570
+ demo_name VARCHAR(128) NOT NULL COMMENT '名称',
571
+ demo_code VARCHAR(64) NOT NULL COMMENT '编码',
572
+ status INT NOT NULL DEFAULT 0 COMMENT '状态(0:启用 1:禁用)',
573
+ PRIMARY KEY (id),
574
+ UNIQUE KEY uk_{demoResource}_code (tenant_id, demo_code),
575
+ KEY idx_{demoResource}_name (demo_name)
576
+ ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='示例资源';
577
+ ```
578
+
579
+ ### 6.7 Demo CRUD 完整生成
580
+
581
+ 按 [full-crud-example.md](full-crud-example.md) 模板生成完整六件套:
582
+
583
+ - `entity/{Demo}DO.java`(extends BaseEntity)
584
+ - `mapper/{Demo}Mapper.java`(extends BaseMapper<{Demo}DO>)
585
+ - `mapper/ext/{Demo}MapperExt.java`(分页两段式 / 唯一性检查)
586
+ - `convert/{Demo}Convert.java`(MapStruct + BaseMapperConfig)
587
+ - `service/I{Demo}Service.java` + `service/impl/{Demo}ServiceImpl.java`
588
+ - `controller/{Demo}Controller.java`(POST /page、GET /{id}、POST、PUT、DELETE、PATCH /{id}/v、PATCH /{id}/x)
589
+ - `vo/param/{Demo}PageParamVO.java` + `{Demo}SaveParamVO.java`
590
+ - `vo/resp/{Demo}RespVO.java` + `{Demo}DetailVO.java`
591
+
592
+ > 不要重新发明示例代码,直接抄 [full-crud-example.md](full-crud-example.md) 并替换名字。
593
+
594
+ ---
595
+
596
+ ## 七、阶段 4:前端骨架生成
597
+
598
+ 仅当 Q1 含「前端」时执行。Q7 决定形态。
599
+
600
+ ### 7.1 独立仓库形态(默认)
601
+
602
+ **严格按 [microapp-guide.md](microapp-guide.md) §二必备文件清单 + §三关键配置详解** 逐文件生成。**禁止** 拷贝任何现有业务样板项目(含其历史业务名、banner、README 等)。
603
+
604
+ 每个文件按 microapp-guide 中给出的模板写,替换以下占位符:
605
+
606
+ | 占位符 | 取值 |
607
+ |--------|------|
608
+ | `{domain}` | Q2 派生 |
609
+ | `{Domain}` | 首字母大写 |
610
+ | `{DOMAIN}` | 全大写 |
611
+ | `{PascalDomain}` | 同 `{Domain}` |
612
+ | `{PORT}` | Q2 派生(30XX 段) |
613
+ | `{业务中文名}` | 询问用户 |
614
+ | `{your-host-domain}` | 询问用户(联调线上后端域名) |
615
+ | `{your-gitlab-host}` | 询问用户(GitLab 域名) |
616
+
617
+ ### 7.2 前端 .gitignore
618
+
619
+ ```gitignore
620
+ node_modules/
621
+ dist/
622
+ *.tsbuildinfo
623
+ .pnpm-store/
624
+ .DS_Store
625
+ .idea/
626
+ .vscode/
627
+ *.log
628
+ .env.local
629
+ .env.*.local
630
+ src/types/auto-imports.d.ts
631
+ src/types/components.d.ts
632
+ ```
633
+
634
+ ### 7.3 monorepo 内置形态(备选)
635
+
636
+ 按 [microapp-guide.md](microapp-guide.md) 同样的配置,但放进 `nebula-web/apps/nebula-web-{domain}/`,依赖改成 `workspace:*`,scripts 沿用 monorepo 根的 dev/build。
637
+
638
+ ### 7.4 主应用注册
639
+
640
+ 按 [microapp-guide.md](microapp-guide.md) §四执行。
641
+
642
+ ---
643
+
644
+ ## 八、阶段 5:启动验证 + 配置修改指引
645
+
646
+ ### 8.1 启动命令(必须告诉用户)
647
+
648
+ **后端**(在 `nebula-{domain}` 根目录执行):
649
+
650
+ ```bash
651
+ # 第一次构建(拉依赖 + 编译)—— 若挂内网/VPN,5~10 分钟
652
+ ./gradlew clean build -x test
653
+
654
+ # 本地启动(推荐,IDE 外)
655
+ ./gradlew :nebula-{domain}-server:bootRun
656
+
657
+ # 用 IDE 启动:右键 Nebula{Domain}Application.java → Run
658
+ #(IDE 启动前确认 §一-IDEA 必备配置已完成)
659
+
660
+ # 打可执行 jar
661
+ ./gradlew :nebula-{domain}-server:bootJar
662
+ # 产物:nebula-{domain}-server/build/libs/nebula-{domain}-server.jar
663
+ java -jar nebula-{domain}-server/build/libs/nebula-{domain}-server.jar
664
+ ```
665
+
666
+ **前端**(在 `nebula-{domain}-web` 根目录执行):
667
+
668
+ ```bash
669
+ # 配置 GitLab token(首次必做)
670
+ export GITLAB_TOKEN=glpat-xxxxx
671
+
672
+ # 安装依赖
673
+ pnpm install
674
+
675
+ # 本地启动(本地 IP 后端)
676
+ pnpm dev
677
+
678
+ # 联调线上后端
679
+ pnpm dev:remote
680
+
681
+ # 类型检查
682
+ pnpm typecheck
683
+
684
+ # 构建
685
+ pnpm build
686
+ ```
687
+
688
+ ### 8.2 MySQL / Redis 配置修改位置(必须告诉用户)
689
+
690
+ 后端项目 application 配置入口:
691
+
692
+ | 想修改的内容 | 文件路径 | 关键 key |
693
+ |------------|---------|---------|
694
+ | MySQL 连接 | `nebula-{domain}-server/src/main/resources/application-dev.yml` | `spring.datasource.url` / `username` / `password` |
695
+ | Redis 连接 | 同上 | `spring.data.redis.host` / `port` / `password` / `database` |
696
+ | 服务端口 | `application.yml` | `server.port` |
697
+ | 当前 profile | `application.yml` | `spring.profiles.active`(默认 `dev`) |
698
+ | Flyway 开关 | `application-dev.yml` | `nebula.flyway.enabled` |
699
+ | 多租户隔离模式 | `application-dev.yml` | `nebula.tenant.isolation-mode`(FIELD / SCHEMA / DATABASE) |
700
+ | Swagger 开关 | `application-dev.yml` | `nebula.swagger.enabled` |
701
+ | 日志级别 | `nebula-{domain}-server/src/main/resources/log4j2-dev.xml` | `<Logger level="..."/>` |
702
+
703
+ > 生产环境改 `application-prod.yml`,**永远不要**改 `application.yml` 主文件中的具体连接串。
704
+
705
+ ### 8.3 启动验证(AI 自动 Bash)
706
+
707
+ ```bash
708
+ # 后端 Swagger
709
+ curl -fs "http://localhost:{PORT}/v3/api-docs" > /dev/null && echo "✅ Swagger OK"
710
+
711
+ # 后端 Demo 接口(无 token 应返回 401 而非 connection refused)
712
+ curl -s -o /dev/null -w "%{http_code}\n" "http://localhost:{PORT}/{domain}/{demo}/1"
713
+
714
+ # 主应用
715
+ curl -fs "http://127.0.0.1:3000/" > /dev/null && echo "✅ 主应用 OK"
716
+
717
+ # 子应用 mount 文件
718
+ curl -fs "http://127.0.0.1:{frontend-PORT}/" > /dev/null && echo "✅ 子应用 OK"
719
+ ```
720
+
721
+ ### 8.4 浏览器手工验证
722
+
723
+ 告知用户依次操作:
724
+
725
+ 1. 打开 `http://localhost:{PORT}/swagger-ui.html` → 看到 Demo CRUD 接口列表
726
+ 2. 打开 `http://127.0.0.1:3000/` → 登录
727
+ 3. 菜单点击「{业务中文名}」→ 应看到子应用页面
728
+
729
+ 白屏时立即查 [pitfalls-checklist.md](pitfalls-checklist.md) §G。
730
+
731
+ ---
732
+
733
+ ## 九、阶段 6:收尾交付清单
734
+
735
+ AI 输出给用户的最终消息必须包含以下结构(按项目情况增删):
736
+
737
+ ```markdown
738
+ ## ✅ 后端项目已创建
739
+
740
+ - 位置:`{path}/nebula-{domain}`
741
+ - Group:`com.huida.{domain}`
742
+ - 启动命令:
743
+ - `cd {path}/nebula-{domain} && ./gradlew :nebula-{domain}-server:bootRun`
744
+ - 或在 IDE 中运行 `Nebula{Domain}Application.java`
745
+ - Swagger:http://localhost:{PORT}/swagger-ui.html
746
+ - 数据库:`nebula_{domain}`(已建库,已执行 V1.0.0__init.sql)
747
+ - 示例资源:`t_{demo}`(CRUD 8 个接口可用)
748
+ - 关键技术栈:JDK 17 / Spring Boot 3.5.9 / Gradle 8.12 / MyBatis-Plus 3.5.9
749
+ - 依赖版本:nebula-boot=0.1.22-SNAPSHOT / nebula-auth=0.1.8-SNAPSHOT / nebula-system=0.2.47-SNAPSHOT
750
+
751
+ ## ✅ 前端项目已创建
752
+
753
+ - 位置:`{path}/nebula-{domain}-web`
754
+ - 启动命令:
755
+ - `cd {path}/nebula-{domain}-web && pnpm install && pnpm dev`
756
+ - 端口:{frontend-PORT}
757
+ - 主应用菜单入口:/{domain}app
758
+
759
+ ## 🔧 修改 MySQL / Redis 配置
760
+
761
+ - 开发环境:`nebula-{domain}-server/src/main/resources/application-dev.yml`
762
+ - 生产环境:`application-prod.yml`
763
+ - 服务端口:`application.yml`
764
+
765
+ ## 🔧 IntelliJ IDEA 必须调整的设置
766
+
767
+ 1. Project SDK = 17
768
+ 2. Project language level = 17
769
+ 3. Gradle JVM = 17
770
+ 4. Annotation Processing 启用(Lombok / MapStruct 必需)
771
+ 5. File Encoding 全 UTF-8
772
+ 6. 必装插件:Lombok / MapStruct Support / MyBatisX
773
+
774
+ ## 🌐 公司内网约束
775
+
776
+ - 拉依赖必须连公司内网或 VPN(Nexus:`http://10.10.1.130:8081/repository/nebula-public/`)
777
+ - 拉不到包时第一时间检查网络
778
+
779
+ ## 📋 后续仍需要做
780
+
781
+ 1. 在 `nebula-system` 中配置菜单 + 权限
782
+ 2. 接入 `nebula-auth` 登录(如果独立部署)
783
+ 3. 把 §六-4 的 3 个占位 SPI 替换为真实持久化实现(启用任务调度时)
784
+ 4. 配置生产环境 Nginx
785
+ 5. 配置 `deploy/build-deploy.sh` 的 `SERVER_HOST`
786
+ 6. `git remote` 设置 + 首次 `git push`
787
+ ```
788
+
789
+ ---
790
+
791
+ ## 十、阶段中决策点速查
792
+
793
+ 向导执行中常见的二次询问:
794
+
795
+ | 场景 | AskUserQuestion 选项 |
796
+ |------|---------------------|
797
+ | 数据库不存在 | 自动建库 / 手动建 / 仅生成脚本不执行 |
798
+ | 端口冲突 | 自动 +1 探测可用端口 / 用户手动指定 / 终止流程 |
799
+ | 选 MQ 但本机没装 | 跳过 MQ starter / 用 docker 起 / 改用本地内存模拟 |
800
+ | Demo 资源名与现有冲突 | 改名 / 跳过 Demo 生成 |
801
+ | 已有同名项目目录 | 备份原目录 / 用新目录名 / 终止 |
802
+ | GITLAB_TOKEN 未设置(前端) | 引导设置 / 跳过 install / 终止 |
803
+ | Nexus 503 / 连不上 | **提示连公司内网或 VPN** / 重试 / 跳过 |
804
+ | Gradle 拉依赖超时 | 同上 / 增加超时配置 |
805
+
806
+ ---
807
+
808
+ ## 十一、依赖拉取异常处理
809
+
810
+ ### 11.1 现象
811
+
812
+ - `Could not resolve com.huida.nebula:...`
813
+ - `Connection refused: /10.10.1.130:8081`
814
+ - `Read timed out`
815
+ - `503 Service Unavailable`
816
+
817
+ ### 11.2 AI 处理流程
818
+
819
+ 1. **第一步必须做**:用 `curl -s -o /dev/null -w "%{http_code}" http://10.10.1.130:8081/repository/nebula-public/` 检测连通性
820
+ 2. 不通 → **明确告知用户「请确认已连接公司内网或 VPN」**
821
+ 3. 通了仍报错 → 检查依赖版本号是否在 Nexus 实际存在
822
+ 4. `503` → 暂时性问题,等 1~2 分钟重试;连续 503 反馈给 Nexus 管理员
823
+
824
+ **严禁**:自动替换为其他公网镜像源(如阿里云 maven 镜像)—— nebula 包不在公网仓库。
825
+
826
+ ---
827
+
828
+ ## 十二、本向导与其他文档的依赖关系
829
+
830
+ 向导执行时大量引用:
831
+
832
+ - 后端结构 → [backend-standards.md](backend-standards.md)
833
+ - 后端 CRUD 模板 → [full-crud-example.md](full-crud-example.md)
834
+ - 后端建表规范 → [db-standards.md](db-standards.md)
835
+ - 框架工具与日志模板 → [framework-standards.md](framework-standards.md)
836
+ - Boot 组件引入 → [boot-components-catalog.md](boot-components-catalog.md)
837
+ - RPC 接入 auth/system → [rpc-api-reference.md](rpc-api-reference.md)
838
+ - 前端独立仓库样板 → [microapp-guide.md](microapp-guide.md)
839
+ - 前端 monorepo 子应用 → [frontend-standards.md](frontend-standards.md)
840
+ - 异常处理 → [pitfalls-checklist.md](pitfalls-checklist.md)
841
+
842
+ > AI 在执行向导时,对应阶段务必先读上述同 skill 内的文档,**禁止** 去本地项目源码目录验证,**禁止** 拷贝任何现有业务样板项目。