日期:2026-06-18
问题陈述:后端 PerformanceService.validateScoreParts 明确限制 OKR 为 0-40、KPI/工作成果为 0-60;上级打分页也按 OKR 40、工作成果 60 计算。但员工自评页曾按 OKR 60、工作成果 40 展示和计算。
证据:
backend/src/main/java/com/emoon/okr/service/PerformanceService.java:328:requireRange("OKR得分", okrScore, 0, 40)。backend/src/main/java/com/emoon/okr/service/PerformanceService.java:330:requireRange("KPI得分", kpiScore, 0, 60)。frontend/src/views/scoring/ScoringEditView.vue:130:上级终评 OKR 满分 40。frontend/src/views/scoring/ScoringEditView.vue:157:上级终评工作成果满分 60。frontend/src/views/scoring/MyResultView.vue 自评表单是本轮重点修复对象。影响:员工 OKR 自评满分时会向后端提交 60 分,触发后端 0-40 校验失败;即便未满分,也会造成自评与终评不可比。
建议修复方向:以后端为权威,前端所有自评、终评、结果展示统一为 OKR 40 + 工作成果 60。本轮已纳入直接修复,并增加前端评分契约检查脚本。
问题陈述:系统真实计算是 OKR 分 + 工作成果分 + 加分 - 扣分,不是结果页展示的 OKR × 0.6 / 工作成果 × 0.4。
证据:
backend/src/main/java/com/emoon/okr/service/PerformanceService.java:319:calculateTotal 直接相加。frontend/src/views/scoring/MyResultView.vue 结果页是本轮重点修复对象。frontend/src/views/scoring/ScoringEditView.vue:192 的 “OKR(40%) + 工作成果(60%)” 容易被理解为二次加权,也需要改为分值制描述。影响:员工查看绩效结果时会误解评分算法,降低绩效系统可信度。
建议修复方向:结果页展示改为 “OKR X / 40”“工作成果 Y / 60”;评分预览文案改为 “OKR 40 分制 + 工作成果 60 分制 + 加分 - 扣分”。本轮已纳入直接修复。
问题陈述:周期发布后会生成参与人快照。进入执行或考评前,系统要求所有参与人均完成并通过 OKR、锁定 KPI。如果某个参与人在周期内离职、调岗或不再参与,但没有被豁免,周期可能无法推进。
证据:
backend/src/main/java/com/emoon/okr/service/AssessmentPeriodService.java:169:流转检查读取参与人 ID。backend/src/main/java/com/emoon/okr/service/AssessmentPeriodService.java:179:统计未提交或未通过 OKR 人数。backend/src/main/java/com/emoon/okr/service/AssessmentPeriodService.java:189:统计未配置并锁定 KPI 人数。backend/src/main/java/com/emoon/okr/service/AssessmentPeriodService.java:511:周期发布时生成参与人快照。影响:真实公司运行中,单个离职或豁免人员就可能阻塞全公司周期。
建议修复方向:保留“参与人快照”的审计价值,但 HR/管理员必须能在参与人弹窗中把人员标记为豁免。本轮前端已提供树形参与人勾选/豁免入口;后续还应在流转检查文案中列出具体阻塞人员,而不只是人数。
问题陈述:上级评分主要校验 evaluatorId == targetUser.superiorId,未在评分服务内显式校验评分人仍为有效在职用户。
证据:
backend/src/main/java/com/emoon/okr/service/PerformanceService.java:83:只取目标用户并校验其直属上级。backend/src/main/java/com/emoon/okr/service/PerformanceService.java:86:权限判断依赖 superiorId。影响:若离职或禁用账号的 JWT 仍有效,并且组织关系未清理,理论上存在继续评分风险。
建议修复方向:在评分、发布、确认等敏感操作中统一通过用户状态守卫,校验 actor 未删除、未禁用、未离职。若当前依赖登录态拦截,也应补服务层纵深防御。
本轮处理:已在上级评分与自评提交链路增加用户有效性守卫,拒绝 deleted=1、DISABLED、LEFT、INACTIVE、disabledAt 或 leftAt 命中的用户继续提交绩效评分,并补充单元测试。
问题陈述:直属上级查看下属评分列表时,对每个下属调用一次 getScoringPage,每次都会拉取 OKR、评分、反馈等数据。
证据:
backend/src/main/java/com/emoon/okr/service/PerformanceService.java:222:查询下属列表。backend/src/main/java/com/emoon/okr/service/PerformanceService.java:225:逐个调用 getScoringPage。影响:50 人内企业短期可接受,但部门 20 人以上会明显放大数据库查询次数。
建议修复方向:列表页使用轻量 DTO,批量查询下属用户、评分状态、最终评分和必要 OKR 摘要;详情页再加载完整证据。
问题陈述:OkrService.enrichObjectives 为少量 OKR 富集用户名时拉取全部用户、全部部门。
证据:
backend/src/main/java/com/emoon/okr/service/OkrService.java:584:userMapper.selectList(null)。backend/src/main/java/com/emoon/okr/service/OkrService.java:586:departmentMapper.selectList(null)。影响:50 人内影响有限,但属于典型可优化点,后续组织规模扩大后会浪费查询和内存。
建议修复方向:按 objectives、aligned objectives、source KR 涉及的 userId / departmentId 集合批量查询。
本轮处理:已把用户名和部门名富集从全表扫描改为按引用 ID 批量查询,并把 source KR / source objective 查询改为批量加载,补充单元测试防止回退到 selectList(null)。
问题陈述:进入考评阶段后,业务层禁止员工修改 OKR,但评分页仍读取实时 OKR 数据;归档时才写 snapshot。
证据:
backend/src/main/java/com/emoon/okr/service/PerformanceService.java:159:评分页读取 okrService.getMyOkr 实时数据。backend/src/main/java/com/emoon/okr/service/AssessmentPeriodService.java:533:归档时才执行 snapshotPeriod。影响:若管理员或脚本直接修改数据库,考评依据会变化。对真实绩效上线而言,证据冻结时间应早于评分。
建议修复方向:进入 ASSESSING 时创建评分证据快照;评分页优先读取快照,归档只负责最终归档副本。
问题陈述:员工申诉经 HR 处理为 ACCEPTED 后,系统只更新申诉记录状态,不修改原评分。员工申诉成功,但绩效结果仍是原分,必须 HR 另走 audit correction 流程手动改分。
证据:
backend/src/main/java/com/emoon/okr/service/ScoreAppealService.java:89:resolveAppeal 仅 appeal.setStatus(status)、setReply、setResolvedBy,无评分回写。backend/src/main/java/com/emoon/okr/service/ScoreAppealService.java:93:更新后只发通知,未触发 PerformanceService 改分。backend/src/main/java/com/emoon/okr/entity/ScoreAppeal.java:申诉记录关联 scoreId,但受理后无任何反向写入 performance_score。影响:申诉"成功"是名义上的,员工看不到分数变化,绩效系统可信度受损。HR 需记住"受理后还要去改分",容易遗漏,造成申诉已处理但分数不对的脏数据。
建议修复方向:明确制度规则——申诉受理后是否自动改分、还是仅作为 HR 改分凭据。建议至少在受理为 ACCEPTED 时:(a) 在 UI 上提示"需通过绩效修正流程调整分数";(b) 在申诉详情页提供直达 audit correction 的入口。是否自动改分涉及绩效制度,需业务确认,本轮不静默实现。
问题陈述:PeriodScheduler.checkPeriodDeadlines 在周期结束前 3 天,对所有用户(userMapper.selectList(null))逐一发通知,未过滤在职状态。
证据:
backend/src/main/java/com/emoon/okr/schedule/PeriodScheduler.java:36:List<SysUser> users = userMapper.selectList(null)。backend/src/main/java/com/emoon/okr/schedule/PeriodScheduler.java:37-42:遍历全部用户发 PERIOD_DEADLINE 通知。影响:已离职、已禁用账号仍会收到通知(虽不登录,但通知表会写入无效数据);组织规模增长后每次扫描全表。
建议修复方向:仅向周期参与人快照中 status=ACTIVE 的成员发通知;或按 sys_user.status NOT IN (DISABLED, LEFT) 过滤。
问题陈述:PeriodScheduler.checkKrLagging 每天 10:07 扫描所有执行中周期,对进度落后时间线 20% 以上的 KR 推送通知,不检查当天是否已推过、也不检查历史通知,连续多天会产生重复通知轰炸。
证据:
backend/src/main/java/com/emoon/okr/schedule/PeriodScheduler.java:78-86:滞后即 publishEvent,无去重逻辑。影响:一条滞后 KR 在执行期可能累计推送数十条相同通知,淹没重要通知,员工对告警脱敏。
建议修复方向:对同一 KR 的滞后通知加冷却期(如 3 天内不重复),或在通知表增加 type+refId+userId 的当日去重校验。
问题陈述:FeedbackService.saveInterviewNotes 在面谈已存在时,直接覆盖 interviewNotes/highlights/issues/improvements,并把状态强制重置回 PENDING_EMPLOYEE、清空 closedAt。若员工已回复(状态为 REPLIED)或已关闭,重复发起会清空员工反馈状态。
证据:
backend/src/main/java/com/emoon/okr/service/FeedbackService.java:42-51:existing 分支无条件 setStatus(PENDING_EMPLOYEE)、setClosedAt(null)。employeeFeedback 字段未被清空(保留),但状态回退会让员工误以为需重新回复。影响:上级修正面谈记录时,可能无意中把已沟通闭环的面谈重新打开,员工收到"新的绩效面谈"通知造成困惑;闭环状态丢失。
建议修复方向:重复发起时区分场景——若已是 REPLIED/CLOSED,应二次确认或保留状态;或在已关闭的面谈上禁止覆盖,要求先重新打开。
问题陈述:DimensionController.delete 调用 service.delete(id),未校验该维度是否已被某周期的 KPI 配置引用。若维度被已锁定 KPI 引用,删除后历史评分依据的解释会缺失(虽然 KPI 配置有快照,但维度管理页会显示空)。
证据:
backend/src/main/java/com/emoon/okr/controller/DimensionController.java:43-49:delete 仅校验角色,未校验引用。backend/src/main/java/com/emoon/okr/service/DimensionService.java(delete 实现):逻辑删除,无引用检查。影响:删除被引用维度后,管理员维度列表不完整;虽然历史评分有 KPI 快照保护,但维度维护页与历史数据脱节。
建议修复方向:删除前校验是否有 KPI 配置引用,有则提示"已被 N 个周期引用,建议禁用而非删除",或改为软禁用。
问题陈述:后端对总分四舍五入到 1 位小数后再判定等级,临界值附近可能引发解释争议。
证据:
backend/src/main/java/com/emoon/okr/service/PerformanceService.java:319:总分保留 1 位小数。backend/src/main/java/com/emoon/okr/enums/Grade.java:等级阈值为 S≥90、A≥75、B≥60。影响:89.94 与 89.95 的等级差异会被放大。
建议修复方向:结果页展示“等级按四舍五入后一位小数判定”;或显示距下一等级差值。
问题陈述:多个核心页存在硬编码颜色、渐变徽章、emoji、营销式副标题,与内部医疗科技系统的专业感冲突。
证据:
frontend/src/views/okr/MyOkrView.vue 存在营销口号、渐变徽章和大量硬编码色。frontend/src/views/scoring/MyResultView.vue 存在 emoji、Material 色等级圆环和错误评分展示。frontend/src/components/MainLayout.vue 侧边栏选中态和角色标签仍偏强视觉。影响:系统看起来像生成式原型,不像真实企业内部工具。
建议修复方向:本轮按医疗科技样板页方案改造 LoginView、MainLayout、MyOkrView、ScoringEditView、MyResultView,并扩展 theme.css token。
sequenceDiagram
participant HR as HR/管理员
participant CEO as CEO/高层
participant Lead as 部门负责人
participant Emp as 员工
participant Sys as 系统
HR->>Sys: 创建考核周期 DRAFT
HR->>Sys: 发布 OKR 对齐
Sys->>Sys: 生成参与人快照(默认全员)
CEO->>Sys: 提交公司级 OKR
Lead->>Sys: 对齐上级 OKR 并提交部门 OKR
Emp->>Sys: 对齐上级 OKR 并提交个人 OKR
Lead->>Sys: 审核下属 OKR
HR->>Sys: 检查 OKR/KPI 就绪并进入 EXECUTING
Emp->>Sys: 周期内更新 KR 进度
HR->>Sys: 进入 ASSESSING
Emp->>Sys: 提交自评
Lead->>Sys: 上级终评并生成 FINAL
HR->>Sys: 发布绩效结果
Emp->>Sys: 确认或申诉
HR->>Sys: 处理申诉/修正
HR->>Sys: 归档周期
stateDiagram-v2
[*] --> DRAFT
DRAFT --> OKR_ALIGN: 发布 OKR 对齐 / 生成参与人快照
OKR_ALIGN --> EXECUTING: 所有 ACTIVE 参与人 OKR 通过且 KPI 锁定
EXECUTING --> OKR_ALIGN: 管理员回退
EXECUTING --> ASSESSING: 进入考评
EXECUTING --> ARCHIVED: 直接归档(需满足归档检查)
ASSESSING --> ARCHIVED: 最终评分发布且员工确认
ARCHIVED --> [*]
| 项目 | 权威口径 | 本轮处理 |
|---|---|---|
| OKR 得分 | 0-40 | 前端自评、终评、结果展示统一到 40 分制 |
| 工作成果/KPI | 0-60 | 前端自评、终评、结果展示统一到 60 分制 |
| 加分 | 0-20(当前 UI 控制为 0-5) | 后端允许 20,UI 后续可按制度再收紧或放开 |
| 扣分 | 0-20 | 后端权威校验 |
| 总分 | 0-100 | 直接相加,不做二次权重 |
| 等级 | S≥90 / A≥75 / B≥60 / C<60 | 以保留 1 位小数后的总分判定 |
为可审计起见,本轮把诊断中发现的高优问题分两批处理:评分口径为确定性 bug,直接修复;权限守卫与性能优化为诊断建议中可安全落地的项,一并修复并附测试。其余涉及制度或数据模型的项(严重-3 参与人豁免、严重-4 申诉改分、中等-4 证据快照)需业务确认规则,列为后续高优,本轮不静默实现。
| 报告编号 | 问题 | 本轮处理 | 验证 |
|---|---|---|---|
| 严重-1 | 自评口径 OKR 60 与后端 40 冲突 | 直接修复:前端自评/终评/结果统一 OKR 40 + 工作 60 | verify:scoring 通过 |
| 严重-2 | 结果页误写 ×0.6/×0.4 加权 |
直接修复:改分值制直述 | verify:scoring 通过 |
| 报告编号 | 问题 | 本轮处理 | 验证 |
|---|---|---|---|
| 中等-1 | 评分权限缺在职校验 | 已修复:自评/终评加 ensureActiveUser 守卫 |
PerformanceServiceTest |
| 中等-3 | enrich 全表扫描用户/部门 | 已修复:按引用 ID 批量查询 | OkrServiceTest |
| —(验证暴露) | SQLite WAL 下通知锁 | 已修复:@TransactionalEventListener(AFTER_COMMIT) + 重试 |
NotificationEventListenerTest |
| 报告编号 | 问题 | 阻塞原因 |
|---|---|---|
| 严重-3 | 参与人豁免机制 | 需定义豁免规则与状态机回退语义 |
| 严重-4 | 申诉受理后改分 | 需定义自动改分 vs 凭据改分的制度规则 |
| 中等-2 | 下属评分 N+1 | 50 人内可接受,需 DTO 重构,单独排期 |
| 中等-4 | 进入 ASSESSING 冻结证据快照 | 改数据模型,需迁移方案 |
| 中等-5/6/7/8 | 定时任务去重、面谈防覆盖、维度引用校验 | 边界场景,需制度与优先级确认 |