65ede9fcff
CLAUDE.md — AI 工作指南: - 项目全景:目录结构/技术栈/端口 - 系统架构:前端路由/后端模块/认证流程 - 权限系统:三层角色/26项权限/守卫流水线/解析链路 - 考核系统:数据模型/出题算法/模板配置 - 测试脚本:7个Playwright测试说明 - 开发指南:启动/测试/重启/数据库管理 - 代码规范:TypeScript模式/权限装饰器/React约定 - Playwright测试技巧:React受控输入框/等待策略 README.md — 人可读英文说明书: - 系统介绍 + 功能特性 - 完整使用指南(用户管理/权限管理/考核模板/组织考试) - 核心流程说明(认证/出题/权限解析) - 测试命令参考 - 项目结构 + 配置参考 README_ZH.md — 人可读中文说明书: - 全面中文版本,包含所有新功能 - 步骤式操作指南,便于管理员使用 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
399 lines
12 KiB
Markdown
399 lines
12 KiB
Markdown
# AuraK — AI 工作指南
|
||
|
||
> 本文件指导 AI 助手(Claude Code 等)如何理解和使用此仓库。
|
||
> 项目:AuraK — 企业级多租户 AI 知识库与人才评价平台
|
||
|
||
---
|
||
|
||
## 一、项目全景
|
||
|
||
```
|
||
AuraK/
|
||
├── web/ # React 19 + Vite 前端 (端口 13001)
|
||
│ ├── components/ # UI 组件(含 views/ 目录下的主要页面)
|
||
│ ├── contexts/ # AuthContext / LanguageContext / ConfirmContext
|
||
│ ├── services/ # API 客户端(apiClient.ts + 各服务)
|
||
│ ├── hooks/ # usePermissions 等自定义 Hook
|
||
│ └── src/pages/ # 路由页面(薄壳,委托给 components/views)
|
||
├── server/ # NestJS 后端 (端口 3001)
|
||
│ ├── src/auth/ # 认证(JWT/API Key)+ 权限(RBAC)
|
||
│ │ └── permission/ # Permission/Role 实体 + Service + Guard
|
||
│ ├── src/assessment/ # 考核评估子系统(最复杂)
|
||
│ ├── src/tenant/ # 多租户 + 租户成员管理
|
||
│ └── src/user/ # 用户 CRUD
|
||
├── docs/plans/ # 设计文档和计划
|
||
├── test-*.mjs # Playwright 测试脚本(项目根目录)
|
||
└── docker-compose.yml # 基础设施(Elasticsearch, Tika, LibreOffice)
|
||
```
|
||
|
||
### 核心技术栈
|
||
|
||
| 层 | 技术 |
|
||
|---|---|
|
||
| 前端 | React 19, TypeScript, Vite 6, Tailwind CSS v4, Framer Motion, Lucide icons |
|
||
| 后端 | NestJS 11, TypeORM, SQLite (better-sqlite3), LangChain/LangGraph |
|
||
| 搜索 | Elasticsearch 9(向量+全文混合检索)|
|
||
| 认证 | JWT + API Key 双机制 |
|
||
| 部署 | Docker Compose(Nginx 反向代理)|
|
||
|
||
---
|
||
|
||
## 二、系统架构
|
||
|
||
### 2.1 前端路由
|
||
|
||
| 路径 | 组件 | 说明 |
|
||
|---|---|---|
|
||
| `/login` | LoginPage | 公开 |
|
||
| `/` | OverviewPage | 工作台首页 |
|
||
| `/chat` | ChatPage | AI 对话 |
|
||
| `/assessment` | AssessmentPage | 考核评估 |
|
||
| `/assessment-stats` | AssessmentStatsView | 评估统计 |
|
||
| `/question-banks` | QuestionBankView | 题库管理 |
|
||
| `/settings` | SettingsPage | 系统设置 |
|
||
|
||
### 2.2 后端模块
|
||
|
||
app.module.ts 注册了 27 个模块,核心模块:
|
||
|
||
- **AuthModule** — 认证(JWT/API Key 双策略)
|
||
- **PermissionModule** — RBAC 权限(Role/RolePermission 实体 + PermissionService + PermissionsGuard)
|
||
- **UserModule** — 用户 CRUD
|
||
- **TenantModule** — 多租户(全局模块)
|
||
- **AssessmentModule** — 考核评估(最复杂,含会话/出题/评分/证书)
|
||
- **AdminModule / SuperAdminModule** — 管理端 API
|
||
- **KnowledgeBaseModule** — 知识库索引和检索
|
||
- **ModelConfigModule** — AI 模型配置
|
||
|
||
### 2.3 认证流程
|
||
|
||
```
|
||
密码登录:
|
||
POST /api/auth/login (username, password)
|
||
→ LocalAuthGuard → JwtService.sign(payload) → { access_token, user }
|
||
|
||
获取 API Key:
|
||
GET /api/users/api-key (Authorization: Bearer <JWT>)
|
||
→ 返回 kb_xxxxxxxx...(存前端 localStorage)
|
||
|
||
后续所有请求:
|
||
Headers: { x-api-key: <apiKey>, x-tenant-id: <tenantId> }
|
||
|
||
全局守卫: CombinedAuthGuard(APP_GUARD 注册)
|
||
→ 先尝试 API Key 认证,失败则回退 JWT
|
||
→ @Public() 装饰器跳过认证
|
||
```
|
||
|
||
---
|
||
|
||
## 三、权限系统(RBAC)核心
|
||
|
||
### 3.1 三层角色
|
||
|
||
| 角色 | 权限数 | 能做什么 |
|
||
|---|---|---|
|
||
| **SUPER_ADMIN** | 26 项 | 全部权限:用户/租户/知识库/考核/模型/插件/设置 |
|
||
| **TENANT_ADMIN** | 21 项 | 管理本租户用户和资源;不能跨租户/删用户/改系统设置 |
|
||
| **USER** | 5 项 | kb:view/create/edit, assess:view, plugin:view |
|
||
|
||
### 3.2 权限常量定义
|
||
|
||
位于 `server/src/auth/permission/permission.constants.ts`
|
||
|
||
```typescript
|
||
// 用户管理: user:view / user:create / user:edit / user:delete / user:role / user:password
|
||
// 租户管理: tenant:view / tenant:create / tenant:edit / tenant:delete / tenant:members
|
||
// 知识库: kb:view / kb:create / kb:edit / kb:delete / kb:publish
|
||
// 考核: assess:view / assess:manage / assess:template / assess:bank
|
||
// 模型: model:view / model:config
|
||
// 插件: plugin:view / plugin:manage
|
||
// 设置: settings:view / settings:system
|
||
```
|
||
|
||
### 3.3 权限守卫流水线
|
||
|
||
```
|
||
CombinedAuthGuard (认证) → 已有,全局注册
|
||
→ RolesGuard (角色门控) → @Roles(SUPER_ADMIN, TENANT_ADMIN)
|
||
→ PermissionsGuard (权限门控) → @Permission('user:view')
|
||
```
|
||
|
||
**系统角色保护**: 系统角色(isSystem=true)的权限不可通过 API 修改。
|
||
|
||
### 3.4 权限解析链路
|
||
|
||
```
|
||
PermissionService.getUserPermissions(userId, tenantId)
|
||
→ 1. 如果 user.isAdmin === true,返回全部 SUPER_ADMIN 权限
|
||
→ 2. 查找 TenantMember(userId, tenantId)
|
||
→ 3. TenantMember.role → Role.baseRole → RolePermission.permissionKey
|
||
```
|
||
|
||
---
|
||
|
||
## 四、考核评估系统
|
||
|
||
### 4.1 数据模型
|
||
|
||
```
|
||
AssessmentTemplate (模板)
|
||
└── question_count, dimensions[{name, label, weight}], passingScore
|
||
└── QuestionBank (题库,一对一关联模板)
|
||
└── QuestionBankItem (题目)
|
||
├── questionType: SHORT_ANSWER / MULTIPLE_CHOICE / TRUE_FALSE
|
||
└── dimension: PROMPT / LLM / IDE / DEV_PATTERN / WORK_CAPABILITY
|
||
|
||
AssessmentSession (会话)
|
||
├── questions_json (缓存的题目列表)
|
||
├── messages (对话历史)
|
||
├── scores / dimensionScores
|
||
└── finalReport (AI 生成的报告)
|
||
|
||
AssessmentCertificate (证书)
|
||
└── level: Novice / Proficient / Advanced / Expert
|
||
```
|
||
|
||
### 4.2 出题算法
|
||
|
||
`selectQuestions()` 在 `question-bank.service.ts`:
|
||
|
||
```typescript
|
||
// 按维度权重公平分配(floor + remainder,保证总和 = count)
|
||
// 20题模板: PROMPT(30%)→6题, LLM(30%)→6题, IDE(20%)→4题, DEV_PATTERN(20%)→4题
|
||
```
|
||
|
||
### 4.3 模板配置(数据库 `assessment_templates` 表)
|
||
|
||
```typescript
|
||
// dimensions 字段是 JSON 数组,前端「测评模板」页面可编辑
|
||
dimensions = [
|
||
{ name: "PROMPT", label: "Prompt Engineering", weight: 30 },
|
||
{ name: "LLM", label: "LLM Knowledge", weight: 30 },
|
||
{ name: "IDE", label: "IDE Proficiency", weight: 20 },
|
||
{ name: "DEV_PATTERN", label: "Dev Patterns", weight: 20 },
|
||
]
|
||
```
|
||
|
||
---
|
||
|
||
## 五、测试脚本
|
||
|
||
所有 Playwright 测试位于项目根目录:
|
||
|
||
| 脚本 | 用途 | 运行命令 |
|
||
|---|---|---|
|
||
| `test-systematic.mjs` | **142 项系统性测试**(身份/CRUD/权限/边界/UI) | `node test-systematic.mjs` |
|
||
| `test-e2e-full.mjs` | 94 项全角色 E2E 测试 | `node test-e2e-full.mjs` |
|
||
| `test-user-lifecycle.mjs` | 42 项用户生命周期测试 | `node test-user-lifecycle.mjs` |
|
||
| `test-multiround.mjs` | 考核多轮对话测试 | `node test-multiround.mjs` |
|
||
| `test-permission-flow.mjs` | 三层角色权限验证 | `node test-permission-flow.mjs` |
|
||
| `test-question-distribution.mjs` | 出题分布验证 | `node test-question-distribution.mjs` |
|
||
| `exam-organizer.mjs` | 考试组织者场景(创建考生+考核+查看结果) | `node exam-organizer.mjs` |
|
||
|
||
---
|
||
|
||
## 六、开发指南
|
||
|
||
### 6.1 启动项目
|
||
|
||
```bash
|
||
# 后端
|
||
cd server && node dist/main.js # 生产模式(需要先 nest build)
|
||
cd server && yarn start:dev # 开发模式(热加载)
|
||
|
||
# 前端
|
||
cd web && npx vite --port 13001 # 开发模式
|
||
cd web && npx vite build # 编译
|
||
|
||
# 基础设施(需要 Docker)
|
||
docker-compose up -d elasticsearch tika libreoffice
|
||
```
|
||
|
||
### 6.2 运行测试
|
||
|
||
```bash
|
||
cd /d/AuraK && node test-systematic.mjs # 142 项全面测试
|
||
```
|
||
|
||
### 6.3 重启服务
|
||
|
||
```bash
|
||
taskkill //F //IM node.exe 2>/dev/null # 杀所有 Node 进程
|
||
cd /d/AuraK/server && node dist/main.js & # 启动后端
|
||
cd /d/AuraK/web && npx vite --port 13001 & # 启动前端
|
||
```
|
||
|
||
### 6.4 数据库管理
|
||
|
||
```bash
|
||
# SQLite 文件路径
|
||
server/data/metadata.db
|
||
|
||
# 直接查询
|
||
cd /d/AuraK && node -e "
|
||
const sqlite3 = require('better-sqlite3');
|
||
const db = new sqlite3('server/data/metadata.db');
|
||
db.prepare('SELECT * FROM users').all();
|
||
db.close();
|
||
"
|
||
```
|
||
|
||
### 6.5 常见问题
|
||
|
||
- **端口占用**: `netstat -ano | grep 3001` → `taskkill //F //PID <pid>`
|
||
- **后端启动失败**: 确保从 `server/` 目录启动以加载 .env
|
||
- **Windows 杀进程**: 用 `taskkill //F //PID`,`kill` 不可靠
|
||
- **Dist 目录修改**: `server/dist/` 下的 .js 直接修改即可生效(无需 nest build)
|
||
|
||
---
|
||
|
||
## 七、代码规范
|
||
|
||
### 7.1 TypeScript 模式
|
||
|
||
- **实体定义**: TypeORM `@Entity` + `@Column` + 枚举类型
|
||
- **控制器**: `@Controller` + `@UseGuards(CombinedAuthGuard)` + `@Permission()`
|
||
- **服务**: `@Injectable` 类,方法名用动词开头
|
||
- **前端组件**: React FC + TypeScript interface props
|
||
|
||
### 7.2 权限装饰器使用
|
||
|
||
```typescript
|
||
// 后端 API — 在控制器方法上标注所需权限
|
||
@Post()
|
||
@UseGuards(PermissionsGuard)
|
||
@Permission('user:create')
|
||
async createUser(@Request() req, @Body() body: CreateUserDto) { ... }
|
||
|
||
// 前端 — 用 PermissionGate 组件或 usePermissions hook
|
||
<PermissionGate permission="user:create">
|
||
<Button>创建用户</Button>
|
||
</PermissionGate>
|
||
|
||
const { hasPermission } = usePermissions();
|
||
{hasPermission('user:view') && <UserList />}
|
||
```
|
||
|
||
### 7.3 通用 React 约定
|
||
|
||
- 使用 `cn()` 工具函数(tailwind-merge)合并 className
|
||
- 图标用 `lucide-react`
|
||
- 动画用 `framer-motion`
|
||
- API 调用用 `apiClient` 或原生 `fetch`
|
||
- 模态框用 `createPortal` + `AnimatePresence`
|
||
|
||
---
|
||
|
||
## 八、Playwright 测试指南
|
||
|
||
### 8.1 测试模式
|
||
|
||
```javascript
|
||
import { chromium } from 'playwright';
|
||
const browser = await chromium.launch({ headless: true });
|
||
const page = await browser.newPage({ viewport: { width: 1440, height: 900 } });
|
||
|
||
// 模拟 API 调用
|
||
const r = await fetch(`${API}/api/auth/login`, {
|
||
method: 'POST',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({ username, password }),
|
||
});
|
||
```
|
||
|
||
### 8.2 React 受控输入框操作(关键)⚠️
|
||
|
||
React 的受控 input/textarea 依赖 native setter 触发 `onChange`。**不要用 `type()` 或 `fill()`**,应使用:
|
||
|
||
```javascript
|
||
await page.evaluate((text) => {
|
||
const ta = document.querySelector('textarea');
|
||
if (!ta) return;
|
||
const setter = Object.getOwnPropertyDescriptor(HTMLTextAreaElement.prototype, 'value')?.set;
|
||
setter?.call(ta, text);
|
||
ta.dispatchEvent(new Event('input', { bubbles: true }));
|
||
}, '要输入的文字');
|
||
```
|
||
|
||
### 8.3 等待按钮可用
|
||
|
||
```javascript
|
||
await page.waitForFunction(() => {
|
||
const btn = document.querySelector('button:has(svg.lucide-send)');
|
||
return btn && !btn.disabled;
|
||
}, { timeout: 10000 });
|
||
```
|
||
|
||
### 8.4 等待 spinner 消失
|
||
|
||
```javascript
|
||
await page.waitForFunction(() => !document.querySelector('.animate-spin'), { timeout: 60000 });
|
||
```
|
||
|
||
### 8.5 检测考核题型
|
||
|
||
```javascript
|
||
const state = await page.evaluate(() => {
|
||
// 选择题选项按钮
|
||
const optionBtns = Array.from(document.querySelectorAll('button.w-full.text-left.px-5.py-4'))
|
||
.filter(b => !b.textContent?.includes('确认答案'));
|
||
// 简答题 textarea
|
||
const ta = document.querySelector('textarea');
|
||
return { choiceCount: optionBtns.length, hasTextarea: ta?.offsetParent !== null };
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
## 九、部署配置
|
||
|
||
### 端口
|
||
|
||
| 服务 | 端口 |
|
||
|---|---|
|
||
| 前端(开发) | 13001 |
|
||
| 前端(生产/Nginx) | 80/443 |
|
||
| 后端 API | 3001 |
|
||
| Elasticsearch | 9200 |
|
||
| Apache Tika | 9998 |
|
||
| LibreOffice | 8100 |
|
||
|
||
### 环境变量
|
||
|
||
`server/.env` 关键配置:
|
||
|
||
```
|
||
PORT=3001
|
||
DATABASE_PATH=./data/metadata.db
|
||
ELASTICSEARCH_HOST=http://127.0.0.1:9200
|
||
JWT_SECRET=your-secret
|
||
```
|
||
|
||
---
|
||
|
||
## 十、快速参考命令
|
||
|
||
```bash
|
||
# 启动
|
||
cd /d/AuraK/server && node dist/main.js &
|
||
cd /d/AuraK/web && npx vite --port 13001 &
|
||
|
||
# 后端编译
|
||
cd /d/AuraK/server && npx nest build
|
||
|
||
# 运行测试
|
||
cd /d/AuraK && node test-systematic.mjs
|
||
|
||
# 数据库查询
|
||
cd /d/AuraK && node -e "
|
||
const s=require('better-sqlite3');
|
||
const d=new s('server/data/metadata.db');
|
||
const r=d.prepare('...').all(); console.log(r); d.close();"
|
||
|
||
# 清理端口
|
||
taskkill //F //IM node.exe 2>/dev/null
|
||
|
||
# 前端编译
|
||
cd /d/AuraK/web && npx vite build
|
||
```
|