一、业务背景与挑战

1.1 绩效管理的痛点

在企业人力资源管理中,绩效管理是最具挑战性的核心业务之一。传统的绩效管理模式存在诸多问题:

传统绩效评估的局限性

  • 单一视角:仅由上级评估,缺乏全面性
  • 主观偏差:评估者个人偏好影响评估结果
  • 反馈延迟:年度或季度评估,反馈周期过长
  • 缺乏数据:凭印象评估,缺乏客观数据支撑
  • 发展导向弱:重考核轻发展,难以促进员工成长
  • 形式主义:评估流于形式,难以反映真实绩效

相关链接

在这里插入图片描述

1.2 360度评估的价值

什么是360度绩效评估
360度绩效评估(360-Degree Feedback)是一种全方位的绩效评估方法,通过被评估者的上级、下级、同事、自评、客户等多维度角色进行评估,全面、客观地反映员工的工作表现和能力素质。

360度评估的价值

  1. 全面性:多视角评估,全方位了解员工表现
  2. 客观性:多人评估,减少个人偏见影响
  3. 发展性:帮助员工识别优势与改进空间
  4. 参与性:促进团队沟通,增强组织凝聚力
  5. 数据化:量化评估结果,便于分析和决策
  6. 持续性:支持持续评估,及时反馈改进

1.3 技术挑战

实现360度评估系统面临的技术挑战

  1. 评估模型设计复杂

    • 如何设计科学合理的评估维度?
    • 如何平衡不同评估者的权重?
    • 如何处理评估者之间的评分差异?
  2. 数据计算量大

    • 评估者数量多:一个员工可能有10+个评估者
    • 评估指标多:几十个评估指标
    • 计算复杂:权重计算、异常值处理、数据聚合
  3. 实时性要求高

    • 评估流程实时推进
    • 评估进度实时跟踪
    • 评估结果实时计算
  4. 数据隐私敏感

    • 评估结果涉及个人隐私
    • 需要严格的权限控制
    • 匿名评估的技术实现
  5. 业务流程复杂

    • 评估周期管理
    • 评估者选择策略
    • 异常情况处理(评估者离职、请假等)

二、整体架构设计

2.1 系统架构全景

┌─────────────────────────────────────────────────────────────────┐
│                    360度绩效评估系统                              │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                    评估配置管理层                          │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 评估项目配置 │  │ 评估维度配置 │  │ 评估指标配置    │  │   │
│  │  │(Project)     │  │(Dimension)   │  │(Indicator)      │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 评估流程配置 │  │ 权重规则配置 │  │ 评估者选择规则  │  │   │
│  │  │(Workflow)    │  │(Weight Rule) │  │(Selector Rule)  │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  └──────────────────────────────────────────────────────────┘   │
│                           │                                      │
│                           ▼                                      │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                    评估执行引擎层                          │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 评估任务调度 │  │ 评估流程引擎 │  │ 进度监控引擎    │  │   │
│  │  │(Scheduler)   │  │(Workflow)    │  │(Monitor)        │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 提醒通知引擎 │  │ 异常处理引擎 │  │ 权限控制引擎    │  │   │
│  │  │(Notifier)    │  │(Handler)      │  │(Permission)     │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  └──────────────────────────────────────────────────────────┘   │
│                           │                                      │
│                           ▼                                      │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                    数据计算分析层                          │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 评估数据聚合 │  │ 权重计算引擎 │  │ 异常值检测引擎  │  │   │
│  │  │(Aggregator)  │  │(Weight Calc) │  │(Outlier Detect) │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 评分计算引擎 │  │ 结果排名引擎 │  │ 报表生成引擎    │  │   │
│  │  │(Score Calc)  │  │(Ranking)     │  │(Report)         │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  └──────────────────────────────────────────────────────────┘   │
│                           │                                      │
│                           ▼                                      │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                    结果应用层                              │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 结果反馈查看 │  │ 绩效改进计划 │  │ 人才盘点分析    │  │   │
│  │  │(Feedback)    │  │(Improve Plan)│  │(Talent Review)  │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 数据可视化   │  │ 趋势分析     │  │ 对比分析        │  │   │
│  │  │(Dashboard)   │  │(Trend)        │  │(Comparison)     │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

在这里插入图片描述

2.2 核心领域模型

评估项目模型(EvaluationProject)

/**
 * 360度评估项目
 * 表示一次完整的360度评估活动
 */
@Data
@Entity
@Table(name = "hrm_evaluation_project")
public class EvaluationProject {
    /**
     * 项目ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;
    
    /**
     * 项目名称
     */
    private String name;
    
    /**
     * 项目编码
     */
    @Column(unique = true, nullable = false)
    private String code;
    
    /**
     * 项目描述
     */
    @Lob
    private String description;
    
    /**
     * 评估周期类型:ANNUAL(年度), QUARTERLY(季度), 
     *               MONTHLY(月度), ON_DEMAND(按需)
     */
    @Enumerated(EnumType.STRING)
    private PeriodType periodType;
    
    /**
     * 评估开始时间
     */
    private LocalDateTime startTime;
    
    /**
     * 评估结束时间
     */
    private LocalDateTime endTime;
    
    /**
     * 项目状态:DRAFT(草稿), PENDING(待开始), 
     *           IN_PROGRESS(进行中), COMPLETED(已完成), ARCHIVED(已归档)
     */
    @Enumerated(EnumType.STRING)
    private ProjectStatus status;
    
    /**
     * 适用范围类型:ALL(全员), DEPARTMENT(部门), 
     *              POSITION(岗位), EMPLOYEE(指定员工)
     */
    @Enumerated(EnumType.STRING)
    private ScopeType scopeType;
    
    /**
     * 适用范围配置(JSON格式)
     * 示例:{"departmentIds": ["dept001", "dept002"]}
     */
    @Lob
    private String scopeConfig;
    
    /**
     * 评估模板ID
     */
    private String templateId;
    
    /**
     * 是否匿名评估
     */
    private Boolean anonymous;
    
    /**
     * 是否启用自评
     */
    private Boolean enableSelfEvaluation;
    
    /**
     * 是否启用上级评估
     */
    private Boolean enableSuperiorEvaluation;
    
    /**
     * 是否启用下级评估
     */
    private Boolean enableSubordinateEvaluation;
    
    /**
     * 是否启用同事评估
     */
    private Boolean enablePeerEvaluation;
    
    /**
     * 是否启用客户评估
     */
    private Boolean enableCustomerEvaluation;
    
    /**
     * 评估者最小数量
     */
    private Integer minRaterCount;
    
    /**
     * 评估者最大数量
     */
    private Integer maxRaterCount;
    
    /**
     * 创建人
     */
    private String creator;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
    
    /**
     * 扩展配置(JSON格式)
     */
    @Lob
    private String extConfig;
}

/**
 * 周期类型枚举
 */
public enum PeriodType {
    /** 年度评估 */
    ANNUAL,
    
    /** 季度评估 */
    QUARTERLY,
    
    /** 月度评估 */
    MONTHLY,
    
    /** 按需评估 */
    ON_DEMAND
}

/**
 * 项目状态枚举
 */
public enum ProjectStatus {
    /** 草稿 */
    DRAFT,
    
    /** 待开始 */
    PENDING,
    
    /** 进行中 */
    IN_PROGRESS,
    
    /** 已完成 */
    COMPLETED,
    
    /** 已归档 */
    ARCHIVED
}

/**
 * 范围类型枚举
 */
public enum ScopeType {
    /** 全员 */
    ALL,
    
    /** 部门 */
    DEPARTMENT,
    
    /** 岗位 */
    POSITION,
    
    /** 指定员工 */
    EMPLOYEE
}

评估维度模型(EvaluationDimension)

/**
 * 评估维度
 * 表示评估的一级分类,如:工作业绩、工作能力、工作态度等
 */
@Data
@Entity
@Table(name = "hrm_evaluation_dimension")
public class EvaluationDimension {
    /**
     * 维度ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;
    
    /**
     * 维度编码
     */
    @Column(unique = true, nullable = false)
    private String code;
    
    /**
     * 维度名称
     */
    private String name;
    
    /**
     * 维度描述
     */
    @Lob
    private String description;
    
    /**
     * 所属模板ID
     */
    private String templateId;
    
    /**
     * 父维度ID(支持多级维度)
     */
    private String parentId;
    
    /**
     * 维度类型:CORE(核心维度), OPTIONAL(可选维度), CUSTOM(自定义维度)
     */
    @Enumerated(EnumType.STRING)
    private DimensionType type;
    
    /**
     * 维度权重(0-100)
     */
    private BigDecimal weight;
    
    /**
     * 最小分值
     */
    private Integer minScore;
    
    /**
     * 最大分值
     */
    private Integer maxScore;
    
    /**
     * 显示顺序
     */
    private Integer sortOrder;
    
    /**
     * 是否启用
     */
    private Boolean enabled;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
    
    /**
     * 扩展配置(JSON格式)
     */
    @Lob
    private String extConfig;
}

/**
 * 维度类型枚举
 */
public enum DimensionType {
    /** 核心维度 */
    CORE,
    
    /** 可选维度 */
    OPTIONAL,
    
    /** 自定义维度 */
    CUSTOM
}

评估指标模型(EvaluationIndicator)

/**
 * 评估指标
 * 表示具体的评估项,如:工作完成质量、团队协作能力等
 */
@Data
@Entity
@Table(name = "hrm_evaluation_indicator")
public class EvaluationIndicator {
    /**
     * 指标ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;
    
    /**
     * 指标编码
     */
    @Column(unique = true, nullable = false)
    private String code;
    
    /**
     * 指标名称
     */
    private String name;
    
    /**
     * 指标描述
     */
    @Lob
    private String description;
    
    /**
     * 所属维度ID
     */
    private String dimensionId;
    
    /**
     * 指标类型:QUANTITATIVE(定量), QUALITATIVE(定性)
     */
    @Enumerated(EnumType.STRING)
    private IndicatorType type;
    
    /**
     * 评分方式:SCORE(打分制), RANKING(等级制), 
     *           CHOICE(选择制), TEXT(文本制)
     */
    @Enumerated(EnumType.STRING)
    private ScoringType scoringType;
    
    /**
     * 指标权重(0-100)
     */
    private BigDecimal weight;
    
    /**
     * 最小分值
     */
    private Integer minScore;
    
    /**
     * 最大分值
     */
    private Integer maxScore;
    
    /**
     * 评分等级定义(JSON格式)
     * 示例:[{"level":5,"name":"优秀","description":"表现卓越"}]
     */
    @Lob
    private String scoreLevels;
    
    /**
     * 是否必填
     */
    private Boolean required;
    
    /**
     * 显示顺序
     */
    private Integer sortOrder;
    
    /**
     * 是否启用
     */
    private Boolean enabled;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
    
    /**
     * 扩展配置(JSON格式)
     */
    @Lob
    private String extConfig;
}

/**
 * 指标类型枚举
 */
public enum IndicatorType {
    /** 定量指标 */
    QUANTITATIVE,
    
    /** 定性指标 */
    QUALITATIVE
}

/**
 * 评分方式枚举
 */
public enum ScoringType {
    /** 打分制 */
    SCORE,
    
    /** 等级制 */
    RANKING,
    
    /** 选择制 */
    CHOICE,
    
    /** 文本制 */
    TEXT
}

评估记录模型(EvaluationRecord)

/**
 * 评估记录
 * 存储一次评估的完整数据
 */
@Data
@Entity
@Table(name = "hrm_evaluation_record")
public class EvaluationRecord {
    /**
     * 记录ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;
    
    /**
     * 评估项目ID
     */
    private String projectId;
    
    /**
     * 被评估者ID
     */
    private String evaluateeId;
    
    /**
     * 被评估者姓名
     */
    private String evaluateeName;
    
    /**
     * 评估者ID
     */
    private String raterId;
    
    /**
     * 评估者姓名
     */
    private String raterName;
    
    /**
     * 评估关系类型:SELF(自评), SUPERIOR(上级), 
     *                 SUBORDINATE(下级), PEER(同事), CUSTOMER(客户)
     */
    @Enumerated(EnumType.STRING)
    private RaterType raterType;
    
    /**
     * 是否匿名
     */
    private Boolean anonymous;
    
    /**
     * 评估状态:PENDING(待评估), IN_PROGRESS(评估中), 
     *           COMPLETED(已完成), SKIPPED(已跳过)
     */
    @Enumerated(EnumType.STRING)
    private EvaluationStatus status;
    
    /**
     * 评估开始时间
     */
    private LocalDateTime startTime;
    
    /**
     * 评估完成时间
     */
    private LocalDateTime completeTime;
    
    /**
     * 评估用时(秒)
     */
    private Long duration;
    
    /**
     * 总分
     */
    private BigDecimal totalScore;
    
    /**
     * 各维度得分(JSON格式)
     */
    @Lob
    private String dimensionScores;
    
    /**
     * 评估备注
     */
    @Lob
    private String remark;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
    
    /**
     * 扩展配置(JSON格式)
     */
    @Lob
    private String extConfig;
    
    /**
     * 评估明细列表
     */
    @OneToMany(mappedBy = "evaluationRecord", cascade = CascadeType.ALL)
    private List<EvaluationDetail> details;
}

/**
 * 评估关系类型枚举
 */
public enum RaterType {
    /** 自评 */
    SELF,
    
    /** 上级 */
    SUPERIOR,
    
    /** 下级 */
    SUBORDINATE,
    
    /** 同事 */
    PEER,
    
    /** 客户 */
    CUSTOMER
}

/**
 * 评估状态枚举
 */
public enum EvaluationStatus {
    /** 待评估 */
    PENDING,
    
    /** 评估中 */
    IN_PROGRESS,
    
    /** 已完成 */
    COMPLETED,
    
    /** 已跳过 */
    SKIPPED
}

评估明细模型(EvaluationDetail)

/**
 * 评估明细
 * 存储对单个指标的评分
 */
@Data
@Entity
@Table(name = "hrm_evaluation_detail")
public class EvaluationDetail {
    /**
     * 明细ID
     */
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;
    
    /**
     * 评估记录ID
     */
    private String evaluationRecordId;
    
    /**
     * 关联的评估记录
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "evaluation_record_id")
    private EvaluationRecord evaluationRecord;
    
    /**
     * 指标ID
     */
    private String indicatorId;
    
    /**
     * 维度ID
     */
    private String dimensionId;
    
    /**
     * 指标名称
     */
    private String indicatorName;
    
    /**
     * 维度名称
     */
    private String dimensionName;
    
    /**
     * 评分
     */
    private BigDecimal score;
    
    /**
     * 评语
     */
    @Lob
    private String comment;
    
    /**
     * 附件列表(JSON格式)
     */
    @Lob
    private String attachments;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}

2.3 数据模型关系图

┌────────────────────────────────────────────────────────────────┐
│                    评估数据模型关系                              │
├────────────────────────────────────────────────────────────────┤
│                                                                   │
│  EvaluationProject(评估项目)                                     │
│         │                                                        │
│         ├──► EvaluationTemplate(评估模板)                       │
│         │         │                                             │
│         │         ├──► EvaluationDimension(评估维度)             │
│         │         │         │                                   │
│         │         │         └──► EvaluationIndicator(评估指标) │
│         │                                                     │
│         └──► EvaluationRecord(评估记录)                        │
│                   │                                             │
│                   ├──► EvaluationDetail(评估明细)               │
│                   │         │                                   │
│                   │         └──► Indicator, Dimension          │
│                   │                                             │
│                   └──► EvaluationResult(评估结果)               │
│                             │                                   │
│                             ├──► ResultDimension(维度结果)       │
│                             └──► ImprovementPlan(改进计划)     │
│                                                                   │
└────────────────────────────────────────────────────────────────┘

三、核心模块实现

3.1 评估任务调度引擎

评估任务调度器

/**
 * 评估任务调度引擎
 * 负责创建和分配评估任务
 */
@Component
@Slf4j
public class EvaluationTaskScheduler {
    
    @Autowired
    private EvaluationProjectRepository projectRepository;
    
    @Autowired
    private EvaluationRecordRepository recordRepository;
    
    @Autowired
    private RaterSelectorEngine raterSelectorEngine;
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    /**
     * 启动评估项目
     * 
     * @param projectId 项目ID
     * @return 启动结果
     */
    @Transactional(rollbackFor = Exception.class)
    public LaunchResult launchProject(String projectId) {
        log.info("启动评估项目: projectId={}", projectId);
        
        try {
            // 1. 获取项目信息
            EvaluationProject project = projectRepository.findById(projectId)
                .orElseThrow(() -> new BusinessException("评估项目不存在"));
            
            // 2. 验证项目状态
            if (project.getStatus() != ProjectStatus.PENDING) {
                throw new BusinessException("项目状态不允许启动");
            }
            
            // 3. 获取被评估者列表
            List<Employee> evaluatees = getEvaluatees(project);
            log.info("获取被评估者列表: count={}", evaluatees.size());
            
            // 4. 为每个被评估者创建评估任务
            List<EvaluationRecord> allRecords = new ArrayList<>();
            int successCount = 0;
            int failCount = 0;
            
            for (Employee evaluatee : evaluatees) {
                try {
                    // 选择评估者
                    List<RaterSelectionResult> raterSelections = 
                        raterSelectorEngine.selectRaters(project, evaluatee);
                    
                    // 验证评估者数量
                    if (raterSelections.size() < project.getMinRaterCount()) {
                        log.warn("评估者数量不足: evaluateeId={}, raterCount={}", 
                            evaluatee.getId(), raterSelections.size());
                        failCount++;
                        continue;
                    }
                    
                    // 创建评估记录
                    for (RaterSelectionResult selection : raterSelections) {
                        EvaluationRecord record = createEvaluationRecord(
                            project, evaluatee, selection
                        );
                        allRecords.add(record);
                    }
                    
                    successCount++;
                    
                } catch (Exception e) {
                    log.error("创建评估任务失败: evaluateeId={}, error={}", 
                        evaluatee.getId(), e.getMessage());
                    failCount++;
                }
            }
            
            // 5. 批量保存评估记录
            if (!allRecords.isEmpty()) {
                recordRepository.saveAll(allRecords);
            }
            
            // 6. 更新项目状态
            project.setStatus(ProjectStatus.IN_PROGRESS);
            project.setUpdateTime(LocalDateTime.now());
            projectRepository.save(project);
            
            // 7. 发布项目启动事件
            eventPublisher.publishEvent(new ProjectLaunchedEvent(
                projectId, evaluatees.size(), allRecords.size()
            ));
            
            log.info("评估项目启动成功: projectId={}, successCount={}, failCount={}", 
                projectId, successCount, failCount);
            
            return LaunchResult.builder()
                .projectId(projectId)
                .success(successCount)
                .failed(failCount)
                .totalRecords(allRecords.size())
                .build();
            
        } catch (Exception e) {
            log.error("启动评估项目失败: projectId={}, error={}", projectId, e.getMessage());
            throw new BusinessException("启动评估项目失败", e);
        }
    }
    
    /**
     * 获取被评估者列表
     */
    private List<Employee> getEvaluatees(EvaluationProject project) {
        ScopeType scopeType = project.getScopeType();
        String scopeConfig = project.getScopeConfig();
        
        // 解析范围配置
        JSONObject scopeJson = JSON.parseObject(scopeConfig);
        
        switch (scopeType) {
            case ALL:
                // 获取所有在职员工
                return employeeRepository.findAllActiveEmployees();
                
            case DEPARTMENT:
                // 获取指定部门的员工
                List<String> departmentIds = scopeJson.getJSONArray("departmentIds")
                    .toJavaList(String.class);
                return employeeRepository.findByDepartmentIds(departmentIds);
                
            case POSITION:
                // 获取指定岗位的员工
                List<String> positionIds = scopeJson.getJSONArray("positionIds")
                    .toJavaList(String.class);
                return employeeRepository.findByPositionIds(positionIds);
                
            case EMPLOYEE:
                // 获取指定员工
                List<String> employeeIds = scopeJson.getJSONArray("employeeIds")
                    .toJavaList(String.class);
                return employeeRepository.findByEmployeeIds(employeeIds);
                
            default:
                throw new BusinessException("不支持的评估范围类型: " + scopeType);
        }
    }
    
    /**
     * 创建评估记录
     */
    private EvaluationRecord createEvaluationRecord(
        EvaluationProject project,
        Employee evaluatee,
        RaterSelectionResult selection
    ) {
        EvaluationRecord record = new EvaluationRecord();
        record.setProjectId(project.getId());
        record.setEvaluateeId(evaluatee.getId());
        record.setEvaluateeName(evaluatee.getName());
        record.setRaterId(selection.getRater().getId());
        record.setRaterName(selection.getRater().getName());
        record.setRaterType(selection.getRaterType());
        record.setAnonymous(project.getAnonymous() && selection.isAnonymous());
        record.setStatus(EvaluationStatus.PENDING);
        record.setCreateTime(LocalDateTime.now());
        
        return record;
    }
    
    /**
     * 发送评估提醒
     * 
     * @param recordId 评估记录ID
     */
    public void sendEvaluationReminder(String recordId) {
        EvaluationRecord record = recordRepository.findById(recordId)
            .orElseThrow(() -> new BusinessException("评估记录不存在"));
        
        // 发送提醒通知
        notificationService.sendEvaluationReminder(record);
        
        // 记录提醒日志
        reminderLogRepository.save(ReminderLog.builder()
            .recordId(recordId)
            .raterId(record.getRaterId())
            .reminderTime(LocalDateTime.now())
            .build());
    }
}

/**
 * 评估者选择结果
 */
@Data
@Builder
public class RaterSelectionResult {
    /**
     * 评估者
     */
    private Employee rater;
    
    /**
     * 评估关系类型
     */
    private RaterType raterType;
    
    /**
     * 是否匿名
     */
    private boolean anonymous;
    
    /**
     * 权重
     */
    private BigDecimal weight;
}

评估者选择引擎

/**
 * 评估者选择引擎
 * 根据配置规则选择合适的评估者
 */
@Component
@Slf4j
public class RaterSelectorEngine {
    
    @Autowired
    private EmployeeRepository employeeRepository;
    
    @Autowired
    private OrganizationService organizationService;
    
    /**
     * 选择评估者
     * 
     * @param project 评估项目
     * @param evaluatee 被评估者
     * @return 评估者选择结果列表
     */
    public List<RaterSelectionResult> selectRaters(
        EvaluationProject project,
        Employee evaluatee
    ) {
        List<RaterSelectionResult> results = new ArrayList<>();
        
        // 1. 自评
        if (project.getEnableSelfEvaluation()) {
            results.add(RaterSelectionResult.builder()
                .rater(evaluatee)
                .raterType(RaterType.SELF)
                .anonymous(false)
                .weight(BigDecimal.ZERO) // 自评不参与加权计算
                .build());
        }
        
        // 2. 上级评估
        if (project.getEnableSuperiorEvaluation()) {
            List<Employee> superiors = getSuperiors(evaluatee);
            for (Employee superior : superiors) {
                results.add(RaterSelectionResult.builder()
                    .rater(superior)
                    .raterType(RaterType.SUPERIOR)
                    .anonymous(false) // 上级评估不匿名
                    .weight(new BigDecimal("0.4")) // 上级权重40%
                    .build());
            }
        }
        
        // 3. 下级评估
        if (project.getEnableSubordinateEvaluation()) {
            List<Employee> subordinates = getSubordinates(evaluatee);
            int maxCount = Math.min(subordinates.size(), project.getMaxRaterCount() / 3);
            for (int i = 0; i < maxCount; i++) {
                results.add(RaterSelectionResult.builder()
                    .rater(subordinates.get(i))
                    .raterType(RaterType.SUBORDINATE)
                    .anonymous(true) // 下级评估匿名
                    .weight(new BigDecimal("0.2")) // 下级权重20%
                    .build());
            }
        }
        
        // 4. 同事评估
        if (project.getEnablePeerEvaluation()) {
            List<Employee> peers = getPeers(evaluatee);
            int maxCount = Math.min(peers.size(), project.getMaxRaterCount() / 3);
            // 随机选择同事
            Collections.shuffle(peers);
            for (int i = 0; i < maxCount; i++) {
                results.add(RaterSelectionResult.builder()
                    .rater(peers.get(i))
                    .raterType(RaterType.PEER)
                    .anonymous(true) // 同事评估匿名
                    .weight(new BigDecimal("0.2")) // 同事权重20%
                    .build());
            }
        }
        
        // 5. 客户评估
        if (project.getEnableCustomerEvaluation()) {
            List<Employee> customers = getCustomers(evaluatee);
            for (Employee customer : customers) {
                results.add(RaterSelectionResult.builder()
                    .rater(customer)
                    .raterType(RaterType.CUSTOMER)
                    .anonymous(true) // 客户评估匿名
                    .weight(new BigDecimal("0.2")) // 客户权重20%
                    .build());
            }
        }
        
        // 验证评估者数量
        if (results.size() < project.getMinRaterCount()) {
            log.warn("评估者数量不足: evaluateeId={}, count={}, min={}",
                evaluatee.getId(), results.size(), project.getMinRaterCount());
        }
        
        return results;
    }
    
    /**
     * 获取上级
     */
    private List<Employee> getSuperiors(Employee employee) {
        // 获取直接上级
        Employee directSuperior = employeeRepository.findById(employee.getSuperiorId())
            .orElse(null);
        
        if (directSuperior == null) {
            return Collections.emptyList();
        }
        
        List<Employee> superiors = new ArrayList<>();
        superiors.add(directSuperior);
        
        // 可选:获取更高级别的上级
        if (directSuperior.getSuperiorId() != null) {
            Employee grandSuperior = employeeRepository.findById(directSuperior.getSuperiorId())
                .orElse(null);
            if (grandSuperior != null) {
                superiors.add(grandSuperior);
            }
        }
        
        return superiors;
    }
    
    /**
     * 获取下级
     */
    private List<Employee> getSubordinates(Employee employee) {
        return employeeRepository.findBySuperiorId(employee.getId());
    }
    
    /**
     * 获取同事
     */
    private List<Employee> getPeers(Employee employee) {
        // 获取同一部门的其他同事
        List<Employee> departmentEmployees = employeeRepository
            .findByDepartmentId(employee.getDepartmentId());
        
        // 排除自己和上级下级关系
        return departmentEmployees.stream()
            .filter(e -> !e.getId().equals(employee.getId()))
            .filter(e -> !e.getSuperiorId().equals(employee.getId()))
            .filter(e -> !employee.getId().equals(e.getSuperiorId()))
            .collect(Collectors.toList());
    }
    
    /**
     * 获取客户
     */
    private List<Employee> getCustomers(Employee employee) {
        // 根据业务场景获取客户
        // 这里简化处理,实际可能需要从客户管理系统获取
        return Collections.emptyList();
    }
}

3.2 评估数据聚合引擎

评估数据聚合器

/**
 * 评估数据聚合引擎
 * 负责聚合多个评估者的评分,计算最终得分
 */
@Component
@Slf4j
public class EvaluationDataAggregator {
    
    @Autowired
    private EvaluationRecordRepository recordRepository;
    
    @Autowired
    private EvaluationDetailRepository detailRepository;
    
    /**
     * 聚合评估数据
     * 
     * @param projectId 项目ID
     * @param evaluateeId 被评估者ID
     * @return 聚合结果
     */
    @Transactional(rollbackFor = Exception.class)
    public EvaluationAggregationResult aggregateEvaluation(
        String projectId,
        String evaluateeId
    ) {
        log.info("开始聚合评估数据: projectId={}, evaluateeId={}", projectId, evaluateeId);
        
        try {
            // 1. 获取所有已完成的评估记录
            List<EvaluationRecord> records = recordRepository
                .findByProjectIdAndEvaluateeIdAndStatus(
                    projectId, evaluateeId, EvaluationStatus.COMPLETED
                );
            
            if (records.isEmpty()) {
                log.warn("没有已完成的评估记录: projectId={}, evaluateeId={}", 
                    projectId, evaluateeId);
                return EvaluationAggregationResult.empty();
            }
            
            // 2. 聚合维度得分
            Map<String, DimensionAggregation> dimensionAggregations = 
                aggregateDimensionScores(records);
            
            // 3. 聚合指标得分
            Map<String, IndicatorAggregation> indicatorAggregations = 
                aggregateIndicatorScores(records);
            
            // 4. 按评估关系类型聚合
            Map<RaterType, RaterTypeAggregation> raterTypeAggregations = 
                aggregateByRaterType(records);
            
            // 5. 计算总分
            BigDecimal totalScore = calculateTotalScore(dimensionAggregations);
            
            // 6. 聚合评语
            List<String> comments = aggregateComments(records);
            
            // 7. 构建聚合结果
            EvaluationAggregationResult result = EvaluationAggregationResult.builder()
                .projectId(projectId)
                .evaluateeId(evaluateeId)
                .totalScore(totalScore)
                .dimensionAggregations(new ArrayList<>(dimensionAggregations.values()))
                .indicatorAggregations(new ArrayList<>(indicatorAggregations.values()))
                .raterTypeAggregations(new ArrayList<>(raterTypeAggregations.values()))
                .comments(comments)
                .raterCount(records.size())
                .aggregateTime(LocalDateTime.now())
                .build();
            
            log.info("评估数据聚合完成: projectId={}, evaluateeId={}, totalScore={}", 
                projectId, evaluateeId, totalScore);
            
            return result;
            
        } catch (Exception e) {
            log.error("聚合评估数据失败: projectId={}, evaluateeId={}, error={}", 
                projectId, evaluateeId, e.getMessage());
            throw new AggregationException("聚合评估数据失败", e);
        }
    }
    
    /**
     * 聚合维度得分
     */
    private Map<String, DimensionAggregation> aggregateDimensionScores(
        List<EvaluationRecord> records
    ) {
        Map<String, DimensionAggregation> aggregations = new LinkedHashMap<>();
        
        // 按维度分组聚合
        for (EvaluationRecord record : records) {
            String dimensionScores = record.getDimensionScores();
            if (StringUtils.isBlank(dimensionScores)) {
                continue;
            }
            
            // 解析维度得分
            JSONObject scoresJson = JSON.parseObject(dimensionScores);
            
            for (String dimensionId : scoresJson.keySet()) {
                BigDecimal score = scoresJson.getBigDecimal(dimensionId);
                
                DimensionAggregation aggregation = aggregations.computeIfAbsent(
                    dimensionId,
                    k -> DimensionAggregation.builder()
                        .dimensionId(dimensionId)
                        .scores(new ArrayList<>())
                        .build()
                );
                
                aggregation.getScores().add(score);
            }
        }
        
        // 计算统计值
        for (DimensionAggregation aggregation : aggregations.values()) {
            List<BigDecimal> scores = aggregation.getScores();
            
            if (!scores.isEmpty()) {
                // 计算平均分(加权平均)
                BigDecimal avgScore = calculateWeightedAverage(scores);
                aggregation.setAverageScore(avgScore);
                
                // 计算最高分
                BigDecimal maxScore = scores.stream().max(BigDecimal::compareTo)
                    .orElse(BigDecimal.ZERO);
                aggregation.setMaxScore(maxScore);
                
                // 计算最低分
                BigDecimal minScore = scores.stream().min(BigDecimal::compareTo)
                    .orElse(BigDecimal.ZERO);
                aggregation.setMinScore(minScore);
                
                // 计算标准差
                BigDecimal stdDev = calculateStandardDeviation(scores, avgScore);
                aggregation.setStandardDeviation(stdDev);
            }
        }
        
        return aggregations;
    }
    
    /**
     * 聚合指标得分
     */
    private Map<String, IndicatorAggregation> aggregateIndicatorScores(
        List<EvaluationRecord> records
    ) {
        Map<String, IndicatorAggregation> aggregations = new LinkedHashMap<>();
        
        // 获取所有评估明细
        List<String> recordIds = records.stream()
            .map(EvaluationRecord::getId)
            .collect(Collectors.toList());
        
        List<EvaluationDetail> details = detailRepository.findByRecordIds(recordIds);
        
        // 按指标分组聚合
        Map<String, List<EvaluationDetail>> groupedDetails = details.stream()
            .collect(Collectors.groupingBy(EvaluationDetail::getIndicatorId));
        
        for (Map.Entry<String, List<EvaluationDetail>> entry : groupedDetails.entrySet()) {
            String indicatorId = entry.getKey();
            List<EvaluationDetail> indicatorDetails = entry.getValue();
            
            // 提取评分
            List<BigDecimal> scores = indicatorDetails.stream()
                .map(EvaluationDetail::getScore)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
            
            if (scores.isEmpty()) {
                continue;
            }
            
            IndicatorAggregation aggregation = IndicatorAggregation.builder()
                .indicatorId(indicatorId)
                .indicatorName(indicatorDetails.get(0).getIndicatorName())
                .dimensionId(indicatorDetails.get(0).getDimensionId())
                .dimensionName(indicatorDetails.get(0).getDimensionName())
                .scores(scores)
                .build();
            
            // 计算统计值
            BigDecimal avgScore = calculateWeightedAverage(scores);
            aggregation.setAverageScore(avgScore);
            
            BigDecimal maxScore = scores.stream().max(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);
            aggregation.setMaxScore(maxScore);
            
            BigDecimal minScore = scores.stream().min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);
            aggregation.setMinScore(minScore);
            
            aggregations.put(indicatorId, aggregation);
        }
        
        return aggregations;
    }
    
    /**
     * 按评估关系类型聚合
     */
    private Map<RaterType, RaterTypeAggregation> aggregateByRaterType(
        List<EvaluationRecord> records
    ) {
        Map<RaterType, RaterTypeAggregation> aggregations = new LinkedHashMap<>();
        
        // 按评估关系类型分组
        Map<RaterType, List<EvaluationRecord>> groupedRecords = records.stream()
            .collect(Collectors.groupingBy(EvaluationRecord::getRaterType));
        
        for (Map.Entry<RaterType, List<EvaluationRecord>> entry : groupedRecords.entrySet()) {
            RaterType raterType = entry.getKey();
            List<EvaluationRecord> typeRecords = entry.getValue();
            
            // 提取总分
            List<BigDecimal> scores = typeRecords.stream()
                .map(EvaluationRecord::getTotalScore)
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
            
            if (scores.isEmpty()) {
                continue;
            }
            
            // 计算平均分
            BigDecimal avgScore = scores.stream()
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(new BigDecimal(scores.size()), 2, RoundingMode.HALF_UP);
            
            RaterTypeAggregation aggregation = RaterTypeAggregation.builder()
                .raterType(raterType)
                .raterCount(typeRecords.size())
                .averageScore(avgScore)
                .scores(scores)
                .build();
            
            aggregations.put(raterType, aggregation);
        }
        
        return aggregations;
    }
    
    /**
     * 计算总分
     */
    private BigDecimal calculateTotalScore(
        Map<String, DimensionAggregation> dimensionAggregations
    ) {
        // 获取维度权重配置
        // 这里简化处理,实际应从配置获取
        Map<String, BigDecimal> dimensionWeights = getDimensionWeights();
        
        BigDecimal totalScore = BigDecimal.ZERO;
        BigDecimal totalWeight = BigDecimal.ZERO;
        
        for (DimensionAggregation aggregation : dimensionAggregations.values()) {
            BigDecimal weight = dimensionWeights.getOrDefault(
                aggregation.getDimensionId(),
                new BigDecimal("1.0")
            );
            
            totalScore = totalScore.add(
                aggregation.getAverageScore().multiply(weight)
            );
            totalWeight = totalWeight.add(weight);
        }
        
        // 归一化
        if (totalWeight.compareTo(BigDecimal.ZERO) > 0) {
            totalScore = totalScore.divide(totalWeight, 2, RoundingMode.HALF_UP);
        }
        
        return totalScore;
    }
    
    /**
     * 聚合评语
     */
    private List<String> aggregateComments(List<EvaluationRecord> records) {
        List<String> comments = new ArrayList<>();
        
        // 获取所有评估明细
        List<String> recordIds = records.stream()
            .map(EvaluationRecord::getId)
            .collect(Collectors.toList());
        
        List<EvaluationDetail> details = detailRepository.findByRecordIds(recordIds);
        
        // 提取评语
        for (EvaluationDetail detail : details) {
            if (StringUtils.isNotBlank(detail.getComment())) {
                comments.add(detail.getComment());
            }
        }
        
        return comments;
    }
    
    /**
     * 计算加权平均
     */
    private BigDecimal calculateWeightedAverage(List<BigDecimal> values) {
        if (values.isEmpty()) {
            return BigDecimal.ZERO;
        }
        
        BigDecimal sum = values.stream()
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        return sum.divide(
            new BigDecimal(values.size()),
            2,
            RoundingMode.HALF_UP
        );
    }
    
    /**
     * 计算标准差
     */
    private BigDecimal calculateStandardDeviation(
        List<BigDecimal> values,
        BigDecimal mean
    ) {
        if (values.isEmpty()) {
            return BigDecimal.ZERO;
        }
        
        // 计算方差
        BigDecimal variance = values.stream()
            .map(value -> value.subtract(mean).pow(2))
            .reduce(BigDecimal.ZERO, BigDecimal::add)
            .divide(new BigDecimal(values.size()), 2, RoundingMode.HALF_UP);
        
        // 计算标准差(方差开方)
        return new BigDecimal(Math.sqrt(variance.doubleValue()))
            .setScale(2, RoundingMode.HALF_UP);
    }
    
    /**
     * 获取维度权重
     */
    private Map<String, BigDecimal> getDimensionWeights() {
        // 从配置或数据库获取
        // 这里返回默认权重
        Map<String, BigDecimal> weights = new HashMap<>();
        weights.put("WORK_PERFORMANCE", new BigDecimal("0.4"));    // 工作业绩40%
        weights.put("WORK_ABILITY", new BigDecimal("0.3"));       // 工作能力30%
        weights.put("WORK_ATTITUDE", new BigDecimal("0.2"));       // 工作态度20%
        weights.put("TEAM_COLLABORATION", new BigDecimal("0.1"));  // 团队协作10%
        return weights;
    }
}

/**
 * 评估聚合结果
 */
@Data
@Builder
public class EvaluationAggregationResult {
    /**
     * 项目ID
     */
    private String projectId;
    
    /**
     * 被评估者ID
     */
    private String evaluateeId;
    
    /**
     * 总分
     */
    private BigDecimal totalScore;
    
    /**
     * 维度聚合结果列表
     */
    private List<DimensionAggregation> dimensionAggregations;
    
    /**
     * 指标聚合结果列表
     */
    private List<IndicatorAggregation> indicatorAggregations;
    
    /**
     * 评估关系类型聚合结果列表
     */
    private List<RaterTypeAggregation> raterTypeAggregations;
    
    /**
     * 评语列表
     */
    private List<String> comments;
    
    /**
     * 评估者数量
     */
    private Integer raterCount;
    
    /**
     * 聚合时间
     */
    private LocalDateTime aggregateTime;
    
    public static EvaluationAggregationResult empty() {
        return EvaluationAggregationResult.builder()
            .totalScore(BigDecimal.ZERO)
            .dimensionAggregations(Collections.emptyList())
            .indicatorAggregations(Collections.emptyList())
            .raterTypeAggregations(Collections.emptyList())
            .comments(Collections.emptyList())
            .raterCount(0)
            .build();
    }
}

/**
 * 维度聚合结果
 */
@Data
@Builder
public class DimensionAggregation {
    /**
     * 维度ID
     */
    private String dimensionId;
    
    /**
     * 维度名称
     */
    private String dimensionName;
    
    /**
     * 评分列表
     */
    private List<BigDecimal> scores;
    
    /**
     * 平均分
     */
    private BigDecimal averageScore;
    
    /**
     * 最高分
     */
    private BigDecimal maxScore;
    
    /**
     * 最低分
     */
    private BigDecimal minScore;
    
    /**
     * 标准差
     */
    private BigDecimal standardDeviation;
}

/**
 * 指标聚合结果
 */
@Data
@Builder
public class IndicatorAggregation {
    /**
     * 指标ID
     */
    private String indicatorId;
    
    /**
     * 指标名称
     */
    private String indicatorName;
    
    /**
     * 维度ID
     */
    private String dimensionId;
    
    /**
     * 维度名称
     */
    private String dimensionName;
    
    /**
     * 评分列表
     */
    private List<BigDecimal> scores;
    
    /**
     * 平均分
     */
    private BigDecimal averageScore;
    
    /**
     * 最高分
     */
    private BigDecimal maxScore;
    
    /**
     * 最低分
     */
    private BigDecimal minScore;
}

/**
 * 评估关系类型聚合结果
 */
@Data
@Builder
public class RaterTypeAggregation {
    /**
     * 评估关系类型
     */
    private RaterType raterType;
    
    /**
     * 评估者数量
     */
    private Integer raterCount;
    
    /**
     * 平均分
     */
    private BigDecimal averageScore;
    
    /**
     * 评分列表
     */
    private List<BigDecimal> scores;
}

3.3 异常值检测与处理

异常值检测引擎

/**
 * 评估异常值检测引擎
 * 检测和处理异常的评估数据
 */
@Component
@Slf4j
public class EvaluationOutlierDetector {
    
    /**
     * 检测异常值
     * 
     * @param values 数值列表
     * @return 异常值检测结果
     */
    public OutlierDetectionResult detectOutliers(List<BigDecimal> values) {
        if (values == null || values.isEmpty()) {
            return OutlierDetectionResult.empty();
        }
        
        OutlierDetectionResult result = new OutlierDetectionResult();
        result.setTotalCount(values.size());
        
        // 1. 使用IQR方法检测异常值
        List<BigDecimal> iqrOutliers = detectByIQR(values);
        result.setIqrOutliers(iqrOutliers);
        
        // 2. 使用Z-Score方法检测异常值
        List<BigDecimal> zScoreOutliers = detectByZScore(values);
        result.setZScoreOutliers(zScoreOutliers);
        
        // 3. 合并异常值
        Set<BigDecimal> allOutliers = new HashSet<>();
        allOutliers.addAll(iqrOutliers);
        allOutliers.addAll(zScoreOutliers);
        result.setAllOutliers(new ArrayList<>(allOutliers));
        
        // 4. 计算异常值比例
        double outlierRatio = (double) allOutliers.size() / values.size();
        result.setOutlierRatio(outlierRatio);
        
        return result;
    }
    
    /**
     * 使用IQR(四分位距)方法检测异常值
     * 
     * @param values 数值列表
     * @return 异常值列表
     */
    private List<BigDecimal> detectByIQR(List<BigDecimal> values) {
        List<BigDecimal> sortedValues = new ArrayList<>(values);
        Collections.sort(sortedValues);
        
        // 计算四分位数
        int n = sortedValues.size();
        BigDecimal q1 = sortedValues.get(n / 4);
        BigDecimal q3 = sortedValues.get(3 * n / 4);
        
        // 计算IQR
        BigDecimal iqr = q3.subtract(q1);
        
        // 计算上下限
        BigDecimal lowerBound = q1.subtract(iqr.multiply(new BigDecimal("1.5")));
        BigDecimal upperBound = q3.add(iqr.multiply(new BigDecimal("1.5")));
        
        // 找出异常值
        List<BigDecimal> outliers = sortedValues.stream()
            .filter(value -> value.compareTo(lowerBound) < 0 || 
                            value.compareTo(upperBound) > 0)
            .collect(Collectors.toList());
        
        log.debug("IQR异常值检测: count={}, outliers={}", n, outliers.size());
        
        return outliers;
    }
    
    /**
     * 使用Z-Score方法检测异常值
     * 
     * @param values 数值列表
     * @return 异常值列表
     */
    private List<BigDecimal> detectByZScore(List<BigDecimal> values) {
        // 计算平均值
        BigDecimal mean = values.stream()
            .reduce(BigDecimal.ZERO, BigDecimal::add)
            .divide(new BigDecimal(values.size()), 2, RoundingMode.HALF_UP);
        
        // 计算标准差
        BigDecimal variance = values.stream()
            .map(value -> value.subtract(mean).pow(2))
            .reduce(BigDecimal.ZERO, BigDecimal::add)
            .divide(new BigDecimal(values.size()), 2, RoundingMode.HALF_UP);
        
        BigDecimal stdDev = new BigDecimal(Math.sqrt(variance.doubleValue()));
        
        // 计算Z-Score阈值(通常使用3作为阈值)
        BigDecimal threshold = new BigDecimal("3.0");
        
        // 找出异常值
        List<BigDecimal> outliers = new ArrayList<>();
        for (BigDecimal value : values) {
            BigDecimal zScore = value.subtract(mean)
                .divide(stdDev, 2, RoundingMode.HALF_UP);
            
            if (zScore.abs().compareTo(threshold) > 0) {
                outliers.add(value);
            }
        }
        
        log.debug("Z-Score异常值检测: count={}, outliers={}", values.size(), outliers.size());
        
        return outliers;
    }
    
    /**
     * 处理异常值
     * 
     * @param values 原始数值列表
     * @param strategy 处理策略
     * @return 处理后的数值列表
     */
    public List<BigDecimal> handleOutliers(
        List<BigDecimal> values,
        OutlierHandlingStrategy strategy
    ) {
        OutlierDetectionResult detectionResult = detectOutliers(values);
        List<BigDecimal> outliers = detectionResult.getAllOutliers();
        
        if (outliers.isEmpty()) {
            return values;
        }
        
        switch (strategy) {
            case REMOVE:
                // 移除异常值
                return values.stream()
                    .filter(value -> !outliers.contains(value))
                    .collect(Collectors.toList());
                
            case REPLACE_WITH_MEAN:
                // 用平均值替换异常值
                BigDecimal mean = values.stream()
                    .filter(value -> !outliers.contains(value))
                    .reduce(BigDecimal.ZERO, BigDecimal::add)
                    .divide(
                        new BigDecimal(values.size() - outliers.size()),
                        2,
                        RoundingMode.HALF_UP
                    );
                
                return values.stream()
                    .map(value -> outliers.contains(value) ? mean : value)
                    .collect(Collectors.toList());
                
            case REPLACE_WITH_MEDIAN:
                // 用中位数替换异常值
                List<BigDecimal> normalValues = values.stream()
                    .filter(value -> !outliers.contains(value))
                    .sorted()
                    .collect(Collectors.toList());
                
                BigDecimal median = normalValues.get(normalValues.size() / 2);
                
                return values.stream()
                    .map(value -> outliers.contains(value) ? median : value)
                    .collect(Collectors.toList());
                
            case CAP:
                // 用上下限替换异常值
                List<BigDecimal> sortedValues = new ArrayList<>(values);
                Collections.sort(sortedValues);
                
                BigDecimal lowerBound = sortedValues.get(1); // 使用次小值作为下限
                BigDecimal upperBound = sortedValues.get(sortedValues.size() - 2); // 使用次大值作为上限
                
                return values.stream()
                    .map(value -> {
                        if (value.compareTo(lowerBound) < 0) {
                            return lowerBound;
                        } else if (value.compareTo(upperBound) > 0) {
                            return upperBound;
                        } else {
                            return value;
                        }
                    })
                    .collect(Collectors.toList());
                
            default:
                return values;
        }
    }
}

/**
 * 异常值检测结果
 */
@Data
public class OutlierDetectionResult {
    /**
     * 总数量
     */
    private Integer totalCount;
    
    /**
     * IQR方法检测出的异常值
     */
    private List<BigDecimal> iqrOutliers;
    
    /**
     * Z-Score方法检测出的异常值
     */
    private List<BigDecimal> zScoreOutliers;
    
    /**
     * 所有异常值
     */
    private List<BigDecimal> allOutliers;
    
    /**
     * 异常值比例
     */
    private Double outlierRatio;
    
    public static OutlierDetectionResult empty() {
        OutlierDetectionResult result = new OutlierDetectionResult();
        result.setTotalCount(0);
        result.setIqrOutliers(Collections.emptyList());
        result.setZScoreOutliers(Collections.emptyList());
        result.setAllOutliers(Collections.emptyList());
        result.setOutlierRatio(0.0);
        return result;
    }
}

/**
 * 异常值处理策略枚举
 */
public enum OutlierHandlingStrategy {
    /** 移除异常值 */
    REMOVE,
    
    /** 用平均值替换异常值 */
    REPLACE_WITH_MEAN,
    
    /** 用中位数替换异常值 */
    REPLACE_WITH_MEDIAN,
    
    /** 用上下限替换异常值 */
    CAP,
    
    /** 不处理 */
    NONE
}

3.4 评估结果排名与分析

评估结果排名引擎

/**
 * 评估结果排名引擎
 * 对评估结果进行排名和对比分析
 */
@Component
@Slf4j
public class EvaluationRankingEngine {
    
    @Autowired
    private EvaluationResultRepository resultRepository;
    
    /**
     * 计算排名
     * 
     * @param projectId 项目ID
     * @return 排名结果
     */
    public RankingResult calculateRanking(String projectId) {
        log.info("开始计算评估排名: projectId={}", projectId);
        
        // 1. 获取所有评估结果
        List<EvaluationResult> results = resultRepository.findByProjectId(projectId);
        
        if (results.isEmpty()) {
            return RankingResult.empty();
        }
        
        // 2. 按总分排序
        List<EvaluationResult> sortedResults = results.stream()
            .sorted(Comparator.comparing(EvaluationResult::getTotalScore).reversed())
            .collect(Collectors.toList());
        
        // 3. 计算排名
        List<RankingItem> rankingItems = new ArrayList<>();
        int rank = 1;
        
        for (int i = 0; i < sortedResults.size(); i++) {
            EvaluationResult result = sortedResults.get(i);
            
            // 处理并列排名
            if (i > 0) {
                EvaluationResult prevResult = sortedResults.get(i - 1);
                if (result.getTotalScore().compareTo(prevResult.getTotalScore()) == 0) {
                    // 分数相同,排名相同
                    // rank保持不变
                } else {
                    // 分数不同,排名更新
                    rank = i + 1;
                }
            }
            
            RankingItem item = RankingItem.builder()
                .rank(rank)
                .evaluateeId(result.getEvaluateeId())
                .evaluateeName(result.getEvaluateeName())
                .departmentId(result.getDepartmentId())
                .departmentName(result.getDepartmentName())
                .positionId(result.getPositionId())
                .positionName(result.getPositionName())
                .totalScore(result.getTotalScore())
                .dimensionScores(result.getDimensionScores())
                .build();
            
            rankingItems.add(item);
        }
        
        // 4. 计算百分位排名
        for (RankingItem item : rankingItems) {
            int percentile = calculatePercentile(rank, sortedResults.size());
            item.setPercentile(percentile);
        }
        
        // 5. 构建排名结果
        RankingResult rankingResult = RankingResult.builder()
            .projectId(projectId)
            .rankingItems(rankingItems)
            .totalCount(sortedResults.size())
            .averageScore(calculateAverageScore(sortedResults))
            .medianScore(calculateMedianScore(sortedResults))
            .maxScore(sortedResults.get(0).getTotalScore())
            .minScore(sortedResults.get(sortedResults.size() - 1).getTotalScore())
            .build();
        
        log.info("评估排名计算完成: projectId={}, count={}", projectId, sortedResults.size());
        
        return rankingResult;
    }
    
    /**
     * 计算百分位
     */
    private int calculatePercentile(int rank, int totalCount) {
        if (totalCount == 0) {
            return 0;
        }
        
        // 百分位 = (1 - rank/totalCount) * 100
        double percentile = (1.0 - (double) rank / totalCount) * 100;
        return (int) Math.round(percentile);
    }
    
    /**
     * 计算平均分
     */
    private BigDecimal calculateAverageScore(List<EvaluationResult> results) {
        BigDecimal sum = results.stream()
            .map(EvaluationResult::getTotalScore)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        return sum.divide(
            new BigDecimal(results.size()),
            2,
            RoundingMode.HALF_UP
        );
    }
    
    /**
     * 计算中位数
     */
    private BigDecimal calculateMedianScore(List<EvaluationResult> results) {
        int size = results.size();
        if (size == 0) {
            return BigDecimal.ZERO;
        }
        
        if (size % 2 == 0) {
            // 偶数个,取中间两个数的平均值
            BigDecimal score1 = results.get(size / 2 - 1).getTotalScore();
            BigDecimal score2 = results.get(size / 2).getTotalScore();
            return score1.add(score2)
                .divide(new BigDecimal("2"), 2, RoundingMode.HALF_UP);
        } else {
            // 奇数个,取中间数
            return results.get(size / 2).getTotalScore();
        }
    }
    
    /**
     * 部门排名对比
     * 
     * @param projectId 项目ID
     * @return 部门排名对比结果
     */
    public DepartmentRankingComparison compareDepartmentRanking(String projectId) {
        log.info("开始部门排名对比: projectId={}", projectId);
        
        // 1. 获取所有评估结果
        List<EvaluationResult> results = resultRepository.findByProjectId(projectId);
        
        // 2. 按部门分组
        Map<String, List<EvaluationResult>> departmentGroups = results.stream()
            .collect(Collectors.groupingBy(EvaluationResult::getDepartmentId));
        
        // 3. 计算各部门的平均分
        List<DepartmentRankingItem> departmentRankings = new ArrayList<>();
        
        for (Map.Entry<String, List<EvaluationResult>> entry : departmentGroups.entrySet()) {
            String departmentId = entry.getKey();
            List<EvaluationResult> departmentResults = entry.getValue();
            
            BigDecimal avgScore = calculateAverageScore(departmentResults);
            BigDecimal maxScore = departmentResults.stream()
                .map(EvaluationResult::getTotalScore)
                .max(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);
            BigDecimal minScore = departmentResults.stream()
                .map(EvaluationResult::getTotalScore)
                .min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);
            
            DepartmentRankingItem item = DepartmentRankingItem.builder()
                .departmentId(departmentId)
                .departmentName(departmentResults.get(0).getDepartmentName())
                .averageScore(avgScore)
                .maxScore(maxScore)
                .minScore(minScore)
                .participantCount(departmentResults.size())
                .build();
            
            departmentRankings.add(item);
        }
        
        // 4. 按平均分排序
        departmentRankings.sort(
            Comparator.comparing(DepartmentRankingItem::getAverageScore).reversed()
        );
        
        // 5. 构建对比结果
        return DepartmentRankingComparison.builder()
            .projectId(projectId)
            .departmentRankings(departmentRankings)
            .build();
    }
}

/**
 * 排名结果
 */
@Data
@Builder
public class RankingResult {
    /**
     * 项目ID
     */
    private String projectId;
    
    /**
     * 排名列表
     */
    private List<RankingItem> rankingItems;
    
    /**
     * 总人数
     */
    private Integer totalCount;
    
    /**
     * 平均分
     */
    private BigDecimal averageScore;
    
    /**
     * 中位数
     */
    private BigDecimal medianScore;
    
    /**
     * 最高分
     */
    private BigDecimal maxScore;
    
    /**
     * 最低分
     */
    private BigDecimal minScore;
    
    public static RankingResult empty() {
        return RankingResult.builder()
            .rankingItems(Collections.emptyList())
            .totalCount(0)
            .averageScore(BigDecimal.ZERO)
            .medianScore(BigDecimal.ZERO)
            .maxScore(BigDecimal.ZERO)
            .minScore(BigDecimal.ZERO)
            .build();
    }
}

/**
 * 排名项
 */
@Data
@Builder
public class RankingItem {
    /**
     * 排名
     */
    private Integer rank;
    
    /**
     * 百分位
     */
    private Integer percentile;
    
    /**
     * 被评估者ID
     */
    private String evaluateeId;
    
    /**
     * 被评估者姓名
     */
    private String evaluateeName;
    
    /**
     * 部门ID
     */
    private String departmentId;
    
    /**
     * 部门名称
     */
    private String departmentName;
    
    /**
     * 岗位ID
     */
    private String positionId;
    
    /**
     * 岗位名称
     */
    private String positionName;
    
    /**
     * 总分
     */
    private BigDecimal totalScore;
    
    /**
     * 各维度得分(JSON格式)
     */
    private String dimensionScores;
}

/**
 * 部门排名对比结果
 */
@Data
@Builder
public class DepartmentRankingComparison {
    /**
     * 项目ID
     */
    private String projectId;
    
    /**
     * 部门排名列表
     */
    private List<DepartmentRankingItem> departmentRankings;
}

/**
 * 部门排名项
 */
@Data
@Builder
public class DepartmentRankingItem {
    /**
     * 部门ID
     */
    private String departmentId;
    
    /**
     * 部门名称
     */
    private String departmentName;
    
    /**
     * 平均分
     */
    private BigDecimal averageScore;
    
    /**
     * 最高分
     */
    private BigDecimal maxScore;
    
    /**
     * 最低分
     */
    private BigDecimal minScore;
    
    /**
     * 参与人数
     */
    private Integer participantCount;
}

3.5 绩效改进计划生成

改进计划生成引擎

/**
 * 绩效改进计划生成引擎
 * 根据评估结果自动生成改进建议
 */
@Component
@Slf4j
public class ImprovementPlanGenerator {
    
    @Autowired
    private EvaluationResultRepository resultRepository;
    
    @Autowired
    private ImprovementAdviceRepository adviceRepository;
    
    /**
     * 生成改进计划
     * 
     * @param projectId 项目ID
     * @param evaluateeId 被评估者ID
     * @return 改进计划
     */
    @Transactional(rollbackFor = Exception.class)
    public ImprovementPlan generateImprovementPlan(
        String projectId,
        String evaluateeId
    ) {
        log.info("生成改进计划: projectId={}, evaluateeId={}", projectId, evaluateeId);
        
        // 1. 获取评估结果
        EvaluationResult result = resultRepository.findByProjectIdAndEvaluateeId(
            projectId, evaluateeId
        ).orElseThrow(() -> new BusinessException("评估结果不存在"));
        
        // 2. 分析优势和不足
        List<String> strengths = analyzeStrengths(result);
        List<String> weaknesses = analyzeWeaknesses(result);
        
        // 3. 生成改进建议
        List<ImprovementAdvice> advices = generateAdvices(result, weaknesses);
        
        // 4. 构建改进计划
        ImprovementPlan plan = ImprovementPlan.builder()
            .projectId(projectId)
            .evaluateeId(evaluateeId)
            .evaluateeName(result.getEvaluateeName())
            .totalScore(result.getTotalScore())
            .strengths(strengths)
            .weaknesses(weaknesses)
            .advices(advices)
            .planStatus(PlanStatus.DRAFT)
            .createTime(LocalDateTime.now())
            .build();
        
        // 5. 保存改进计划
        improvementPlanRepository.save(plan);
        
        log.info("改进计划生成完成: projectId={}, evaluateeId={}, adviceCount={}", 
            projectId, evaluateeId, advices.size());
        
        return plan;
    }
    
    /**
     * 分析优势
     */
    private List<String> analyzeStrengths(EvaluationResult result) {
        List<String> strengths = new ArrayList<>();
        
        // 解析维度得分
        List<DimensionScore> dimensionScores = parseDimensionScores(
            result.getDimensionScores()
        );
        
        // 找出得分较高的维度(高于平均分)
        BigDecimal totalScore = result.getTotalScore();
        
        for (DimensionScore dimensionScore : dimensionScores) {
            if (dimensionScore.getScore().compareTo(totalScore) > 0) {
                // 根据维度ID生成优势描述
                String strength = generateStrengthDescription(dimensionScore);
                strengths.add(strength);
            }
        }
        
        return strengths;
    }
    
    /**
     * 分析不足
     */
    private List<String> analyzeWeaknesses(EvaluationResult result) {
        List<String> weaknesses = new ArrayList<>();
        
        // 解析维度得分
        List<DimensionScore> dimensionScores = parseDimensionScores(
            result.getDimensionScores()
        );
        
        // 找出得分较低的维度(低于平均分)
        BigDecimal totalScore = result.getTotalScore();
        
        for (DimensionScore dimensionScore : dimensionScores) {
            if (dimensionScore.getScore().compareTo(totalScore) < 0) {
                // 根据维度ID生成不足描述
                String weakness = generateWeaknessDescription(dimensionScore);
                weaknesses.add(weakness);
            }
        }
        
        return weaknesses;
    }
    
    /**
     * 生成改进建议
     */
    private List<ImprovementAdvice> generateAdvices(
        EvaluationResult result,
        List<String> weaknesses
    ) {
        List<ImprovementAdvice> advices = new ArrayList<>();
        
        // 为每个不足生成改进建议
        for (String weakness : weaknesses) {
            // 根据不足类型获取改进建议模板
            List<AdviceTemplate> templates = adviceRepository
                .findByWeaknessType(weakness);
            
            if (templates.isEmpty()) {
                // 使用通用建议
                templates = adviceRepository.findGenericAdvices();
            }
            
            // 选择合适的建议模板
            for (AdviceTemplate template : templates) {
                ImprovementAdvice advice = ImprovementAdvice.builder()
                    .adviceType(template.getAdviceType())
                    .title(template.getTitle())
                    .description(template.getDescription())
                    .actionSteps(parseActionSteps(template.getActionSteps()))
                    .expectedOutcome(template.getExpectedOutcome())
                    .priority(calculateAdvicePriority(result, weakness))
                    .estimatedDuration(template.getEstimatedDuration())
                    .resources(template.getResources())
                    .build();
                
                advices.add(advice);
            }
        }
        
        return advices;
    }
    
    /**
     * 解析维度得分
     */
    private List<DimensionScore> parseDimensionScores(String dimensionScoresJson) {
        if (StringUtils.isBlank(dimensionScoresJson)) {
            return Collections.emptyList();
        }
        
        JSONObject jsonObject = JSON.parseObject(dimensionScoresJson);
        List<DimensionScore> dimensionScores = new ArrayList<>();
        
        for (String key : jsonObject.keySet()) {
            JSONObject dimensionObj = jsonObject.getJSONObject(key);
            DimensionScore dimensionScore = DimensionScore.builder()
                .dimensionId(key)
                .dimensionName(dimensionObj.getString("name"))
                .score(dimensionObj.getBigDecimal("score"))
                .build();
            dimensionScores.add(dimensionScore);
        }
        
        return dimensionScores;
    }
    
    /**
     * 生成优势描述
     */
    private String generateStrengthDescription(DimensionScore dimensionScore) {
        String dimensionId = dimensionScore.getDimensionId();
        BigDecimal score = dimensionScore.getScore();
        
        switch (dimensionId) {
            case "WORK_PERFORMANCE":
                return String.format("工作业绩表现优秀,得分为%.2f分,展现出较强的业务能力", score);
            case "WORK_ABILITY":
                return String.format("工作能力突出,得分为%.2f分,具备良好的专业素养", score);
            case "WORK_ATTITUDE":
                return String.format("工作态度积极,得分为%.2f分,表现出高度的责任心", score);
            case "TEAM_COLLABORATION":
                return String.format("团队协作能力强,得分为%.2f分,善于与同事合作", score);
            default:
                return String.format("%s表现优秀,得分为%.2f分", 
                    dimensionScore.getDimensionName(), score);
        }
    }
    
    /**
     * 生成不足描述
     */
    private String generateWeaknessDescription(DimensionScore dimensionScore) {
        String dimensionId = dimensionScore.getDimensionId();
        BigDecimal score = dimensionScore.getScore();
        
        switch (dimensionId) {
            case "WORK_PERFORMANCE":
                return String.format("工作业绩有待提升,当前得分为%.2f分,建议加强业务能力", score);
            case "WORK_ABILITY":
                return String.format("工作能力需要改进,当前得分为%.2f分,建议参加相关培训", score);
            case "WORK_ATTITUDE":
                return String.format("工作态度需要调整,当前得分为%.2f分,建议增强责任心", score);
            case "TEAM_COLLABORATION":
                return String.format("团队协作需要加强,当前得分为%.2f分,建议多与同事沟通", score);
            default:
                return String.format("%s需要改进,当前得分为%.2f分", 
                    dimensionScore.getDimensionName(), score);
        }
    }
    
    /**
     * 解析行动步骤
     */
    private List<String> parseActionSteps(String actionStepsJson) {
        if (StringUtils.isBlank(actionStepsJson)) {
            return Collections.emptyList();
        }
        
        return JSON.parseArray(actionStepsJson, String.class);
    }
    
    /**
     * 计算建议优先级
     */
    private Priority calculateAdvicePriority(EvaluationResult result, String weakness) {
        // 根据得分差异决定优先级
        BigDecimal scoreGap = result.getTotalScore().subtract(new BigDecimal("60"));
        
        if (scoreGap.compareTo(new BigDecimal("20")) < 0) {
            return Priority.HIGH;
        } else if (scoreGap.compareTo(new BigDecimal("10")) < 0) {
            return Priority.MEDIUM;
        } else {
            return Priority.LOW;
        }
    }
}

/**
 * 改进计划
 */
@Data
@Builder
@Entity
@Table(name = "hrm_improvement_plan")
public class ImprovementPlan {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;
    
    /**
     * 项目ID
     */
    private String projectId;
    
    /**
     * 被评估者ID
     */
    private String evaluateeId;
    
    /**
     * 被评估者姓名
     */
    private String evaluateeName;
    
    /**
     * 总分
     */
    private BigDecimal totalScore;
    
    /**
     * 优势列表
     */
    @Lob
    private String strengths;
    
    /**
     * 不足列表
     */
    @Lob
    private String weaknesses;
    
    /**
     * 改进建议列表
     */
    @OneToMany(mappedBy = "improvementPlan", cascade = CascadeType.ALL)
    private List<ImprovementAdvice> advices;
    
    /**
     * 计划状态
     */
    @Enumerated(EnumType.STRING)
    private PlanStatus planStatus;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}

/**
 * 计划状态枚举
 */
public enum PlanStatus {
    /** 草稿 */
    DRAFT,
    
    /** 进行中 */
    IN_PROGRESS,
    
    /** 已完成 */
    COMPLETED,
    
    /** 已归档 */
    ARCHIVED
}

/**
 * 改进建议
 */
@Data
@Builder
@Entity
@Table(name = "hrm_improvement_advice")
public class ImprovementAdvice {
    @Id
    @GeneratedValue(strategy = GenerationType.UUID)
    private String id;
    
    /**
     * 改进计划ID
     */
    private String improvementPlanId;
    
    /**
     * 关联的改进计划
     */
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "improvement_plan_id")
    private ImprovementPlan improvementPlan;
    
    /**
     * 建议类型:TRAINING(培训), COACHING(辅导), PROJECT(项目), 
     *           READING(阅读), PRACTICE(练习)
     */
    @Enumerated(EnumType.STRING)
    private AdviceType adviceType;
    
    /**
     * 标题
     */
    private String title;
    
    /**
     * 描述
     */
    @Lob
    private String description;
    
    /**
     * 行动步骤(JSON格式)
     */
    @Lob
    private String actionSteps;
    
    /**
     * 预期成果
     */
    private String expectedOutcome;
    
    /**
     * 优先级
     */
    @Enumerated(EnumType.STRING)
    private Priority priority;
    
    /**
     * 预计时长(天)
     */
    private Integer estimatedDuration;
    
    /**
     * 资源(JSON格式)
     */
    @Lob
    private String resources;
    
    /**
     * 完成状态
     */
    @Enumerated(EnumType.STRING)
    private CompletionStatus completionStatus;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}

/**
 * 建议类型枚举
 */
public enum AdviceType {
    /** 培训 */
    TRAINING,
    
    /** 辅导 */
    COACHING,
    
    /** 项目 */
    PROJECT,
    
    /** 阅读 */
    READING,
    
    /** 练习 */
    PRACTICE
}

/**
 * 优先级枚举
 */
public enum Priority {
    /** 高 */
    HIGH,
    
    /** 中 */
    MEDIUM,
    
    /** 低 */
    LOW
}

/**
 * 完成状态枚举
 */
public enum CompletionStatus {
    /** 待开始 */
    PENDING,
    
    /** 进行中 */
    IN_PROGRESS,
    
    /** 已完成 */
    COMPLETED,
    
    /** 已跳过 */
    SKIPPED
}

/**
 * 维度得分
 */
@Data
@Builder
public class DimensionScore {
    private String dimensionId;
    private String dimensionName;
    private BigDecimal score;
}

四、高级特性实现

4.1 匿名评估实现

匿名评估处理

/**
 * 匿名评估处理器
 * 确保评估者的匿名性
 */
@Component
@Slf4j
public class AnonymousEvaluationHandler {
    
    @Autowired
    private EvaluationRecordRepository recordRepository;
    
    /**
     * 脱敏处理评估记录
     * 
     * @param record 评估记录
     * @return 脱敏后的评估记录
     */
    public EvaluationRecord maskEvaluationRecord(EvaluationRecord record) {
        // 如果不是匿名评估,直接返回
        if (!record.getAnonymous()) {
            return record;
        }
        
        // 创建脱敏副本
        EvaluationRecord maskedRecord = new EvaluationRecord();
        BeanUtils.copyProperties(record, maskedRecord);
        
        // 脱敏评估者信息
        maskedRecord.setRaterId(generateAnonymousId(record.getRaterId()));
        maskedRecord.setRaterName("匿名评估者");
        
        return maskedRecord;
    }
    
    /**
     * 批量脱敏处理评估记录
     * 
     * @param records 评估记录列表
     * @return 脱敏后的评估记录列表
     */
    public List<EvaluationRecord> maskEvaluationRecords(List<EvaluationRecord> records) {
        return records.stream()
            .map(this::maskEvaluationRecord)
            .collect(Collectors.toList());
    }
    
    /**
     * 生成匿名ID
     * 
     * @param originalId 原始ID
     * @return 匿名ID
     */
    private String generateAnonymousId(String originalId) {
        // 使用哈希算法生成匿名ID
        return DigestUtils.md5Hex(originalId + "_anonymous");
    }
    
    /**
     * 验证匿名权限
     * 
     * @param userId 用户ID
     * @param recordId 评估记录ID
     * @return 是否有权限查看
     */
    public boolean validateAnonymousPermission(String userId, String recordId) {
        EvaluationRecord record = recordRepository.findById(recordId)
            .orElseThrow(() -> new BusinessException("评估记录不存在"));
        
        // 如果不是匿名评估,所有人都可以查看
        if (!record.getAnonymous()) {
            return true;
        }
        
        // 匿名评估只有以下人员可以查看真实身份:
        // 1. 被评估者本人
        // 2. 系统管理员
        // 3. HR管理员
        
        return userId.equals(record.getEvaluateeId()) || 
               hasAdminRole(userId) || 
               hasHrAdminRole(userId);
    }
    
    /**
     * 判断是否为管理员
     */
    private boolean hasAdminRole(String userId) {
        User user = userService.getById(userId);
        return user != null && user.getRoles().contains("ADMIN");
    }
    
    /**
     * 判断是否为HR管理员
     */
    private boolean hasHrAdminRole(String userId) {
        User user = userService.getById(userId);
        return user != null && user.getRoles().contains("HR_ADMIN");
    }
}

4.2 评估进度监控

评估进度监控器

/**
 * 评估进度监控器
 * 实时监控评估进度和异常情况
 */
@Component
@Slf4j
public class EvaluationProgressMonitor {
    
    @Autowired
    private EvaluationRecordRepository recordRepository;
    
    @Autowired
    private NotificationService notificationService;
    
    /**
     * 获取项目评估进度
     * 
     * @param projectId 项目ID
     * @return 评估进度
     */
    public ProjectProgress getProjectProgress(String projectId) {
        // 获取所有评估记录
        List<EvaluationRecord> allRecords = recordRepository.findByProjectId(projectId);
        
        if (allRecords.isEmpty()) {
            return ProjectProgress.empty();
        }
        
        // 统计各状态的评估记录数量
        Map<EvaluationStatus, Long> statusCounts = allRecords.stream()
            .collect(Collectors.groupingBy(
                EvaluationRecord::getStatus,
                Collectors.counting()
            ));
        
        // 获取被评估者数量
        long evaluateeCount = allRecords.stream()
            .map(EvaluationRecord::getEvaluateeId)
            .distinct()
            .count();
        
        // 计算完成率
        long completedCount = statusCounts.getOrDefault(EvaluationStatus.COMPLETED, 0L);
        double completionRate = (double) completedCount / allRecords.size();
        
        // 构建进度信息
        return ProjectProgress.builder()
            .projectId(projectId)
            .totalRecords(allRecords.size())
            .evaluateeCount((int) evaluateeCount)
            .pendingCount(statusCounts.getOrDefault(EvaluationStatus.PENDING, 0L).intValue())
            .inProgressCount(statusCounts.getOrDefault(EvaluationStatus.IN_PROGRESS, 0L).intValue())
            .completedCount(completedCount)
            .skippedCount(statusCounts.getOrDefault(EvaluationStatus.SKIPPED, 0L).intValue())
            .completionRate(completionRate)
            .build();
    }
    
    /**
     * 获取被评估者的评估进度
     * 
     * @param projectId 项目ID
     * @param evaluateeId 被评估者ID
     * @return 评估进度
     */
    public EvaluateeProgress getEvaluateeProgress(String projectId, String evaluateeId) {
        // 获取该被评估者的所有评估记录
        List<EvaluationRecord> records = recordRepository
            .findByProjectIdAndEvaluateeId(projectId, evaluateeId);
        
        if (records.isEmpty()) {
            return EvaluateeProgress.empty();
        }
        
        // 统计各状态的评估记录数量
        Map<EvaluationStatus, Long> statusCounts = records.stream()
            .collect(Collectors.groupingBy(
                EvaluationRecord::getStatus,
                Collectors.counting()
            ));
        
        // 计算完成率
        long completedCount = statusCounts.getOrDefault(EvaluationStatus.COMPLETED, 0L);
        double completionRate = (double) completedCount / records.size();
        
        // 构建进度信息
        return EvaluateeProgress.builder()
            .projectId(projectId)
            .evaluateeId(evaluateeId)
            .totalRaters(records.size())
            .completedRaters(completedCount)
            .completionRate(completionRate)
            .statusCounts(statusCounts)
            .build();
    }
    
    /**
     * 监控异常情况
     * 
     * @param projectId 项目ID
     * @return 异常情况列表
     */
    public List<ProgressAlert> checkProgressAlerts(String projectId) {
        List<ProgressAlert> alerts = new ArrayList<>();
        
        // 获取项目进度
        ProjectProgress progress = getProjectProgress(projectId);
        
        // 1. 检查是否有评估即将过期
        List<EvaluationRecord> expiringRecords = findExpiringRecords(projectId);
        if (!expiringRecords.isEmpty()) {
            alerts.add(ProgressAlert.builder()
                .alertType(AlertType.EXPIRING_SOON)
                .severity(AlertSeverity.MEDIUM)
                .message(String.format("有%d条评估即将过期", expiringRecords.size()))
                .count(expiringRecords.size())
                .build());
        }
        
        // 2. 检查完成率是否过低
        if (progress.getCompletionRate() < 0.5) {
            alerts.add(ProgressAlert.builder()
                .alertType(AlertType.LOW_COMPLETION_RATE)
                .severity(AlertSeverity.HIGH)
                .message(String.format("评估完成率仅为%.2f%%,低于50%%", 
                    progress.getCompletionRate() * 100))
                .build());
        }
        
        // 3. 检查是否有被评估者缺少足够的评估者
        List<String> insufficientRaters = findEvaluateesWithInsufficientRaters(projectId);
        if (!insufficientRaters.isEmpty()) {
            alerts.add(ProgressAlert.builder()
                .alertType(AlertType.INSUFFICIENT_RATERS)
                .severity(AlertSeverity.HIGH)
                .message(String.format("有%d名被评估者缺少足够的评估者", 
                    insufficientRaters.size()))
                .count(insufficientRaters.size())
                .build());
        }
        
        // 4. 检查是否有异常评分
        List<String> outlierEvaluatees = findEvaluateesWithOutlierScores(projectId);
        if (!outlierEvaluatees.isEmpty()) {
            alerts.add(ProgressAlert.builder()
                .alertType(AlertType.OUTLIER_SCORES)
                .severity(AlertSeverity.LOW)
                .message(String.format("有%d名被评估者的评分存在异常", 
                    outlierEvaluatees.size()))
                .count(outlierEvaluatees.size())
                .build());
        }
        
        return alerts;
    }
    
    /**
     * 查找即将过期的评估记录
     */
    private List<EvaluationRecord> findExpiringRecords(String projectId) {
        LocalDateTime threshold = LocalDateTime.now().plusDays(3);
        
        return recordRepository.findByProjectIdAndStatusAndEndTimeBefore(
            projectId,
            EvaluationStatus.PENDING,
            threshold
        );
    }
    
    /**
     * 查找评估者数量不足的被评估者
     */
    private List<String> findEvaluateesWithInsufficientRaters(String projectId) {
        // 获取项目配置
        EvaluationProject project = projectRepository.findById(projectId).orElse(null);
        if (project == null) {
            return Collections.emptyList();
        }
        
        int minRaterCount = project.getMinRaterCount();
        
        // 按被评估者分组
        Map<String, List<EvaluationRecord>> groupedRecords = recordRepository
            .findByProjectId(projectId).stream()
            .collect(Collectors.groupingBy(EvaluationRecord::getEvaluateeId));
        
        // 找出评估者数量不足的被评估者
        List<String> insufficientRaters = new ArrayList<>();
        for (Map.Entry<String, List<EvaluationRecord>> entry : groupedRecords.entrySet()) {
            if (entry.getValue().size() < minRaterCount) {
                insufficientRaters.add(entry.getKey());
            }
        }
        
        return insufficientRaters;
    }
    
    /**
     * 查找评分存在异常的被评估者
     */
    private List<String> findEvaluateesWithOutlierScores(String projectId) {
        // 获取所有已完成的评估记录
        List<EvaluationRecord> completedRecords = recordRepository
            .findByProjectIdAndStatus(projectId, EvaluationStatus.COMPLETED);
        
        // 提取所有评分
        List<BigDecimal> allScores = completedRecords.stream()
            .map(EvaluationRecord::getTotalScore)
            .collect(Collectors.toList());
        
        // 检测异常值
        OutlierDetectionResult detectionResult = outlierDetector.detectOutliers(allScores);
        
        if (detectionResult.getAllOutliers().isEmpty()) {
            return Collections.emptyList();
        }
        
        // 找出有异常值的被评估者
        List<String> outlierEvaluatees = completedRecords.stream()
            .filter(record -> detectionResult.getAllOutliers().contains(record.getTotalScore()))
            .map(EvaluationRecord::getEvaluateeId)
            .distinct()
            .collect(Collectors.toList());
        
        return outlierEvaluatees;
    }
}

/**
 * 项目评估进度
 */
@Data
@Builder
public class ProjectProgress {
    /**
     * 项目ID
     */
    private String projectId;
    
    /**
     * 总评估记录数
     */
    private Integer totalRecords;
    
    /**
     * 被评估者数量
     */
    private Integer evaluateeCount;
    
    /**
     * 待评估数量
     */
    private Integer pendingCount;
    
    /**
     * 评估中数量
     */
    private Integer inProgressCount;
    
    /**
     * 已完成数量
     */
    private Integer completedCount;
    
    /**
     * 已跳过数量
     */
    private Integer skippedCount;
    
    /**
     * 完成率
     */
    private Double completionRate;
    
    public static ProjectProgress empty() {
        return ProjectProgress.builder()
            .totalRecords(0)
            .evaluateeCount(0)
            .pendingCount(0)
            .inProgressCount(0)
            .completedCount(0)
            .skippedCount(0)
            .completionRate(0.0)
            .build();
    }
}

/**
 * 被评估者评估进度
 */
@Data
@Builder
public class EvaluateeProgress {
    /**
     * 项目ID
     */
    private String projectId;
    
    /**
     * 被评估者ID
     */
    private String evaluateeId;
    
    /**
     * 评估者总数
     */
    private Integer totalRaters;
    
    /**
     * 已完成评估者数量
     */
    private Integer completedRaters;
    
    /**
     * 完成率
     */
    private Double completionRate;
    
    /**
     * 各状态数量
     */
    private Map<EvaluationStatus, Long> statusCounts;
    
    public static EvaluateeProgress empty() {
        return EvaluateeProgress.builder()
            .totalRaters(0)
            .completedRaters(0)
            .completionRate(0.0)
            .statusCounts(Collections.emptyMap())
            .build();
    }
}

/**
 * 进度告警
 */
@Data
@Builder
public class ProgressAlert {
    /**
     * 告警类型
     */
    private AlertType alertType;
    
    /**
     * 告警级别
     */
    private AlertSeverity severity;
    
    /**
     * 告警消息
     */
    private String message;
    
    /**
     * 数量
     */
    private Integer count;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
}

/**
 * 告警类型枚举
 */
public enum AlertType {
    /** 即将过期 */
    EXPIRING_SOON,
    
    /** 完成率低 */
    LOW_COMPLETION_RATE,
    
    /** 评估者不足 */
    INSUFFICIENT_RATERS,
    
    /** 异常评分 */
    OUTLIER_SCORES
}

/**
 * 告警级别枚举
 */
public enum AlertSeverity {
    /** 低 */
    LOW,
    
    /** 中 */
    MEDIUM,
    
    /** 高 */
    HIGH
}

4.3 数据可视化

评估结果可视化

/**
 * 评估数据可视化服务
 * 生成各种图表和报表
 */
@Service
@Slf4j
public class EvaluationVisualizationService {
    
    @Autowired
    private EvaluationResultRepository resultRepository;
    
    /**
     * 生成评分分布图数据
     * 
     * @param projectId 项目ID
     * @return 图表数据
     */
    public ChartData generateScoreDistributionChart(String projectId) {
        // 获取所有评估结果
        List<EvaluationResult> results = resultRepository.findByProjectId(projectId);
        
        // 按分数段分组
        Map<String, Long> distribution = new LinkedHashMap<>();
        distribution.put("90-100", 0L);
        distribution.put("80-89", 0L);
        distribution.put("70-79", 0L);
        distribution.put("60-69", 0L);
        distribution.put("0-59", 0L);
        
        for (EvaluationResult result : results) {
            BigDecimal score = result.getTotalScore();
            if (score.compareTo(new BigDecimal("90")) >= 0) {
                distribution.put("90-100", distribution.get("90-100") + 1);
            } else if (score.compareTo(new BigDecimal("80")) >= 0) {
                distribution.put("80-89", distribution.get("80-89") + 1);
            } else if (score.compareTo(new BigDecimal("70")) >= 0) {
                distribution.put("70-79", distribution.get("70-79") + 1);
            } else if (score.compareTo(new BigDecimal("60")) >= 0) {
                distribution.put("60-69", distribution.get("60-69") + 1);
            } else {
                distribution.put("0-59", distribution.get("0-59") + 1);
            }
        }
        
        // 构建图表数据
        List<String> categories = new ArrayList<>(distribution.keySet());
        List<Long> values = new ArrayList<>(distribution.values());
        
        return ChartData.builder()
            .chartType(ChartType.BAR)
            .title("评分分布")
            .categories(categories)
            .series(List.of(
                ChartSeries.builder()
                    .name("人数")
                    .data(values)
                    .build()
            ))
            .build();
    }
    
    /**
     * 生成维度对比雷达图数据
     * 
     * @param projectId 项目ID
     * @param evaluateeIds 被评估者ID列表
     * @return 图表数据
     */
    public ChartData generateDimensionRadarChart(
        String projectId,
        List<String> evaluateeIds
    ) {
        // 获取评估结果
        List<EvaluationResult> results = evaluateeIds.stream()
            .map(evaluateeId -> resultRepository.findByProjectIdAndEvaluateeId(projectId, evaluateeId))
            .filter(Optional::isPresent)
            .map(Optional::get)
            .collect(Collectors.toList());
        
        // 提取维度名称
        Set<String> dimensionNames = new LinkedHashSet<>();
        for (EvaluationResult result : results) {
            List<DimensionScore> dimensionScores = parseDimensionScores(result.getDimensionScores());
            dimensionScores.forEach(ds -> dimensionNames.add(ds.getDimensionName()));
        }
        
        // 构建图表数据
        List<String> categories = new ArrayList<>(dimensionNames);
        List<ChartSeries> series = new ArrayList<>();
        
        for (EvaluationResult result : results) {
            List<DimensionScore> dimensionScores = parseDimensionScores(result.getDimensionScores());
            
            // 按维度名称顺序提取分数
            List<BigDecimal> scores = dimensionNames.stream()
                .map(dimName -> {
                    return dimensionScores.stream()
                        .filter(ds -> ds.getDimensionName().equals(dimName))
                        .map(DimensionScore::getScore)
                        .findFirst()
                        .orElse(BigDecimal.ZERO);
                })
                .collect(Collectors.toList());
            
            series.add(ChartSeries.builder()
                .name(result.getEvaluateeName())
                .data(scores.stream().map(BigDecimal::doubleValue).collect(Collectors.toList()))
                .build());
        }
        
        return ChartData.builder()
            .chartType(ChartType.RADAR)
            .title("维度能力对比")
            .categories(categories)
            .series(series)
            .build();
    }
    
    /**
     * 生成趋势图数据
     * 
     * @param evaluateeId 被评估者ID
     * @param projectIds 项目ID列表
     * @return 图表数据
     */
    public ChartData generateTrendChart(
        String evaluateeId,
        List<String> projectIds
    ) {
        // 获取历史评估结果
        List<EvaluationResult> results = projectIds.stream()
            .map(projectId -> resultRepository.findByProjectIdAndEvaluateeId(projectId, evaluateeId))
            .filter(Optional::isPresent)
            .map(Optional::get)
            .sorted(Comparator.comparing(EvaluationResult::getCreateTime))
            .collect(Collectors.toList());
        
        // 提取数据
        List<String> projectNames = results.stream()
            .map(EvaluationResult::getProjectName)
            .collect(Collectors.toList());
        
        List<BigDecimal> totalScores = results.stream()
            .map(EvaluationResult::getTotalScore)
            .collect(Collectors.toList());
        
        // 按维度提取趋势
        Map<String, List<BigDecimal>> dimensionTrends = new LinkedHashMap<>();
        
        for (EvaluationResult result : results) {
            List<DimensionScore> dimensionScores = parseDimensionScores(result.getDimensionScores());
            
            for (DimensionScore ds : dimensionScores) {
                dimensionTrends.computeIfAbsent(ds.getDimensionName(), k -> new ArrayList<>())
                    .add(ds.getScore());
            }
        }
        
        // 构建图表数据
        List<ChartSeries> series = new ArrayList<>();
        
        // 添加总分趋势
        series.add(ChartSeries.builder()
            .name("总分")
            .data(totalScores.stream().map(BigDecimal::doubleValue).collect(Collectors.toList()))
            .build());
        
        // 添加各维度趋势
        for (Map.Entry<String, List<BigDecimal>> entry : dimensionTrends.entrySet()) {
            series.add(ChartSeries.builder()
                .name(entry.getKey())
                .data(entry.getValue().stream().map(BigDecimal::doubleValue).collect(Collectors.toList()))
                .build());
        }
        
        return ChartData.builder()
            .chartType(ChartType.LINE)
            .title("绩效趋势")
            .categories(projectNames)
            .series(series)
            .build();
    }
    
    /**
     * 解析维度得分
     */
    private List<DimensionScore> parseDimensionScores(String dimensionScoresJson) {
        if (StringUtils.isBlank(dimensionScoresJson)) {
            return Collections.emptyList();
        }
        
        JSONObject jsonObject = JSON.parseObject(dimensionScoresJson);
        List<DimensionScore> dimensionScores = new ArrayList<>();
        
        for (String key : jsonObject.keySet()) {
            JSONObject dimensionObj = jsonObject.getJSONObject(key);
            DimensionScore dimensionScore = DimensionScore.builder()
                .dimensionId(key)
                .dimensionName(dimensionObj.getString("name"))
                .score(dimensionObj.getBigDecimal("score"))
                .build();
            dimensionScores.add(dimensionScore);
        }
        
        return dimensionScores;
    }
}

/**
 * 图表数据
 */
@Data
@Builder
public class ChartData {
    /**
     * 图表类型
     */
    private ChartType chartType;
    
    /**
     * 图表标题
     */
    private String title;
    
    /**
     * 类别标签
     */
    private List<String> categories;
    
    /**
     * 系列数据
     */
    private List<ChartSeries> series;
    
    /**
     * 图表配置(JSON格式)
     */
    private String options;
}

/**
 * 图表类型枚举
 */
public enum ChartType {
    /** 柱状图 */
    BAR,
    
    /** 折线图 */
    LINE,
    
    /** 饼图 */
    PIE,
    
    /** 雷达图 */
    RADAR,
    
    /** 散点图 */
    SCATTER
}

/**
 * 图表系列
 */
@Data
@Builder
public class ChartSeries {
    /**
     * 系列名称
     */
    private String name;
    
    /**
     * 数据
     */
    private List<Double> data;
    
    /**
     * 颜色
     */
    private String color;
}

五、性能优化

5.1 批量评估处理

批量评估任务处理器

/**
 * 批量评估任务处理器
 * 使用异步处理和消息队列优化大规模评估任务
 */
@Component
@Slf4j
public class BatchEvaluationProcessor {
    
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    @Autowired
    private EvaluationDataAggregator aggregator;
    
    /**
     * 批量聚合评估数据
     * 
     * @param projectId 项目ID
     * @param evaluateeIds 被评估者ID列表
     */
    public void batchAggregateEvaluation(
        String projectId,
        List<String> evaluateeIds
    ) {
        log.info("开始批量聚合评估数据: projectId={}, count={}", 
            projectId, evaluateeIds.size());
        
        // 分批发送消息到MQ
        int batchSize = 50;
        List<List<String>> batches = Lists.partition(evaluateeIds, batchSize);
        
        for (int i = 0; i < batches.size(); i++) {
            List<String> batch = batches.get(i);
            
            EvaluationAggregationMessage message = EvaluationAggregationMessage.builder()
                .projectId(projectId)
                .evaluateeIds(batch)
                .batchNumber(i + 1)
                .totalBatches(batches.size())
                .build();
            
            rocketMQTemplate.asyncSend(
                "EVALUATION_AGGREGATION_TOPIC",
                message,
                new SendCallback() {
                    @Override
                    public void onSuccess(SendResult sendResult) {
                        log.info("批量聚合消息发送成功: batchNumber={}", i + 1);
                    }
                    
                    @Override
                    public void onException(Throwable e) {
                        log.error("批量聚合消息发送失败: batchNumber={}", i + 1, e);
                    }
                }
            );
        }
    }
}

/**
 * 评估聚合消息监听器
 */
@Component
@RocketMQMessageListener(
    topic = "EVALUATION_AGGREGATION_TOPIC",
    consumerGroup = "EVALUATION_AGGREGATION_CONSUMER_GROUP"
)
@Slf4j
public class EvaluationAggregationConsumer implements 
    RocketMQListener<EvaluationAggregationMessage> {
    
    @Autowired
    private EvaluationDataAggregator aggregator;
    
    @Autowired
    private EvaluationResultRepository resultRepository;
    
    @Override
    public void onMessage(EvaluationAggregationMessage message) {
        String projectId = message.getProjectId();
        int batchNumber = message.getBatchNumber();
        
        log.info("处理评估聚合批次: projectId={}, batchNumber={}", projectId, batchNumber);
        
        try {
            // 并行处理本批次的所有被评估者
            List<EvaluationAggregationResult> results = message.getEvaluateeIds()
                .parallelStream()
                .map(evaluateeId -> aggregator.aggregateEvaluation(projectId, evaluateeId))
                .collect(Collectors.toList());
            
            // 批量保存聚合结果
            List<EvaluationResult> evaluationResults = results.stream()
                .map(this::convertToEvaluationResult)
                .collect(Collectors.toList());
            
            resultRepository.saveAll(evaluationResults);
            
            log.info("评估聚合批次处理完成: projectId={}, batchNumber={}, count={}", 
                projectId, batchNumber, evaluationResults.size());
            
        } catch (Exception e) {
            log.error("评估聚合批次处理失败: projectId={}, batchNumber={}, error={}",
                projectId, batchNumber, e.getMessage());
        }
    }
    
    /**
     * 转换为评估结果
     */
    private EvaluationResult convertToEvaluationResult(
        EvaluationAggregationResult aggregationResult
    ) {
        EvaluationResult result = new EvaluationResult();
        result.setProjectId(aggregationResult.getProjectId());
        result.setEvaluateeId(aggregationResult.getEvaluateeId());
        result.setTotalScore(aggregationResult.getTotalScore());
        
        // 转换维度得分
        JSONObject dimensionScores = new JSONObject();
        for (DimensionAggregation dimension : aggregationResult.getDimensionAggregations()) {
            JSONObject dimensionObj = new JSONObject();
            dimensionObj.put("name", dimension.getDimensionName());
            dimensionObj.put("score", dimension.getAverageScore());
            dimensionScores.put(dimension.getDimensionId(), dimensionObj);
        }
        result.setDimensionScores(dimensionScores.toJSONString());
        
        return result;
    }
}

/**
 * 评估聚合消息
 */
@Data
@Builder
public class EvaluationAggregationMessage implements Serializable {
    /**
     * 项目ID
     */
    private String projectId;
    
    /**
     * 被评估者ID列表
     */
    private List<String> evaluateeIds;
    
    /**
     * 批次号
     */
    private Integer batchNumber;
    
    /**
     * 总批次数
     */
    private Integer totalBatches;
}

5.2 缓存策略

评估数据缓存管理

/**
 * 评估数据缓存管理器
 * 使用多级缓存提升查询性能
 */
@Component
@Slf4j
public class EvaluationCacheManager {
    
    /**
     * L1缓存:本地缓存(Caffeine)
     */
    private final Cache<String, Object> localCache;
    
    /**
     * L2缓存:分布式缓存(Redis)
     */
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    public EvaluationCacheManager() {
        this.localCache = Caffeine.newBuilder()
            .maximumSize(50_000)
            .expireAfterWrite(1, TimeUnit.HOURS)
            .recordStats()
            .build();
    }
    
    /**
     * 获取项目进度缓存
     * 
     * @param projectId 项目ID
     * @return 项目进度
     */
    public ProjectProgress getProjectProgress(String projectId) {
        String cacheKey = buildProjectProgressKey(projectId);
        
        // 先查L1缓存
        Object cached = localCache.getIfPresent(cacheKey);
        if (cached != null) {
            return (ProjectProgress) cached;
        }
        
        // 再查L2缓存
        cached = redisTemplate.opsForValue().get(cacheKey);
        if (cached != null) {
            // 回写L1缓存
            localCache.put(cacheKey, cached);
            return (ProjectProgress) cached;
        }
        
        return null;
    }
    
    /**
     * 缓存项目进度
     * 
     * @param projectId 项目ID
     * @param progress 项目进度
     */
    public void cacheProjectProgress(String projectId, ProjectProgress progress) {
        String cacheKey = buildProjectProgressKey(projectId);
        
        // 写入L1缓存
        localCache.put(cacheKey, progress);
        
        // 写入L2缓存
        redisTemplate.opsForValue().set(cacheKey, progress, 30, TimeUnit.MINUTES);
    }
    
    /**
     * 获取评估结果缓存
     * 
     * @param projectId 项目ID
     * @param evaluateeId 被评估者ID
     * @return 评估结果
     */
    public EvaluationResult getEvaluationResult(String projectId, String evaluateeId) {
        String cacheKey = buildEvaluationResultKey(projectId, evaluateeId);
        
        // 先查L1缓存
        Object cached = localCache.getIfPresent(cacheKey);
        if (cached != null) {
            return (EvaluationResult) cached;
        }
        
        // 再查L2缓存
        cached = redisTemplate.opsForValue().get(cacheKey);
        if (cached != null) {
            // 回写L1缓存
            localCache.put(cacheKey, cached);
            return (EvaluationResult) cached;
        }
        
        return null;
    }
    
    /**
     * 缓存评估结果
     * 
     * @param projectId 项目ID
     * @param evaluateeId 被评估者ID
     * @param result 评估结果
     */
    public void cacheEvaluationResult(
        String projectId,
        String evaluateeId,
        EvaluationResult result
    ) {
        String cacheKey = buildEvaluationResultKey(projectId, evaluateeId);
        
        // 写入L1缓存
        localCache.put(cacheKey, result);
        
        // 写入L2缓存
        redisTemplate.opsForValue().set(cacheKey, result, 1, TimeUnit.HOURS);
    }
    
    /**
     * 清除项目相关缓存
     * 
     * @param projectId 项目ID
     */
    public void invalidateProjectCache(String projectId) {
        // 清除L1缓存
        Set<String> keys = localCache.asMap().keySet().stream()
            .filter(key -> key.startsWith(projectId))
            .collect(Collectors.toSet());
        localCache.invalidateAll(keys);
        
        // 清除L2缓存
        Set<String> redisKeys = redisTemplate.keys(projectId + ":*");
        if (redisKeys != null && !redisKeys.isEmpty()) {
            redisTemplate.delete(redisKeys);
        }
        
        log.info("清除项目缓存: projectId={}", projectId);
    }
    
    /**
     * 构建项目进度缓存键
     */
    private String buildProjectProgressKey(String projectId) {
        return String.format("evaluation:project:progress:%s", projectId);
    }
    
    /**
     * 构建评估结果缓存键
     */
    private String buildEvaluationResultKey(String projectId, String evaluateeId) {
        return String.format("evaluation:result:%s:%s", projectId, evaluateeId);
    }
}

六、最佳实践

6.1 评估设计最佳实践

1. 评估维度设计原则

- 全面性:覆盖工作业绩、工作能力、工作态度、团队协作等维度
- 独立性:各维度之间相对独立,避免重复评价
- 可衡量性:每个维度都可以用具体指标量化
- 相关性:维度设置与企业战略和岗位要求相关
- 数量适中:建议4-6个维度,避免评估疲劳

2. 评估指标设计原则

- SMART原则:具体、可衡量、可达成、相关性、有时限
- 行为化:用具体行为描述,而非抽象概念
- 权重合理:根据指标重要性分配权重
- 分层设计:核心指标、重要指标、一般指标
- 可观察性:指标应该是可以直接观察到的

3. 评估者选择策略

- 上级评估:1-2人,权重30-40%
- 同事评估:3-5人,权重20-30%
- 下级评估:2-3人,权重15-20%
- 自评:1人,权重5-10%(仅供参考)
- 客户评估:根据实际情况,权重10-20%

- 评估者数量:建议5-10人
- 匿名原则:同事、下级、客户评估匿名
- 多样性:选择不同背景、不同合作深度的评估者

6.2 数据分析最佳实践

1. 异常值处理策略

// 示例:处理评估异常值
public class OutlierHandlingExample {
    
    public void handleOutliers(List<EvaluationRecord> records) {
        // 1. 检测异常值
        EvaluationOutlierDetector detector = new EvaluationOutlierDetector();
        
        List<BigDecimal> scores = records.stream()
            .map(EvaluationRecord::getTotalScore)
            .collect(Collectors.toList());
        
        OutlierDetectionResult result = detector.detectOutliers(scores);
        
        // 2. 根据异常值比例选择处理策略
        if (result.getOutlierRatio() > 0.3) {
            // 异常值比例过高,可能存在系统性问题
            log.warn("异常值比例过高: ratio={}", result.getOutlierRatio());
            // 需要人工复核
            return;
        }
        
        // 3. 处理异常值
        List<BigDecimal> processedScores = detector.handleOutliers(
            scores,
            OutlierHandlingStrategy.REPLACE_WITH_MEDIAN
        );
        
        // 4. 使用处理后的数据
        // ...
    }
}

2. 趋势分析方法

// 示例:分析绩效趋势
public class TrendAnalysisExample {
    
    public TrendAnalysisResult analyzeTrend(String employeeId) {
        // 1. 获取历史评估结果
        List<EvaluationResult> historyResults = getHistoryResults(employeeId);
        
        // 2. 计算移动平均
        List<BigDecimal> movingAverages = calculateMovingAverage(
            historyResults,
            3 // 3期移动平均
        );
        
        // 3. 判断趋势
        TrendType trend = determineTrend(movingAverages);
        
        // 4. 计算趋势强度
        BigDecimal trendStrength = calculateTrendStrength(movingAverages);
        
        // 5. 预测未来表现
        BigDecimal predictedScore = predictNextScore(movingAverages);
        
        return TrendAnalysisResult.builder()
            .trendType(trend)
            .trendStrength(trendStrength)
            .predictedScore(predictedScore)
            .build();
    }
}

6.3 系统监控最佳实践

关键监控指标

/**
 * 360度评估系统监控指标
 */
public class EvaluationSystemMetrics {
    
    /**
     * 核心指标
     */
    public enum CoreMetric {
        /** 评估参与率 */
        EVALUATION_PARTICIPATION_RATE,
        
        /** 评估完成率 */
        EVALUATION_COMPLETION_RATE,
        
        /** 平均评估时长 */
        AVERAGE_EVALUATION_DURATION,
        
        /** 异常评分比例 */
        OUTLIER_SCORE_RATIO,
        
        /** 缓存命中率 */
        CACHE_HIT_RATE
    }
    
    /**
     * 业务指标
     */
    public enum BusinessMetric {
        /** 评估项目执行周期 */
        PROJECT_EXECUTION_CYCLE,
        
        /** 改进计划完成率 */
        IMPROVEMENT_PLAN_COMPLETION_RATE,
        
        /** 绩效提升率 */
        PERFORMANCE_IMPROVEMENT_RATE,
        
        /** 员工满意度 */
        EMPLOYEE_SATISFACTION_SCORE
    }
}

七、总结

本文详细介绍了360度绩效评估系统的设计与实现,主要内容包括:

核心技术点

  1. 多维度评估模型:支持工作业绩、工作能力、工作态度、团队协作等多维度评估
  2. 智能评估者选择:根据组织结构自动选择合适的评估者
  3. 数据聚合算法:加权平均、异常值检测、数据清洗等算法
  4. 结果分析引擎:排名计算、趋势分析、对比分析
  5. 改进计划生成:基于评估结果自动生成改进建议

业务价值

  1. 全面性:多视角评估,全面了解员工表现
  2. 客观性:多人评估减少个人偏见
  3. 发展性:帮助员工识别优势和改进空间
  4. 数据化:量化评估结果,便于决策
  5. 自动化:自动化流程降低管理成本

扩展方向

  1. AI辅助评估:使用NLP技术分析评语,提取关键信息
  2. 智能推荐:根据历史数据推荐改进措施
  3. 预测分析:预测员工绩效发展趋势
  4. 实时反馈:支持实时评估和即时反馈
  5. 移动端优化:优化移动端评估体验

参考资料

  1. 360度反馈评估方法论
  2. 绩效管理最佳实践
  3. 《绩效管理:理论与实践》(第四版)
  4. 《组织行为学》(第18版)
  5. 《数据化运营:系统方法与实践案例》

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐