with one click
nop-nodejs-backend
// Node.js 后端服务技术架构设计。基于 NestJS + Prisma + SQLite 的原型开发规范,支持快速原型开发和未来迁移到 Java。触发词:Node.js 后端、NestJS、Prisma、后端架构、API 设计。
// Node.js 后端服务技术架构设计。基于 NestJS + Prisma + SQLite 的原型开发规范,支持快速原型开发和未来迁移到 Java。触发词:Node.js 后端、NestJS、Prisma、后端架构、API 设计。
Nop项目Git专家 - 智能提交、Rebase、历史搜索(基于项目风格固化)
(opencode-project - Skill) Nop平台数据库设计规范。定义表命名、列命名、主键设计、索引设计、通用字段、域定义、关系设计等规范。触发词:数据库设计、表设计、DDL、ORM模型、字段命名。
涉及git worktree的操作优先使用这个skill。管理 Git bare 仓库和多个并行 worktree 的开发环境。支持初始化 bare 仓库结构、自动生成分支名并创建 feature worktree。
苏格拉底式深度访谈,用数学化的模糊度评分来澄清需求。适用于模糊的想法、不确定的需求、需要暴露隐藏假设的场景。触发词:"deep interview"、"深度访谈"、"需求澄清"、"帮我理清思路"、"不知道要做什么"。
使用 nop-cli gen 命令从 ORM 模型文件生成 Nop 平台初始项目脚手架(仅初次生成)。生成后通过 mvn install 迭代。触发词:代码生成、gen、生成项目、脚手架、初始化项目。
Generate, validate, and modify Nop ORM models from MySQL DDL/SQL or business requirements. Covers entity modeling, relationships, domains, dictionaries, displayName localization, and ORM file organization (Delta mode). Use for database-first or requirements-first ORM development.
| name | nop-nodejs-backend |
| description | Node.js 后端服务技术架构设计。基于 NestJS + Prisma + SQLite 的原型开发规范,支持快速原型开发和未来迁移到 Java。触发词:Node.js 后端、NestJS、Prisma、后端架构、API 设计。 |
本文档用于指导基于 Node.js 的业务原型后端开发,目标是尽快做出一个可以演示真实业务流程的后端原型,并与现有产品接口风格保持一致。它不是通用 Node.js 最佳实践文档,也不是生产架构规范,而是一个面向 AI 生成代码、面向业务演示、面向后续 AI 迁移分析的实现约束。
为降低 AI 生成歧义并提升开发效率,本文档采用如下固定约定:
@map 指令实现映射未来后端会由 AI 迁移为定制 Java 框架实现,因此这里的 Node.js 代码主要承担两类职责:
@map 实现字段名映射| 组件 | 选型 | 版本 | 说明 |
|---|---|---|---|
| 运行时 | Node.js | 22.x LTS | 最新的长期支持版本 |
| Web框架 | NestJS | 11.x | 模块化、TypeScript优先 |
| 数据库 | SQLite | 3.x | 嵌入式数据库,零配置 |
| ORM/数据层 | Prisma | 7.3.0 | 模型驱动,支持字段映射 |
| API文档 | @nestjs/swagger | 11.2.5 | 集成 Swagger UI |
| 验证 | class-validator | 0.15.0 | 声明式验证 |
| 类型转换 | class-transformer | 最新 | 配合验证使用 |
| 配置管理 | @nestjs/config | 最新 | 环境变量管理 |
| 日志 | NestJS 内置日志 | - | 开发阶段简洁日志 |
| 测试 | Jest | 29.x | 单元测试、e2e测试 |
| 包管理器 | pnpm | 8.x | 高效磁盘利用、严格依赖管理,推荐使用 corepack 启用 |
schema.prisma 为唯一事实来源。GET、PUT、PATCH、DELETE 风格接口。project-root/
├── prisma/
│ ├── schema.prisma # 数据模型定义(camelCase字段,snake_case列通过@map映射)
│ └── migrations/ # 迁移文件(可选)
├── src/
│ ├── main.ts
│ ├── app.module.ts
│ ├── config/
│ │ ├── env.validation.ts
│ │ └── database.config.ts
│ ├── common/
│ │ ├── filters/
│ │ ├── interceptors/
│ │ ├── interfaces/
│ │ │ ├── api-response.interface.ts
│ │ │ └── page-bean.interface.ts
│ │ └── ...
│ ├── modules/
│ │ └── users/
│ │ ├── dto/
│ │ │ ├── getUser.dto.ts
│ │ │ ├── createUser.dto.ts
│ │ │ ├── updateUser.dto.ts
│ │ │ ├── deleteUser.dto.ts
│ │ │ └── userPageQuery.dto.ts
│ │ ├── users.controller.ts
│ │ ├── users.service.ts
│ │ └── users.module.ts
│ └── shared/
│ └── prisma/
│ ├── prisma.service.ts
│ └── prisma.module.ts
├── test/
│ └── users.e2e-spec.ts
├── .env.example
├── package.json
└── ...
新增一个业务模块时,至少生成以下内容:
src/modules/<module>/<module>.module.tssrc/modules/<module>/<module>.controller.tssrc/modules/<module>/<module>.service.tssrc/modules/<module>/dto/create<Module>.dto.tssrc/modules/<module>/dto/update<Module>.dto.tssrc/modules/<module>/dto/get<Module>.dto.tssrc/modules/<module>/dto/delete<Module>.dto.tssrc/modules/<module>/dto/<module>PageQuery.dto.ts如模块较简单,可根据实际情况减少 DTO 数量,但必须保持接口风格、命名风格和分页结构一致。
@@map 指定。full_name、is_active)。fullName、isActive),通过 Prisma 的 @map 映射到底层列名。id,类型为 String,使用 UUID 填充。userId),通过 @map 映射为 user_id。createdAt、updatedAt,映射为 created_at、updated_at。// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
model users {
id String @id @default(uuid()) @map("id") // 列名 id
email String @unique @map("email") // 列名 email
fullName String? @map("full_name") // 列名 full_name
isActive Boolean @default(true) @map("is_active") // 列名 is_active
createdAt DateTime @default(now()) @map("created_at") // 列名 created_at
updatedAt DateTime @updatedAt @map("updated_at") // 列名 updated_at
@@map("users") // 表名 users (snake_case)
}
说明:
user.fullName、user.isActive,而数据库列名分别为 full_name、is_active。原型开发阶段,允许使用 prisma db push 自动同步表结构,以减少迁移维护成本:
// src/main.ts
if (process.env.NODE_ENV === 'development' && process.env.DATABASE_URL?.startsWith('file:')) {
execSync('npx prisma db push --accept-data-loss', { stdio: 'inherit' });
}
该策略仅适用于本 skill 定义的原型开发场景,不用于生产环境。
/users/get、/users/create)。这是产品特定要求,用于与现有技术体系兼容,并降低 AI 生成和迁移时的歧义。id、分页参数等。{}。{ "status": 0, "data": ... }{ "status": 非0, "code": "...", "msg": "...", "errors": {...} }{ "status": 0, "data": PageBean }DTO 使用 camelCase,与 Prisma 模型属性名一致(无需关心数据库列名)。
示例:createUser.dto.ts
import { ApiProperty } from '@nestjs/swagger';
import { IsEmail, IsString, MinLength, IsOptional, IsBoolean } from 'class-validator';
export class CreateUserDto {
@ApiProperty()
@IsEmail()
email: string;
@ApiProperty({ required: false })
@IsOptional()
@IsString()
@MinLength(2)
fullName?: string;
@ApiProperty({ required: false, default: true })
@IsOptional()
@IsBoolean()
isActive?: boolean;
}
示例:getUser.dto.ts
import { ApiProperty } from '@nestjs/swagger';
import { IsUUID } from 'class-validator';
export class GetUserDto {
@ApiProperty()
@IsUUID()
id: string;
}
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post('get')
async get(@Body() dto: GetUserDto) {
const user = await this.usersService.findOne(dto.id);
if (!user) throw new BusinessException('USER_NOT_FOUND', '用户不存在');
return user; // 拦截器自动包装
}
@Post('create')
async create(@Body() dto: CreateUserDto) {
return this.usersService.create(dto);
}
@Post('page')
async page(@Body() query: UserPageQueryDto) {
return this.usersService.findPage(query);
}
}
由于 Prisma 模型属性名与 DTO 字段名一致,服务层可直接传递 DTO 对象,无需手动映射字段名(映射已在 schema 层通过 @map 处理)。对于原型项目,这是有意保留的简化策略。
@Injectable()
export class UsersService {
constructor(private prisma: PrismaService) {}
async create(dto: CreateUserDto) {
return this.prisma.users.create({
data: dto, // dto 字段名与模型属性名一致
});
}
async findOne(id: string) {
return this.prisma.users.findUnique({ where: { id } });
}
async findPage(query: UserPageQueryDto): Promise<PageBean<UserResponseDto>> {
const { page, pageSize, filters, sort } = query;
const offset = (page - 1) * pageSize;
const limit = pageSize;
const [items, total] = await Promise.all([
this.prisma.users.findMany({
skip: offset,
take: limit,
where: this.buildWhere(filters),
orderBy: this.buildOrderBy(sort),
}),
this.prisma.users.count({ where: this.buildWhere(filters) }),
]);
return {
items: items.map(user => new UserResponseDto(user)),
total,
offset,
limit,
hasPrev: offset > 0,
hasNext: offset + limit < total,
};
}
}
注意:UserResponseDto 的字段也应使用 camelCase,与模型一致,可直接通过构造函数赋值。
.env.example:
DATABASE_URL="file:./dev.db"
PORT=3000
NODE_ENV=development
prisma db push 重置。原型阶段直接启动,数据库文件 dev.db 随代码部署。该后端仅用于开发和演示真实业务原型,不要求具备生产部署能力。
所有 API 路径、请求/响应结构由 OpenAPI 文档定义,Java 后端可据此重新实现。
@map 将字段映射为 snake_case,符合生产数据库规范。provider 和连接字符串,模型定义中的 @map 可继续保留。String 存储 UUID,后续迁移时只需保证接口层继续以字符串形式传递即可。git clone <repo>
# 启用 corepack(如未启用)
corepack enable
# 安装依赖
pnpm install
# 复制 .env.example 为 .env
# Windows PowerShell: Copy-Item .env.example .env
# macOS/Linux: cp .env.example .env
pnpm run start:dev
# 访问 http://localhost:3000/api