# Repository Guidelines

恭学教育基地管理系统（gongxue-base）——教培公司集训基地管理系统：宿舍水电费「人天数加权」计费、学生/班级/排课/考勤管理、学生档案（报读/成绩/花名册）、AI 助手与导入向导。代码注释、用户可见文案、commit message 均为中文。

## Project Overview

npm workspaces 单体仓库（monorepo），Turborepo 编排：

| 包 | 技术栈 | 说明 |
|---|---|---|
| `apps/server` | NestJS 11 + TypeORM 0.3 + MySQL | REST API，JWT + RBAC 权限 |
| `apps/admin` | React + Vite + antd + TanStack Query + zustand | 管理后台 SPA |
| `packages/typescript-config` | 共享 tsconfig（base/nestjs/react-vite） | 各包 extends 使用 |

远端：Gitea（`git.gongxue100.com`，wangziqi/gongxue-base）。`main` 为受保护分支，改动经分支 + PR（`tea` CLI 合并，rebase 风格）。

## Architecture & Data Flow

```
admin (axios /api, Bearer token) → NestJS controllers → services → TypeORM repos → MySQL (utf8mb4)
```

- **后端**：`app.module.ts` 显式列出全部实体；TypeORM `migrationsRun: true` + glob 自动发现迁移；`synchronize` 仅当显式 `DB_SYNCHRONIZE=true`（生产禁用）。全局前缀 `/api`，`ValidationPipe({ transform: true })`，`/api/classes` 路由放宽 body 上限 2mb（花名册批量导入）。
- **权限**：服务端 `@RequirePermission('module:action')` + CASL 策略守卫；前端 `usePermission()` + `<PermissionButton permission="...">`。权限码由 RbacSeedService 播种。
- **前端**：react-router 懒加载页面；数据请求走 TanStack Query（`useApiQuery`/`useApiMutation`，`api/queryKeys.ts` 统一 key）；全局状态 zustand（`store/user` 存 token，`store/permission` 存权限）。
- **认证**：JWT；axios 拦截器自动带 token，401 跳登录。

## Key Directories

- `apps/server/src/` — 按功能模块分目录：`auth`、`rbac`、`students`、`classes`、`exams`、`archive`（学生档案：报读/成绩/学习记录/附件/花名册同步）、`attendance`、`rooms`、`bills`、`wallets`、`imports`（导入向导）、`ai-chat`、`ai-config`、`agent-tools`、`integration`、`sync`、`operation-logs` 等
  - `entities/` — 全部 TypeORM 实体；`migrations/` — 迁移文件（时间戳前缀）；`common/` — dayjs（UTC+8 固定）、mime、`with-audit-log` 等
- `apps/admin/src/` — `pages/`（路由页面）、`components/`（含 `EditableCell` 可编辑单元格体系）、`api/`、`hooks/`、`store/`、`layouts/`、`ui/`、`utils/`、`test/`
- `.gitea/workflows/` — `ci.yml`（PR 校验）、`dependency-check.yml`（npm audit）、`deploy.yml`（PM2 部署）
- 根目录 — `deploy.sh`（部署脚本）、`ecosystem.config.cjs`（pm2）、`turbo.json`

## Development Commands

```bash
# 根目录（turbo 透传）
npm run dev          # 并行起 server(3000) + admin(3002)
npm run build / lint / typecheck / format / test

# 后端（apps/server）
npm run dev -w @gongxue/server        # SEED_DEV=true nest start --watch
npm run start:prod -w @gongxue/server # node dist/main（PM2 用这个）
npm run test -w @gongxue/server       # jest，全部 *.spec.ts
npm run migration:generate -w @gongxue/server -- src/migrations/Name   # 生成迁移（-d datasource.ts）
npm run migration:run -w @gongxue/server                              # 执行迁移（部署时也跑）

# 前端（apps/admin）
npm run dev -w @gongxue/admin         # vite :3002，代理 /api → :3000
npm run test -w @gongxue/admin        # vitest run
```

开发环境数据库：`.env`（gitignore，仅服务器保留生产版）。`apps/server/.env` 有本地开发配置（DB_PASSWORD 等），服务启动时 `dotenv` 加载。

## Code Conventions & Common Patterns

- **实体**：表名复数 snake_case；列用 `@Column({ name: 'snake_case' })`；`student` 关系多用 `eager: true`；状态枚举用 TS enum（如 `ClassStatus`）；唯一约束用 `@Unique`。
- **迁移**：`MigrationInterface`，文件名 `{timestamp}-{Name}.ts`；**必须幂等**——用 `hasColumn()`/`hasTable()`/`information_schema` 守卫再改结构。新增迁移文件零登记（glob 自动发现）。注意 `rank` 等 MySQL 保留字列名要反引号。
- **DTO**：class-validator 装饰器链（`@IsOptional() @IsString()` 等），更新用 `PartialType(CreateXxxDto)`；数值上限与列宽对齐（如 `decimal(5,2)` → `@Max(999.99)`），否则超范围写库报 500。
- **服务**：构造器注入 `@InjectRepository(Entity)` + 服务类；跨表写操作用 `dataSource.transaction(async (manager) => ...)`；业务错误抛 `BadRequestException`/`NotFoundException`（中文消息）。
- **审计**：controller 里包 `withAuditLog(this.logService, req, (result) => ({ module, action, targetId, targetType, detail }), () => service.method(...))`。
- **权限**：路由 `@RequirePermission('student:edit')` 等；`student:view`/`student:edit` 是档案读写常用码。
- **前端数据流**：组件内 `useApiMutation(fn, { invalidate: [['archive', studentId]] })`——变更后按 query key 失效重取；错误提示统一走 `ui/app-message`（`message.success/error`），不要在组件里重复 try/catch 弹窗。
- **可编辑单元格**：`EditableCell` editor 类型 `text|number|select|date|multi-select|tags`；`serializeEditableValue`/`normalizeEditableValue` 负责提交/展示值转换；`editableValuesEqual` 判空提交。
- **日期**：后端 `common/dayjs` 固定 UTC+8（`dayjs().utcOffset(8).format('YYYY-MM-DD')`）；前端 dayjs 加载 zh-cn locale 与 antd 面板插件。
- **commit message**：`type(scope): 中文描述`（如 `feat(server):`、`fix(admin):`、`refactor(server):`），scope 为 `server`/`admin`。

## Important Files

- `apps/server/src/main.ts` — bootstrap：全局前缀、ValidationPipe、body 上限、helmet/compression/cors
- `apps/server/src/app.module.ts` — 模块注册 + TypeORM 配置（entities 显式数组、migrations glob + `migrationsRun: true`）
- `apps/server/datasource.ts` — CLI 迁移 DataSource（glob 实体/迁移，根目录 + 本地 `.env` 双加载）
- `apps/server/src/entities/student-enrollment.entity.ts` — 报读实体（`class_id` 关联班级，花名册同步的源）
- `apps/admin/src/api/index.ts` — axios 实例（baseURL `/api`、token 注入、401 登出）
- `apps/admin/src/App.tsx` — 路由表（lazy 页面 + `PermissionRoute`）
- `ecosystem.config.cjs` — pm2 应用 `gongxue-backend`（`apps/server/dist/main.js`）
- `deploy.sh` / `.gitea/workflows/deploy.yml` — 部署（见下）
- `README.md` — 功能模块总览

## Runtime/Tooling Preferences

- **Node 22 + npm 11**（workspaces，`packageManager: npm@11.12.1`；CI 容器 `node:22.22.0-bookworm`），无 `.nvmrc`/engines 锁定
- **Turborepo**：`turbo.json` 任务 `build`/`dev`/`lint`/`test`/`format`/`typecheck`
- **MySQL 8**，库名 `dorm_billing_v2`（生产 `jidi.gongxue100.com` 对应实例），`utf8mb4`，迁移记录在 `migrations` 表
- **TypeScript** `~6.0.2`（server），共享配置 `packages/typescript-config`（`strictNullChecks: true`）
- **Gitea Actions**：runner `nonlocal-runner`（个人作用域，标签 `ubuntu-latest` 等，宿主机直跑无容器——deploy job 依赖此访问 PM2/部署目录）。CI job 显式声明 `container: node:22.22.0-bookworm`
- **tea CLI**：PR 创建/合并（`tea pr create`/`tea pr merge -s rebase`）、触发 workflow（`tea actions workflows dispatch`，需 Gitea ≥1.25；旧版用 API POST `actions/workflows/deploy.yml/dispatches`）
- 部署：手动 dispatch「PM2 本地部署」workflow 或 `./deploy.sh <ssh_host>`；生产目录 `/www/wwwroot/jidi.gongxue100.com`（workflow 默认值，可用仓库变量 `REMOTE_DIR` 覆盖）；顺序为 `git pull → npm ci → build → migration:run → pm2 startOrReload → 健康检查(401/200)`，迁移失败在 PM2 重载前中止

## Testing & QA

- **后端**：Jest + ts-jest，`testRegex: .*\.spec\.ts$`，测试文件与源码同目录（`*.spec.ts` 就近放置）。Repo 约定：`npm run test -w @gongxue/server -- --runInBand --forceExit`（CI 用）。服务测试用 jest.fn 构造 repo mock（新增构造参数会破坏 spec，需同步补 `{} as never` 占位）。关键服务有边界测试（`archive.boundaries.spec.ts`、`archive.enrollment-roster.spec.ts` 等）。
- **前端**：Vitest + `@vitest/browser` + Playwright（`src/test/setup.ts`），组件测试含截图快照（`__screenshots__/`）。
- **CI**（`.gitea/workflows/ci.yml`，PR → main 触发）：`npm ci → lint → typecheck → build admin → server tests`。
- **变更验证惯例**：UI 改动在浏览器验证；服务端逻辑跑相关 spec；迁移改动在本地库实跑并查 `migrations` 表；全量回归 `npx jest --silent`（server，约 155 suites/1200+ tests）。
