相关链接

一、业务背景与挑战

1.1 传统绩效管理的痛点

在数字化转型的浪潮中,企业越来越重视目标管理。传统的KPI(关键绩效指标)管理模式存在诸多问题:

传统KPI模式的局限性

  • 目标脱节:员工目标与企业战略脱节,各自为政
  • 短期导向:过度关注短期指标,忽视长期价值创造
  • 僵化执行:目标设定后难以调整,无法适应变化
  • 考核至上:以考核为目的,而非以改进为目的
  • 缺乏激励:员工对目标缺乏认同感,内驱力不足
  • 沟通不畅:目标制定过程缺乏透明度和员工参与


在这里插入图片描述

1.2 OKR方法论的价值

什么是OKR
OKR(Objectives and Key Results,目标与关键结果)是一种目标管理框架,由Intel引入Google并在硅谷科技企业中广为流行。

OKR的核心要素

  • Objective(目标):回答"我想实现什么",定性描述,鼓舞人心
  • Key Result(关键结果):回答"我如何实现目标",定量衡量,可验证达成
  • 周期性:按季度或月度设定,保持灵活性和适应性
  • 透明公开:全员可见,促进对齐和协作
  • 挑战性:设定具有挑战性的目标,激发团队潜能

OKR管理的价值

  1. 战略对齐:确保个人目标与团队目标、企业战略高度对齐
  2. 聚焦重点:明确优先级,集中精力在重要事项上
  3. 透明协作:目标公开透明,促进跨部门协作
  4. 持续改进:定期检视和调整,保持敏捷性
  5. 内驱激发:自主设定目标,增强员工参与感和责任感

1.3 技术实现挑战

OKR系统的技术挑战

  1. 目标对齐算法:如何实现多层目标的有效对齐和联动
  2. 进度跟踪机制:如何实时跟踪关键结果的完成进度
  3. 数据聚合计算:如何高效地聚合和计算OKR达成率
  4. 变更控制流程:如何管理目标的变更历史和影响分析
  5. 可视化展示:如何直观地展示OKR层级关系和进度状态

二、整体架构设计

2.1 系统架构全景

┌─────────────────────────────────────────────────────────────────┐
│                    OKR目标管理系统                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                   │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                    目标配置管理层                          │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 目标模板管理 │  │ 目标周期管理 │  │ 权重配置管理    │  │   │
│  │  │(Template)    │  │(Cycle)       │  │(Weight)         │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 目标对齐配置 │  │ 关键结果配置 │  │ 进度跟踪配置    │  │   │
│  │  │(Alignment)   │  │(Key Result)  │  │(Tracking)       │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  └──────────────────────────────────────────────────────────┘   │
│                           │                                      │
│                           ▼                                      │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                    目标管理引擎层                            │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 目标创建引擎 │  │ 目标对齐引擎 │  │ 进度计算引擎    │  │   │
│  │  │(Creator)     │  │(Alignment)   │  │(ProgressCalc)   │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 变更控制引擎 │  │ 通知提醒引擎 │  │ 审批流程引擎    │  │   │
│  │  │(ChangeCtrl)  │  │(Notifier)    │  │(Approval)       │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  └──────────────────────────────────────────────────────────┘   │
│                           │                                      │
│                           ▼                                      │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                    数据分析层                                │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 达成率分析   │  │ 趋势分析引擎 │  │ 对比分析引擎    │  │   │
│  │  │(Achievement)  │  │(Trend)       │  │(Comparison)     │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 预测分析引擎 │  │ 异常检测引擎 │  │ 智能推荐引擎    │  │   │
│  │  │(Prediction)   │  │(Anomaly)     │  │(Recommend)      │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  └──────────────────────────────────────────────────────────┘   │
│                           │                                      │
│                           ▼                                      │
│  ┌──────────────────────────────────────────────────────────┐   │
│  │                    可视化展示层                              │   │
│  │  ┌──────────────┐  ┌──────────────┐  ┌─────────────────┐  │   │
│  │  │ 目标树状图   │  │ 进度仪表盘   │  │ 达成率报表      │  │   │
│  │  │(Tree View)   │  │(Dashboard)   │  │(Report)         │  │   │
│  │  └──────────────┘  └──────────────┘  └─────────────────┘  │   │
│  └──────────────────────────────────────────────────────────┘   │
│                                                                   │
└─────────────────────────────────────────────────────────────────┘

2.2 核心领域模型

OKR目标模型(ObjectiveDO)

/**
 * OKR目标领域对象
 * 表示一个OKR周期内的目标
 */
@Data
@TableName("hrm_okr_objective")
public class ObjectiveDO {
    /**
     * 目标ID
     */
    @TableId(type = IdType.ASSIGN_ID)
    private String id;
    
    /**
     * 目标周期ID
     */
    private String cycleId;
    
    /**
     * 目标类型:COMPANY(公司), DEPARTMENT(部门), TEAM(团队), PERSONAL(个人)
     */
    private ObjectiveType type;
    
    /**
     * 目标所属对象ID(部门ID、团队ID或员工ID)
     */
    private String ownerId;
    
    /**
     * 目标所属对象名称
     */
    private String ownerName;
    
    /**
     * 父目标ID(用于目标对齐)
     */
    private String parentObjectiveId;
    
    /**
     * 目标标题(定性描述,鼓舞人心)
     * 示例:"打造业界领先的移动办公体验"
     */
    private String title;
    
    /**
     * 目标描述
     */
    @Lob
    private String description;
    
    /**
     * 目标状态:DRAFT(草稿), ACTIVE(进行中), PAUSED(暂停), COMPLETED(已完成), CANCELLED(已取消)
     */
    private ObjectiveStatus status;
    
    /**
     * 目标进度(0-100)
     */
    private BigDecimal progress;
    
    /**
     * 优先级:P0(最高), P1(高), P2(中), P3(低)
     */
    private ObjectivePriority priority;
    
    /**
     * 目标分类:STRATEGIC(战略), GROWTH(增长), PRODUCT(产品), CULTURE(文化)
     */
    private ObjectiveCategory category;
    
    /**
     * 开始时间
     */
    private LocalDate startDate;
    
    /**
     * 结束时间
     */
    private LocalDate endDate;
    
    /**
     * 创建人ID
     */
    private String creatorId;
    
    /**
     * 创建人姓名
     */
    private String creatorName;
    
    /**
     * 目标负责人ID
     */
    private String ownerId;
    
    /**
     * 目标负责人姓名
     */
    private String ownerName;
    
    /**
     * 参与人ID列表(JSON数组)
     */
    @Lob
    private String participantIds;
    
    /**
     * 观察者ID列表(JSON数组)
     */
    @Lob
    private String watcherIds;
    
    /**
     * 是否公开
     */
    private Boolean isPublic;
    
    /**
     * 置信度评分(0-10)
     */
    private Integer confidenceScore;
    
    /**
     * 难度评分(0-10)
     */
    private Integer difficultyScore;
    
    /**
     * 扩展属性(JSON格式)
     */
    @Lob
    private String extProperties;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}

/**
 * 目标类型枚举
 */
public enum ObjectiveType {
    /** 公司级目标 */
    COMPANY,
    
    /** 部门级目标 */
    DEPARTMENT,
    
    /** 团队级目标 */
    TEAM,
    
    /** 个人目标 */
    PERSONAL
}

/**
 * 目标状态枚举
 */
public enum ObjectiveStatus {
    /** 草稿 */
    DRAFT,
    
    /** 进行中 */
    ACTIVE,
    
    /** 暂停 */
    PAUSED,
    
    /** 已完成 */
    COMPLETED,
    
    /** 已取消 */
    CANCELLED
}

/**
 * 目标优先级枚举
 */
public enum ObjectivePriority {
    /** P0 - 最高优先级 */
    P0(0),
    
    /** P1 - 高优先级 */
    P1(1),
    
    /** P2 - 中优先级 */
    P2(2),
    
    /** P3 - 低优先级 */
    P3(3);
    
    private final Integer code;
    
    ObjectivePriority(Integer code) {
        this.code = code;
    }
    
    public Integer getCode() {
        return code;
    }
}

关键结果模型(KeyResultDO)

/**
 * 关键结果领域对象
 * 表示目标下的关键结果,用于量化目标达成情况
 */
@Data
@TableName("hrm_okr_key_result")
public class KeyResultDO {
    /**
     * 关键结果ID
     */
    @TableId(type = IdType.ASSIGN_ID)
    private String id;
    
    /**
     * 所属目标ID
     */
    private String objectiveId;
    
    /**
     * 关键结果描述
     * 示例:"移动端DAU提升50%"
     */
    private String title;
    
    /**
     * 关键结果描述
     */
    @Lob
    private String description;
    
    /**
     * 当前值
     */
    private BigDecimal currentValue;
    
    /**
     * 目标值
     */
    private BigDecimal targetValue;
    
    /**
     * 单位
     */
    private String unit;
    
    /**
     * 数据类型:NUMBER(数值), PERCENTAGE(百分比), CURRENCY(金额), COUNT(计数)
     */
    private KeyResultDataType dataType;
    
    /**
     * 进度方向:INCREASE(增长), DECREASE(减少), MAINTAIN(维持)
     */
    private ProgressDirection direction;
    
    /**
     * 权重(0-100)
     */
    private BigDecimal weight;
    
    /**
     * 进度(0-100)
     */
    private BigDecimal progress;
    
    /**
     * 状态:NOT_STARTED(未开始), IN_PROGRESS(进行中), ACHIEVED(已达成), AT_RISK(有风险), NOT_ACHIEVED(未达成)
     */
    private KeyResultStatus status;
    
    /**
     * 截止时间
     */
    private LocalDate dueDate;
    
    /**
     * 负责人ID
     */
    private String ownerId;
    
    /**
     * 负责人姓名
     */
    private String ownerName;
    
    /**
     * 数据源类型:MANUAL(手动录入), SYSTEM(系统同步), API(外部接口)
     */
    private DataSourceType dataSourceType;
    
    /**
     * 数据源配置(JSON格式)
     * 记录数据源配置,如API地址、同步频率等
     */
    @Lob
    private String dataSourceConfig;
    
    /**
     * 最后同步时间
     */
    private LocalDateTime lastSyncTime;
    
    /**
     * 进度更新时间
     */
    private LocalDateTime progressUpdateTime;
    
    /**
     * 扩展属性(JSON格式)
     */
    @Lob
    private String extProperties;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}

/**
 * 关键结果数据类型枚举
 */
public enum KeyResultDataType {
    /** 数值型 */
    NUMBER,
    
    /** 百分比型 */
    PERCENTAGE,
    
    /** 金额型 */
    CURRENCY,
    
    /** 计数型 */
    COUNT
}

/**
 * 进度方向枚举
 */
public enum ProgressDirection {
    /** 增长型:越大越好 */
    INCREASE,
    
    /** 下降型:越小越好 */
    DECREASE,
    
    /** 维持型:稳定在目标值附近 */
    MAINTAIN
}

/**
 * 关键结果状态枚举
 */
public enum KeyResultStatus {
    /** 未开始 */
    NOT_STARTED,
    
    /** 进行中 */
    IN_PROGRESS,
    
    /** 已达成 */
    ACHIEVED,
    
    /** 有风险 */
    AT_RISK,
    
    /** 未达成 */
    NOT_ACHIEVED
}

2.3 OKR周期模型

OKR周期模型(OkrCycleDO)

/**
 * OKR周期领域对象
 * 定义一个OKR管理周期
 */
@Data
@TableName("hrm_okr_cycle")
public class OkrCycleDO {
    /**
     * 周期ID
     */
    @TableId(type = IdType.ASSIGN_ID)
    private String id;
    
    /**
     * 周期名称
     * 示例:"2024年Q1", "2024年3月"
     */
    private String name;
    
    /**
     * 周期类型:QUARTERLY(季度), MONTHLY(月度), WEEKLY(周度), CUSTOM(自定义)
     */
    private CycleType type;
    
    /**
     * 年份
     */
    private Integer year;
    
    /**
     * 季度(1-4)
     */
    private Integer quarter;
    
    /**
     * 月份(1-12)
     */
    private Integer month;
    
    /**
     * 开始日期
     */
    private LocalDate startDate;
    
    /**
     * 结束日期
     */
    private LocalDate endDate;
    
    /**
     * 周期状态:DRAFT(草稿), ACTIVE(进行中), CLOSED(已关闭)
     */
    private CycleStatus status;
    
    /**
     * 目标制定开始时间
     */
    private LocalDateTime planningStartTime;
    
    /**
     * 目标制定截止时间
     */
    private LocalDateTime planningEndTime;
    
    /**
     * 目标执行开始时间
     */
    private LocalDateTime executionStartTime;
    
    /**
     * 目标执行截止时间
     */
    private LocalDateTime executionEndTime;
    
    /**
     * 复盘开始时间
     */
    private LocalDateTime reviewStartTime;
    
    /**
     * 复盘截止时间
     */
    private LocalDateTime reviewEndTime;
    
    /**
     * 参与人员范围
     * ALL(全员), DEPARTMENT(指定部门), TEAM(指定团队)
     */
    private ParticipantScope participantScope;
    
    /**
     * 参与人员配置(JSON格式)
     */
    @Lob
    private String participantConfig;
    
    /**
     * OKR模板ID
     */
    private String templateId;
    
    /**
     * 目标总数
     */
    private Integer totalObjectives;
    
    /**
     * 进行中目标数
     */
    private Integer activeObjectives;
    
    /**
     * 已完成目标数
     */
    private Integer completedObjectives;
    
    /**
     * 平均达成率
     */
    private BigDecimal averageAchievementRate;
    
    /**
     * 扩展配置(JSON格式)
     */
    @Lob
    private String extConfig;
    
    /**
     * 创建时间
     */
    private LocalDateTime createTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updateTime;
}

/**
 * 周期类型枚举
 */
public enum CycleType {
    /** 季度周期 */
    QUARTERLY,
    
    /** 月度周期 */
    MONTHLY,
    
    /** 周度周期 */
    WEEKLY,
    
    /** 自定义周期 */
    CUSTOM
}

/**
 * 周期状态枚举
 */
public enum CycleStatus {
    /** 草稿 */
    DRAFT,
    
    /** 进行中 */
    ACTIVE,
    
    /** 已关闭 */
    CLOSED
}

/**
 * 参与人员范围枚举
 */
public enum ParticipantScope {
    /** 全员 */
    ALL,
    
    /** 指定部门 */
    DEPARTMENT,
    
    /** 指定团队 */
    TEAM
}

三、核心模块实现

3.1 目标对齐引擎

目标对齐算法实现

/**
 * OKR目标对齐引擎
 * 实现个人目标与团队目标、团队目标与部门目标、部门目标与公司目标的对齐
 */
@Service
@Slf4j
public class ObjectiveAlignmentEngine {
    
    @Autowired
    private ObjectiveMapper objectiveMapper;
    
    @Autowired
    private KeyResultMapper keyResultMapper;
    
    /**
     * 创建对齐关系
     * 
     * @param childObjectiveId 子目标ID
     * @param parentObjectiveId 父目标ID
     */
    @Transactional(rollbackFor = Exception.class)
    public void createAlignment(String childObjectiveId, String parentObjectiveId) {
        log.info("创建目标对齐: child={}, parent={}", childObjectiveId, parentObjectiveId);
        
        // 1. 获取目标信息
        ObjectiveDO childObjective = objectiveMapper.selectById(childObjectiveId);
        ObjectiveDO parentObjective = objectiveMapper.selectById(parentObjectiveId);
        
        if (childObjective == null || parentObjective == null) {
            throw new BusinessException("目标不存在");
        }
        
        // 2. 验证对齐关系
        validateAlignment(childObjective, parentObjective);
        
        // 3. 建立对齐关系
        childObjective.setParentObjectiveId(parentObjectiveId);
        objectiveMapper.updateById(childObjective);
        
        // 4. 记录对齐日志
        ObjectiveAlignmentLog alignmentLog = new ObjectiveAlignmentLog();
        alignmentLog.setId(IdUtil.fastUUID());
        alignmentLog.setChildObjectiveId(childObjectiveId);
        alignmentLog.setParentObjectiveId(parentObjectiveId);
        alignmentLog.setChildObjectiveTitle(childObjective.getTitle());
        alignmentLog.setParentObjectiveTitle(parentObjective.getTitle());
        alignmentLog.setAlignmentTime(LocalDateTime.now());
        alignmentLog.setOperator(SecurityContextHolder.getUserId());
        alignmentLogMapper.insert(alignmentLog);
        
        log.info("目标对齐创建完成: child={}, parent={}", childObjectiveId, parentObjectiveId);
    }
    
    /**
     * 批量自动对齐
     * 
     * @param cycleId 周期ID
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchAutoAlignment(String cycleId) {
        log.info("开始批量自动对齐: cycleId={}", cycleId);
        
        // 1. 获取该周期下所有未对齐的目标
        List<ObjectiveDO> unalignedObjectives = objectiveMapper.selectList(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycleId)
                .isNull(ObjectiveDO::getParentObjectiveId)
                .ne(ObjectiveDO::getStatus, ObjectiveStatus.CANCELLED)
        );
        
        log.info("找到未对齐目标: count={}", unalignedObjectives.size());
        
        // 2. 按目标类型分组对齐
        for (ObjectiveDO objective : unalignedObjectives) {
            try {
                alignObjectiveToParent(objective);
            } catch (Exception e) {
                log.error("对齐目标失败: objectiveId={}, error={}", 
                    objective.getId(), e.getMessage());
            }
        }
        
        log.info("批量自动对齐完成: cycleId={}", cycleId);
    }
    
    /**
     * 对齐目标到父目标
     * 
     * @param objective 目标
     */
    private void alignObjectiveToParent(ObjectiveDO objective) {
        switch (objective.getType()) {
            case PERSONAL:
                // 个人目标对齐到团队目标
                alignPersonalToTeam(objective);
                break;
                
            case TEAM:
                // 团队目标对齐到部门目标
                alignTeamToDepartment(objective);
                break;
                
            case DEPARTMENT:
                // 部门目标对齐到公司目标
                alignDepartmentToCompany(objective);
                break;
                
            case COMPANY:
                // 公司级目标无需对齐
                break;
        }
    }
    
    /**
     * 个人目标对齐到团队目标
     */
    private void alignPersonalToTeam(ObjectiveDO personalObjective) {
        // 1. 获取员工的团队信息
        String employeeId = personalObjective.getOwnerId();
        String teamId = getTeamIdByEmployeeId(employeeId);
        
        if (StringUtils.isBlank(teamId)) {
            log.warn("员工未分配团队: employeeId={}", employeeId);
            return;
        }
        
        // 2. 查找团队的活跃目标
        ObjectiveDO teamObjective = objectiveMapper.selectOne(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, personalObjective.getCycleId())
                .eq(ObjectiveDO::getOwnerId, teamId)
                .eq(ObjectiveDO::getType, ObjectiveType.TEAM)
                .eq(ObjectiveDO::getStatus, ObjectiveStatus.ACTIVE)
                .orderByDesc(ObjectiveDO::getCreateTime)
                .last("LIMIT 1")
        );
        
        if (teamObjective != null) {
            // 创建对齐关系
            createAlignment(personalObjective.getId(), teamObjective.getId());
            
            log.info("个人目标已对齐到团队目标: personal={}, team={}", 
                personalObjective.getTitle(), teamObjective.getTitle());
        }
    }
    
    /**
     * 团队目标对齐到部门目标
     */
    private void alignTeamToDepartment(ObjectiveDO teamObjective) {
        // 1. 获取团队的部门信息
        String teamId = teamObjective.getOwnerId();
        String departmentId = getDepartmentIdByTeamId(teamId);
        
        if (StringUtils.isBlank(departmentId)) {
            log.warn("团队未分配部门: teamId={}", teamId);
            return;
        }
        
        // 2. 查找部门的活跃目标
        ObjectiveDO departmentObjective = objectiveMapper.selectOne(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, teamObjective.getCycleId())
                .eq(ObjectiveDO::getOwnerId, departmentId)
                .eq(ObjectiveDO::getType, ObjectiveType.DEPARTMENT)
                .eq(ObjectiveDO::getStatus, ObjectiveStatus.ACTIVE)
                .orderByDesc(ObjectiveDO::getCreateTime)
                .last("LIMIT 1")
        );
        
        if (departmentObjective != null) {
            // 创建对齐关系
            createAlignment(teamObjective.getId(), departmentObjective.getId());
            
            log.info("团队目标已对齐到部门目标: team={}, department={}", 
                teamObjective.getTitle(), departmentObjective.getTitle());
        }
    }
    
    /**
     * 部门目标对齐到公司目标
     */
    private void alignDepartmentToCompany(ObjectiveDO departmentObjective) {
        // 1. 查找公司的活跃目标
        ObjectiveDO companyObjective = objectiveMapper.selectOne(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, departmentObjective.getCycleId())
                .eq(ObjectiveDO::getType, ObjectiveType.COMPANY)
                .eq(ObjectiveDO::getStatus, ObjectiveStatus.ACTIVE)
                .orderByDesc(ObjectiveDO::getCreateTime)
                .last("LIMIT 1")
        );
        
        if (companyObjective != null) {
            // 创建对齐关系
            createAlignment(departmentObjective.getId(), companyObjective.getId());
            
            log.info("部门目标已对齐到公司目标: department={}, company={}", 
                departmentObjective.getTitle(), companyObjective.getTitle());
        }
    }
    
    /**
     * 验证对齐关系
     */
    private void validateAlignment(ObjectiveDO child, ObjectiveDO parent) {
        // 1. 检查周期是否相同
        if (!child.getCycleId().equals(parent.getCycleId())) {
            throw new BusinessException("父子目标的周期不一致");
        }
        
        // 2. 检查对齐路径是否合理
        ObjectiveType childType = child.getType();
        ObjectiveType parentType = parent.getType();
        
        if (!isValidAlignment(childType, parentType)) {
            throw new BusinessException(
                String.format("无效的对齐关系: %s -> %s", childType, parentType)
            );
        }
        
        // 3. 检查是否会造成循环对齐
        if (wouldCreateCircularAlignment(child.getId(), parent.getId())) {
            throw new BusinessException("对齐关系会造成循环依赖");
        }
    }
    
    /**
     * 检查对齐关系是否有效
     */
    private boolean isValidAlignment(ObjectiveType childType, ObjectiveType parentType) {
        // 个人 -> 团队
        if (childType == ObjectiveType.PERSONAL && parentType == ObjectiveType.TEAM) {
            return true;
        }
        
        // 团队 -> 部门
        if (childType == ObjectiveType.TEAM && parentType == ObjectiveType.DEPARTMENT) {
            return true;
        }
        
        // 部门 -> 公司
        if (childType == ObjectiveType.DEPARTMENT && parentType == ObjectiveType.COMPANY) {
            return true;
        }
        
        return false;
    }
    
    /**
     * 检查是否会造成循环对齐
     */
    private boolean wouldCreateCircularAlignment(String childId, String parentId) {
        // 检查父目标是否已经对齐到子目标(直接或间接)
        Set<String> ancestorIds = new HashSet<>();
        String currentParentId = parentId;
        
        while (StringUtils.isNotBlank(currentParentId)) {
            if (childId.equals(currentParentId)) {
                return true; // 发现循环
            }
            
            if (ancestorIds.contains(currentParentId)) {
                return true; // 发现循环
            }
            
            ancestorIds.add(currentParentId);
            
            ObjectiveDO currentParent = objectiveMapper.selectById(currentParentId);
            if (currentParent == null) {
                break;
            }
            
            currentParentId = currentParent.getParentObjectiveId();
        }
        
        return false;
    }
    
    /**
     * 获取员工的团队ID
     */
    private String getTeamIdByEmployeeId(String employeeId) {
        // 从员工信息中获取团队ID
        EmployeeDO employee = employeeMapper.selectById(employeeId);
        return employee != null ? employee.getTeamId() : null;
    }
    
    /**
     * 获取团队的部门ID
     */
    private String getDepartmentIdByTeamId(String teamId) {
        // 从团队信息中获取部门ID
        TeamDO team = teamMapper.selectById(teamId);
        return team != null ? team.getDepartmentId() : null;
    }
}

3.2 进度计算引擎

OKR进度计算引擎

/**
 * OKR进度计算引擎
 * 负责计算目标整体进度和关键结果进度
 */
@Service
@Slf4j
public class OkrProgressCalculationEngine {
    
    @Autowired
    private KeyResultMapper keyResultMapper;
    
    @Autowired
    private ObjectiveMapper objectiveMapper;
    
    /**
     * 计算目标进度
     * 
     * @param objectiveId 目标ID
     * @return 计算后的进度值
     */
    @Transactional(rollbackFor = Exception.class)
    public BigDecimal calculateObjectiveProgress(String objectiveId) {
        log.info("计算目标进度: objectiveId={}", objectiveId);
        
        // 1. 获取目标信息
        ObjectiveDO objective = objectiveMapper.selectById(objectiveId);
        if (objective == null) {
            throw new BusinessException("目标不存在");
        }
        
        // 2. 获取目标的所有关键结果
        List<KeyResultDO> keyResults = keyResultMapper.selectList(
            new LambdaQueryWrapper<KeyResultDO>()
                .eq(KeyResultDO::getObjectiveId, objectiveId)
                .eq(KeyResultDO::getStatus, KeyResultStatus.ACTIVE)
        );
        
        if (keyResults.isEmpty()) {
            log.warn("目标没有关键结果: objectiveId={}", objectiveId);
            return BigDecimal.ZERO;
        }
        
        // 3. 加权计算整体进度
        BigDecimal totalProgress = BigDecimal.ZERO;
        BigDecimal totalWeight = BigDecimal.ZERO;
        
        for (KeyResultDO keyResult : keyResults) {
            // 计算单个关键结果的进度
            BigDecimal krProgress = calculateKeyResultProgress(keyResult);
            
            // 更新关键结果进度
            keyResult.setProgress(krProgress);
            keyResultMapper.updateById(keyResult);
            
            // 加权汇总
            BigDecimal weight = keyResult.getWeight() != null ? 
                keyResult.getWeight() : new BigDecimal("1");
            
            totalProgress = totalProgress.add(krProgress.multiply(weight));
            totalWeight = totalWeight.add(weight);
        }
        
        // 4. 计算平均进度
        BigDecimal averageProgress = totalWeight.compareTo(BigDecimal.ZERO) > 0 ?
            totalProgress.divide(totalWeight, 2, RoundingMode.HALF_UP) : 
            BigDecimal.ZERO;
        
        // 5. 更新目标进度
        objective.setProgress(averageProgress);
        objectiveMapper.updateById(objective);
        
        // 6. 根据进度更新状态
        updateObjectiveStatus(objective);
        
        log.info("目标进度计算完成: objectiveId={}, progress={}%", 
            objectiveId, averageProgress);
        
        return averageProgress;
    }
    
    /**
     * 计算关键结果进度
     * 
     * @param keyResult 关键结果
     * @return 进度值(0-100)
     */
    private BigDecimal calculateKeyResultProgress(KeyResultDO keyResult) {
        BigDecimal currentValue = keyResult.getCurrentValue();
        BigDecimal targetValue = keyResult.getTargetValue();
        
        if (currentValue == null || targetValue == null || 
            targetValue.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        
        ProgressDirection direction = keyResult.getDirection();
        if (direction == null) {
            direction = ProgressDirection.INCREASE;
        }
        
        switch (direction) {
            case INCREASE:
                // 增长型:越大越好
                return currentValue.multiply(new BigDecimal("100"))
                    .divide(targetValue, 2, RoundingMode.HALF_UP)
                    .min(new BigDecimal("100"));
                
            case DECREASE:
                // 下降型:越小越好
                return currentValue.divide(targetValue, 2, RoundingMode.HALF_UP)
                    .multiply(new BigDecimal("100"))
                    .min(new BigDecimal("100"));
                
            case MAINTAIN:
                // 维持型:在目标值附近
                BigDecimal deviation = currentValue.subtract(targetValue).abs();
                BigDecimal tolerance = targetValue.multiply(new BigDecimal("0.1")); // 10%容差
                
                if (deviation.compareTo(tolerance) <= 0) {
                    return new BigDecimal("100");
                } else {
                    BigDecimal excess = deviation.subtract(tolerance);
                    BigDecimal penalty = excess.multiply(new BigDecimal("100"))
                        .divide(targetValue, 2, RoundingMode.HALF_UP);
                    return new BigDecimal("100").subtract(penalty).max(BigDecimal.ZERO);
                }
                
            default:
                return BigDecimal.ZERO;
        }
    }
    
    /**
     * 批量计算周期内所有目标的进度
     * 
     * @param cycleId 周期ID
     */
    @Transactional(rollbackFor = Exception.class)
    public void batchCalculateCycleProgress(String cycleId) {
        log.info("批量计算周期进度: cycleId={}", cycleId);
        
        // 1. 获取周期内所有活跃目标
        List<ObjectiveDO> activeObjectives = objectiveMapper.selectList(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycleId)
                .eq(ObjectiveDO::getStatus, ObjectiveStatus.ACTIVE)
        );
        
        log.info("找到活跃目标: count={}", activeObjectives.size());
        
        // 2. 并行计算各目标进度
        activeObjectives.parallelStream().forEach(objective -> {
            try {
                calculateObjectiveProgress(objective.getId());
            } catch (Exception e) {
                log.error("计算目标进度失败: objectiveId={}, error={}", 
                    objective.getId(), e.getMessage());
            }
        });
        
        // 3. 计算周期整体达成率
        BigDecimal cycleAchievementRate = calculateCycleAchievementRate(cycleId);
        
        // 4. 更新周期统计
        OkrCycleDO cycle = okrCycleMapper.selectById(cycleId);
        if (cycle != null) {
            cycle.setAverageAchievementRate(cycleAchievementRate);
            
            // 更新统计信息
            updateCycleStatistics(cycle);
            
            okrCycleMapper.updateById(cycle);
        }
        
        log.info("周期进度批量计算完成: cycleId={}, achievementRate={}%", 
            cycleId, cycleAchievementRate);
    }
    
    /**
     * 计算周期整体达成率
     */
    private BigDecimal calculateCycleAchievementRate(String cycleId) {
        // 获取周期内所有已完成的目标
        List<ObjectiveDO> completedObjectives = objectiveMapper.selectList(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycleId)
                .eq(ObjectiveDO::getStatus, ObjectiveStatus.COMPLETED)
        );
        
        if (completedObjectives.isEmpty()) {
            return BigDecimal.ZERO;
        }
        
        // 计算平均达成率
        BigDecimal totalProgress = completedObjectives.stream()
            .map(ObjectiveDO::getProgress)
            .reduce(BigDecimal.ZERO, BigDecimal::add);
        
        return totalProgress.divide(
            new BigDecimal(completedObjectives.size()),
            2,
            RoundingMode.HALF_UP
        );
    }
    
    /**
     * 更新周期统计信息
     */
    private void updateCycleStatistics(OkrCycleDO cycle) {
        Long totalCount = objectiveMapper.selectCount(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycle.getId())
        );
        
        Long activeCount = objectiveMapper.selectCount(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycle.getId())
                .eq(ObjectiveDO::getStatus, ObjectiveStatus.ACTIVE)
        );
        
        Long completedCount = objectiveMapper.selectCount(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycle.getId())
                .eq(ObjectiveDO::getStatus, ObjectiveStatus.COMPLETED)
        );
        
        cycle.setTotalObjectives(totalCount.intValue());
        cycle.setActiveObjectives(activeCount.intValue());
        cycle.setCompletedObjectives(completedCount.intValue());
    }
    
    /**
     * 更新目标状态
     */
    private void updateObjectiveStatus(ObjectiveDO objective) {
        ObjectiveStatus currentStatus = objective.getStatus();
        BigDecimal progress = objective.getProgress();
        LocalDate endDate = objective.getEndDate();
        
        // 如果已完成,无需更新
        if (currentStatus == ObjectiveStatus.COMPLETED || 
            currentStatus == ObjectiveStatus.CANCELLED) {
            return;
        }
        
        // 检查是否应该标记为已完成
        if (progress.compareTo(new BigDecimal("100")) >= 0) {
            objective.setStatus(ObjectiveStatus.COMPLETED);
            objectiveMapper.updateById(objective);
            return;
        }
        
        // 检查是否已过期
        if (endDate != null && LocalDate.now().isAfter(endDate)) {
            objective.setStatus(ObjectiveStatus.COMPLETED);
            objectiveMapper.updateById(objective);
            return;
        }
        
        // 检查是否应该暂停
        if (shouldPause(objective)) {
            objective.setStatus(ObjectiveStatus.PAUSED);
            objectiveMapper.updateById(objective);
        }
    }
    
    /**
     * 检查是否应该暂停目标
     */
    private boolean shouldPause(ObjectiveDO objective) {
        // 检查关键结果是否全部有风险
        List<KeyResultDO> keyResults = keyResultMapper.selectList(
            new LambdaQueryWrapper<KeyResultDO>()
                .eq(KeyResultDO::getObjectiveId, objective.getId())
        );
        
        long atRiskCount = keyResults.stream()
            .filter(kr -> KeyResultStatus.AT_RISK.equals(kr.getStatus()))
            .count();
        
        // 如果超过50%的关键结果有风险,标记为暂停
        return !keyResults.isEmpty() && 
               (double) atRiskCount / keyResults.size() > 0.5;
    }
}

3.3 目标创建引擎

目标创建服务

/**
 * OKR目标创建服务
 * 支持从模板创建、手动创建、批量创建等多种创建方式
 */
@Service
@Slf4j
public class ObjectiveCreationService {
    
    @Autowired
    private ObjectiveMapper objectiveMapper;
    
    @Autowired
    private ObjectiveTemplateMapper templateMapper;
    
    @Autowired
    private OkrProgressCalculationEngine progressCalculationEngine;
    
    /**
     * 从模板创建目标
     * 
     * @param cycleId 周期ID
     * @param templateId 模板ID
     * @param ownerId 所有者ID
     * @param customParams 自定义参数
     * @return 创建的目标ID
     */
    @Transactional(rollbackFor = Exception.class)
    public String createFromTemplate(
        String cycleId,
        String templateId,
        String ownerId,
        Map<String, Object> customParams
    ) {
        log.info("从模板创建目标: cycleId={}, templateId={}, ownerId={}", 
            cycleId, templateId, ownerId);
        
        // 1. 获取模板
        ObjectiveTemplateDO template = templateMapper.selectById(templateId);
        if (template == null) {
            throw new BusinessException("模板不存在");
        }
        
        // 2. 解析模板内容
        List<ObjectiveTemplateItem> templateItems = JSON.parseArray(
            template.getContent(), ObjectiveTemplateItem.class
        );
        
        if (templateItems.isEmpty()) {
            throw new BusinessException("模板内容为空");
        }
        
        // 3. 创建主目标
        ObjectiveTemplateItem mainItem = templateItems.get(0);
        ObjectiveDO objective = new ObjectiveDO();
        objective.setId(IdUtil.fastUUID());
        objective.setCycleId(cycleId);
        objective.setType(template.getType());
        objective.setOwnerId(ownerId);
        objective.setTitle(mainItem.getTitle());
        objective.setDescription(mainItem.getDescription());
        objective.setStatus(ObjectiveStatus.DRAFT);
        objective.setPriority(ObjectivePriority.P2);
        objective.setCreateTime(LocalDateTime.now());
        
        // 应用自定义参数
        applyCustomParams(objective, customParams);
        
        objectiveMapper.insert(objective);
        
        // 4. 创建关键结果
        List<KeyResultDO> keyResults = new ArrayList<>();
        
        for (int i = 1; i < templateItems.size(); i++) {
            ObjectiveTemplateItem item = templateItems.get(i);
            
            KeyResultDO keyResult = new KeyResultDO();
            keyResult.setId(IdUtil.fastUUID());
            keyResult.setObjectiveId(objective.getId());
            keyResult.setTitle(item.getTitle());
            keyResult.setDescription(item.getDescription());
            keyResult.setTargetValue(new BigDecimal(item.getTargetValue()));
            keyResult.setUnit(item.getUnit());
            keyResult.setDataType(parseDataType(item.getDataType()));
            keyResult.setDirection(parseDirection(item.getDirection()));
            keyResult.setWeight(new BigDecimal(item.getWeight()));
            keyResult.setStatus(KeyResultStatus.NOT_STARTED);
            keyResult.setCreateTime(LocalDateTime.now());
            
            keyResults.add(keyResult);
        }
        
        // 5. 批量保存关键结果
        if (!keyResults.isEmpty()) {
            keyResultMapper.insertBatch(keyResults);
        }
        
        // 6. 初始计算进度
        progressCalculationEngine.calculateObjectiveProgress(objective.getId());
        
        log.info("从模板创建目标完成: objectiveId={}, keyResultCount={}", 
            objective.getId(), keyResults.size());
        
        return objective.getId();
    }
    
    /**
     * 手动创建目标
     * 
     * @param request 创建请求
     * @return 创建的目标ID
     */
    @Transactional(rollbackFor = Exception.class)
    public String createManually(ObjectiveCreateRequest request) {
        log.info("手动创建目标: ownerId={}, title={}", 
            request.getOwnerId(), request.getTitle());
        
        // 1. 验证请求
        validateCreateRequest(request);
        
        // 2. 创建目标
        ObjectiveDO objective = new ObjectiveDO();
        objective.setId(IdUtil.fastUUID());
        objective.setCycleId(request.getCycleId());
        objective.setType(request.getType());
        objective.setOwnerId(request.getOwnerId());
        objective.setOwnerName(request.getOwnerName());
        objective.setTitle(request.getTitle());
        objective.setDescription(request.getDescription());
        objective.setStatus(ObjectiveStatus.DRAFT);
        objective.setPriority(request.getPriority());
        objective.setCategory(request.getCategory());
        objective.setStartDate(request.getStartDate());
        objective.setEndDate(request.getEndDate());
        objective.setIsPublic(request.getIsPublic());
        objective.setCreatorId(SecurityContextHolder.getUserId());
        objective.setCreateTime(LocalDateTime.now());
        
        objectiveMapper.insert(objective);
        
        // 3. 创建关键结果
        if (request.getKeyResults() != null && !request.getKeyResults().isEmpty()) {
            List<KeyResultDO> keyResults = new ArrayList<>();
            
            for (KeyResultCreateRequest krRequest : request.getKeyResults()) {
                KeyResultDO keyResult = new KeyResultDO();
                keyResult.setId(IdUtil.fastUUID());
                keyResult.setObjectiveId(objective.getId());
                keyResult.setTitle(krRequest.getTitle());
                keyResult.setDescription(krRequest.getDescription());
                keyResult.setTargetValue(krRequest.getTargetValue());
                keyResult.setUnit(krRequest.getUnit());
                keyResult.setDataType(krRequest.getDataType());
                keyResult.setDirection(krRequest.getDirection());
                keyResult.setWeight(krRequest.getWeight());
                keyResult.setStatus(KeyResultStatus.NOT_STARTED);
                keyResult.setOwnerId(krRequest.getOwnerId());
                keyResult.setOwnerName(krRequest.getOwnerName());
                keyResult.setCreateTime(LocalDateTime.now());
                
                keyResults.add(keyResult);
            }
            
            keyResultMapper.insertBatch(keyResults);
        }
        
        // 4. 发送通知给参与者和观察者
        sendNotificationToParticipants(objective);
        
        log.info("手动创建目标完成: objectiveId={}", objective.getId());
        
        return objective.getId();
    }
    
    /**
     * 应用自定义参数
     */
    private void applyCustomParams(ObjectiveDO objective, Map<String, Object> customParams) {
        if (customParams == null || customParams.isEmpty()) {
            return;
        }
        
        // 应用标题自定义
        if (customParams.containsKey("title")) {
            String title = (String) customParams.get("title");
            if (StringUtils.isNotBlank(title)) {
                objective.setTitle(title);
            }
        }
        
        // 应用描述自定义
        if (customParams.containsKey("description")) {
            String description = (String) customParams.get("description");
            if (StringUtils.isNotBlank(description)) {
                objective.setDescription(description);
            }
        }
        
        // 应用优先级自定义
        if (customParams.containsKey("priority")) {
            String priorityStr = (String) customParams.get("priority");
            if (StringUtils.isNotBlank(priorityStr)) {
                objective.setPriority(ObjectivePriority.valueOf(priorityStr));
            }
        }
        
        // 应用时间范围自定义
        if (customParams.containsKey("startDate")) {
            String startDateStr = (String) customParams.get("startDate");
            if (StringUtils.isNotBlank(startDateStr)) {
                objective.setStartDate(LocalDate.parse(startDateStr));
            }
        }
        
        if (customParams.containsKey("endDate")) {
            String endDateStr = (String) customParams.get("endDate");
            if (StringUtils.isNotBlank(endDateStr)) {
                objective.setEndDate(LocalDate.parse(endDateStr));
            }
        }
    }
    
    /**
     * 发送通知给参与者和观察者
     */
    private void sendNotificationToParticipants(ObjectiveDO objective) {
        try {
            // 构建通知消息
            String message = String.format(
                "【OKR通知】您被邀请参与目标 \"%s\",请及时查看并制定您的关键结果。",
                objective.getTitle()
            );
            
            // 发送给参与者
            if (StringUtils.isNotBlank(objective.getParticipantIds())) {
                List<String> participantIds = JSON.parseArray(
                    objective.getParticipantIds(), String.class
                );
                
                for (String participantId : participantIds) {
                    notificationService.sendNotification(participantId, message);
                }
            }
            
            // 发送给观察者
            if (StringUtils.isNotBlank(objective.getWatcherIds())) {
                List<String> watcherIds = JSON.parseArray(
                    objective.getWatcherIds(), String.class
                );
                
                for (String watcherId : watcherIds) {
                    notificationService.sendNotification(watcherId, message);
                }
            }
            
        } catch (Exception e) {
            log.error("发送通知失败: objectiveId={}, error={}", 
                objective.getId(), e.getMessage());
        }
    }
}

3.4 智能推荐引擎

OKR智能推荐服务

/**
 * OKR智能推荐引擎
 * 基于历史数据和AI算法推荐关键结果目标和数值
 */
@Service
@Slf4j
public class OkrRecommendationEngine {
    
    @Autowired
    private ObjectiveMapper objectiveMapper;
    
    @Autowired
    private KeyResultMapper keyResultMapper;
    
    /**
     * 推荐关键结果目标值
     * 
     * @param employeeId 员工ID
     * @param historicalData 历史数据
     * @return 推荐结果
     */
    public RecommendationResult recommendTargetValue(
        String employeeId,
        Map<String, Object> historicalData
    ) {
        log.info("推荐关键结果目标值: employeeId={}", employeeId);
        
        RecommendationResult result = new RecommendationResult();
        result.setEmployeeId(employeeId);
        
        // 1. 分析历史数据
        HistoricalPerformanceAnalysis analysis = analyzeHistoricalData(historicalData);
        
        // 2. 应用推荐算法
        BigDecimal recommendedValue = calculateRecommendedValue(analysis);
        
        result.setRecommendedValue(recommendedValue);
        result.setConfidence(calculateConfidence(analysis));
        result.setReason(buildRecommendationReason(analysis));
        
        log.info("推荐完成: employeeId={}, recommendedValue={}, confidence={}%", 
            employeeId, recommendedValue, result.getConfidence());
        
        return result;
    }
    
    /**
     * 分析历史绩效数据
     */
    private HistoricalPerformanceAnalysis analyzeHistoricalData(
        Map<String, Object> historicalData
    ) {
        HistoricalPerformanceAnalysis analysis = new HistoricalPerformanceAnalysis();
        
        // 1. 分析历史OKR完成率
        List<BigDecimal> completionRates = (List<BigDecimal>) 
            historicalData.getOrDefault("completionRates", Collections.emptyList());
        
        if (!completionRates.isEmpty()) {
            BigDecimal avgRate = completionRates.stream()
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(new BigDecimal(completionRates.size()), 2, RoundingMode.HALF_UP);
            analysis.setAverageCompletionRate(avgRate);
        }
        
        // 2. 分析绩效得分趋势
        List<BigDecimal> performanceScores = (List<BigDecimal>) 
            historicalData.getOrDefault("performanceScores", Collections.emptyList());
        
        if (!performanceScores.isEmpty()) {
            BigDecimal latestScore = performanceScores.get(performanceScores.size() - 1);
            analysis.setLatestPerformanceScore(latestScore);
            
            // 计算趋势
            BigDecimal trend = calculateTrend(performanceScores);
            analysis.setTrend(trend);
        }
        
        // 3. 分析同级别员工表现
        List<BigDecimal> peerScores = (List<BigDecimal>) 
            historicalData.getOrDefault("peerScores", Collections.emptyList());
        
        if (!peerScores.isEmpty()) {
            BigDecimal peerAvg = peerScores.stream()
                .reduce(BigDecimal.ZERO, BigDecimal::add)
                .divide(new BigDecimal(peerScores.size()), 2, RoundingMode.HALF_UP);
            analysis.setPeerAverageScore(peerAvg);
            
            // 计算员工在同级中的位置
            long betterThanCount = peerScores.stream()
                .filter(score -> score.compareTo(latestScore) < 0)
                .count();
            
            double percentile = (double) betterThanCount / peerScores.size();
            analysis.setPercentileRank(percentile);
        }
        
        return analysis;
    }
    
    /**
     * 计算推荐值
     */
    private BigDecimal calculateRecommendedValue(HistoricalPerformanceAnalysis analysis) {
        // 基于历史表现计算推荐值
        BigDecimal baseValue = analysis.getLatestPerformanceScore();
        BigDecimal trend = analysis.getTrend();
        
        // 如果趋势向上,增加目标
        if (trend.compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal increaseFactor = new BigDecimal("1.1"); // 增加10%
            return baseValue.multiply(increaseFactor).setScale(2, RoundingMode.HALF_UP);
        }
        
        // 如果趋势向下,适当降低目标
        if (trend.compareTo(BigDecimal.ZERO) < 0) {
            BigDecimal decreaseFactor = new BigDecimal("0.95"); // 降低5%
            return baseValue.multiply(decreaseFactor).setScale(2, RoundingMode.HALF_UP);
        }
        
        // 趋势平稳,保持目标
        return baseValue;
    }
    
    /**
     * 计算趋势
     */
    private BigDecimal calculateTrend(List<BigDecimal> values) {
        if (values.size() < 2) {
            return BigDecimal.ZERO;
        }
        
        // 使用线性回归计算趋势
        BigDecimal sumX = BigDecimal.ZERO;
        BigDecimal sumY = BigDecimal.ZERO;
        BigDecimal sumXY = BigDecimal.ZERO;
        BigDecimal sumX2 = BigDecimal.ZERO;
        
        int n = values.size();
        
        for (int i = 0; i < n; i++) {
            BigDecimal x = new BigDecimal(i);
            BigDecimal y = values.get(i);
            
            sumX = sumX.add(x);
            sumY = sumY.add(y);
            sumXY = sumXY.add(x.multiply(y));
            sumX2 = sumX2.add(x.multiply(x));
        }
        
        BigDecimal denominator = n.multiply(sumX2).subtract(sumX.multiply(sumX));
        
        if (denominator.compareTo(BigDecimal.ZERO) == 0) {
            return BigDecimal.ZERO;
        }
        
        BigDecimal slope = n.multiply(sumXY).subtract(sumX.multiply(sumY))
            .divide(denominator, 4, RoundingMode.HALF_UP);
        
        return slope;
    }
    
    /**
     * 计算置信度
     */
    private Double calculateConfidence(HistoricalPerformanceAnalysis analysis) {
        double confidence = 0.5; // 基础置信度
        
        // 历史数据越多,置信度越高
        if (analysis.getAverageCompletionRate() != null) {
            double rate = analysis.getAverageCompletionRate().doubleValue();
            if (rate >= 0.8) {
                confidence += 0.2; // 完成率高,增加置信度
            }
        }
        
        // 趋势明显,增加置信度
        if (analysis.getTrend().abs().compareTo(new BigDecimal("0.05")) > 0) {
            confidence += 0.15;
        }
        
        // 数据稳定,增加置信度
        if (analysis.getPercentileRank() != null) {
            double rank = analysis.getPercentileRank();
            if (rank >= 0.6) { // 前40%
                confidence += 0.15;
            }
        }
        
        return Math.min(confidence, 0.95); // 最高95%置信度
    }
    
    /**
     * 构建推荐原因说明
     */
    private String buildRecommendationReason(HistoricalPerformanceAnalysis analysis) {
        List<String> reasons = new ArrayList<>();
        
        if (analysis.getLatestPerformanceScore() != null) {
            reasons.add(String.format("基于最新绩效得分%.2f", 
                analysis.getLatestPerformanceScore()));
        }
        
        if (analysis.getTrend().compareTo(BigDecimal.ZERO) > 0) {
            reasons.add("考虑到近期表现上升趋势");
        } else if (analysis.getTrend().compareTo(BigDecimal.ZERO) < 0) {
            reasons.add("考虑到近期表现下降趋势");
        }
        
        if (analysis.getPeerAverageScore() != null) {
            reasons.add(String.format("参考同级平均绩效%.2f", 
                analysis.getPeerAverageScore()));
        }
        
        if (analysis.getPercentileRank() != null) {
            double rank = analysis.getPercentileRank() * 100;
            reasons.add(String.format("历史排名位于前%.1f%%", 
                (1 - rank)));
        }
        
        return String.join(",", reasons);
    }
}

/**
 * 推荐结果
 */
@Data
@Builder
public class RecommendationResult {
    private String employeeId;
    private BigDecimal recommendedValue;
    private Double confidence;
    private String reason;
    private List<String> factors;
}

/**
 * 历史绩效分析
 */
@Data
public class HistoricalPerformanceAnalysis {
    private BigDecimal averageCompletionRate;
    private BigDecimal latestPerformanceScore;
    private BigDecimal trend;
    private BigDecimal peerAverageScore;
    private Double percentileRank;
}

四、高级特性实现

4.1 目标树形结构可视化

目标树构建器

/**
 * OKR目标树形结构构建器
 * 构建用于可视化展示的目标层级关系树
 */
@Service
@Slf4j
public class ObjectiveTreeBuilder {
    
    @Autowired
    private ObjectiveMapper objectiveMapper;
    
    @Autowired
    private KeyResultMapper keyResultMapper;
    
    /**
     * 构建目标树
     * 
     * @param cycleId 周期ID
     * @param rootObjectiveId 根目标ID(可选)
     * @return 目标树
     */
    public ObjectiveTree buildTree(String cycleId, String rootObjectiveId) {
        log.info("构建目标树: cycleId={}, rootId={}", cycleId, rootObjectiveId);
        
        // 1. 确定根节点
        String actualRootId = StringUtils.isNotBlank(rootObjectiveId) ? 
            rootObjectiveId : findRootObjectiveId(cycleId);
        
        // 2. 递归构建树
        TreeNode root = buildTreeNode(actualRootId, true);
        
        // 3. 构建目标树
        ObjectiveTree tree = ObjectiveTree.builder()
            .cycleId(cycleId)
            .root(root)
            .totalNodes(countNodes(root))
            .totalDepth(calculateDepth(root))
            .build();
        
        log.info("目标树构建完成: nodes={}, depth={}", 
            tree.getTotalNodes(), tree.getTotalDepth());
        
        return tree;
    }
    
    /**
     * 查找根目标ID
     */
    private String findRootObjectiveId(String cycleId) {
        // 查找公司级目标作为根节点
        ObjectiveDO companyObjective = objectiveMapper.selectOne(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycleId)
                .eq(ObjectiveDO::getType, ObjectiveType.COMPANY)
                .eq(ObjectiveDO::getStatus, ObjectiveStatus.ACTIVE)
                .orderByDesc(ObjectiveDO::getCreateTime)
                .last("LIMIT 1")
        );
        
        return companyObjective != null ? companyObjective.getId() : null;
    }
    
    /**
     * 递归构建树节点
     */
    private TreeNode buildTreeNode(String objectiveId, boolean isRoot) {
        // 1. 获取目标信息
        ObjectiveDO objective = objectiveMapper.selectById(objectiveId);
        if (objective == null) {
            return null;
        }
        
        // 2. 获取关键结果
        List<KeyResultDO> keyResults = keyResultMapper.selectList(
            new LambdaQueryWrapper<KeyResultDO>()
                .eq(KeyResultDO::getObjectiveId, objectiveId)
        );
        
        // 3. 获取子目标
        List<ObjectiveDO> childObjectives = objectiveMapper.selectList(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getParentObjectiveId, objectiveId)
                .eq(ObjectiveDO::getStatus, ObjectiveStatus.ACTIVE)
                .orderByAsc(ObjectiveDO::getPriority)
        );
        
        // 4. 构建节点
        TreeNode.TreeNodeBuilder nodeBuilder = TreeNode.builder()
            .id(objective.getId())
            .type("OBJECTIVE")
            .title(objective.getTitle())
            .objectiveType(objective.getType().name())
            .status(objective.getStatus().name())
            .progress(objective.getProgress())
            .priority(objective.getPriority().getCode())
            .startDate(objective.getStartDate())
            .endDate(objective.getEndDate())
            .keyResults(convertToKeyResultNodes(keyResults))
            .build();
        
        // 5. 递归构建子节点
        List<TreeNode> children = new ArrayList<>();
        for (ObjectiveDO childObjective : childObjectives) {
            TreeNode childNode = buildTreeNode(childObjective.getId(), false);
            if (childNode != null) {
                children.add(childNode);
            }
        }
        nodeBuilder.children(children);
        
        return nodeBuilder.build();
    }
    
    /**
     * 转换关键结果为树节点
     */
    private List<TreeNode.KeyResultNode> convertToKeyResultNodes(List<KeyResultDO> keyResults) {
        return keyResults.stream()
            .map(kr -> TreeNode.KeyResultNode.builder()
                .id(kr.getId())
                .title(kr.getTitle())
                .currentValue(kr.getCurrentValue())
                .targetValue(kr.getTargetValue())
                .unit(kr.getUnit())
                .progress(kr.getProgress())
                .status(kr.getStatus().name())
                .direction(kr.getDirection().name())
                .weight(kr.getWeight())
                .dueDate(kr.getDueDate())
                .build())
            .collect(Collectors.toList());
    }
    
    /**
     * 计算树的深度
     */
    private int calculateDepth(TreeNode root) {
        if (root == null || root.getChildren().isEmpty()) {
            return 1;
        }
        
        int maxChildDepth = 0;
        for (TreeNode child : root.getChildren()) {
            int childDepth = calculateDepth(child);
            if (childDepth > maxChildDepth) {
                maxChildDepth = childDepth;
            }
        }
        
        return maxChildDepth + 1;
    }
    
    /**
     * 计算树的节点数
     */
    private int countNodes(TreeNode root) {
        if (root == null) {
            return 0;
        }
        
        int count = 1;
        for (TreeNode child : root.getChildren()) {
            count += countNodes(child);
        }
        
        return count;
    }
}

/**
 * 目标树
 */
@Data
@Builder
public class ObjectiveTree {
    private String cycleId;
    private TreeNode root;
    private Integer totalNodes;
    private Integer totalDepth;
    private Map<String, Object> statistics;
}

/**
 * 树节点
 */
@Data
@Builder
class TreeNode {
    private String id;
    private String type; // OBJECTIVE or KEY_RESULT
    private String title;
    private String objectiveType;
    private String status;
    private BigDecimal progress;
    private Integer priority;
    private LocalDate startDate;
    private LocalDate endDate;
    private List<KeyResultNode> keyResults;
    private List<TreeNode> children;
    
    @Data
    @Builder
    static class KeyResultNode {
        private String id;
        private String title;
        private BigDecimal currentValue;
        private BigDecimal targetValue;
        private String unit;
        private BigDecimal progress;
        private String status;
        private String direction;
        private BigDecimal weight;
        private LocalDate dueDate;
    }
}

4.2 复盘分析引擎

OKR复盘分析服务

/**
 * OKR复盘分析引擎
 * 对OKR周期结束后进行全面的复盘分析
 */
@Service
@Slf4j
public class OkrReviewAnalysisEngine {
    
    @Autowired
    private ObjectiveMapper objectiveMapper;
    
    @Autowired
    private KeyResultMapper keyResultMapper;
    
    /**
     * 生成复盘报告
     * 
     * @param cycleId 周期ID
     * @return 复盘报告
     */
    public ReviewReport generateReviewReport(String cycleId) {
        log.info("生成复盘报告: cycleId={}", cycleId);
        
        // 1. 获取周期信息
        OkrCycleDO cycle = okrCycleMapper.selectById(cycleId);
        if (cycle == null) {
            throw new BusinessException("周期不存在");
        }
        
        // 2. 构建报告框架
        ReviewReport report = ReviewReport.builder()
            .cycleId(cycleId)
            .cycleName(cycle.getName())
            .reportDate(LocalDateTime.now())
            .build();
        
        // 3. 整体达成分析
        OverallAchievementAnalysis overallAnalysis = analyzeOverallAchievement(cycleId);
        report.setOverallAnalysis(overallAnalysis);
        
        // 4. 分层级达成分析
        List<LevelAchievementAnalysis> levelAnalyses = analyzeLevelAchievement(cycleId);
        report.setLevelAnalyses(levelAnalyses);
        
        // 5. 关键结果分析
        KeyResultAnalysis keyResultAnalysis = analyzeKeyResults(cycleId);
        report.setKeyResultAnalysis(keyResultAnalysis);
        
        // 6. 目标完成情况分析
        CompletionAnalysis completionAnalysis = analyzeCompletion(cycleId);
        report.setCompletionAnalysis(completionAnalysis);
        
        // 7. 最佳实践识别
        BestPracticesAnalysis bestPractices = identifyBestPractices(cycleId);
        report.setBestPractices(bestPractices);
        
        // 8. 问题与改进建议
        IssuesAndRecommendations issues = identifyIssuesAndRecommendations(cycleId);
        report.setIssuesAndRecommendations(issues);
        
        // 9. 下周期建议
        NextCycleRecommendation nextCycle = generateNextCycleRecommendation(cycleId);
        report.setNextCycleRecommendation(nextCycle);
        
        // 10. 保存报告
        saveReviewReport(report);
        
        log.info("复盘报告生成完成: cycleId={}", cycleId);
        
        return report;
    }
    
    /**
     * 整体达成分析
     */
    private OverallAchievementAnalysis analyzeOverallAchievement(String cycleId) {
        // 获取周期内所有目标
        List<ObjectiveDO> objectives = objectiveMapper.selectList(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycleId)
        );
        
        if (objectives.isEmpty()) {
            return OverallAchievementAnalysis.empty();
        }
        
        // 计算整体统计
        long totalCount = objectives.size();
        long completedCount = objectives.stream()
            .filter(o -> ObjectiveStatus.COMPLETED.equals(o.getStatus()))
            .count();
        
        BigDecimal averageProgress = objectives.stream()
            .map(ObjectiveDO::getProgress)
            .reduce(BigDecimal.ZERO, BigDecimal::add)
            .divide(new BigDecimal(totalCount), 2, RoundingMode.HALF_UP);
        
        // 按优先级统计
        Map<ObjectivePriority, Long> countByPriority = objectives.stream()
            .collect(Collectors.groupingBy(ObjectiveDO::getPriority, Collectors.counting()));
        
        // 按类型统计
        Map<ObjectiveType, Long> countByType = objectives.stream()
            .collect(Collectors.groupingBy(ObjectiveDO::getType, Collectors.counting()));
        
        return OverallAchievementAnalysis.builder()
            .totalObjectives((int) totalCount)
            .completedObjectives((int) completedCount)
            .completionRate(totalCount > 0 ? 
                new BigDecimal(completedCount).divide(new BigDecimal(totalCount), 2, RoundingMode.HALF_UP) : 
                BigDecimal.ZERO)
            .averageProgress(averageProgress)
            .countByPriority(countByPriority)
            .countByType(countByType)
            .build();
    }
    
    /**
     * 分层级达成分析
     */
    private List<LevelAchievementAnalysis> analyzeLevelAchievement(String cycleId) {
        List<LevelAchievementAnalysis> analyses = new ArrayList<>();
        
        // 1. 公司级分析
        LevelAchievementAnalysis companyAnalysis = analyzeByLevel(cycleId, ObjectiveType.COMPANY);
        if (companyAnalysis != null) {
            analyses.add(companyAnalysis);
        }
        
        // 2. 部门级分析
        List<ObjectiveDO> departmentObjectives = objectiveMapper.selectList(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycleId)
                .eq(ObjectiveDO::getType, ObjectiveType.DEPARTMENT)
        );
        
        Map<String, List<ObjectiveDO>> groupedByDept = departmentObjectives.stream()
            .collect(Collectors.groupingBy(ObjectiveDO::getOwnerId));
        
        for (Map.Entry<String, List<ObjectiveDO>> entry : groupedByDept.entrySet()) {
            LevelAchievementAnalysis deptAnalysis = analyzeByLevel(
                cycleId, ObjectiveType.DEPARTMENT, entry.getKey()
            );
            if (deptAnalysis != null) {
                analyses.add(deptAnalysis);
            }
        }
        
        // 3. 团队级分析(类似处理)
        // 4. 个人级分析(类似处理)
        
        return analyses;
    }
    
    /**
     * 按层级分析达成情况
     */
    private LevelAchievementAnalysis analyzeByLevel(
        String cycleId,
        ObjectiveType type,
        String ownerId
    ) {
        List<ObjectiveDO> objectives = objectiveMapper.selectList(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycleId)
                .eq(ObjectiveDO::getType, type)
                .eq(ObjectiveDO::getOwnerId, ownerId)
        );
        
        if (objectives.isEmpty()) {
            return null;
        }
        
        // 计算该层级达成情况
        long totalCount = objectives.size();
        long completedCount = objectives.stream()
            .filter(o -> ObjectiveStatus.COMPLETED.equals(o.getStatus()))
            .count();
        
        BigDecimal averageProgress = objectives.stream()
            .map(ObjectiveDO::getProgress)
            .reduce(BigDecimal.ZERO, BigDecimal::add)
            .divide(new BigDecimal(totalCount), 2, RoundingMode.HALF_UP);
        
        return LevelAchievementAnalysis.builder()
            .type(type)
            .ownerId(ownerId)
            .totalObjectives((int) totalCount)
            .completedObjectives((int) completedCount)
            .completionRate(totalCount > 0 ? 
                new BigDecimal(completedCount).divide(new BigDecimal(totalCount), 2, RoundingMode.HALF_UP) : 
                BigDecimal.ZERO)
            .averageProgress(averageProgress)
            .build();
    }
    
    /**
     * 识别最佳实践
     */
    private BestPracticesAnalysis identifyBestPractices(String cycleId) {
        BestPracticesAnalysis analysis = new BestPracticesAnalysis();
        
        // 1. 找出高达成率目标
        List<ObjectiveDO> highAchievementObjectives = objectiveMapper.selectList(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycleId)
                .eq(ObjectiveDO::getStatus, ObjectiveStatus.COMPLETED)
                .ge(ObjectiveDO::getProgress, new BigDecimal("80"))
                .orderByDesc(ObjectiveDO::getProgress)
                .last("LIMIT 10")
        );
        
        List<BestPractice> bestPractices = highAchievementObjectives.stream()
            .map(obj -> {
                BestPractice bp = new BestPractice();
                bp.setObjectiveTitle(obj.getTitle());
                bp.setAchievementRate(obj.getProgress());
                bp.setOwnerId(obj.getOwnerId());
                bp.setOwnerName(obj.getOwnerName());
                
                // 分析关键成功因素
                List<KeyResultDO> keyResults = keyResultMapper.selectList(
                    new LambdaQueryWrapper<KeyResultDO>()
                        .eq(KeyResultDO::getObjectiveId, obj.getId())
                );
                
                List<String> successFactors = new ArrayList<>();
                for (KeyResultDO kr : keyResults) {
                    if (kr.getProgress().compareTo(new BigDecimal("80")) >= 0) {
                        successFactors.add(kr.getTitle());
                    }
                }
                bp.setSuccessFactors(successFactors);
                
                return bp;
            })
            .collect(Collectors.toList());
        
        analysis.setTopAchievingObjectives(bestPractices);
        
        // 2. 分析成功因素
        Map<String, Long> factorCounts = new HashMap<>();
        bestPractices.forEach(bp -> {
            bp.getSuccessFactors().forEach(factor -> {
                factorCounts.merge(factor, 1L, Long::sum);
            });
        });
        
        List<SuccessFactor> rankedFactors = factorCounts.entrySet().stream()
            .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
            .map(entry -> SuccessFactor.builder()
                .factor(entry.getKey())
                .count(entry.getValue())
                .build())
            .collect(Collectors.toList());
        
        analysis.setCommonSuccessFactors(rankedFactors);
        
        return analysis;
    }
    
    /**
     * 识别问题与改进建议
     */
    private IssuesAndRecommendations identifyIssuesAndRecommendations(String cycleId) {
        IssuesAndRecommendations issues = new IssuesAndRecommendations();
        
        // 1. 找出未达成目标
        List<ObjectiveDO> notAchievedObjectives = objectiveMapper.selectList(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycleId)
                .ne(ObjectiveDO::getStatus, ObjectiveStatus.COMPLETED)
                .lt(ObjectiveDO::getProgress, new BigDecimal("80"))
        );
        
        List<Issue> issueList = notAchievedObjectives.stream()
            .map(obj -> Issue.builder()
                .objectiveId(obj.getId())
                .objectiveTitle(obj.getTitle())
                .type(obj.getType().name())
                .progress(obj.getProgress())
                .ownerId(obj.getOwnerId())
                .ownerName(obj.getOwnerName())
                .rootCause(analyzeRootCause(obj))
                .recommendation(generateRecommendation(obj))
                .build())
            .collect(Collectors.toList());
        
        issues.setIssues(issueList);
        
        // 2. 汇总问题类型
        Map<String, Long> issueTypes = issueList.stream()
            .collect(Collectors.groupingBy(
                issue -> issue.getType() != null ? issue.getType() : "OTHER",
                Collectors.counting()
            ));
        
        issues.setIssueTypeSummary(issueTypes);
        
        // 3. 生成改进建议
        List<Recommendation> recommendations = generateRecommendations(issueList);
        issues.setRecommendations(recommendations);
        
        return issues;
    }
    
    /**
     * 分析根本原因
     */
    private String analyzeRootCause(ObjectiveDO objective) {
        // 获取关键结果完成情况
        List<KeyResultDO> keyResults = keyResultMapper.selectList(
            new LambdaQueryWrapper<KeyResultDO>()
                .eq(KeyResultDO::getObjectiveId, objective.getId())
        );
        
        long notStartedCount = keyResults.stream()
            .filter(kr -> KeyResultStatus.NOT_STARTED.equals(kr.getStatus()))
            .count();
        
        long atRiskCount = keyResults.stream()
            .filter(kr -> KeyResultStatus.AT_RISK.equals(kr.getStatus()))
            .count();
        
        if (notStartedCount > 0) {
            return "关键结果尚未启动";
        }
        
        if (atRiskCount > 0) {
            return "关键结果存在风险";
        }
        
        return "进度缓慢";
    }
    
    /**
     * 生成改进建议
     */
    private String generateRecommendation(ObjectiveDO objective) {
        return "建议:定期检视关键结果进度,及时调整策略,加强资源投入";
    }
}

4.3 数据同步接口

外部数据同步服务

/**
 * OKR数据同步服务
 * 从外部系统同步数据作为关键结果值
 */
@Service
@Slf4j
public class OkrDataSyncService {
    
    @Autowired
    private KeyResultMapper keyResultMapper;
    
    @Autowired
 private ObjectiveMapper objectiveMapper;
    
    /**
     * 同步关键结果数据
     * 
     * @param keyResultId 关键结果ID
     * @return 同步结果
     */
    @Transactional(rollbackFor = Exception.class)
    public SyncResult syncKeyResultData(String keyResultId) {
        log.info("同步关键结果数据: keyResultId={}", keyResultId);
        
        // 1. 获取关键结果信息
        KeyResultDO keyResult = keyResultMapper.selectById(keyResultId);
        if (keyResult == null) {
            throw new BusinessException("关键结果不存在");
        }
        
        // 2. 获取数据源配置
        String dataSourceConfig = keyResult.getDataSourceConfig();
        if (StringUtils.isBlank(dataSourceConfig)) {
            throw new BusinessException("未配置数据源");
        }
        
        try {
            // 3. 解析数据源配置
            DataSourceConfig config = JSON.parseObject(
                dataSourceConfig, DataSourceConfig.class
            );
            
            // 4. 根据数据源类型同步数据
            BigDecimal syncedValue = syncFromDataSource(config);
            
            // 5. 更新关键结果值
            keyResult.setCurrentValue(syncedValue);
            keyResult.setLastSyncTime(LocalDateTime.now());
            keyResultMapper.updateById(keyResult);
            
            // 6. 重新计算目标进度
            progressCalculationEngine.calculateObjectiveProgress(
                keyResult.getObjectiveId()
            );
            
            log.info("关键结果数据同步完成: keyResultId={}, value={}", 
                keyResultId, syncedValue);
            
            return SyncResult.success(keyResultId, syncedValue);
            
        } catch (Exception e) {
            log.error("同步关键结果数据失败: keyResultId={}, error={}", 
                keyResultId, e.getMessage());
            
            // 记录同步失败日志
            recordSyncFailure(keyResultId, e.getMessage());
            
            return SyncResult.failure(keyResultId, e.getMessage());
        }
    }
    
    /**
     * 从数据源同步数据
     */
    private BigDecimal syncFromDataSource(DataSourceConfig config) throws Exception {
        switch (config.getType()) {
            case "API":
                return syncFromApi(config);
                
            case "DATABASE":
                return syncFromDatabase(config);
                
            case "MANUAL":
                return syncFromManual(config);
                
            default:
                throw new BusinessException("不支持的数据源类型: " + config.getType());
        }
    }
    
    /**
     * 从API同步数据
     */
    private BigDecimal syncFromApi(DataSourceConfig config) throws Exception {
        // 构建API请求
        String apiUrl = config.getUrl();
        Map<String, String> headers = config.getHeaders();
        Map<String, Object> params = config.getParams();
        
        // 发送HTTP请求
        ApiResponse response = httpClient.get(apiUrl)
            .headers(headers)
            .queryParams(params)
            .execute();
        
        if (!response.isSuccess()) {
            throw new BusinessException("API调用失败: " + response.getStatusText());
        }
        
        // 解析响应数据
        JSONObject jsonResponse = JSON.parseObject(response.body());
        return jsonResponse.getBigDecimal(config.getValueField());
    }
    
    /**
     * 从数据库同步数据
     */
    private BigDecimal syncFromDatabase(DataSourceConfig config) throws Exception {
        // 构建SQL查询
        String sql = config.getSql();
        Map<String, Object> params = config.getParams();
        
        // 执行查询
        List<Map<String, Object>> results = jdbcTemplate.queryForList(sql, params);
        
        if (results.isEmpty()) {
            return BigDecimal.ZERO;
        }
        
        // 提取值
        Object value = results.get(0).get(config.getValueField());
        if (value == null) {
            return BigDecimal.ZERO;
        }
        
        if (value instanceof Number) {
            return new BigDecimal(value.toString());
        }
        
        return new BigDecimal(value.toString());
    }
    
    /**
     * 批量同步周期数据
     * 
     * @param cycleId 周期ID
     */
    public void batchSyncCycleData(String cycleId) {
        log.info("批量同步周期数据: cycleId={}", cycleId);
        
        // 1. 获取需要同步的关键结果
        List<KeyResultDO> keyResultsToSync = keyResultMapper.selectList(
            new LambdaQueryWrapper<KeyResultDO>()
                .eq(KeyResultDO::getObjectiveId, 
                    objectiveMapper.selectList(
                        new LambdaQueryWrapper<ObjectiveDO>()
                            .eq(ObjectiveDO::getCycleId, cycleId)
                    ).stream()
                    .map(ObjectiveDO::getId)
                    .collect(Collectors.toList())
                )
                .isNotNull(KeyResultDO::getDataSourceConfig)
                .isNotNull(KeyResultDO::getDataSourceConfig)
        );
        
        log.info("找到需要同步的关键结果: count={}", keyResultsToSync.size());
        
        // 2. 批量同步
        for (KeyResultDO keyResult : keyResultsToSync) {
            try {
                syncKeyResultData(keyResult.getId());
            } catch (Exception e) {
                log.error("同步失败: keyResultId={}, error={}", 
                    keyResult.getId(), e.getMessage());
            }
        }
        
        log.info("批量同步完成: cycleId={}", cycleId);
    }
}

/**
 * 数据源配置
 */
@Data
class DataSourceConfig {
    private String type; // API, DATABASE, MANUAL
    private String url;
    private Map<String, String> headers;
    private Map<String, Object> params;
    private String sql;
    private String valueField;
}

五、前端实现

5.1 OKR目标管理页面

目标列表页面(Vue3)

<template>
  <div class="okr-objective-list">
    <!-- 周期选择器 -->
    <div class="cycle-selector">
      <el-select v-model="currentCycleId" @change="handleCycleChange" placeholder="选择周期">
        <el-option
          v-for="cycle in cycles"
          :key="cycle.id"
          :label="cycle.name"
          :value="cycle.id">
        </el-option>
      </el-select>
      
      <el-button type="primary" @click="createObjective" icon="Plus">
        创建目标
      </el-button>
      
      <el-button @click="batchAutoAlignment" :loading="aligning">
        批量对齐
      </el-button>
    </div>

    <!-- 目标树状图 -->
    <div class="objective-tree" v-loading="treeLoading">
      <el-tree
        :data="objectiveTreeData"
        :props="treeProps"
        :expand-on-click-node="false"
        :default-expand-all="false"
        node-key="id"
        @node-click="handleNodeClick"
      >
        <template #default="{ node, data }">
          <div class="tree-node">
            <div class="node-header">
              <el-icon>
                <component :is="getNodeIcon(node)" />
              </el-icon>
              
              <span class="node-title">{{ node.title }}</span>
              
              <el-tag 
                :type="getStatusType(node.status)" 
                size="small"
                effect="plain"
              >
                {{ getStatusText(node.status) }}
              </el-tag>
              
              <span class="node-progress">{{ node.progress }}%</span>
            </div>
            
            <!-- 关键结果列表 -->
            <div class="key-results" v-if="node.keyResults">
              <el-progress
                v-for="kr in node.keyResults"
                :key="kr.id"
                :percentage="kr.progress"
                :format="getProgressFormat"
                :color="getProgressColor(kr.progress)"
              >
                <span class="kr-title">{{ kr.title }}</span>
                <span class="kr-value">
                  {{ formatValue(kr.currentValue, kr.unit) }} / 
                  {{ formatValue(kr.targetValue, kr.unit) }}
                </span>
              </el-progress>
            </div>
          </div>
        </template>
      </el-tree>
    </div>

    <!-- 目标详情抽屉 -->
    <el-drawer
      v-model="detailDrawerVisible"
      :title="selectedObjective?.title"
      size="60%"
      direction="rtl"
    >
      <ObjectiveDetail
        v-if="selectedObjective"
        :objective-id="selectedObjective.id"
        @refresh="loadObjectiveTree"
      />
    </el-drawer>
  </div>
</template>

<script lang="ts" setup>
import { ref, computed, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { useRouter } from 'vue-router'
import { 
  getObjectiveTree, 
  createObjective, 
  batchAutoAlignment,
  getCycles 
} from '@/api/hrm/okr'
import ObjectiveDetail from './components/ObjectiveDetail.vue'

const router = useRouter()

// 状态定义
const currentCycleId = ref('')
const cycles = ref([])
const objectiveTreeData = ref([])
const treeLoading = ref(false)
const detailDrawerVisible = ref(false)
const selectedObjective = ref(null)
const aligning = ref(false)

// 树形结构配置
const treeProps = {
  children: 'children',
  label: 'title',
  disabled: 'disabled'
}

// 周期变更
const handleCycleChange = () => {
  loadObjectiveTree()
}

// 创建目标
const createObjective = () => {
  router.push('/okr/objective/create', {
    cycleId: currentCycleId.value
  })
}

// 批量对齐
const batchAutoAlignment = async () => {
  const loading = ElLoading.service({
    lock: true,
    text: '正在批量对齐...'
  })
  
  try {
    await batchAutoAlignment({ cycleId: currentCycleId.value })
    ElMessage.success('批量对齐完成')
    loadObjectiveTree()
  } finally {
    loading.close()
  }
}

// 加载目标树
const loadObjectiveTree = async () => {
  if (!currentCycleId.value) {
    return
  }
  
  treeLoading.value = true
  
  try {
    const { data } = await getObjectiveTree({
      cycleId: currentCycleId.value
    })
    
    objectiveTreeData.value = [data.root]
  } finally {
    treeLoading.value = false
  }
}

// 节点点击
const handleNodeClick = (node, data) => {
  selectedObjective.value = node
  detailDrawerVisible.value = true
}

// 辅助函数
const getNodeIcon = (node) => {
  if (node.type === 'OBJECTIVE') {
    return 'Objective'
  } else {
    return 'KeyResult'
  }
}

const getStatusType = (status) => {
  switch (status) {
    case 'COMPLETED':
      return 'success'
    case 'ACTIVE':
      return 'primary'
    case 'PAUSED':
      return 'warning'
    case 'CANCELLED':
      return 'danger'
    default:
      return 'info'
  }
}

const getStatusText = (status) => {
  const statusMap = {
    'COMPLETED': '已完成',
    'LANGUAGE': '进行中',
    'PAUSED': '已暂停',
    'CANCELLED': '已取消',
    'DRAFT': '草稿'
  }
  return statusMap[status] || '未知'
}

const formatValue = (value, unit) => {
  if (unit === 'PERCENTAGE') {
    return `${value}%`
  }
  return `${value} ${unit}`
}

const getProgressFormat = (percentage) => {
  return `${percentage}%`
}

const getProgressColor = (progress) => {
  if (progress >= 100) {
    return '#67c23a'
  } else if (progress >= 80) {
    return '#e6a23c'
  } else if (progress >= 50) {
    return '#e5e5e5'
  } else if (progress >= 20) {
    return '#f56c6c'
  } else {
    return '#909399'
  }
}

// 初始化
onMounted(() => {
  loadCycles()
  // 加载当前周期
  const currentCycle = cycles.value.find(c => c.status === 'ACTIVE')
  if (currentCycle) {
    currentCycleId.value = currentCycle.id
    loadObjectiveTree()
  }
})

// 加载周期列表
const loadCycles = async () => {
  const { data } = await getCycles()
  cycles.value = data || []
}
</script>

<style lang="scss" scoped>
.okr-objective-list {
  padding: 20px;
  
  .cycle-selector {
    margin-bottom: 20px;
    display: flex;
    gap: 12px;
    align-items: center;
    
    .el-select {
      width: 300px;
    }
  }
  
  .objective-tree {
    border: 1px solid #ebeef5;
    border-radius: 4px;
    padding: 20px;
    min-height: 400px;
    
    .tree-node {
      width: 100%;
      
      .node-header {
        display: flex;
        align-items: center;
        gap: 8px;
        margin-bottom: 8px;
        
        .node-title {
          font-weight: 500;
          flex: 1;
        }
        
        .node-progress {
          font-size: 12px;
          color: #909399;
        }
      }
      
      .key-results {
        margin-top: 12px;
        padding-left: 24px;
        
        .kr-title {
          font-size: 12px;
          color: #606266;
          margin-bottom: 4px;
        }
        
        .kr-value {
          font-size: 12px;
          color: #909399;
        }
      }
    }
  }
}
</style>

5.2 关键结果编辑器

关键结果编辑器(Vue3)

<template>
  <div class="key-result-editor">
    <!-- 进度条显示 -->
    <div class="progress-display">
      <el-progress
        :percentage="calculatedProgress"
        :format="progressFormat"
        :color="getProgressColor"
      />
    </div>

    <!-- 数据源配置 -->
    <div class="datasource-config" v-if="keyResult.dataSourceType !== 'MANUAL'">
      <div class="config-header">
        <span>数据源配置</span>
        <el-button 
          type="primary" 
          size="small" 
          @click="syncNow" 
          :loading="syncing"
        >
          立即同步
        </el-button>
      </div>
      
      <el-descriptions :column="1" border>
        <el-descriptions-item label="数据源类型">
          {{ getDataSourceTypeText() }}
        </el-descriptions-item>
        <el-descriptions-item label="最后同步时间">
          {{ keyResult.lastSyncTime || '未同步' }}
        </el-descriptions-item>
      </el-descriptions>
    </div>

    <!-- 快速设置进度 -->
    <div class="quick-set">
      <div class="set-item">
        <span>快速设置进度</span>
        <el-slider
          v-model="quickProgress"
          :marks="progressMarks"
          :step="10"
          show-stops
          @change="handleQuickProgressChange"
        />
      </div>
    </div>

    <!-- 手动录入 -->
    <div class="manual-input">
      <el-form :model="formData" :rules="formRules" ref="formRef" label-width="120px">
        <el-form-item label="当前值" prop="currentValue">
          <el-input-number
            v-model="formData.currentValue"
            :precision="2"
            :controls="true"
            @change="handleManualInput"
          />
          <span class="unit-label">{{ keyResult.unit }}</span>
        </el-form-item>
      </el-form>
    </div>
  </div>
</template>

<script lang="ts" setup>
import { ref, computed, watch, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { updateKeyResult, syncKeyResultData } from '@/api/hrm/okr'

const props = defineProps({
  keyResultId: {
    type: String,
    required: true
  }
})

const emit = defineEmits(['update', 'sync'])

// 状态
const keyResult = ref({})
const syncing = ref(false)
const calculatedProgress = ref(0)
const quickProgress = ref(0)

// 表单数据
const formData = ref({
  currentValue: 0,
  targetValue: 0
})

const formRules = {
  currentValue: [
    { required: true, message: '请输入当前值', trigger: 'blur' }
  ]
}

// 计算属性
const progressMarks = [0, 25, 50, 75, 100]

// 方法
const calculateProgress = () => {
  if (!keyResult.value.targetValue || keyResult.value.targetValue === 0) {
    calculatedProgress.value = 0
    return
  }
  
  let progress = 0
  
  switch (keyResult.value.direction) {
    case 'INCREASE':
      // 增长型:当前值 / 目标值
      progress = keyResult.value.currentValue.divide(
        keyResult.value.targetValue,
        2,
        MathContext.HALF_UP
      ).multiply(100)
      break
      
    case 'DECREASE':
      // 下降型:1 - (当前值 - 目标值) / 目标值
      progress = BigDecimal.ONE.subtract(
        keyResult.value.currentValue.subtract(keyResult.value.targetValue)
          .abs()
          .divide(keyResult.value.targetValue, 2, MathContext.HALF_UP)
      ).multiply(100)
      break
      
    case 'MAINTAIN':
      // 维持型:计算偏差
      const deviation = keyResult.value.currentValue
        .subtract(keyResult.value.targetValue)
        .abs()
      const tolerance = keyResult.value.targetValue.multiply(new BigDecimal('0.1'))
      
      if (deviation.compareTo(tolerance) <= 0) {
        progress = 100
      } else {
        progress = 100 - deviation
          .multiply(new BigDecimal('100'))
          .divide(keyResult.value.targetValue, 2, MathContext.HALF_UP)
          .intValue()
      }
      break
  }
  
  calculatedProgress.value = progress.min(100).intValue(0)
}

const handleManualInput = () => {
  emit('update', {
    keyResultId: props.keyResultId,
    currentValue: formData.value.currentValue
  })
}

const syncNow = async () => {
  syncing.value = true
  
  try {
    await syncKeyResultData(props.keyResultId)
    ElMessage.success('同步成功')
    emit('sync')
  } finally {
    syncing.value = false
  }
}

const getDataSourceTypeText = () => {
  const typeMap = {
    'MANUAL': '手动录入',
    'SYSTEM': '系统同步',
    'API': '外部接口'
  }
  return typeMap[keyResult.value.dataSourceType] || '未知'
}

// 监听数据变化
watch(() => props.keyResultId, () => {
  loadKeyResult()
})

// 初始化
onMounted(() => {
  loadKeyResult()
})

// 加载关键结果数据
const loadKeyResult = async () => {
  // 从API获取关键结果详情
  const { data } = await getKeyResultDetail(props.keyResultId)
  keyResult.value = data
  formData.value.currentValue = data.currentValue
  calculateProgress()
}
</script>

六、最佳实践

6.1 OKR制定最佳实践

1. 目标设定原则(SMART原则增强版)

✅ Specific(具体明确)
- 目标描述清晰,避免模糊表述
- 明确业务范围和边界条件
- 示例:❌ "提升用户体验" 
       ✅ "将移动端页面加载时间从3s优化到1.5s"

✅ Measurable(可衡量)
- 关键结果可量化
- 有明确的数据来源
- 示例:❌ "提高销售额" 
       ✅ "Q4销售额同比增长30%,达到5000万"

✅ Achievable(可达成)
- 目标具有挑战性但可实现
- 考虑资源限制和外部因素
- 建议:设定达成率70-80%的目标

✅ Relevant(相关联)
- 与团队目标和公司战略对齐
- 与岗位职责高度相关
- 支持上级目标的实现

✅ Time-bound(有时限)
- 明确的截止日期
- 分阶段里程碑
- 定期检视机制

2. 关键结果设计原则

✅ 关键结果数量:每个目标3-5个关键结果
  - 太少:无法全面衡量目标
  - 太多:分散精力,难以聚焦

✅ 权重分配:关键结果总权重应为100%
  - 根据重要性分配权重
  - 避免权重过于平均

✅ 进度方向明确
  - 增长型:值越大越好(如DAU、GMV)
  - 下降型:值越小越好(如成本、投诉率)
  - 维持型:保持在目标值附近

✅ 数据来源可靠
  - 优先使用系统自动同步
  - 手动录入需注明数据来源
  - 定期验证数据准确性

6.2 团队对齐最佳实践

1. 对齐会议流程

/**
 * OKR对齐会议流程
 */
public class AlignmentMeetingProcess {
    /**
     * 步骤1:公司目标发布(季度初)
     * - CEO发布公司级OKR
     * - 全员参加目标发布会
     * - 各部门负责人理解公司战略
     */
    void publishCompanyObjectives(String cycleId);
    
    /**
     * 步骤2:部门目标对齐会议(季度第一周)
     * - 部门负责人传达公司目标
     * - 团队负责人讨论如何支撑部门目标
     * - 确定部门OKR和团队OKR
     */
    void alignDepartmentObjectives(String cycleId);
    
    /**
     * 步骤3:团队目标对齐会议(季度第一周)
     * - 团队负责人传达部门目标
     * - 个人讨论如何支撑团队目标
     * - 确定个人OKR
     */
    void alignTeamObjectives(String cycleId);
    
    /**
     * 步骤4:目标公示(季度第二周)
     * - 所有OKR公开透明
     - 定期更新进度
     - 鼓励跨部门协作
     */
    void publishObjectives(String cycleId);
    
    /**
     * 步骤5:定期检视(月度)
     * - 月度OKR检视会议
     - 识别风险和障碍
     - 及时调整策略
     */
    void reviewProgress(String cycleId);
}

2. 对齐关系可视化

┌────────────────────────────────────────────────────────┐
│              公司级OKR                               │
│              "Q1实现GMV 1亿"                         │
└────────────┬───────────────────────┬─────────────────────┘
             │                       │
┌────────────┴──────────────┐  ┌──────────────────┴──────────────┐
│     研发部OKR          │  │    市场部OKR        │
│ "Q1提升DAU 50%"   │  │    "Q1新增客户1000"  │
└────────────┬──────────────┘  └──────────┬──────────────┘
             │                       │
┌────────────┴──────────┐  ┌──────────┴──────────────┐
│   前端团队OKR      │  │   后端团队OKR       │
│ "Q1降低首屏延迟"   │  │   "Q1提升API性能"    │
└────────────┬──────────┘  └──────────┬──────────────┘
             │                       │
┌────────────┴───────────────────────────┴─────────────┐
│          前端开发个人OKR                        │
│          "Q1优化首页加载速度"                      │
└───────────────────────────────────────────────────────┘

6.3 进度跟踪最佳实践

1. 进度更新频率

✅ 推荐频率:
- 关键结果:每周更新一次
- 目标整体进度:每月更新一次
- 接近截止日期:每周更新一次
- 发生重大变化时:立即更新

❌ 避免的频率:
- 每天更新:过于频繁,增加负担
- 仅期末更新:无法及时发现风险

2. 进度异常预警

/**
 * 进度异常预警规则
 */
@Component
public class ProgressAlertEngine {
    
    /**
     * 检查并预警异常情况
     */
    public void checkAndAlert(String cycleId) {
        // 1. 查找所有活跃目标
        List<ObjectiveDO> activeObjectives = objectiveMapper.selectList(
            new LambdaQueryWrapper<ObjectiveDO>()
                .eq(ObjectiveDO::getCycleId, cycleId)
                .eq(ObjectiveDO::getStatus, ObjectiveStatus.ACTIVE)
        );
        
        LocalDate now = LocalDate.now();
        
        for (ObjectiveDO objective : activeObjectives) {
            // 2. 检查关键结果风险
            List<KeyResultDO> atRiskKRs = findAtRiskKeyResults(objective.getId());
            
            if (!atRiskKRs.isEmpty()) {
                sendAlert(objective, atRiskKRs);
            }
            
            // 3. 检查是否接近截止日期
            LocalDate endDate = objective.getEndDate();
            if (endDate != null) {
                int daysUntilDue = (int) ChronoUnit.DAYS.between(now, endDate);
                
                if (daysUntilDue <= 7 && daysUntilDue >= 0) {
                    sendDueDateReminder(objective, daysUntilDue);
                }
            }
        }
    }
    
    /**
     * 发送风险预警
     */
    private void sendAlert(ObjectiveDO objective, List<KeyResultDO> atRiskKRs) {
        String message = String.format(
            "【OKR预警】目标「%s」存在风险,%d个关键结果进度落后",
            objective.getTitle(),
            atRiskKRs.size()
        );
        
        // 发送给目标负责人
        if (StringUtils.isNotBlank(objective.getOwnerId())) {
            notificationService.sendAlert(
                objective.getOwnerId(), 
                message
            );
        }
        
        // 发送给观察者
        if (StringUtils.isNotBlank(objective.getWatcherIds())) {
            List<String> watcherIds = JSON.parseArray(
                objective.getWatcherIds(), String.class
            );
            
            for (String watcherId : watcherIds) {
                notificationService.sendAlert(watcherId, message);
            }
        }
    }
}

七、总结

本文详细介绍了OKR目标管理系统设计与实现,主要内容包括:

核心技术点

  1. 目标对齐算法:多层目标的自动对齐和联动机制
  2. 进度计算引擎:加权计算目标整体进度和关键结果进度
  3. 智能推荐引擎:基于历史数据和算法推荐目标值
  4. 复盘分析引擎:全面的周期复盘分析和问题识别
  5. 数据同步机制:支持API、数据库、手动等多种数据源
  6. 可视化展示:目标树形结构和进度仪表板

业务价值

  1. 战略对齐:确保个人目标与团队、部门、公司战略高度对齐
  2. 聚焦重点:明确优先级,集中精力在重要事项上
  3. 透明协作:全员可见,促进跨部门协作
  4. 持续改进:定期检视和调整,保持敏捷性
  5. 内驱激发:自主设定目标,增强员工参与感

扩展方向

  1. AI智能推荐:使用机器学习算法优化目标值推荐
  2. 协作功能:支持目标协作者和跨部门项目OKR
  3. OKR模板库:提供行业标准的OKR模板
  4. 预测分析:预测目标达成概率和风险
  5. 移动端优化:提供便捷的移动端OKR管理
Logo

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

更多推荐