臭名昭著aTrust隔离计划1.臭名昭著aTrust隔离计划2.VPN原理与aTrust隔离网络实践3.docker-easyconnect到底做了什么4.TUN(tunnel-隧道-虚拟网卡)模式
基于Quartz搭建的个人博客1.Quartz个人博客使用教程2.使用 rsync 增量部署 Quartz 博客3.使用 GitHub Actions 自动部署 Quartz 博客5.域名绑定6.CDN加速Obsidian、双向链接与知识图谱
旅行
行程攻略青岛三日游行程攻略
前端
NginxNginx配置与反向代理入门
Nodenpm与npx的区别
中华财险-公司项目
对外接口文档
基础数据码表对外接口(仅支持rpc调用)业务码表对外接口应用主数据对外接口
基础运营查询版本更新日志详情消息中心对外接口站内信对外接口站内信模板配置手册
权限中心权限中心对外接口文档(最新)权限中心对外接口文档前端ACL-CORE FACADE依赖版本
审计中心审计中心对外接口文档audit-center-facade版本
审批中心工作流迁审批流现状工作流迁审批流API能力替换方案老审批流接口文档审批中心接口文档审批中心业务回调FAQ
账号中心内部系统对接单点登录内部系统对接认证中心三方网页应用登录授权账号中心对外接口文档前端账号中心RPC接口文档账户变更对外广播消息文档账户中心对外接口HRMS外部员工变更广播消息文档HRMS外部员工对外接口
组织员工岗位🔥机构映射SDK接口文档员工岗位变更通知说明组织机构管营一体概念与用法组织员工对外广播消息文档组织员工岗位对外接口文档组织员工岗位对外接口文档前端组织员工岗位数据模型组织员工主数据业务场景案例organization-facade版本
hrms(大型人力资源外包管理系统)
项目架构说明HRMS Maven模块与依赖说明
项目说明0.流程中心模块0.组织模块说明1.业务线模块2.计划模块3.项目模块4.协议模块5.供应商模块6.合约域模块7.外包人员模块*核心:外包人员生命周期
项目运维外包人员项目编制差异排查与修复2.HRMS相关问题排查4.修改externalId(externalId和accountId不一致)
需求-系分
1.内部转外包0723外包人员关联历史内部账号系分新增外包人员关联历史账号内容(紧急0723上线)需求
2.工作岗位0820工作岗位0820需求工作岗位0820需求-系分
3.用工模式调整0917内部人员转外包用工系统需求2内部人员转外包用工系统需求-系分
sso(账号中心-单点登录)
项目架构说明aboss-sso项目架构入门AOP统一日志打印链路Maven多模块项目高级知识OAuth2.0
项目说明1.SSO-OAuth2.0与IDaaS登录流程2.外部账号创建流水号并发问题分析
AI
使用说明
第三方插件&技能简介Archify使用与安装指南Ponytail使用指南
CodexCodex CLI与IDE区别及使用指南Codex Hook单独配置与提交通知Codex MCP安装与使用指南Codex第三方插件安装与使用指南
Agent开发1.LLM、Token、上下文窗口与模型参数2.大模型 API、请求参数与响应结构3.Spring AI ChatClient4.Prompt、System Prompt、Prompt 模板6.结构化输出、JSON Schema7.SSE 流式响应8.会话 ID、聊天记录、Redis9.超时、重试、限流、降级10.完成可运行聊天接口
GitGit常用命令与Obsidian推送排查
Java
面试题Java基础与集合面试题
AtomicInteger原子计数与并发安全ConcurrentHashMap并发安全与计数Java线程、线程池与Future
python
基础Python基础语法
HTTPXHRMS员工详情接口调用(Python HTTPX)

人员手机号检验规则

创建外包人员时,phone 字段会经过以下校验:

  1. 必填校验:手机号不能为空;为空时返回“手机号不能为空”。
  2. 格式校验:必须是 11 位中国大陆手机号,格式为 ^1[3-9]\\d{9}$:以 1 开头,第二位为 39,其余为 9 位数字;不符合时返回“手机号格式错误”。
  3. 唯一性校验:保存时由数据库唯一索引 uk_contact_number 兜底限制,手机号重复时返回“手机号已存在”。

校验位置

  • 请求字段的必填与格式:CreateStaffCmd.phone
  • 请求参数校验:OutsourcedStaffWebValidator.validateCreateStaff 调用统一参数校验。
  • 手机号唯一性:新增员工插入数据库时捕获唯一索引冲突并转换为业务提示。

人员批量导入提示“批次号不存在有效记录”

点击“批量保存员工”时,系统会按批次号查询导入明细中状态为 1 的记录;查询结果为空时,即提示“批量导入员工信息批次号不存在有效记录”。

常见触发情况:

  1. 该批次没有成功写入导入明细,例如文件导入未完成或未插入任何记录。
  2. 导入明细已被全部删除或清空。
  3. 导入的所有员工均校验失败,明细状态均为 0
  4. 原本有效的明细在后续保存处理中因手机号与已有员工冲突等原因被标记为失败。

导入明细可能因机构或岗位不存在、无机构权限、手机号重复、证件类型或号码不合法、码值校验失败、线客必填项缺失,以及员工状态不允许关联当前项目等原因而变为失败。应先在批次明细页查看各条记录的失败原因,修正后重新导入。

校验代码:OutsourcedStaffBatchSaveHandler.getValidBatchRecords,查询条件为 batch_id = 当前批次号status = 1

人员批量导入提示“无此机构权限”

报错原因:本次导入传入的 permissionOrgCode 权限范围不包含待新增人员的归属部门/团队。

permissionOrgCode 可以管理哪些机构

permissionOrgCode 所属机构可管理范围
总公司 210000000000不限制机构范围
一级机构本机构及下属二、三级机构
二级机构本机构及下属三级机构
三级机构本机构及其上级二级机构

如何查看机构级别

在组织中心按 permissionOrgCode 查询机构详情,查看 branchOrgLevel

branchOrgLevel机构级别
0总公司(一级机构)
1分公司(二级机构)
2中心支公司(三级机构)

如果 permissionOrgCode 是团队编码,系统会向上找到最近的经营组织,再按该经营组织的级别计算权限。

示例

permissionOrgCode 属于 A 支公司,只能管理 A 支公司及其下属机构;Excel 中待新增人员填写的是 B 支公司的电融团队,因此超出权限范围,提示“无此机构权限”。

排查时只需对比:

  1. 导入请求中的 permissionOrgCode
  2. Excel 中待新增人员的“人员归属部门/团队编码”(branchOrgCode)。

如果一个批次的人员全部因此校验失败,保存时还会提示“批次号不存在有效记录”。

申请单不存在、任务实例为空或流程不存在


问题结论

“申请单不存在、任务实例为空或流程不存在”是流程中心在处理审批动作前返回的通用提示,表示申请单、当前任务实例或流程实例中至少有一项不满足操作条件,并不代表三个对象同时不存在。

本次账号 zhex08330100094 的实际情况是:离场申请已经驳回,当前审批任务已经结束,再次点击驳回时流程中心无法查询到可处理的当前任务实例,因此返回上述提示。与此同时,HRMS 人员状态没有随驳回结果恢复,形成了“申请已驳回、人员仍显示离场审批中”的数据不一致。

只读查询确认的关键数据如下:

数据查询结果
人员状态2,离场审批中
离场申请单ID7498996947656392704
流程实例IDPS2026087498996981226729472
申请单状态rejected
当前申请明细0条
历史申请明细1条

这些数据说明流程中心的第一次驳回已经完成,HRMS 也已将申请单改为驳回并将申请明细迁入历史表;页面再次驳回时,实际命中的是“任务实例为空”。

数据库排查顺序

本问题需要按照“人员 → 当前申请明细 → 历史申请明细 → 申请单主表 → 数量交叉验证”的顺序查询。前一步查出的人员ID或申请批次ID,是后一步的查询条件,不要直接用登录账号去所有表中模糊查询。

第一步:查询人员主表

表:outsourced_staff

重点字段:

字段用途
idHRMS 人员主键,后续申请明细表的 account_id 实际保存该值
account_id新一代账号名称,用于根据页面账号定位人员
staff_status人员当前状态;2 表示离场审批中,0 表示已入场
onboard_date入场日期,用于确认人员当前是否应处于已入场状态
offboard_date离场日期,用于判断离场是否已经实际生效
project_id当前关联项目编号,可用于辅助核对申请明细
agreement_id当前关联合同编号,可用于辅助核对申请明细
gmt_creategmt_modified人员创建及最后修改时间,用于还原状态变化时间线
is_valid数据是否有效

本次查询:

SELECT id,
       account_id,
       staff_status,
       onboard_date,
       offboard_date,
       project_id,
       agreement_id,
       gmt_create,
       gmt_modified,
       is_valid
FROM outsourced_staff
WHERE account_id = 'zhex08330100094';

本次结果:人员ID为 7483405542666027008staff_status = '2',说明 HRMS 仍认为该人员处于离场审批中。后续查询申请明细时,应使用人员ID 7483405542666027008,不能继续使用账号名称。

第二步:查询当前申请明细表

主表:outsourced_staff_onoffboard_application_record
关联表:outsourced_staff_onoffboard_application

重点字段:

字段用途
id申请明细主键
application_batch_id申请单ID,用于关联申请单主表
account_id字段名虽然是账号ID,当前代码实际写入 outsourced_staff.id
status明细处理状态:0 待处理、1 处理中、2 成功、3 失败
project_idagreement_id申请时关联的项目和合同
error_msg明细处理失败原因
gmt_creategmt_modified申请明细创建及修改时间
is_valid数据是否有效
申请单的 application_type用于区分当前明细属于入场申请还是离场申请
申请单的 status用于确认关联申请单当前处于审批中、通过还是驳回状态
申请单的 flow_instance_id流程实例ID,用于后续关联流程中心

只根据人员ID查询当前明细表,可能同时查到以前的入场申请或其他已完成记录,因此必须关联申请单主表,并通过 application_type = 'offboard' 明确筛选离场申请。

本次查询:

SELECT r.id,
       r.application_batch_id,
       r.account_id AS staff_id,
       r.status AS record_status,
       r.project_id,
       r.agreement_id,
       r.error_msg,
       a.application_type,
       a.status AS application_status,
       a.flow_instance_id,
       r.gmt_create,
       r.gmt_modified,
       r.is_valid
FROM outsourced_staff_onoffboard_application_record r
JOIN outsourced_staff_onoffboard_application a
  ON a.id = r.application_batch_id
WHERE r.account_id = '7483405542666027008'
  AND a.application_type = 'offboard'
ORDER BY r.gmt_create DESC;

如果该查询有结果,说明目标离场申请明细仍保存在当前表,可以继续根据返回的 application_statusflow_instance_id 判断审批状态。如果该查询没有结果,只能说明“当前表没有该人员的离场申请明细”,不能说明该人员从未发起过离场申请;此时需要继续查询历史申请明细表。

本次查询没有结果。单独查询当前明细表虽然能看到该人员以前的入场记录,但入场记录不是本次排查目标,因此不能据此判断离场流程状态。

第三步:查询历史申请明细表

主表:outsourced_staff_onoffboard_application_record_history
关联表:outsourced_staff_onoffboard_application

该表字段含义与当前申请明细表基本一致,重点仍是 application_batch_idaccount_idstatusproject_idagreement_iderror_msggmt_creategmt_modified

历史表也需要关联申请单主表并筛选 application_type = 'offboard',确保查到的是目标离场申请,而不是以前归档的入场记录。

本次查询:

SELECT r.id,
       r.application_batch_id,
       r.account_id AS staff_id,
       r.status AS record_status,
       r.project_id,
       r.agreement_id,
       r.error_msg,
       a.application_type,
       a.status AS application_status,
       a.flow_instance_id,
       r.gmt_create,
       r.gmt_modified,
       r.is_valid
FROM outsourced_staff_onoffboard_application_record_history r
JOIN outsourced_staff_onoffboard_application a
  ON a.id = r.application_batch_id
WHERE r.account_id = '7483405542666027008'
  AND a.application_type = 'offboard'
ORDER BY r.gmt_create DESC;

本次在历史表中查到目标离场申请明细,application_batch_id = '7498996947656392704'application_status = 'rejected'。这说明该离场明细已经从当前表迁入历史表,并且关联申请单已经驳回。下一步使用这个申请单ID单独查询申请单主表,核对完整流程字段和时间信息。

第四步:查询申请单主表

表:outsourced_staff_onoffboard_application

重点字段:

字段用途
id申请单ID,对应明细表的 application_batch_id
application_type申请类型:onboard 入场、offboard 离场
application_date申请入场或离场日期
status申请单状态:initapprovingapprovedrejected
flow_req_id启动流程请求ID,审批回调代码使用该字段匹配流程实例ID
flow_instance_id流程中心返回的流程实例ID,用于去流程中心继续排查任务
gmt_creategmt_modified申请单创建及最后修改时间
is_valid数据是否有效

本次查询:

SELECT id,
       application_type,
       application_date,
       status,
       flow_req_id,
       flow_instance_id,
       gmt_create,
       gmt_modified,
       is_valid
FROM outsourced_staff_onoffboard_application
WHERE id = '7498996947656392704';

本次结果为 application_type = 'offboard'status = 'rejected',流程实例ID为 PS2026087498996981226729472。这能够证明 HRMS 申请单已经收到并处理过驳回结果。

第五步:交叉验证当前表、历史表和申请单状态

最后使用同一人员ID和申请单ID统计三处数据,避免只看单表得出错误结论:

SELECT
    (SELECT COUNT(*)
       FROM outsourced_staff_onoffboard_application_record
      WHERE account_id = '7483405542666027008'
        AND application_batch_id = '7498996947656392704') AS current_record_count,
    (SELECT COUNT(*)
       FROM outsourced_staff_onoffboard_application_record_history
      WHERE account_id = '7483405542666027008'
        AND application_batch_id = '7498996947656392704') AS history_record_count,
    (SELECT COUNT(*)
       FROM outsourced_staff_onoffboard_application
      WHERE id = '7498996947656392704'
        AND application_type = 'offboard'
        AND status = 'rejected') AS rejected_application_count;

本次结果为:

current_record_count = 0
history_record_count = 1
rejected_application_count = 1

结合第一步的 staff_status = '2',最终确认数据不一致:申请单已经驳回、明细已经归档,但人员仍处于离场审批中。

第六步:使用流程实例ID排查流程中心

HRMS 库只能确认业务申请单和人员状态,不能直接证明流程中心当前任务实例是否存在。需要使用第四步得到的 flow_instance_id 去流程中心查询:

  1. 流程实例是否仍处于运行状态。
  2. 是否存在当前待办任务实例。
  3. 当前任务是否已经进入历史任务。
  4. 驳回操作是否已经产生 PROCESS_REJECT 事件。

流程中心表结构不在 HRMS 项目和 hrms_ps_mysql 数据源中,因此不能根据 HRMS 代码猜测具体表名;应通过流程中心管理页面、日志、接口或其数据库查询。本次 HRMS 申请单已是 rejected 且明细已进入历史表,结合页面再次驳回时报错,可以判断当前可处理任务已经不存在。

根因

旧的 HRMS 驳回回调执行顺序存在一致性风险:

  1. 将申请单状态更新为 rejected
  2. 删除当前申请明细并写入历史表。
  3. 最后才恢复人员状态。

申请单归档和人员状态恢复不是一个不可分割的操作,并且旧逻辑没有校验人员状态更新的影响行数。如果人员状态更新阶段发生异常、被中断或没有更新成功,就会留下以下半完成状态:

申请单:rejected
申请明细:已经迁入历史表
人员状态:仍为 2(离场审批中)

数据库快照能够确认本次案例正处于上述不一致状态;但数据库本身不能证明当时人员更新失败的具体异常,需要结合对应时间段的应用日志进一步确认。

驳回结果处理链路

流程中心完成驳回
  → 发送 PROCESS_REJECT 消息
  → StaffOnOffboardFlowListener.staffOffboard 注册的 MessageListener.consume 接收消息
  → OutsourcedStaffOnOffboardRepository.confirmOnOffboardRequest
  → OutsourcedStaffApprovalHandler.confirmOnOffboardRequest
  → 根据流程实例ID查询申请单、申请明细和人员
  → 恢复人员基础状态
  → 将申请单和申请明细归档为驳回状态

StaffOnOffboardFlowListener.staffOffboard 负责注册并启动入场、离场审批结果的 MQ 消费者,真正逐条接收消息的入口是内部的 MessageListener.consume。当消息事件为 PROCESS_REJECT 时,监听器将流程实例ID和 REJECTED 状态传给审批处理器。

修复方式

代码调整后的处理顺序如下:

  1. 先根据申请类型恢复人员状态。
    • 离场驳回:2/3 → 0,恢复为已入场。
    • 入场驳回:-3/-2 → -1;历史离场人员再次入场且已有账号时恢复为已离场。
  2. 更新人员时同时限定人员ID和原审批状态,避免延迟或重复回调覆盖其他流程已经写入的新状态。
  3. 人员状态处理完成后,再将申请单改为 rejected,并将当前申请明细迁入历史表。
  4. 更新影响0行时记录告警,方便排查人员状态是否已被其他流程修改。

存量异常数据不会因为代码发布自动恢复,需要在确认申请单确实为 rejected 后,将对应人员的 staff_status2 修正为 0。生产数据修复必须使用人员ID、账号、申请单ID、流程实例ID和当前状态共同限定,避免误修改其他人员或后续新流程产生的数据。

zhex08330100106 做离场

http://10.197.33.188/api/testprojectstable-aboss-hrms/platform/api/aboss/hrms/staff/offboard-request
{
“permissionOrgCode”: “210000000000”,
“staffIds”: [
“7500030617691373571”
]
}

http://10.197.33.188/api/testprojectstable-aboss-hrms/platform/api/aboss/hrms/staff/offboard-request-apply

{
“requestId”: “7500439710574854144”,
“applicationDate”: “2026-09-01”,
“remark”: “test”,
“permissionOrgCode”: “210000000000”
}