日期:2026-06-18 作者:ZCode(基于与产品负责人 wangkangyjy 的头脑风暴) 状态:待审查
医梦AI OKR 绩效考核系统(Spring Boot 3.2 + Vue 3 + Element Plus + 单 JAR + SQLite),用于创始人当前创业阶段的内部绩效运营。本规格覆盖两个任务:
已确认的决策(来自头脑风暴):
npm run build + 复制到 backend/src/main/resources/static/一份结构化 Markdown 报告 docs/flow-audit-report.md(不修改任何后端代码)。报告按严重等级组织,每条问题包含:问题陈述 → 证据(file:line)→ 影响 → 建议修复方向。
## 严重程度图例
## 🔴 严重(流程出错 / 权限漏洞 / 数据不一致)
## 🟠 中等(边界场景 / 体验缺陷 / 计算口径歧义)
## 🟡 轻微(文案 / 可维护性 / 性能)
## ✅ 已验证正确的环节
## 附录 A:完整流程时序图
## 附录 B:周期状态机图
## 附录 C:评分口径对照表
这些是走查中已确认的真实问题,报告定稿时会补全证据链与建议。
| 评分人 | 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) |
0~40 直接报错,自评在边界值必崩;即便不崩,自评与终评语义颠倒,横向不可比。validateScoreParts 保持不变作为权威。MyResultView.vue 结果页展示 OKR × 0.6、工作成果 × 0.4,暗示加权系数;但 PerformanceService.calculateTotal 是纯加法 okrScore + kpiScore + bonus - penalty,无任何加权。AGENTS.md 的「OKR(40%) + KPI(60%)」是分值占比(40 分制 + 60 分制),非加权。× 0.6 / × 0.4 表述,改为「OKR 得分 38 / 40」「工作成果 55 / 60」的直述。submitSelfScore 仅校验「是否已存在 SELF 记录」(PerformanceService.java findExisting)。submitSuperiorScore 校验「员工已提交自评」但允许 SELF 与 SUPERIOR/FINAL 各自独立。submitSuperiorScore 只校验 evaluatorId.equals(targetUser.getSuperiorId())(PerformanceService.java),未校验 evaluator 或 target 的 deleted 字段。deleted=0;或依赖定期清理 superior_id 的运维流程并文档化。getSubordinateScores N+1 查询PerformanceService.getSubordinateScores 对每个下属调用一次 getScoringPage,内部 okrService.getMyOkr → enrichObjectives 又触发全表用户扫描(见中等-6)。PerformanceService.java:getSubordinateScores。enrichObjectives 全表用户扫描OkrService.java:584 userMapper.selectList(null) 把所有用户拉进内存建 name map,无论查几个 OKR。objectives 涉及的 userId 集合 selectBatchIds 查询。AssessmentPeriodService.collectTransitionBlockers 要求所有参与人(周期发布时的快照 createParticipantSnapshot)都提交并锁定 OKR+KPI 才能进 EXECUTING/ASSESSING。快照里的人员若离职/调岗,OKR 永不 APPROVED,周期卡死,无管理员强制推进或移除参与人的出口。Grade.fromScore(S≥90/A≥75/B≥60)+ calculateTotal 的 Math.round(*10)/10.0。89.95→90.0→S,89.94→89.9→A。updateMyOkr 拒绝),但 getScoringPage 实时拉 OKR 进度,归档时才 snapshotPeriod。考评期间若管理员直接改库改 KR 进度,评分依据会变。KpiService、KpiController、DimensionController、KPI 锁定时机、维度权重计算SystemController、日志覆盖度、敏感操作是否都记录PeriodScheduler(每日 2am 自动推进)、自动推进的触发条件与失败处理ScoreAppealController、申诉与 FINAL 的关系、HR 处理闭环FeedbackController、面谈记录与评分的关联NotificationEvent 发布点、送达可靠性、已读/未读一致性权威口径(后端 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(与权威一致 ✓)
反直觉的核心洞察:现有 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 + 清反模式 + 品牌节点注入医疗意象」。
: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;
}
/* 字号阶梯:删除 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 硬编码重复 */
var(--color-*),禁止硬编码十六进制var(--radius-*),禁止裸 border-radius: Npxvar(--shadow-panel) 或 var(--shadow-popover)linear-gradient(侧边栏品牌容器除外)var(--color-accent),opacity 0.5,宽度自适应var(--color-brand),3-5 个节点用细线连接var(--color-text-muted)业务页主体不注入任何医疗具象图形,保持纯几何/数据驱动的克制感。
linear-gradient(135deg, rgba(43,31,153,0.08), transparent 42%) 半透叠层 → 改纯 var(--color-bg)border-left: 3px solid #3ad4d8(AI 味),改 2px accent 竖条 + rgba(58,212,216,0.1) 淡底effect="dark" round → effect="plain" 更克制.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#e0f7fa/#00838f/#2e7d32 → --color-surface-tint-* + 语义色@你 → 「指派给我」el-tag;✓ → el-icon Check;+ 添加 KR → el-button 带 icon--shadow-panelresult-preview 的 #f4f2f9 底 + #dcd4f0 边 → --color-surface-tint-brand + --color-border-soft📤 💬 emoji → 内联 SVG(意象 C)/ el-icon.grade-ring:用 --color-grade-s/a/b/c 替换 Material 的 #f1c40f/#27ae60/#3498db/#e74c3clinear-gradient(135deg,#fffcf5,#fef9e7) → --color-surface-tint-warningOKR × 0.6 / 工作成果 × 0.4 → 改正确的「OKR 得分 X / 40」「工作成果 Y / 60」直述(联动任务1严重-2)cd frontend && npm run buildfrontend/dist/ 内容复制到 backend/src/main/resources/static/(覆盖旧产物)scripts/start.sh 流程一致docs/flow-audit-report.md(补全 KPI/维度/日志/定时/申诉/面谈/通知的走查)npm run build 成功,产物已复制到 backend/static