一、业务背景与挑战
1.1 绩效管理的痛点
在企业人力资源管理中,绩效管理是最具挑战性的核心业务之一。传统的绩效管理模式存在诸多问题:
传统绩效评估的局限性:
- 单一视角:仅由上级评估,缺乏全面性
- 主观偏差:评估者个人偏好影响评估结果
- 反馈延迟:年度或季度评估,反馈周期过长
- 缺乏数据:凭印象评估,缺乏客观数据支撑
- 发展导向弱:重考核轻发展,难以促进员工成长
- 形式主义:评估流于形式,难以反映真实绩效
相关链接:
- 🌐 官网:http://www.dianshixinxi.com/
- 📱 演示站:http://cloud.dianshixinxi.com:90/
- 🎨 Gitee:https://gitee.com/glorylion/JFinalOA
- 💻 GitCode:https://gitcode.com/Glory_Lion/pointlion-cloud

1.2 360度评估的价值
什么是360度绩效评估: 360度绩效评估(360-Degree Feedback)是一种全方位的绩效评估方法,通过被评估者的上级、下级、同事、自评、客户等多维度角色进行评估,全面、客观地反映员工的工作表现和能力素质。
360度评估的价值:
1.3 技术挑战
实现360度评估系统面临的技术挑战:
评估模型设计复杂
- 如何设计科学合理的评估维度?
- 如何平衡不同评估者的权重?
- 如何处理评估者之间的评分差异?
数据计算量大
- 评估者数量多:一个员工可能有10+个评估者
- 评估指标多:几十个评估指标
- 计算复杂:权重计算、异常值处理、数据聚合
实时性要求高
- 评估流程实时推进
- 评估进度实时跟踪
- 评估结果实时计算
数据隐私敏感
- 评估结果涉及个人隐私
- 需要严格的权限控制
- 匿名评估的技术实现
业务流程复杂
- 评估周期管理
- 评估者选择策略
- 异常情况处理(评估者离职、请假等)
二、整体架构设计
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 <span class=\"token cla