# Phase 1: 企业级能力补齐 — 通知系统 / RBAC 权限 / 多层级 OKR 拆解

日期：2026-05-13  
状态：设计完成，待进入实现计划

---

## 背景

当前系统（医梦AI OKR绩效考核系统）已具备单人 OKR/KPI 双轨评分闭环，但与字节跳动飞书 OKR、Google OKR 等企业级系统相比，缺少 3 项企业上线必须补齐的能力：消息通知、多角色权限、多层级 OKR 拆解与对齐。

本设计覆盖方案 A 的 Phase 1 全部内容。

---

## 模块一：消息通知系统

### 目标

建立统一的"事件 → 通知 → 推送"管道，覆盖 7 类关键业务事件。

### 数据模型

```sql
CREATE TABLE notification (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    type VARCHAR(32) NOT NULL,
    title VARCHAR(256) NOT NULL,
    content TEXT,
    ref_type VARCHAR(32),
    ref_id INTEGER,
    is_read INTEGER DEFAULT 0,
    read_at TEXT,
    created_at TEXT NOT NULL
);
```

`type` 枚举: `OKR_REVIEW` / `OKR_REVIEWED` / `SCORE_CONFIRM` / `SCORE_CONFIRMED` / `PERIOD_DEADLINE` / `KR_LAGGING` / `FEEDBACK_NOTIFY`
`ref_type` 枚举: `period` / `objective` / `score` / `feedback`

### 触发点

| 事件 | 接收人 | 触发时机 |
|------|--------|---------|
| OKR_REVIEW | 上级 | 下级提交 OKR |
| OKR_REVIEWED | 下级 | 上级审核 OKR（通过/驳回） |
| SCORE_CONFIRM | 下级 | 上级完成评分 |
| SCORE_CONFIRMED | 上级 | 下级确认评分 |
| PERIOD_DEADLINE | 周期内全员 | 周期结束前 3 天（由 PeriodScheduler 触发） |
| KR_LAGGING | OKR 负责人 | KR 进度落后于时间线（每日检查） |
| FEEDBACK_NOTIFY | 下级 | 上级发起/更新面谈记录 |

### 后端架构

- 定义 `NotificationEvent` 事件类，各 Service 在关键动作完成后通过 `ApplicationEventPublisher.publishEvent()` 发布
- `NotificationListener`（`@Async` + `@EventListener`）异步消费事件，写入 `notification` 表
- REST 端点：
  - `GET /api/notifications/unread-count` — 未读数（前端 60s 轮询）
  - `GET /api/notifications?page=&size=` — 通知分页列表
  - `PUT /api/notifications/{id}/read` — 标记已读
  - `PUT /api/notifications/read-all` — 全部已读

### 前端

- 顶部导航栏右侧新增铃铛图标 + 红色未读角标
- 点击展开下拉面板（最近 20 条），点击条目跳转到关联页面并标记已读
- 通知列表页（`/notifications`）：分页、已读/未读筛选、类型筛选

---

## 模块二：RBAC 权限体系

### 目标

从"SUPER_ADMIN + 上下级树推导"升级到 5 级标准角色 + 声明式权限 + 数据范围控制。

### 角色定义

| 角色 | 权限范围 | 授予对象 |
|------|---------|---------|
| `SUPER_ADMIN` | 全局 | 系统管理员 |
| `HR_ADMIN` | 全部人员数据 + 考核配置 + 报表 | HRBP |
| `DEPT_LEADER` | 本部门及下级部门 + 评分审批 | 部门负责人 |
| `TEAM_LEADER` | 直属下级 | 团队主管 |
| `EMPLOYEE` | 仅自己 | 普通员工 |

### 数据库变更

```sql
-- role 字段已有 VARCHAR(20)，值域扩展为上述 5 种
-- 新增数据范围字段
ALTER TABLE sys_user ADD COLUMN scopes TEXT DEFAULT '[]';
```

`scopes` 为 JSON 数组，格式: `["dept:3", "dept:5"]`，表示该角色可见的部门范围。`DEPT_LEADER` 自动获得本部门 scope。

### 权限矩阵

| API 操作 | SUPER_ADMIN | HR_ADMIN | DEPT_LEADER | TEAM_LEADER | EMPLOYEE |
|----------|:-----------:|:--------:|:-----------:|:-----------:|:--------:|
| 组织管理 CRUD | Y | Y | N | N | N |
| 考核周期管理 | Y | Y | N | N | N |
| 查看本部门全部 OKR | Y | Y | Y | N | N |
| 查看下级 OKR | Y | Y | Y | Y | N |
| 打分下级 | Y | Y | Y | Y | N |
| 自己的 OKR/分数 | Y | Y | Y | Y | Y |
| 导出报表 | Y | Y | Y | N | N |
| 操作日志 | Y | Y | N | N | N |

### 后端实现

- 新增 `@RequireRole` 注解：
  ```java
  @RequireRole({UserRole.SUPER_ADMIN, UserRole.HR_ADMIN})
  ```
- AOP 切面 `RoleAspect` 在 Controller 方法执行前校验角色，无权限返回 403
- `SecurityUtils` 新增方法：
  - `hasRole(UserRole role)` — 角色判断
  - `getDataScopes()` — 获取当前用户数据范围
  - `hasSubordinate(Long userId)` — 保留现有上下级树逻辑
- 数据范围过滤：Mapper 层查询时拼接 `AND (dept_id IN (?) OR user_id = ?)` 条件

### 前端

- 组织管理页用户列表增加角色选择器（仅 SUPER_ADMIN / HR_ADMIN 可见和操作）
- 侧边栏菜单项根据角色显示/隐藏：
  - 组织架构：SUPER_ADMIN / HR_ADMIN
  - 绩效考核模板：SUPER_ADMIN / HR_ADMIN
  - 考核维度配置：SUPER_ADMIN / HR_ADMIN
  - 操作日志：SUPER_ADMIN / HR_ADMIN

---

## 模块三：多层级 OKR 拆解与对齐

### 目标

支持四层 OKR（公司 → 部门 → 团队 → 个人）的逐级拆解与对齐，新增对齐可视化视图。

### 数据模型变更

```sql
ALTER TABLE okr_objective ADD COLUMN level VARCHAR(16) NOT NULL DEFAULT 'INDIVIDUAL';
ALTER TABLE okr_objective ADD COLUMN parent_objective_id INTEGER DEFAULT NULL;
ALTER TABLE okr_objective ADD COLUMN sort_order INTEGER DEFAULT 0;
ALTER TABLE okr_objective ADD COLUMN progress REAL DEFAULT 0;
```

`level` 取值: `COMPANY` / `DEPARTMENT` / `TEAM` / `INDIVIDUAL`

### 层级规则

| 层级 | 创建权限 | 可视范围 | 每周期建议数量 |
|------|---------|---------|:---:|
| COMPANY | SUPER_ADMIN / HR_ADMIN | 全员 | 3-5 |
| DEPARTMENT | DEPT_LEADER / HR_ADMIN | 本部门及下级 | 3-5 |
| TEAM | TEAM_LEADER | 本团队 | 1-3 |
| INDIVIDUAL | 本人 | 汇报链内 | 3-5 |

### 对齐流程

```
周期 DRAFT → OKR_ALIGN 阶段：

1. 管理员创建 COMPANY 级 O + KR（全员可见，作为对齐参照）
2. DEPT_LEADER 创建部门 O，选择对齐到某一公司 O
3. 员工创建个人 O，对齐到上级 O 或公司 O
4. 上级审核对齐关系，标记孤立/错位目标
```

### 进度汇总

- **KR → O**: Σ(KR.current / KR.target × KR.weight) / Σ(KR.weight)，百分制
- **下级 O → 上级 O**: 等权平均
- 触发时机：KR 进度更新时重算关联 O 的 progress

### 新增 API

| 端点 | 说明 |
|------|------|
| `GET /api/okr/period/{periodId}/tree` | OKR 对齐树（全层级） |
| `GET /api/okr/period/{periodId}/orphans` | 未对齐的个人 OKR |
| `POST /api/okr/{id}/align` | 设置/修改 parent_objective_id |
| `GET /api/okr/{id}/children` | 获取对齐到该目标的下级 O |

### 前端

- **OKR 对齐视图**（`/okr/alignment?periodId=X`）：
  - 左侧树形面板：el-tree 组件，四级展开/折叠，节点显示 O 标题 + 进度条
  - 右侧详情面板：选中节点后展示该 O 的 KR 列表、子目标列表、对齐链路
  - 孤立目标警告：未对齐的个人 O 在树中置灰并显示警告图标

---

## 通用约束

- 所有表使用 SQLite 兼容语法，不使用存储过程或触发器等不可移植特性
- 前端路由在 `MainLayout.vue` 子路由中注册，侧边栏自动展示
- 通知轮询间隔：60 秒
- 仅 SUPER_ADMIN 可修改他人角色和 scopes

---

## 不变更

- 现有单 JAR 部署方式不变
- 现有 OKR/KPI 双轨评分逻辑不变
- 现有周期状态机不变（DRAFT → OKR_ALIGN → EXECUTING → ASSESSING → ARCHIVED）
- 现有审计修正留痕机制不变
- 现有前端技术栈不变（Vue 3 + Element Plus）
