Files
aurak/docs/plans/2026-04-18-l1-talent-assessment-implementation-plan.md
T
Developer 0a9588abb7 feat: implement QuestionBank CRUD with pagination and template query
- Add pagination support to findAll (page, limit query params)
- Add findByTemplateId method to service
- Add GET /by-template/:templateId endpoint to controller
- Service already includes CRUD for QuestionBank and QuestionBankItem
2026-04-23 17:19:11 +08:00

11 KiB
Raw Blame History

L1人才育成评估系统实施计划

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: 实现L1人才育成评估系统,支持五门课程考核、多知识组关联、加权计分、证书生成

Architecture: 基于现有AuraK评估系统(LangGraph状态机)扩展,新增模板多知识组支持、维度计分、证书功能

Tech Stack: NestJS + TypeORM + LangGraph + React


Phase 1: 模板扩展(P0

Task 1.1: 扩展 AssessmentTemplate 实体

Files:

  • Modify: server/src/assessment/entities/assessment-template.entity.ts:1-80

Step 1: Add new columns

// Add after existing columns (line ~79)
@Column({ type: 'simple-json', name: 'linked_group_ids', nullable: true })
linkedGroupIds: string[];

@Column({ type: 'simple-json', name: 'weight_config', nullable: true })
weightConfig: {
  prompt: number;
  other: number;
};

@Column({ type: 'simple-json', name: 'difficulty_config', nullable: true })
difficultyConfig: {
  standard: number;
  advanced: number;
  specialist: number;
};

@Column({ type: 'int', name: 'question_count_min', default: 8 })
questionCountMin: number;

@Column({ type: 'int', name: 'question_count_max', default: 10 })
questionCountMax: number;

@Column({ type: 'int', name: 'passing_score', default: 90 })
passingScore: number;

Step 2: Commit

git add server/src/assessment/entities/assessment-template.entity.ts
git commit -m "feat: extend AssessmentTemplate with multi-group and weight config"

Task 1.2: 创建数据库迁移

Files:

  • Create: server/src/migrations/XXXXXX-add-template-extensions.sql

Step 1: Write migration

ALTER TABLE assessment_templates
ADD COLUMN linked_group_ids TEXT,
ADD COLUMN weight_config TEXT,
ADD COLUMN difficulty_config TEXT,
ADD COLUMN question_count_min INTEGER DEFAULT 8,
ADD COLUMN question_count_max INTEGER DEFAULT 10,
ADD COLUMN passing_score INTEGER DEFAULT 90;

Step 2: Test migration

cd server && npx typeorm query "SELECT linked_group_ids, weight_config FROM assessment_templates LIMIT 1"

Step 3: Commit

git add server/src/migrations/
git commit -m "db: add template extension columns"

Task 1.3: 更新 TemplateService CRUD

Files:

  • Modify: server/src/assessment/services/template.service.ts

Step 1: Add linkedGroupIds handling

// In create() method, add:
if (createTemplateDto.linkedGroupIds) {
  template.linkedGroupIds = createTemplateDto.linkedGroupIds;
}
if (createTemplateDto.weightConfig) {
  template.weightConfig = createTemplateDto.weightConfig;
}
// ... other fields

Step 2: Verify compilation

cd server && npx tsc --noEmit

Step 3: Commit

git add server/src/assessment/services/template.service.ts
git commit -m "feat: support linkedGroupIds in TemplateService"

Phase 2: 评估流程适配(P0

Task 2.1: 修改 QuestionGenerator 支持多知识组

Files:

  • Modify: server/src/assessment/assessment.service.ts:266-368 (startSession method)

Step 1: Support multi-group content retrieval

// In startSession(), replace single content retrieval with multi-group:
private async getMultiGroupContent(
  groupIds: string[],
  userId: string,
  tenantId: string,
): Promise<string> {
  const contents: string[] = [];
  for (const groupId of groupIds) {
    const files = await this.groupService.getGroupFiles(groupId, userId, tenantId);
    const content = files.map(f => f.content).join('\n\n---\n\n');
    contents.push(content);
  }
  return contents.join('\n\n');
}

Step 2: Pass content with dimension tag

// In getSessionContent, add dimension tag:
// === Document: [工作能力] ===
// ...content...

Step 3: Commit

git add server/src/assessment/assessment.service.ts
git commit -m "feat: support multi-group content retrieval"

Task 2.2: Generator 输出维度信息

Files:

  • Modify: server/src/assessment/graph/nodes/generator.node.ts:176-182

Step 1: Add dimension field

const mappedNewQuestions = newQuestions.map((q: any) => ({
  id: (existingQuestions.length + 1).toString(),
  questionText: q.question_text,
  keyPoints: q.key_points,
  difficulty: q.difficulty,
  basis: q.basis,
  dimension: q.dimension || this.inferDimension(knowledgeBaseContent), // NEW FIELD
}));

private inferDimension(content: string): string {
  // Detect dimension from content tags
  if (content.includes('[提示词]')) return 'prompt';
  if (content.includes('[LLM]')) return 'llm';
  // ... etc
}

Step 2: Commit

git add server/src/assessment/graph/nodes/generator.node.ts
git commit -m "feat: generator outputs dimension field"

Phase 3: 评分逻辑(P0

Task 3.1: DimensionScores 计算

Files:

  • Modify: server/src/assessment/assessment.service.ts:600-630 (score calculation)

Step 1: Calculate dimension scores

// Replace simple weighted score with dimension-aware calculation:
const calculateDimensionScores = (questions: any[], scores: Record<string, number>) => {
  const dimensionScores: Record<string, number> = {
    prompt: 0, llm: 0, ide: 0, devPattern: 0, workCapability: 0
  };
  const dimensionCounts: Record<string, number> = { prompt: 0, llm: 0, ide: 0, devPattern: 0, workCapability: 0 };

  questions.forEach((q, idx) => {
    const dim = q.dimension || 'workCapability';
    dimensionScores[dim] += scores[q.id || idx.toString()] || 0;
    dimensionCounts[dim]++;
  });

  // Average per dimension
  Object.keys(dimensionScores).forEach(d => {
    dimensionScores[d] = dimensionCounts[d] > 0
      ? dimensionScores[d] / dimensionCounts[d]
      : 0;
  });

  return dimensionScores;
};

Step 2: Commit

git add server/src/assessment/assessment.service.ts
git commit -m "feat: calculate dimension scores"

Task 3.2: 追问策略调整

Files:

  • Modify: server/src/assessment/graph/nodes/grader.node.ts:207

Step 1: Increase follow-up limit

// Current: currentFollowUpCount >= 1
// Change to:
if (currentFollowUpCount >= 2 || result.score >= 8 || saysIDontKnow) {
  shouldFollowUp = false;
}

Step 2: Commit

git add server/src/assessment/graph/nodes/grader.node.ts
git commit -m "feat: increase follow-up limit to 2"

Phase 4: 报告生成(P1

Task 4.1: 扩展 Session 存储 dimension 数据

Files:

  • Modify: server/src/assessment/entities/assessment-session.entity.ts

Step 1: Add dimension fields

@Column({ type: 'simple-json', name: 'dimension_scores', nullable: true })
dimensionScores: Record<string, number>;

@Column({ type: 'simple-json', name: 'radar_data', nullable: true })
radarData: Record<string, number>;

Step 2: Commit

git add server/src/assessment/entities/assessment-session.entity.ts
git commit -m "feat: add dimension scores to session entity"

Task 4.2: Analyzer 生成结构化报告

Files:

  • Modify: server/src/assessment/graph/nodes/analyzer.node.ts

Step 1: Generate dimension-aware report

// Modify system prompt to include:
const dimensionSummary = Object.entries(dimensionScores)
  .map(([dim, score]) => `${dim}: ${score}/10`)
  .join('\n');

const reportPrompt = `...
维度得分:
${dimensionSummary}

请在报告中包含:
1. 各维度得分分析
2. 薄弱环节识别
3. 针对性改进建议
`;

Step 2: Commit

git add server/src/assessment/graph/nodes/analyzer.node.ts
git commit -m "feat: generate structured report with dimension analysis"

Phase 5: 证书功能(P1

Task 5.1: 创建证书表

Files:

  • Create: server/src/assessment/entities/assessment-certificate.entity.ts

Step 1: Define entity

@Entity('assessment_certificates')
export class AssessmentCertificate {
  @PrimaryGeneratedColumn('uuid')
  id: string;

  @Column({ name: 'user_id' })
  userId: string;

  @Column({ name: 'session_id' })
  sessionId: string;

  @Column({ name: 'template_id' })
  templateId: string;

  @Column()
  level: string;

  @Column({ type: 'float', name: 'total_score' })
  totalScore: number;

  @Column({ name: 'qr_code', nullable: true })
  qrCode: string;

  @CreateDateColumn({ name: 'issued_at' })
  issuedAt: Date;
}

Step 2: Commit

git add server/src/assessment/entities/assessment-certificate.entity.ts
git commit -m "feat: create certificate entity"

Task 5.2: 证书生成API

Files:

  • Modify: server/src/assessment/assessment.controller.ts

Step 1: Add certificate endpoints

@Get('certificate/:sessionId')
async getCertificate(
  @Param('sessionId') sessionId: string,
  @Req() req,
): Promise<AssessmentCertificate> {
  // Generate or return existing certificate
}

@Get('certificate/:sessionId/download')
async downloadCertificate(
  @Param('sessionId') sessionId: string,
): Promise<StreamableFile> {
  // Generate PDF with jsPDF
}

Step 2: Commit

git add server/src/assessment/assessment.controller.ts
git commit -m "feat: add certificate endpoints"

Phase 6: 前端集成(P2

Task 6.1: 模板选择页面

Files:

  • Modify: web/components/views/AssessmentView.tsx

Step 1: Add template selection

// Add state:
const [templates, setTemplates] = useState<AssessmentTemplate[]>([]);

// Load templates on mount:
useEffect(() => {
  const loadTemplates = async () => {
    const res = await assessmentService.getTemplates();
    setTemplates(res.data);
  };
  loadTemplates();
}, []);

Step 2: Commit

git add web/components/views/AssessmentView.tsx
git commit -m "feat: add template selection to frontend"

Task 6.2: 报告页面的雷达图

Files:

  • Create: web/components/RadarChart.tsx

Step 1: Create component

export const RadarChart = ({ data }) => {
  // Use recharts or echarts for radar
  // Data: { prompt: 9.6, llm: 9.0, ide: 7.0, devPattern: 5.0, workCapability: 5.0 }
};

Step 2: Commit

git add web/components/RadarChart.tsx
git commit -m "feat: add radar chart component"

实施顺序

  1. Task 1.1 → 1.3 (模板扩展)
  2. Task 2.1 → 2.2 (评估流程)
  3. Task 3.1 → 3.2 (评分逻辑)
  4. Task 4.1 → 4.2 (报告)
  5. Task 5.1 → 5.2 (证书)
  6. Task 6.1 → 6.2 (前端)

每个Task约需1-2小时


依赖说明

  • Phase 1 (模板) → Phase 2 → Phase 3 → Phase 4 → Phase 5
  • 前端(Phase 6) 可在后端基本完成后并行开发
  • 测试贯穿每个Task

验收标准

  • 模板支持多知识组关联
  • 题目带dimension字段
  • 报告含dimensionScores和radarData
  • 可生成书PDF
  • 前端显示雷达图
  • E2E测试通过