人走了,记录不能“空白”: MES×HR 离职人员下拉回显实战

业务场景: SMT 车间,MES 里的"换料记录、异常单、首件确认"等表单需要选择经办人/确认人。人员主数据来自 HR 系统。后来 HR 把离职人员状态置为"离职",MES 人员下拉默认只取在职人员。结果新表单好用了,但历史表单上原经办人一旦离职,下拉框就空白,时间久了,使用人员在查看表单时,不知该表单的经办人、确认人为谁的问题。

涉及系统:

  • HR 系统:人员主数据权威源,维护员工在职/离职状态、部门、工号、姓名。

  • MES 系统:生产工单、换料记录、异常单、首件确认等表单,页面使用人员下拉,业务表存经办人 ID。

  • OA/BPM:审批流,审批记录里存了审批人姓名快照,所以历史审批人基本正常。

  • ERP:接收 MES 工单完工、库存扣料等数据,操作员字段多为文本,用于和 MES 对账。

**原始需求:**业务提了两个看似矛盾的诉求:新建表单时,人员下拉不要显示几千人,也不要让用户选到离职人员;但查看历史表单时,必须能看出当初是谁操作的,不能因为人离职了就空白。IT 之前为了满足第一个诉求,把下拉数据源一刀切过滤成"在职",结果历史单据经办人大量空白,SMT 质量追溯和 ERP 对账都受影响。

**排查/分析过程:**先从 MES 历史换料记录页面开始查。选一条确认人已离职的单据,页面确认人显示空白。打开浏览器网络请求,发现下拉调的是 HR 人员选项接口,参数是 status=active,返回数据里没有这个离职人员。再查 MES 业务表,发现只存了 handler_id,没有存姓名快照。查看页又复用了编辑页的下拉组件,用人员选项去匹配 ID 对应的 label,匹配不到就显示空。继续查 HR,发现人员没有物理删除,只是 status=inactive,姓名还在。OA 那边因为审批记录存了姓名快照,所以历史审批人正常。根因是:下拉数据源承担了历史回显职责,且过滤条件一刀切。

**方案设计:**考虑了三种方案。

方案 A:下拉恢复全量人员。历史能显示,但新建时列表太长,还可能选到离职人员,否决。

方案 B:每个页面单独改,查看时单独查 HR 详情。页面太多,工作量大,容易漏,风险高。

方案 C:公共人员下拉接口加 includeIds,默认只返回在职,但把当前表单已选人员 ID 作为例外带出来;离职人员标记"已离职",只用于回显,不能新选;查看页直接用姓名快照或文本显示。

最终选 C,因为改动集中在公共接口和公共下拉组件,大部分页面不用大调,能满足"正常显示"的目标。后续再补业务表姓名快照和提交校验放行原值。

技术实现:

1)表结构:HR 人员表保留软删除,字段如 employee_id、name、status、leave_date、dept_id。MES 业务表增加 handler_name、handler_dept_name 快照字段,保存时写入。

2)接口设计:

bash 复制代码
GET /api/common/employee-options?status=active&includeIds=1001,1002

后端 SQL 伪代码:

bash 复制代码
select * from hr_employee
where status = 'active'
   or employee_id in (:includeIds)
order by status, name;

返回时对离职人员加 label:

bash 复制代码
[
  { "id": 1, "name": "李四", "status": "active", "label": "李四" },
  { "id": 1001, "name": "张三", "status": "inactive", "label": "张三(已离职)", "disabled": true }
]

3)前端公共下拉组件:加载选项时自动把当前值塞进 includeIds:

bash 复制代码
const includeIds = currentValue ? [currentValue] : [];
const options = await api.get('/common/employee-options', {
  status: 'active',
  includeIds: includeIds.join(',')
});

如果当前值还不在选项里,就用业务表快照或单独查人员详情补一个 option,至少显示"张三(已离职)",不能空白。

4)提交校验:新建时校验必须在职;编辑时如果经办人没变,即使已离职也允许提交;如果换了人,新选人员必须在职。

5)查看页:不再依赖下拉数据源,直接显示 handler_name,离职状态动态补充为"张三(已离职)";查不到就显示"已离职人员(ID:1001)"。

**上线效果:**新建工单下拉从 3000 多人降到 800 多在职人员,选择速度明显提升;历史换料记录、异常单中涉及离职经办人的 600 多条记录恢复显示,经办人空白率从约 5% 降到 0;编辑历史单据不改经办人时不再报"人员必须在职";SMT 质量和 ERP 对账能追溯到具体操作人,业务投诉下降。

**踩坑与反思:**一开始只改前端,把离职人员硬塞进下拉,结果新建时也能选到离职人员,被业务发现;后来加了 disabled 和后端校验才控制住。还遇到后端校验没放行原值,编辑历史单直接报错。另外只存 ID 不存姓名快照,人员改名或部门调整后,历史显示会和当初不一致,后来补了 handler_name 快照。如果重来,应该一开始就把"人员状态"和"可选性"分开,把"历史回显"和"新建选择"分开;人员主数据坚持软删除;业务表保存时冗余姓名快照;能改公共组件和公共接口,就不要逐页打补丁。

相关推荐
博风19 小时前
8、设计模式应用举例:适配器模式:Iterator接口与Enumeration接口
设计模式·适配器模式
高晶20 小时前
ARM 嵌入式 Linux 系统 U-Boot 开发与移植实战M
设计模式·程序员
geovindu1 天前
rust: Chain of Responsibility Pattern
开发语言·后端·设计模式·rust·责任链模式·行为模式
拆房老料1 天前
【第四部分:Agentic 智能体设计模式】15.为什么 Agent 也需要设计模式
人工智能·设计模式·ai·ai-native
geovindu1 天前
rust: Chain of Responsibility Pattern (续)
开发语言·后端·设计模式·rust·责任链模式·行为模式
Lost of 程序猿1 天前
责任链模式实战:审批流里“层层把关“的艺术
后端·设计模式·asp.net·责任链模式
希洛兔2 天前
状态模式详解:让对象随内部状态自动改变行为
java·设计模式·状态模式
博风2 天前
7、设计模式应用举例:策略模式:加密、解密文件
设计模式·策略模式
4SAPI2 天前
Provider Adapter设计模式:如何让AI应用支持多个模型
网络·人工智能·设计模式