HR_DASHBOARD_PART3_IMPLEMENTATION.md 17 KB

人事板块首页分析与实现方案 - 第3部分:实施步骤

🚀 实施优先级和步骤

优先级划分

P0 - 最高优先级(立即实施)✅

  1. 职级分布统计 - 复用 Profile 表,无需新表
  2. 入离职趋势统计 - 复用 Profile 表,需要补充字段
  3. 新人列表 - 复用 Profile 表,需要补充导师字段

P1 - 高优先级(近期实施)🔥

  1. AI简历分析功能 - 创建 Resume + ResumeAnalysis 表
  2. 绩效对比分析 - 创建 PerformanceRecord 表
  3. 部门绩效总览 - 基于 Project 表统计

P2 - 中等优先级(后续实施)⭐

  1. 招聘流程跟踪 - 创建 RecruitmentProcess 表
  2. 离职原因分析 - 创建 ResignationRecord 表
  3. 新人跟进检查点 - 创建 OnboardingCheckpoint 表

📋 第一阶段:P0优先级实施

Step 1: 补充 Profile 表字段

需要添加的字段

Profile.data.hrData {
  // 现有字段
  employeeId: string,
  level: 'junior' | 'intermediate' | 'senior',
  hireDate: Date,
  employmentStatus: string,
  
  // 🆕 新增字段
  mentor: Pointer<Profile>,          // 导师(用于新人管理)
  resignationDate: Date,             // 离职日期
  probationEndDate: Date,            // 试用期结束日期
  convertedDate: Date                // 转正日期
}

实施代码

// dashboard.ts
async loadBasicStatistics() {
  // 1. 职级分布
  const rankDist = await this.loadRankDistribution();
  
  // 2. 入离职趋势
  const monthlyTrend = await this.loadMonthlyTrend();
  
  // 3. 新人列表
  const newbies = await this.loadNewbieList();
  
  return { rankDist, monthlyTrend, newbies };
}

// 职级分布(立即可用)
async loadRankDistribution() {
  const query = new Parse.Query('Profile');
  query.equalTo('isActivated', true);
  query.notEqualTo('isDeleted', true);
  query.equalTo('data.hrData.employmentStatus', 'active');
  
  const profiles = await query.find();
  
  const dist = { junior: 0, intermediate: 0, senior: 0 };
  profiles.forEach(p => {
    const level = p.get('data')?.hrData?.level || 'junior';
    dist[level]++;
  });
  
  return Object.entries(dist).map(([level, count]) => ({
    level: level === 'junior' ? '初级' : level === 'intermediate' ? '中级' : '高级',
    count,
    percentage: ((count / profiles.length) * 100).toFixed(1)
  }));
}

📋 第二阶段:P1高优先级实施

Step 2: 创建 AI 简历分析相关表

2.1 创建 Resume 表

# Parse Dashboard 操作步骤
1. 打开 Parse Dashboard
2. 选择数据库 → 创建新Class → "Resume"
3. 添加以下字段:

字段名                  类型          说明
candidateName         String       候选人姓名
contact              Object       联系方式
resumeText           String       简历文本
resumeFileUrl        File         简历文件
uploadDate           Date         上传日期
status               String       状态
education            Object       教育背景
experience           Object       工作经验
skills               Array        技能列表
projects             Array        项目经历
jobPosition          String       应聘职位
expectedSalary       Number       期望薪资

2.2 创建 ResumeAnalysis 表

字段名                  类型          说明
resume               Pointer       关联简历
analysisDate         Date         分析时间
overallScore         Number       综合评分
dimensions           Object       多维度评分
recommendation       Object       AI推荐
screeningResults     Array        筛选结果

2.3 实现 AI 分析服务

文件dashboard.ts

import { DoubaoAiService } from '../../../services/doubao-ai.service';

export class HRDashboard {
  constructor(
    private doubaoAi: DoubaoAiService
  ) {}
  
  // 上传并分析简历
  async uploadAndAnalyzeResume(file: File, jobPosition: string) {
    // 1. 读取简历文件
    const resumeText = await this.extractTextFromFile(file);
    
    // 2. 创建简历记录
    const Resume = Parse.Object.extend('Resume');
    const resume = new Resume();
    
    const parseFile = new Parse.File(file.name, file);
    await parseFile.save();
    
    resume.set('candidateName', this.extractNameFromResume(resumeText));
    resume.set('resumeText', resumeText);
    resume.set('resumeFileUrl', parseFile);
    resume.set('uploadDate', new Date());
    resume.set('status', 'pending');
    resume.set('jobPosition', jobPosition);
    
    await resume.save();
    
    // 3. 调用 AI 分析
    const analysisRequest = {
      resumeText,
      jobPosition,
      jobRequirements: this.getJobRequirements(jobPosition)
    };
    
    const aiResult = await this.doubaoAi
      .analyzeResume(analysisRequest)
      .toPromise();
    
    // 4. 保存分析结果
    const Analysis = Parse.Object.extend('ResumeAnalysis');
    const analysis = new Analysis();
    
    analysis.set('resume', resume);
    analysis.set('analysisDate', new Date());
    analysis.set('overallScore', aiResult.overallScore);
    analysis.set('dimensions', this.buildDimensionsObject(aiResult));
    analysis.set('recommendation', aiResult.recommendation);
    analysis.set('screeningResults', aiResult.screeningInfo);
    
    await analysis.save();
    
    // 5. 更新简历状态
    resume.set('status', 'analyzed');
    await resume.save();
    
    return { resume, analysis };
  }
  
  // 构建多维度评分对象
  buildDimensionsObject(aiResult: any) {
    return {
      education: this.extractDimension(aiResult, '学历'),
      skills: this.extractDimension(aiResult, '技能'),
      projectExperience: this.extractDimension(aiResult, '项目经验'),
      workExperience: this.extractDimension(aiResult, '工作经验'),
      capability: this.extractDimension(aiResult, '综合能力'),
      cultureFit: this.extractDimension(aiResult, '文化匹配')
    };
  }
  
  extractDimension(aiResult: any, dimensionName: string) {
    const dimension = aiResult.matchDimensions.find(d => 
      d.name.includes(dimensionName)
    );
    
    if (!dimension) {
      return {
        score: 0,
        level: 'low',
        detail: '暂无数据'
      };
    }
    
    return {
      score: dimension.score,
      level: dimension.level,
      detail: dimension.description || ''
    };
  }
  
  // 获取职位要求
  getJobRequirements(jobPosition: string): string[] {
    const requirementsMap = {
      '设计师': [
        '本科及以上学历,设计相关专业优先',
        '3年以上室内设计经验',
        '精通 Photoshop, Illustrator, Sketch, AutoCAD',
        '有住宅或商业空间设计经验',
        '良好的审美能力和创新思维',
        '良好的沟通能力和团队协作精神',
        '能承受工作压力,按时完成任务'
      ],
      '高级设计师': [
        '本科及以上学历,设计相关专业',
        '5年以上室内设计经验',
        '精通各类设计软件',
        '有大型项目独立设计经验',
        '优秀的设计能力和项目管理能力',
        '可以带领团队完成项目',
        '有成功案例和作品集'
      ],
      '客服专员': [
        '大专及以上学历',
        '1年以上客服经验',
        '熟练使用办公软件',
        '良好的沟通能力和服务意识',
        '耐心细致,抗压能力强'
      ]
    };
    
    return requirementsMap[jobPosition] || requirementsMap['设计师'];
  }
}

Step 3: 创建 PerformanceRecord 表并实现绩效分析

3.1 创建表结构

字段名                  类型          说明
profile              Pointer       关联员工
department           Pointer       部门
period               Object       时期(年月季度)
projectMetrics       Object       项目指标
capabilities         Object       能力评估
overallScore         Number       综合评分
rank                 String       排名
level                String       等级

3.2 实现绩效计算逻辑

// 自动生成月度绩效(定时任务,每月1日执行)
async generateMonthlyPerformance() {
  const lastMonth = this.getLastMonthPeriod();
  const designers = await this.getActiveDesigners();
  
  for (const designer of designers) {
    // 查询该设计师上月的项目
    const projectQuery = new Parse.Query('Project');
    projectQuery.equalTo('assignee', designer);
    projectQuery.greaterThanOrEqualTo('createdAt', lastMonth.start);
    projectQuery.lessThan('createdAt', lastMonth.end);
    
    const projects = await projectQuery.find();
    
    // 计算项目指标
    const metrics = {
      totalProjects: projects.length,
      completedProjects: projects.filter(p => 
        p.get('status') === 'completed'
      ).length,
      excellentProjects: projects.filter(p => 
        p.get('data')?.quality?.isExcellent
      ).length,
      overdueProjects: projects.filter(p => {
        const expected = p.get('data')?.timeline?.expectedDate;
        const actual = p.get('data')?.timeline?.actualDate;
        return actual && expected && actual > expected;
      }).length,
      averageQuality: this.calculateAverage(
        projects, p => p.get('data')?.quality?.score || 0
      ),
      averageSatisfaction: this.calculateAverage(
        projects, p => p.get('data')?.clientSatisfaction || 0
      )
    };
    
    // 计算能力评估(基于项目反馈)
    const capabilities = await this.calculateCapabilities(designer, projects);
    
    // 计算综合评分
    const overallScore = this.calculateOverallScore(metrics, capabilities);
    
    // 保存绩效记录
    const Performance = Parse.Object.extend('PerformanceRecord');
    const record = new Performance();
    
    record.set('profile', designer);
    record.set('department', designer.get('department'));
    record.set('period', {
      year: lastMonth.year,
      month: lastMonth.month,
      quarter: Math.ceil(lastMonth.month / 3)
    });
    record.set('projectMetrics', metrics);
    record.set('capabilities', capabilities);
    record.set('overallScore', overallScore);
    record.set('level', this.getLevel(overallScore));
    
    await record.save();
  }
  
  // 计算排名
  await this.calculateRankings(lastMonth);
}

// 计算综合评分
calculateOverallScore(metrics: any, capabilities: any): number {
  // 项目指标权重 60%
  const projectScore = (
    (metrics.completedProjects / metrics.totalProjects) * 20 +  // 完成率 20%
    (metrics.excellentProjects / metrics.totalProjects) * 15 +  // 优秀率 15%
    metrics.averageQuality * 0.15 +                            // 质量 15%
    metrics.averageSatisfaction * 0.10                         // 满意度 10%
  );
  
  // 能力指标权重 40%
  const capabilityScore = (
    capabilities.design * 0.10 +        // 设计能力 10%
    capabilities.communication * 0.08 + // 沟通能力 8%
    capabilities.execution * 0.10 +     // 执行能力 10%
    capabilities.innovation * 0.06 +    // 创新能力 6%
    capabilities.teamwork * 0.04 +      // 团队协作 4%
    capabilities.growth * 0.02          // 成长潜力 2%
  );
  
  return Math.round(projectScore + capabilityScore);
}

// 从项目反馈计算能力评估
async calculateCapabilities(designer: any, projects: any[]): Promise<any> {
  // 这里需要从项目反馈、客户评价等数据中提取
  // 暂时使用基础算法
  
  const capabilities = {
    design: 0,
    communication: 0,
    execution: 0,
    innovation: 0,
    teamwork: 0,
    growth: 0
  };
  
  if (projects.length === 0) {
    return capabilities;
  }
  
  // 从项目质量推断设计能力
  capabilities.design = this.calculateAverage(
    projects, p => p.get('data')?.quality?.score || 60
  );
  
  // 从客户满意度推断沟通能力
  capabilities.communication = this.calculateAverage(
    projects, p => p.get('data')?.clientSatisfaction || 70
  );
  
  // 从完成率推断执行能力
  const completionRate = projects.filter(p => 
    p.get('status') === 'completed'
  ).length / projects.length;
  capabilities.execution = completionRate * 100;
  
  // 从优秀作品率推断创新能力
  const excellentRate = projects.filter(p => 
    p.get('data')?.quality?.isExcellent
  ).length / projects.length;
  capabilities.innovation = excellentRate * 100;
  
  // 团队协作和成长潜力需要更复杂的评估
  capabilities.teamwork = 75;  // 默认值
  capabilities.growth = 70;    // 默认值
  
  return capabilities;
}

🔧 技术实现要点

1. AI 简历分析集成

doubao-ai.service.ts 增强

// 增加多维度分析逻辑
performIntelligentAnalysis(request: ResumeAnalysisRequest): ResumeAnalysisResponse {
  const resumeText = request.resumeText.toLowerCase();
  const requirements = request.jobRequirements;
  
  // 1. 学历维度分析
  const educationScore = this.analyzeEducation(resumeText);
  
  // 2. 技能维度分析
  const skillsAnalysis = this.analyzeSkills(resumeText, requirements);
  
  // 3. 项目经验分析
  const projectAnalysis = this.analyzeProjects(resumeText);
  
  // 4. 工作经验分析
  const workAnalysis = this.analyzeWorkExperience(resumeText);
  
  // 5. 综合能力分析
  const capabilityAnalysis = this.analyzeCapability(resumeText);
  
  // 6. 计算总分
  const overallScore = this.calculateOverallScore({
    education: educationScore,
    skills: skillsAnalysis.score,
    project: projectAnalysis.score,
    work: workAnalysis.score,
    capability: capabilityAnalysis.score
  });
  
  return {
    overallScore,
    matchDimensions: [
      {
        id: 1,
        name: '学历背景',
        score: educationScore.score,
        level: this.getLevel(educationScore.score),
        icon: 'school',
        description: educationScore.detail
      },
      {
        id: 2,
        name: '技能匹配',
        score: skillsAnalysis.score,
        level: this.getLevel(skillsAnalysis.score),
        icon: 'verified',
        description: `匹配${skillsAnalysis.matchedCount}/${skillsAnalysis.totalCount}项技能`
      },
      // ... 其他维度
    ],
    recommendation: this.generateRecommendation(overallScore),
    screeningInfo: this.generateScreeningInfo({
      education: educationScore,
      skills: skillsAnalysis,
      work: workAnalysis
    }),
    analysisTime: new Date()
  };
}

// 学历分析
analyzeEducation(resumeText: string) {
  const degrees = {
    '博士': 100,
    'phd': 100,
    '硕士': 90,
    'master': 90,
    '本科': 80,
    'bachelor': 80,
    '大专': 60,
    'college': 60
  };
  
  let score = 50;  // 默认分数
  let degree = '未知';
  
  for (const [key, value] of Object.entries(degrees)) {
    if (resumeText.includes(key)) {
      score = value;
      degree = key;
      break;
    }
  }
  
  return {
    score,
    degree,
    detail: `学历:${degree},评分:${score}`
  };
}

// 技能分析
analyzeSkills(resumeText: string, requirements: string[]) {
  const commonSkills = [
    'photoshop', 'ps', 'illustrator', 'ai', 
    'sketch', 'autocad', 'cad', '3dmax',
    'vray', 'sketchup', 'lumion', 'enscape'
  ];
  
  const matchedSkills = commonSkills.filter(skill => 
    resumeText.includes(skill)
  );
  
  const score = (matchedSkills.length / commonSkills.length) * 100;
  
  return {
    score: Math.round(score),
    matchedCount: matchedSkills.length,
    totalCount: commonSkills.length,
    matchedSkills,
    missingSkills: commonSkills.filter(s => !matchedSkills.includes(s))
  };
}

2. 图表渲染优化

使用 Chart.js

// dashboard.ts
import { Chart, ChartConfiguration } from 'chart.js';

@ViewChild('radarChart') radarCanvas: ElementRef;
@ViewChild('pieChart') pieCanvas: ElementRef;
@ViewChild('lineChart') lineCanvas: ElementRef;

ngAfterViewInit() {
  this.renderCharts();
}

renderCharts() {
  // 雷达图
  this.renderRadarChart();
  
  // 饼图
  this.renderPieChart();
  
  // 折线图
  this.renderLineChart();
}

renderRadarChart() {
  const ctx = this.radarCanvas.nativeElement.getContext('2d');
  
  new Chart(ctx, {
    type: 'radar',
    data: {
      labels: ['完成率', '优秀率', '满意度', '延期率'],
      datasets: this.departmentPerformance.map(dept => ({
        label: dept.department,
        data: [
          dept.completionRate,
          dept.excellentWorkRate,
          dept.satisfactionRate,
          100 - dept.overdueRate
        ],
        backgroundColor: this.getDepartmentColor(dept.department, 0.2),
        borderColor: this.getDepartmentColor(dept.department),
        borderWidth: 2
      }))
    },
    options: {
      scales: {
        r: {
          beginAtZero: true,
          max: 100
        }
      }
    }
  });
}

待续第4部分:测试方案和部署指南...