12 KiB
12 KiB
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. 依赖注入
@Injectable()
export class UserService {
constructor(
private readonly userRepository: Repository<User>,
private readonly configService: ConfigService,
private readonly logger: Logger,
) {}
}
2. 装饰器使用
@Controller('api/user')
@UseGuards(JwtAuthGuard)
export class UserController {
@Get('list')
@UseInterceptors(ResponseInterceptor)
async getUserList(@Query() query: QueryUserDto) {
// 实现逻辑
}
}
3. 异常处理
@Injectable()
export class UserService {
async findUser(id: number) {
const user = await this.userRepository.findOne({ where: { id } });
if (!user) {
throw new NotFoundException('用户不存在');
}
return user;
}
}
4. 数据验证
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/statusGET /api/ai/recovery/simulate-failure?taskId=T1&severity=high&reason=devPOST /api/ai/recovery/process-onePOST /api/ai/recovery/drain
队列驱动策略
- 开发:
QUEUE_DRIVER=memory,避免 Kafka/Redis 干扰,快速闭环 - 生产:推荐
redis或企业内部kafka,实现跨进程/跨实例协同
依赖注入规范(与 DI 章节一致)
- Boot 层 DI 使用深路径导入:
@wwjcloud/wwjcloud-boot/infra/... - 业务模块按类型消费,避免重复定义令牌与别名
- 事件监听器与服务在
AiModule内提供并导出:AiSelfHealListener、AiRecoveryService
本地端到端验证
# 查看初始队列大小
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 明确默认值的场景保留默认(HeaderChannel默认h5),否则禁止使用|| 'h5'。
- 默认值与返回:
- 禁止新增默认值(如
getSiteId() || 0、|| ''、|| 0);仅保留与 Java 对齐的配置/JSON 读取默认(等价getStr(...,"" )、getInt(...,0))。 - 返回结构与 Java 对齐(例如
{ data: '<json-string>' }),禁止附加额外键。
- 禁止新增默认值(如
- 路由与方法:
- 路径/HTTP 方法严格一致(如
GET /api/verify_detail/{code}),参数名对齐(如out_trade_no)。 - 不新增非 Java 的调试/演示接口。
- 路径/HTTP 方法严格一致(如
- 服务层与控制器职责:
- 登录/权限校验由守卫处理;服务层不做“未登录”等过度校验。
- 服务层使用传参
param.*或上下文读取,禁止在服务层覆盖传入值。 - VO 构建与 Java 一致(如提现
transfer子对象),仅映射必要字段。
- 查询与分页:
- where 条件对齐 Java(
siteId/memberId/id组合等),排序与分页一致(同字段/顺序/limit/skip)。
- where 条件对齐 Java(
- 异常与校验:
- 异常信息与 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。
- 路由/方法/参数名/返回结构与 Java 100% 一致;无默认值污染;控制器不手工注入上下文字段;查询与分页排序一致;异常信息一致;类型明确无无谓