8.7 KiB
8.7 KiB
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显式列出全部实体;TypeORMmigrationsRun: 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
# 根目录(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 弹窗。 - 可编辑单元格:
EditableCelleditor 类型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/corsapps/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 POSTactions/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)。