|
|
@@ -0,0 +1,302 @@
|
|
|
+# OKR 绩效系统 · 流程诊断报告 + 智慧医疗 UI 改造设计
|
|
|
+
|
|
|
+**日期**:2026-06-18
|
|
|
+**作者**:ZCode(基于与产品负责人 wangkangyjy 的头脑风暴)
|
|
|
+**状态**:待审查
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 0. 背景与任务范围
|
|
|
+
|
|
|
+医梦AI OKR 绩效考核系统(Spring Boot 3.2 + Vue 3 + Element Plus + 单 JAR + SQLite),用于创始人当前创业阶段的内部绩效运营。本规格覆盖两个任务:
|
|
|
+
|
|
|
+- **任务1(流程诊断)**:走查从「周期发布 → 各级发布 OKR → 自评 → 上级终评 → 发布 → 确认 → 归档」的完整链路,外加 KPI/维度/日志/定时任务/申诉/面谈/通知环节,输出纯代码走查报告(**不改后端代码**)。
|
|
|
+- **任务2(前端去 AI 味 + 智慧医疗化)**:改造 5 个核心样板页(LoginView / MainLayout / MyOkrView / ScoringEditView / MyResultView),建立升级版设计系统,注入克制的医疗意象。
|
|
|
+
|
|
|
+**已确认的决策**(来自头脑风暴):
|
|
|
+- 任务1 交付物:只要诊断报告,不改后端
|
|
|
+- 任务1 深度:纯代码走查(快)
|
|
|
+- 任务1 范围:全链路全覆盖(核心 9 条 + KPI/维度/日志/定时/申诉/面谈/通知)
|
|
|
+- 任务2 风格:克制专业 + 医疗意象
|
|
|
+- 任务2 范围:先做 5 个核心样板页
|
|
|
+- 任务2 医疗意象重量:仅品牌区点缀
|
|
|
+- 任务2 意象实现:内联 SVG(零依赖)
|
|
|
+- 任务2 打包:改完负责 `npm run build` + 复制到 `backend/src/main/resources/static/`
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 任务1:流程诊断报告设计
|
|
|
+
|
|
|
+### 1.1 交付物
|
|
|
+
|
|
|
+一份结构化 Markdown 报告 `docs/flow-audit-report.md`(不修改任何后端代码)。报告按严重等级组织,每条问题包含:**问题陈述 → 证据(file:line)→ 影响 → 建议修复方向**。
|
|
|
+
|
|
|
+### 1.2 报告骨架
|
|
|
+
|
|
|
+```
|
|
|
+## 严重程度图例
|
|
|
+## 🔴 严重(流程出错 / 权限漏洞 / 数据不一致)
|
|
|
+## 🟠 中等(边界场景 / 体验缺陷 / 计算口径歧义)
|
|
|
+## 🟡 轻微(文案 / 可维护性 / 性能)
|
|
|
+## ✅ 已验证正确的环节
|
|
|
+## 附录 A:完整流程时序图
|
|
|
+## 附录 B:周期状态机图
|
|
|
+## 附录 C:评分口径对照表
|
|
|
+```
|
|
|
+
|
|
|
+### 1.3 已确认的关键发现(预览,共 9+ 条核心)
|
|
|
+
|
|
|
+> 这些是走查中已确认的真实问题,报告定稿时会补全证据链与建议。
|
|
|
+
|
|
|
+#### 🔴 严重-1:前后端评分口径严重不一致(自评 vs 终评满分颠倒)
|
|
|
+
|
|
|
+| 评分人 | OKR 满分 | 工作成果满分 | 证据 |
|
|
|
+|---|---|---|---|
|
|
|
+| 自评(MyResultView 表单) | **60** | **40** | `MyResultView.vue:12` `OKR 目标达成自评…满分 60`、`okrTotal = sum/n * 60` |
|
|
|
+| 上级终评(ScoringEditView) | **40** | **60** | `ScoringEditView.vue` `OKR 目标达成…满分 40`、`computedOkrScore = avg * 40` |
|
|
|
+| 后端校验 | 0-40 | 0-60 | `PerformanceService.java` `validateScoreParts: requireRange("OKR得分", okrScore, 0, 40)` |
|
|
|
+
|
|
|
+- **影响**:员工自评若 OKR 项打满(前端算出 60),后端校验 `0~40` 直接报错,自评在边界值必崩;即便不崩,自评与终评语义颠倒,横向不可比。
|
|
|
+- **建议**:统一为「OKR≤40 + 工作成果≤60」单一口径,前端自评表单与结果展示全部对齐;后端 `validateScoreParts` 保持不变作为权威。
|
|
|
+
|
|
|
+#### 🔴 严重-2:「比例加权」与「分值占比」语义混乱
|
|
|
+
|
|
|
+- **证据**:`MyResultView.vue` 结果页展示 `OKR × 0.6`、`工作成果 × 0.4`,暗示加权系数;但 `PerformanceService.calculateTotal` 是纯加法 `okrScore + kpiScore + bonus - penalty`,无任何加权。AGENTS.md 的「OKR(40%) + KPI(60%)」是分值占比(40 分制 + 60 分制),非加权。
|
|
|
+- **影响**:员工误以为 OKR 分被打六折,结果页数据不可信。
|
|
|
+- **建议**:删除所有 `× 0.6 / × 0.4` 表述,改为「OKR 得分 38 / 40」「工作成果 55 / 60」的直述。
|
|
|
+
|
|
|
+#### 🔴 严重-3:自评与终评的记录独立性缺少一致性校验
|
|
|
+
|
|
|
+- **证据**:`submitSelfScore` 仅校验「是否已存在 SELF 记录」(`PerformanceService.java` findExisting)。`submitSuperiorScore` 校验「员工已提交自评」但允许 SELF 与 SUPERIOR/FINAL 各自独立。
|
|
|
+- **影响**:若管理员修正/删除自评后员工重提交,自评与已存在的 FINAL 可能严重背离,系统无任何提示或阻断。
|
|
|
+- **建议**:终评生成 FINAL 后,若 SELF 被改动应记录偏差告警;或在 UI 上展示「自评 vs 终评」差异提示。
|
|
|
+
|
|
|
+#### 🟠 中等-4:评分权限未校验评分人在职状态
|
|
|
+
|
|
|
+- **证据**:`submitSuperiorScore` 只校验 `evaluatorId.equals(targetUser.getSuperiorId())`(`PerformanceService.java`),未校验 evaluator 或 target 的 `deleted` 字段。
|
|
|
+- **影响**:被软删用户(superior_id 未清理 + JWT 仍有效)理论上可评分。
|
|
|
+- **建议**:评分时校验 evaluator `deleted=0`;或依赖定期清理 superior_id 的运维流程并文档化。
|
|
|
+
|
|
|
+#### 🟠 中等-5:`getSubordinateScores` N+1 查询
|
|
|
+
|
|
|
+- **证据**:`PerformanceService.getSubordinateScores` 对每个下属调用一次 `getScoringPage`,内部 `okrService.getMyOkr → enrichObjectives` 又触发全表用户扫描(见中等-6)。`PerformanceService.java:getSubordinateScores`。
|
|
|
+- **影响**:部门 20 人时,20 次全表用户扫描 + 20 次 OKR 富集,性能劣化。
|
|
|
+- **建议**:批量查询 OKR 与用户名 map,消除 N+1。
|
|
|
+
|
|
|
+#### 🟠 中等-6:`enrichObjectives` 全表用户扫描
|
|
|
+
|
|
|
+- **证据**:`OkrService.java:584` `userMapper.selectList(null)` 把所有用户拉进内存建 name map,无论查几个 OKR。
|
|
|
+- **影响**:用户量增长后内存与查询开销。
|
|
|
+- **建议**:仅按 `objectives` 涉及的 userId 集合 `selectBatchIds` 查询。
|
|
|
+
|
|
|
+#### 🟠 中等-7:周期被快照中的离职/调岗人员永久卡死
|
|
|
+
|
|
|
+- **证据**:`AssessmentPeriodService.collectTransitionBlockers` 要求所有参与人(周期发布时的快照 `createParticipantSnapshot`)都提交并锁定 OKR+KPI 才能进 EXECUTING/ASSESSING。快照里的人员若离职/调岗,OKR 永不 APPROVED,周期卡死,**无管理员强制推进或移除参与人的出口**。
|
|
|
+- **影响**:单个离职员工可阻塞整个周期推进。
|
|
|
+- **建议**:提供管理员「强制跳过」或「移除参与人」接口;或在 readyCheck 中允许离职人员豁免。
|
|
|
+
|
|
|
+#### 🟡 轻微-8:Grade 临界值四舍五入争议
|
|
|
+
|
|
|
+- **证据**:`Grade.fromScore`(S≥90/A≥75/B≥60)+ `calculateTotal` 的 `Math.round(*10)/10.0`。89.95→90.0→S,89.94→89.9→A。
|
|
|
+- **影响**:临界值员工可能认为该是 S 实际是 A。
|
|
|
+- **建议**:文档化临界规则;或在 UI 提示「距下一等级差 X 分」。
|
|
|
+
|
|
|
+#### 🟡 轻微-9:考评阶段评分依据未对 OKR 拍快照
|
|
|
+
|
|
|
+- **证据**:进入 ASSESSING 后 OKR 锁定(`updateMyOkr` 拒绝),但 `getScoringPage` 实时拉 OKR 进度,归档时才 `snapshotPeriod`。考评期间若管理员直接改库改 KR 进度,评分依据会变。
|
|
|
+- **建议**:进入 ASSESSING 时即对 OKR 拍快照,评分读快照不读实时表。
|
|
|
+
|
|
|
+#### 待补充环节(走查时一并覆盖)
|
|
|
+
|
|
|
+- **KPI 模板/配置**:`KpiService`、`KpiController`、`DimensionController`、KPI 锁定时机、维度权重计算
|
|
|
+- **操作日志**:`SystemController`、日志覆盖度、敏感操作是否都记录
|
|
|
+- **定时任务**:`PeriodScheduler`(每日 2am 自动推进)、自动推进的触发条件与失败处理
|
|
|
+- **申诉流程**:`ScoreAppealController`、申诉与 FINAL 的关系、HR 处理闭环
|
|
|
+- **面谈反馈**:`FeedbackController`、面谈记录与评分的关联
|
|
|
+- **通知**:`NotificationEvent` 发布点、送达可靠性、已读/未读一致性
|
|
|
+
|
|
|
+### 1.4 评分口径对照表(附录 C 核心)
|
|
|
+
|
|
|
+```
|
|
|
+权威口径(后端 PerformanceService.calculateTotal):
|
|
|
+ total = okrScore(0-40) + kpiScore(0-60) + bonus(0-20) - penalty(0-20)
|
|
|
+ Grade: S≥90 / A≥75 / B≥60 / C<60
|
|
|
+
|
|
|
+错误展示点(前端):
|
|
|
+ - MyResultView 自评表单:OKR×60 + 工作×40(与权威颠倒)
|
|
|
+ - MyResultView 结果展示:OKR×0.6 + 工作×0.4(误导加权)
|
|
|
+ - ScoringEditView 终评:OKR×40 + 工作×60(与权威一致 ✓)
|
|
|
+```
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 任务2:前端去 AI 味 + 智慧医疗化改造设计
|
|
|
+
|
|
|
+### 2.1 现状诊断结论
|
|
|
+
|
|
|
+**反直觉的核心洞察**:现有 `theme.css` token 体系本身是克制、专业、正确的(深靛 `#2b1f99` + 青 `#3ad4d8`、极浅阴影、8px 圆角)。LoginView 已变量化,是项目里最干净的页面。
|
|
|
+
|
|
|
+**「AI 味」几乎全部来自业务页面无视 token、硬编码典型 AI 生成前端反模式**:
|
|
|
+
|
|
|
+| 反模式 | 证据 |
|
|
|
+|---|---|
|
|
|
+| 营销式副标题 "1O · 3KR · 强对齐 · 看结果" | `MyOkrView.vue:6` |
|
|
|
+| 渐变发光胶囊徽章 + 彩色阴影 | `MyOkrView.vue:691-693` |
|
|
|
+| 硬编码十六进制色值十余个(无视 token) | `MyOkrView.vue:669-700` `#1a1a2e/#888/#27ae60/#e67e22/...` |
|
|
|
+| Material 色板与品牌靛蓝冲突 | `MyOkrView.vue:674-676` `#e0f7fa/#2e7d32/#00838f` |
|
|
|
+| emoji 装饰 `📤` `💬` | `MyResultView.vue` |
|
|
|
+| 符号化口语文案 `@你` `✓` `+ 添加 KR` | `MyOkrView.vue:248,250,310` |
|
|
|
+| 圆角不统一(4/6/8/10/18px 混用) | `MyOkrView.vue:678` |
|
|
|
+| 阴影硬编码重复(比 token 更黑更重) | `MyOkrView.vue:678,697,708` |
|
|
|
+| 渐变背景卡 | `MyResultView.vue:.score-hero.self-submitted` |
|
|
|
+| 等级圆环 Material 配色 | `MyResultView.vue:.grade-ring` |
|
|
|
+
|
|
|
+**结论**:改造不是「推翻重做」,而是「让样板页回归 token + 清反模式 + 品牌节点注入医疗意象」。
|
|
|
+
|
|
|
+### 2.2 升级版设计系统(增量于现有 theme.css)
|
|
|
+
|
|
|
+#### 配色(保留现有,新增医疗语义色)
|
|
|
+
|
|
|
+```css
|
|
|
+:root {
|
|
|
+ /* 保留 */
|
|
|
+ --color-brand: #2b1f99;
|
|
|
+ --color-accent: #3ad4d8;
|
|
|
+ /* …现有中性色保留… */
|
|
|
+
|
|
|
+ /* 新增:等级语义色(统一替换 Material 杂色) */
|
|
|
+ --color-grade-s: #0d7a5f; /* 卓越,深青绿,医疗系非荧光 */
|
|
|
+ --color-grade-a: #2e9d65; /* 良好 */
|
|
|
+ --color-grade-b: #4c6f91; /* 达标,靛蓝系 */
|
|
|
+ --color-grade-c: #c94a4a; /* 待改进,柔红 */
|
|
|
+
|
|
|
+ /* 新增:医疗意象专用色(仅品牌区) */
|
|
|
+ --color-medical: #1a6b7a;
|
|
|
+
|
|
|
+ /* 新增:统一淡底(替代 #e0f7fa/#fef9e7 等) */
|
|
|
+ --color-surface-tint-brand: #f4f3fb;
|
|
|
+ --color-surface-tint-success: #f0f7f3;
|
|
|
+ --color-surface-tint-warning: #faf6ee;
|
|
|
+ --color-surface-tint-danger: #faf2f2;
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+#### 排版与节奏(收敛)
|
|
|
+
|
|
|
+```css
|
|
|
+/* 字号阶梯:删除 28/40/48 等零散值 */
|
|
|
+--font-size-xs: 12px; --font-size-sm: 13px; --font-size-base: 14px;
|
|
|
+--font-size-lg: 16px; --font-size-xl: 18px; --font-size-2xl: 24px; --font-size-3xl: 32px;
|
|
|
+/* 字重:400/500/700(去掉 600/800) */
|
|
|
+/* 间距:4/8/12/16/24/32(8 的倍数) */
|
|
|
+/* 圆角:只用 xs(4)/sm(6)/md(8),删除 10/14/18px */
|
|
|
+/* 阴影:唯一 --shadow-panel,删除所有 box-shadow 硬编码重复 */
|
|
|
+```
|
|
|
+
|
|
|
+#### 页面级规范(样板页须遵守)
|
|
|
+
|
|
|
+1. 所有颜色必须用 `var(--color-*)`,禁止硬编码十六进制
|
|
|
+2. 所有圆角必须用 `var(--radius-*)`,禁止裸 `border-radius: Npx`
|
|
|
+3. 所有阴影必须用 `var(--shadow-panel)` 或 `var(--shadow-popover)`
|
|
|
+4. 删除所有 `linear-gradient`(侧边栏品牌容器除外)
|
|
|
+5. 删除所有 emoji,用内联 SVG 或 el-icon
|
|
|
+6. 标题文案克制功能性,删除营销式口号
|
|
|
+7. 状态色统一映射到 grade 语义色或 tint 色
|
|
|
+
|
|
|
+### 2.3 医疗意象设计(仅品牌区,内联 SVG)
|
|
|
+
|
|
|
+#### 意象 A:心电波形装饰线
|
|
|
+
|
|
|
+- **用途**:侧边栏 logo 区底部装饰线、登录页品牌区
|
|
|
+- **实现**:内联 SVG,1px 描边,`var(--color-accent)`,opacity 0.5,宽度自适应
|
|
|
+- **形态**:一条平直线 + 中间一个 QRS 脉冲波峰,克制不喧宾夺主
|
|
|
+
|
|
|
+#### 意象 B:分子节点连接图形
|
|
|
+
|
|
|
+- **用途**:登录页品牌区右下角点缀
|
|
|
+- **实现**:内联 SVG,描边风格,单色 `var(--color-brand)`,3-5 个节点用细线连接
|
|
|
+- **形态**:象征「组织协同 / OKR 网络」,呼应绩效系统的「对齐」语义
|
|
|
+
|
|
|
+#### 意象 C:空状态描边图标
|
|
|
+
|
|
|
+- **用途**:空状态、加载态
|
|
|
+- **实现**:内联 SVG 描边图标(病历、听诊器、心电图等),单色 `var(--color-text-muted)`
|
|
|
+
|
|
|
+**业务页主体不注入任何医疗具象图形**,保持纯几何/数据驱动的克制感。
|
|
|
+
|
|
|
+### 2.4 五个样板页改造蓝图
|
|
|
+
|
|
|
+#### 页面 1:LoginView(登录页)
|
|
|
+
|
|
|
+- 当前已较干净。保留英文 eyebrow「Internal Access」
|
|
|
+- **删除** `linear-gradient(135deg, rgba(43,31,153,0.08), transparent 42%)` 半透叠层 → 改纯 `var(--color-bg)`
|
|
|
+- **新增** 品牌区右侧分子节点连接图形(意象 B,brand 色描边)
|
|
|
+- **新增** logo 下方心电波形装饰线(意象 A)
|
|
|
+- 输入框/按钮已在用 token,仅微调间距
|
|
|
+- 「账号登录」标题更克制,去掉过强字重
|
|
|
+
|
|
|
+#### 页面 2:MainLayout(主架:侧边栏 + 顶栏)
|
|
|
+
|
|
|
+- **侧边栏**:保留深靛渐变(唯一允许渐变的品牌容器)
|
|
|
+- **新增** logo 区底部心电波形装饰线(意象 A)
|
|
|
+- **改** 菜单选中态:删除 `border-left: 3px solid #3ad4d8`(AI 味),改 2px accent 竖条 + `rgba(58,212,216,0.1)` 淡底
|
|
|
+- **改** 角色标签 `effect="dark" round` → `effect="plain"` 更克制
|
|
|
+- 顶栏已克制,基本不动;通知面板样式收敛到 token
|
|
|
+
|
|
|
+#### 页面 3:MyOkrView(我的 OKR)— 改动最大
|
|
|
+
|
|
|
+- **删除** "1O · 3KR · 强对齐 · 看结果" → 改「本周期目标与关键结果」
|
|
|
+- **删除** 三种渐变发光胶囊 `.section-badge.personal/.superior/.review`(`linear-gradient + box-shadow`)→ 改 token 化纯色小标签 + 左侧 2px 色条
|
|
|
+- **映射** 所有硬编码色:`#1a1a2e→--color-text`、`#888→--color-text-muted`、`#27ae60→--color-success`、`#e67e22→--color-warning`、`#e74c3c→--color-danger`、`#f39c12→--color-warning`
|
|
|
+- **删除** Material 状态条 `#e0f7fa/#00838f/#2e7d32` → `--color-surface-tint-*` + 语义色
|
|
|
+- **改** `@你` → 「指派给我」el-tag;`✓` → el-icon Check;`+ 添加 KR` → el-button 带 icon
|
|
|
+- 规则卡、对齐区、KR 表单圆角统一 8px,阴影统一 `--shadow-panel`
|
|
|
+
|
|
|
+#### 页面 4:ScoringEditView(绩效评分分屏页)
|
|
|
+
|
|
|
+- 左证据面板 + 右打分面板:所有硬编码色收敛到 token
|
|
|
+- KR 打分下拉「卓越/良好/达标/少量推进/未做」分级,对齐 grade 语义色
|
|
|
+- `result-preview` 的 `#f4f2f9` 底 + `#dcd4f0` 边 → `--color-surface-tint-brand` + `--color-border-soft`
|
|
|
+- 删除残留 Material 色
|
|
|
+
|
|
|
+#### 页面 5:MyResultView(我的绩效结果)
|
|
|
+
|
|
|
+- **删除** `📤` `💬` emoji → 内联 SVG(意象 C)/ el-icon
|
|
|
+- **重做** `.grade-ring`:用 `--color-grade-s/a/b/c` 替换 Material 的 `#f1c40f/#27ae60/#3498db/#e74c3c`
|
|
|
+- **删除** `linear-gradient(135deg,#fffcf5,#fef9e7)` → `--color-surface-tint-warning`
|
|
|
+- **修正** `OKR × 0.6` / `工作成果 × 0.4` → 改正确的「OKR 得分 X / 40」「工作成果 Y / 60」直述(联动任务1严重-2)
|
|
|
+- 确认/申诉/对比卡片收敛到 token
|
|
|
+
|
|
|
+### 2.5 构建与部署
|
|
|
+
|
|
|
+- 改完 5 个样板页 + theme.css 后:`cd frontend && npm run build`
|
|
|
+- 将 `frontend/dist/` 内容复制到 `backend/src/main/resources/static/`(覆盖旧产物)
|
|
|
+- 与现有 `scripts/start.sh` 流程一致
|
|
|
+
|
|
|
+---
|
|
|
+
|
|
|
+## 实施顺序
|
|
|
+
|
|
|
+1. **先写任务1 诊断报告** `docs/flow-audit-report.md`(补全 KPI/维度/日志/定时/申诉/面谈/通知的走查)
|
|
|
+2. **任务2 升级 theme.css** 增量 token(不破坏现有引用)
|
|
|
+3. **任务2 改造 5 个样板页**(按 Login → MainLayout → MyOkr → ScoringEdit → MyResult 顺序)
|
|
|
+4. **build + 复制 static**
|
|
|
+5. 交付:诊断报告 + 改造后的 5 个样板页
|
|
|
+
|
|
|
+## 不在本次范围(YAGNI)
|
|
|
+
|
|
|
+- 不改后端代码(任务1 只要报告)
|
|
|
+- 不批量改其余 ~10 个页面(待样板页验收后再决定是否套用)
|
|
|
+- 不引入图标库依赖(用内联 SVG)
|
|
|
+- 不做深色模式
|
|
|
+- 不动业务逻辑、API、数据库
|
|
|
+
|
|
|
+## 验收标准
|
|
|
+
|
|
|
+- **任务1**:诊断报告覆盖全链路,每条问题有 file:line 证据 + 影响 + 建议;含时序图与状态机图
|
|
|
+- **任务2**:
|
|
|
+ - 5 个样板页无硬编码十六进制色值(除 SVG 内描边色)
|
|
|
+ - 无 emoji、无渐变发光胶囊、无营销口号
|
|
|
+ - 品牌区(侧边栏 logo、登录页)有内联 SVG 医疗意象
|
|
|
+ - `npm run build` 成功,产物已复制到 backend/static
|
|
|
+ - 评分口径展示与后端权威口径一致(OKR≤40 + 工作≤60)
|