343 lines
12 KiB
Markdown
343 lines
12 KiB
Markdown
# WWJCloud NestJS 开发指导原则
|
||
|
||
> 基于 v0.1.1 版本真实项目结构分析制定
|
||
> 更新时间: 2025-01-27
|
||
> 适用范围: WWJCloud NestJS 项目开发
|
||
|
||
## 🎯 核心开发原则
|
||
|
||
### 1. 框架使用原则
|
||
- **框架层面**: 100% 使用 NestJS 的方式和特性
|
||
- **业务层面**: 与 Java 项目保持 100% 一致
|
||
- **数据层面**: 与 Java 项目数据库结构 100% 一致
|
||
|
||
### 2. 开发约束条件
|
||
- ✅ **必须**: 基于 Java 项目真实代码进行开发
|
||
- ✅ **必须**: 基于真实数据库表结构进行映射
|
||
- ✅ **必须**: 遵循项目既定的命名规范
|
||
- ❌ **禁止**: 自创业务逻辑或数据结构
|
||
- ❌ **禁止**: 编写骨架代码或 TODO 注释
|
||
- ❌ **禁止**: 硬编码业务数据或配置
|
||
|
||
## 📁 项目结构规范
|
||
|
||
### 目录层级
|
||
```
|
||
src/
|
||
├── config/ # 配置管理层
|
||
│ ├── *.config.ts # 各类配置文件
|
||
│ ├── config.module.ts # 配置模块
|
||
│ └── *.entity.ts # 配置相关实体
|
||
├── common/ # 通用服务层
|
||
│ ├── base/ # 基础服务
|
||
│ ├── utils/ # 工具类
|
||
│ ├── interceptors/ # 拦截器
|
||
│ ├── guards/ # 守卫
|
||
│ └── pipes/ # 管道
|
||
├── core/ # 核心基础设施层
|
||
│ └── (待开发)
|
||
└── vendor/ # 第三方服务适配层
|
||
├── pay/ # 支付服务
|
||
├── sms/ # 短信服务
|
||
└── upload/ # 上传服务
|
||
```
|
||
|
||
## 🏷️ 命名规范标准
|
||
|
||
### 文件命名规范
|
||
|
||
#### 1. 实体文件
|
||
- **格式**: `kebab-case.entity.ts`
|
||
- **示例**: `dynamic-config.entity.ts`, `sys-user.entity.ts`
|
||
- **说明**: 使用短横线分隔,以 `.entity.ts` 结尾
|
||
|
||
#### 2. 服务文件
|
||
- **格式**: `kebab-case.service.ts`
|
||
- **示例**: `config-center.service.ts`, `pay.service.ts`
|
||
- **说明**: 使用短横线分隔,以 `.service.ts` 结尾
|
||
|
||
#### 3. 控制器文件
|
||
- **格式**: `kebab-case.controller.ts`
|
||
- **示例**: `config-center.controller.ts`, `auth.controller.ts`
|
||
- **说明**: 使用短横线分隔,以 `.controller.ts` 结尾
|
||
|
||
#### 4. 工具类文件
|
||
- **格式**: `kebab-case.util.ts`
|
||
- **示例**: `json.util.ts`, `string.util.ts`
|
||
- **说明**: 使用短横线分隔,以 `.util.ts` 结尾
|
||
|
||
#### 5. 模块文件
|
||
- **格式**: `kebab-case.module.ts`
|
||
- **示例**: `config.module.ts`, `auth.module.ts`
|
||
- **说明**: 使用短横线分隔,以 `.module.ts` 结尾
|
||
|
||
#### 6. DTO 文件
|
||
- **格式**: `PascalCaseDto.ts`
|
||
- **示例**: `CreateUserDto.ts`, `UpdateConfigDto.ts`
|
||
- **说明**: 使用 PascalCase,以 `Dto.ts` 结尾
|
||
|
||
### 类命名规范
|
||
|
||
#### 1. 实体类
|
||
- **格式**: `PascalCase`
|
||
- **示例**: `DynamicConfig`, `SysUser`, `MemberLevel`
|
||
- **说明**: 与数据库表名对应,使用 PascalCase
|
||
|
||
#### 2. 服务类
|
||
- **格式**: `PascalCase + Service`
|
||
- **示例**: `ConfigCenterService`, `PayService`, `AuthService`
|
||
- **说明**: 业务名称 + Service 后缀
|
||
|
||
#### 3. 控制器类
|
||
- **格式**: `PascalCase + Controller`
|
||
- **示例**: `ConfigCenterController`, `AuthController`
|
||
- **说明**: 业务名称 + Controller 后缀
|
||
|
||
#### 4. 工具类
|
||
- **格式**: `PascalCase + Util`
|
||
- **示例**: `JsonUtil`, `StringUtil`, `DateUtil`
|
||
- **说明**: 功能名称 + Util 后缀
|
||
|
||
#### 5. DTO 类
|
||
- **格式**: `操作动词 + 业务名称 + Dto`
|
||
- **示例**: `CreateUserDto`, `UpdateConfigDto`, `QueryMemberDto`
|
||
- **说明**: 明确表示操作类型和业务对象
|
||
|
||
### 方法命名规范
|
||
|
||
#### 1. 业务方法
|
||
- **格式**: `camelCase`
|
||
- **原则**: 与 Java 项目方法名保持一致
|
||
- **示例**: `getUserList()`, `addUser()`, `updateConfig()`
|
||
|
||
#### 2. 私有方法
|
||
- **格式**: `camelCase`
|
||
- **示例**: `private validateUser()`, `private formatData()`
|
||
|
||
#### 3. 生命周期方法
|
||
- **格式**: 按 NestJS 标准
|
||
- **示例**: `onModuleInit()`, `onApplicationBootstrap()`
|
||
|
||
### 变量命名规范
|
||
|
||
#### 1. 私有属性
|
||
- **格式**: `private readonly camelCase`
|
||
- **示例**: `private readonly logger`, `private readonly configService`
|
||
|
||
#### 2. 业务变量
|
||
- **格式**: `camelCase`
|
||
- **原则**: 与 Java 项目变量名保持一致
|
||
- **示例**: `userId`, `userName`, `configKey`
|
||
|
||
#### 3. 常量
|
||
- **格式**: `UPPER_SNAKE_CASE`
|
||
- **示例**: `DEFAULT_PAGE_SIZE`, `MAX_RETRY_COUNT`
|
||
|
||
### 数据库相关命名
|
||
|
||
#### 1. 表名映射
|
||
- **原则**: 与 Java 项目表名 100% 一致
|
||
- **示例**: `nc_sys_config`, `nc_sys_user`, `nc_member_level`
|
||
- **说明**: 不能修改任何表名或前缀
|
||
|
||
#### 2. 字段名映射
|
||
- **原则**: 与 Java 项目字段名 100% 一致
|
||
- **示例**: `site_id`, `config_key`, `create_time`, `update_time`
|
||
- **说明**: 不能修改任何字段名或类型
|
||
|
||
## 🔧 开发最佳实践
|
||
|
||
### Nest DI 与导入规范(重要)
|
||
- DI 相关类(如 `MetricsService`、`CacheService`、`LockService`)统一使用深路径导入:`@wwjcloud/wwjcloud-boot/infra/...`。
|
||
- 禁止在 AI 模块中通过聚合路径 `@wwjcloud/wwjcloud-boot` 导入上述 DI 类,避免符号不一致导致依赖解析失败。
|
||
- 优先使用“按类型注入”,仅在确有抽象需要时使用令牌,并确保令牌在提供方模块中唯一、稳定导出。
|
||
- 避免在运行期使用 `ModuleRef.get` 动态解析,除非处理循环依赖或跨域解耦的极端场景。
|
||
- 约定:Boot 层模块负责提供与导出,业务模块仅按类型消费,不再重复定义别名提供者。
|
||
|
||
|
||
|
||
### 1. 依赖注入
|
||
```typescript
|
||
@Injectable()
|
||
export class UserService {
|
||
constructor(
|
||
private readonly userRepository: Repository<User>,
|
||
private readonly configService: ConfigService,
|
||
private readonly logger: Logger,
|
||
) {}
|
||
}
|
||
```
|
||
|
||
### 2. 装饰器使用
|
||
```typescript
|
||
@Controller('api/user')
|
||
@UseGuards(JwtAuthGuard)
|
||
export class UserController {
|
||
@Get('list')
|
||
@UseInterceptors(ResponseInterceptor)
|
||
async getUserList(@Query() query: QueryUserDto) {
|
||
// 实现逻辑
|
||
}
|
||
}
|
||
```
|
||
|
||
### 3. 异常处理
|
||
```typescript
|
||
@Injectable()
|
||
export class UserService {
|
||
async findUser(id: number) {
|
||
const user = await this.userRepository.findOne({ where: { id } });
|
||
if (!user) {
|
||
throw new NotFoundException('用户不存在');
|
||
}
|
||
return user;
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. 数据验证
|
||
```typescript
|
||
export class CreateUserDto {
|
||
@IsString()
|
||
@IsNotEmpty()
|
||
username: string;
|
||
|
||
@IsEmail()
|
||
email: string;
|
||
|
||
@IsOptional()
|
||
@IsString()
|
||
nickname?: string;
|
||
}
|
||
```
|
||
|
||
## 📋 开发检查清单
|
||
|
||
### 开发前检查
|
||
- [ ] 已查看对应的 Java 源码文件
|
||
- [ ] 已查看相关的数据库表结构
|
||
- [ ] 已理解真实的业务逻辑
|
||
- [ ] 已确认所有依赖关系
|
||
|
||
### 开发中检查
|
||
- [ ] 文件命名符合项目规范
|
||
- [ ] 类命名符合项目规范
|
||
- [ ] 方法命名与 Java 项目一致
|
||
- [ ] 数据库字段映射准确
|
||
- [ ] 业务逻辑与 Java 项目一致
|
||
|
||
### 开发后检查
|
||
- [ ] 代码可以正常编译
|
||
- [ ] 所有依赖正确注入
|
||
- [ ] 数据库操作正常
|
||
- [ ] API 接口功能正确
|
||
- [ ] 异常处理完善
|
||
|
||
## 🚫 常见错误避免
|
||
|
||
### 1. 命名错误
|
||
- ❌ 错误: `userController.ts` (camelCase)
|
||
- ✅ 正确: `user.controller.ts` (kebab-case)
|
||
|
||
### 2. 文件结构错误
|
||
- ❌ 错误: 按技术层级划分目录
|
||
- ✅ 正确: 按业务模块划分目录
|
||
|
||
### 3. 业务逻辑错误
|
||
- ❌ 错误: 自创业务逻辑
|
||
- ✅ 正确: 基于 Java 项目真实代码
|
||
|
||
### 4. 数据库映射错误
|
||
- ❌ 错误: 修改表名或字段名
|
||
- ✅ 正确: 与 Java 项目 100% 一致
|
||
|
||
## 📚 参考资源
|
||
|
||
- **项目仓库**: wwjcloud-nsetjs
|
||
- **Java 项目**: niucloud-java
|
||
- **数据库结构**: `sql/wwjcloud.sql`
|
||
- **NestJS 官方文档**: https://nestjs.com/
|
||
- **TypeORM 文档**: https://typeorm.io/
|
||
|
||
---
|
||
|
||
> 本文档基于项目真实结构制定,请严格遵循以确保代码质量和项目一致性。
|
||
|
||
## 🔧 AI 恢复模块集成与测试
|
||
|
||
### 模块挂载
|
||
- 根应用:`AI_ENABLED=true` 时动态导入 `AiModule`
|
||
- `apps/api`:直接导入 `AiModule`,受 `GLOBAL_PREFIX=api` 影响
|
||
- 开发阶段:`AiController` 使用 `@Public()` 便于无令牌验证;生产需加守卫或网关策略
|
||
|
||
### 路由与前缀
|
||
- 推荐统一使用 `GLOBAL_PREFIX=api` 以避免基础设施路由状态码异常
|
||
- 端点示例:
|
||
- `GET /api/ai/recovery/status`
|
||
- `GET /api/ai/recovery/simulate-failure?taskId=T1&severity=high&reason=dev`
|
||
- `POST /api/ai/recovery/process-one`
|
||
- `POST /api/ai/recovery/drain`
|
||
|
||
### 队列驱动策略
|
||
- 开发:`QUEUE_DRIVER=memory`,避免 Kafka/Redis 干扰,快速闭环
|
||
- 生产:推荐 `redis` 或企业内部 `kafka`,实现跨进程/跨实例协同
|
||
|
||
### 依赖注入规范(与 DI 章节一致)
|
||
- Boot 层 DI 使用深路径导入:`@wwjcloud/wwjcloud-boot/infra/...`
|
||
- 业务模块按类型消费,避免重复定义令牌与别名
|
||
- 事件监听器与服务在 `AiModule` 内提供并导出:`AiSelfHealListener`、`AiRecoveryService`
|
||
|
||
### 本地端到端验证
|
||
```bash
|
||
# 查看初始队列大小
|
||
curl -s http://localhost:3001/api/ai/recovery/status
|
||
# 模拟失败(入队)
|
||
curl -s "http://localhost:3001/api/ai/recovery/simulate-failure?taskId=T1&severity=high&reason=dev"
|
||
# 再次查看(应增长)
|
||
curl -s http://localhost:3001/api/ai/recovery/status
|
||
# 处理一个(收敛)
|
||
curl -s -X POST http://localhost:3001/api/ai/recovery/process-one
|
||
# 清空队列(可选)
|
||
curl -s -X POST http://localhost:3001/api/ai/recovery/drain
|
||
```
|
||
|
||
### 测试建议
|
||
- e2e:覆盖失败事件触发 → 入队 → 处理 → 收敛的闭环
|
||
- 异常过滤:`GLOBAL_PREFIX=api` 下 `/api/metrics`、`/api/health` 保留原始状态码
|
||
- 队列驱动兼容:`memory`、`redis`、`kafka` 三模式最小用例
|
||
|
||
### 参考
|
||
- 详细指南:`docs/AI-RECOVERY-DEV.md`
|
||
- 工作流规范:`docs/AI-WORKFLOW-GUIDE.md`
|
||
## 对齐 Java 迁移规则(强约束)
|
||
- 只替换为 NestJS/boot 写法,不改变任何业务逻辑与数据流。
|
||
- 路由/方法/参数/返回结构必须与 Java 1:1 对齐;禁止新增/删改字段与键名。
|
||
- 参数与上下文来源:
|
||
- `siteId/memberId` 统一由 `RequestContextService.getSiteId()` 与 `RequestContextService.memberId` 获取,或由上层传入 `param.siteId/param.memberId`;控制器禁止手动写入上下文字段。
|
||
- `Channel` 仅在 Java 明确默认值的场景保留默认(Header `Channel` 默认 `h5`),否则禁止使用 `|| 'h5'`。
|
||
- 默认值与返回:
|
||
- 禁止新增默认值(如 `getSiteId() || 0`、`|| ''`、`|| 0`);仅保留与 Java 对齐的配置/JSON 读取默认(等价 `getStr(...,"" )`、`getInt(...,0)`)。
|
||
- 返回结构与 Java 对齐(例如 `{ data: '<json-string>' }`),禁止附加额外键。
|
||
- 路由与方法:
|
||
- 路径/HTTP 方法严格一致(如 `GET /api/verify_detail/{code}`),参数名对齐(如 `out_trade_no`)。
|
||
- 不新增非 Java 的调试/演示接口。
|
||
- 服务层与控制器职责:
|
||
- 登录/权限校验由守卫处理;服务层不做“未登录”等过度校验。
|
||
- 服务层使用传参 `param.*` 或上下文读取,禁止在服务层覆盖传入值。
|
||
- VO 构建与 Java 一致(如提现 `transfer` 子对象),仅映射必要字段。
|
||
- 查询与分页:
|
||
- where 条件对齐 Java(`siteId/memberId/id` 组合等),排序与分页一致(同字段/顺序/limit/skip)。
|
||
- 异常与校验:
|
||
- 异常信息与 Java 文案一致;参数校验放在 DTO/管道/验证器,不在服务层硬编码。
|
||
- 类型与结构:
|
||
- 避免 `as any` 类型逃逸;保持 DTO/VO 类型清晰;JSON/VO 转换遵循 Java 结构(等效 `BeanUtils.copyProperties` 行为)。
|
||
- 变更流程(必须遵循):
|
||
- 先“扫描 → 标记 → 按批次改动 → 验证”,不得边看边改。
|
||
- 批次 A:清理 `Number(this.requestContext.getSiteId() || 0)` 与 `getSiteId() || 0`。
|
||
- 批次 B:清理 `RequestUtils.channel() || 'h5'`(保留方法内部默认)。
|
||
- 批次 C:控制器移除手工注入上下文字段(如 `siteId`)。
|
||
- 批次 D:参数名/路由/返回结构对齐(如 `out_trade_no`、VO 子对象)。
|
||
- 批次完成后,用关键模式自检:`getSiteId\(\)\s*\|\|\s*0`、`Number\(this\.requestContext\.getSiteId\(\) \|\| 0\)`、`RequestUtils\.channel\(\) \|\|\s*['"]h5['"]`。
|
||
- 验收标准:
|
||
- 路由/方法/参数名/返回结构与 Java 100% 一致;无默认值污染;控制器不手工注入上下文字段;查询与分页排序一致;异常信息一致;类型明确无无谓 `as any`。
|