# AGENTS.md - Coding Guidelines for Trace Backend ## Build, Lint, and Test Commands ### Essential Commands - `pnpm run build` - Build the NestJS application to dist/ - `pnpm run lint` - Run ESLint and auto-fix issues - `pnpm run format` - Format code with Prettier - `pnpm run test` - Run all Jest tests - `pnpm run test:watch` - Run tests in watch mode - `pnpm run test:cov` - Run tests with coverage report - `pnpm run start:dev` - Start development server with hot reload - `pnpm run init-db` - Initialize database (run scripts/init-db.ts) ### Running Single Tests - `jest path/to/test.spec.ts` - Run a specific test file - `jest -t "test name"` - Run tests matching a pattern ## Project Overview This is a NestJS backend for the Trace (贝凡溯源) management platform with: - **Framework**: NestJS 11.x with TypeScript - **Database**: SQLite via Prisma ORM with better-sqlite3 adapter - **Validation**: Zod schemas with nestjs-zod global pipe - **Auth**: JWT-based authentication with Passport - **Package Manager**: pnpm 10.x ## Code Style Guidelines ### Imports and Organization - Use double quotes for all imports: `import { X } from "package"` - Group external framework imports first, then internal imports - Use relative imports with `../` or `./` notation - Example: ```typescript import { Injectable, UnauthorizedException } from "@nestjs/common"; import { JwtService } from "@nestjs/jwt"; import * as bcrypt from "bcryptjs"; import { DatabaseService } from "../database/database.service"; import { User, AuthUser } from "../types"; ``` ### Naming Conventions - **Classes**: PascalCase - `AuthService`, `AuthGuard`, `SerialsService` - **Methods/Functions**: camelCase - `validateUser`, `login`, `getProfile` - **Variables**: camelCase - `const prisma = ...` - **Interfaces/Types**: PascalCase - `User`, `AuthUser`, `JWTPayload` - **DTOs**: PascalCase with "Dto" suffix - `LoginDto`, `ChangePasswordDto` - **Models**: PascalCase - `User`, `Company`, `Serial` (Prisma models) ### Module Structure Each feature follows the NestJS pattern: - `module-name/` - `module-name.module.ts` - Module definition with imports/providers - `module-name.controller.ts` - HTTP request handlers - `module-name.service.ts` - Business logic and database operations - `dto/` - Zod validation schemas - `module-name.guard.ts` - Guards (if needed) ### Controllers - Use appropriate decorators: `@Controller`, `@Get`, `@Post`, `@Patch`, `@Delete` - Apply guards: `@UseGuards(AuthGuard)` or `@UseGuards(AuthGuard, AdminGuard)` - Set status codes explicitly: `@HttpCode(HttpStatus.OK)` - Extract params/query with decorators: `@Param`, `@Query`, `@Body`, `@Req` - Validate DTOs: `@Body(LoginDto) loginDto: any` - Return structured responses (see Response Format below) Example: ```typescript @Controller("auth") export class AuthController { constructor(private authService: AuthService) {} @Post("login") @HttpCode(HttpStatus.OK) async login(@Body(LoginDto) loginDto: any) { const user = await this.authService.validateUser(loginDto.username, loginDto.password); return this.authService.login(user); } } ``` ### Services - Mark with `@Injectable()` decorator - Inject dependencies via constructor - Use `DatabaseService.getPrisma()` to get Prisma client - Throw `Error` for business logic failures (guards handle auth) - Use transactions with `prisma.$transaction()` for multi-step operations ### Validation (DTOs) - Use Zod schemas in `dto/index.ts` files - Define validation rules with Chinese error messages - Export from `dto/index.ts` barrel file Example: ```typescript export const LoginDto = z.object({ username: z.string().min(1, "用户名不能为空"), password: z.string().min(1, "密码不能为空"), }); ``` ### Error Handling - Throw NestJS exceptions in controllers/guards: - `UnauthorizedException` - Auth failures - `NotFoundException` - Resource not found - Throw `Error` in services for business logic errors - Provide Chinese error messages: `throw new Error("用户名或密码错误")` ### Response Format API responses follow this structure: ```typescript { message: "操作描述(中文)", data?: any, pagination?: { page: number, limit: number, total: number, totalPages: number } } ``` ### Database (Prisma) - Access Prisma client via `DatabaseService.getPrisma()` - Use `select` to limit returned fields for security - Use `include` for relations (user, company) - Serial numbers should be uppercase: `serialNumber.toUpperCase()` ### Authentication - Use `@UseGuards(AuthGuard)` for authenticated routes - Use `@UseGuards(AuthGuard, AdminGuard)` for admin-only routes - Access user via `@Req() req: Request` and cast: `(req as any).user` - User object contains: `id`, `username`, `name`, `role` ### TypeScript Configuration - Target: ES2021 - Strict mode disabled (per tsconfig.json) - Experimental decorators enabled - Module: commonjs ### Database Schema Located in `prisma/schema.prisma`: - Models: User, Company, Serial - Relations: User -> Serial, Company -> Serial - After schema changes: `npx prisma migrate dev` then `npx prisma generate` ### Static Files - Production: serves from `frontend/dist/` - Development: serves from `frontend/public/` - Static assets served via `@nestjs/serve-static` ## Development Notes - Global API prefix: `/api` - CORS enabled - QR code color: dark="#165DFF", light="#ffffff" - Default serial prefix: "BF" + current year (e.g., "BF26")