diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..fb57a2e --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,159 @@ +# 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")