臭名昭著aTrust隔离计划1.臭名昭著aTrust隔离计划2.VPN原理与aTrust隔离网络实践3.docker-easyconnect到底做了什么4.TUN(tunnel-隧道-虚拟网卡)模式
个人项目
personal(个人展示)GitHub Actions 自动部署个人展示项目
基于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)

外包人员项目编制差异排查与修复

1. 问题是什么

外包人员入场提示“项目人员已满”,但实际人员数小于项目额度。

例:项目 XM202600000150

项目额度:28
实际在场人员:27
申请记录净占用:-28
系统可用编制:28 + (-28) = 0

系统依据入离场申请记录判断已占用 28 个编制,所以不允许入场;人员表实际只有 27 人,说明历史申请记录多占用了 1 个编制。

2. 核心口径

入场:request_num 为负数,例如 -1
离场:request_num 为正数,例如  1
diff = 实际人数 actual_count + 申请记录汇总 total_request_num
diff表示什么修复方向
0两套数据一致不处理
小于 0申请记录比实际人员多占用了编制补离场记录,request_num 为正数
大于 0实际人员比申请记录多补入场记录,request_num 为负数

例如:

actual_count = 27
total_request_num = -28
diff = -1

说明申请记录多占 1 个编制。补一条 offboard/+1 后:

27 + (-28 + 1) = 0

3. 差异检查 SQL

SELECT
    t.project_id,
    SUM(t.actual_count) AS actual_count,
    SUM(t.total_request_num) AS total_request_num,
    SUM(t.actual_count + t.total_request_num) AS diff
FROM (
    SELECT
        project_id,
        COUNT(*) AS actual_count,
        0 AS total_request_num
    FROM outsourced_staff
    WHERE staff_status IN ('0', '2', '3', '-3', '-2')
    GROUP BY project_id
 
    UNION ALL
 
    SELECT
        project_id,
        0 AS actual_count,
        COALESCE(SUM(request_num), 0) AS total_request_num
    FROM outsourced_staff_onoffboard_application_project
    GROUP BY project_id
) t
GROUP BY t.project_id
HAVING SUM(t.actual_count + t.total_request_num) != 0;

第一段统计实际外包人员;第二段统计所有入离场申请的净编制变化;最外层只显示不一致的项目。

3.1 查询结果示例

以下是一次实际查询结果:

project_idactual_counttotal_request_numdiff说明与处理方向
XM20260000008223-221实际人员比申请记录多 1 人;排除在途申请后,补 1 条 onboardrequest_num=-1
XM2026000001121-2-1申请记录比实际人员多占 1 个编制;排除在途申请后,补 1 条 offboardrequest_num=1
XM2026000001141-2-1与 XM202600000112 相同:排除在途申请后,补 offboard/+1
XM202600000010193-195-2申请记录比实际人员多占 2 个编制;排除在途申请后,补 1 条 offboardrequest_num=2

以 XM202600000010 为例:

修复前:193 + (-195) = -2
补离场:request_num = 2
修复后:193 + (-195 + 2) = 0

上表的“补数方向”只在没有正常在途申请时成立;具体判断和检查 SQL 见下一节。

4. 补数据前必须排除在途申请

新人已创建入场申请、但人员尚未真正入场时,申请会先占用编制。这种情况出现 diff=-1 是正常现象,不能补离场记录。

SELECT
    a.id,
    a.application_type,
    a.status,
    a.remark,
    p.request_num,
    p.gmt_create
FROM outsourced_staff_onoffboard_application_project p
JOIN outsourced_staff_onoffboard_application a
  ON a.id = p.application_batch_id
WHERE p.project_id = '项目编码'
  AND a.status IN ('init', 'approving')
ORDER BY p.gmt_create DESC;
排查结果应该怎么做
有正常的在途入场申请不补,申请正在预占编制
有废弃或重复草稿清理或终止旧申请,不额外造离场记录
没有在途申请且 diff 小于 0补离场修正记录
没有在途申请且 diff 大于 0补入场修正记录
修正后仍无可用编制项目额度不足,应调整项目额度

同时查询项目最新配置:

SELECT
    project_code,
    project_quota,
    business_line_code,
    start_year,
    end_year,
    version,
    status
FROM project_detail
WHERE project_code = '项目编码'
ORDER BY version DESC
LIMIT 1;
可用编制 = project_quota + SUM(request_num)

5. 两种补数写法

5.1 diff 小于 0:补离场记录,释放编制

适用:申请记录多占了 N 个编制。

application_type = offboard
request_num      = N
INSERT INTO outsourced_staff_onoffboard_application
(
    id, application_type, application_date, remark,
    flow_req_id, flow_instance_id, status,
    gmt_create, gmt_modified, modifier, creator, tenant_code, is_valid
)
VALUES
(
    '申请流水号',
    'offboard',
    '实际修正日期',
    '历史数据迁移差异修正记录项目编码',
    '申请流水号',
    '申请流水号',
    'success',
    NOW(), NOW(),
    '1010001233', '1010001233', 'cic', 1
);

5.2 diff 大于 0:补入场记录,补齐占用

适用:实际人员比申请记录多 N 人。

application_type = onboard
request_num      = -N
INSERT INTO outsourced_staff_onoffboard_application
(
    id, application_type, application_date, remark,
    flow_req_id, flow_instance_id, status,
    gmt_create, gmt_modified, modifier, creator, tenant_code, is_valid
)
VALUES
(
    '申请流水号',
    'onboard',
    '实际修正日期',
    '历史数据迁移差异修正记录项目编码',
    '申请流水号',
    '申请流水号',
    'success',
    NOW(), NOW(),
    '1010001233', '1010001233', 'cic', 1
);

两个字段的关系固定如下:

主表.id = 项目明细.application_batch_id
主表.id = flow_req_id = flow_instance_id

历史迁移场景没有真实流程,所以后两个字段用主表流水号占位。

6. 项目明细怎么填

INSERT INTO outsourced_staff_onoffboard_application_project
(
    id, application_batch_id, project_id, business_line_code, project_year,
    project_num, available_num, request_num, remain_num,
    gmt_create, gmt_modified, modifier, creator, tenant_code, is_valid
)
VALUES
(
    '项目明细流水号',
    '申请流水号',
    '项目编码',
    '项目业务线编码',
    '项目计划年度',
    '项目额度或历史迁移快照人数',
    '修正前可用编制',
    '本次修正数量',
    '修正后可用编制',
    NOW(), NOW(),
    '1010001233', '1010001233', 'cic', 1
);
字段含义
application_batch_id主表 outsourced_staff_onoffboard_application.id
project_id项目编码,例如 XM202600000150
business_line_code从项目最新 project_detail.business_line_code 获取
project_year计划年度字符串,不是完整终止日期;按项目年度区间内启用的计划年度取值
project_num正常业务记录中是项目额度;历史迁移旧记录可能按迁移人数保存,保持同类记录口径一致
available_num修正前可用编制
request_num入场负、离场正
remain_num修正后可用编制

若本次修复的目的就是让新人能入场,应使用一致的快照公式:

project_num   = project_quota
available_num = project_quota + 修正前 SUM(request_num)
remain_num    = available_num + request_num

例如项目额度 28、当前申请汇总 -28,补一条离场 +1:

project_num   = 28
available_num = 0
request_num   = 1
remain_num    = 1

旧历史迁移数据中常见 available_num=0、remain_num=0,那是迁移快照口径;如果目标是立即释放可用编制,以上述计算结果为准。

7. 可直接使用的修复模板

以下模板不使用存储过程、不使用变量、不自动查询项目数据。按查询结果手工替换每个单引号中的占位内容后,依次执行两条 INSERT。

7.1 diff 小于 0 或 request_num 需要补正数:使用离场主表

例如 diff=-1,则项目明细 request_num 填 1;diff=-2,则填 2。

INSERT INTO outsourced_staff_onoffboard_application
(id, application_type, application_date, remark, flow_req_id, flow_instance_id, status, gmt_create, gmt_modified, modifier, creator, tenant_code, is_valid)
VALUES
(
    '申请主表流水号',
    'offboard',
    '实际修正日期,例如2026-07-31',
    '历史数据迁移申请记录项目编码',
    '申请主表流水号',
    '申请主表流水号',
    'success',
    NOW(),
    NOW(),
    '1010001233',
    '1010001233',
    'cic',
    1
);

7.2 diff 大于 0 或 request_num 需要补负数:使用入场主表

例如 diff=1,则项目明细 request_num 填 -1;diff=2,则填 -2。

INSERT INTO outsourced_staff_onoffboard_application
(id, application_type, application_date, remark, flow_req_id, flow_instance_id, status, gmt_create, gmt_modified, modifier, creator, tenant_code, is_valid)
VALUES
(
    '申请主表流水号',
    'onboard',
    '实际修正日期,例如2026-07-31',
    '历史数据迁移申请记录项目编码',
    '申请主表流水号',
    '申请主表流水号',
    'success',
    NOW(),
    NOW(),
    '1010001233',
    '1010001233',
    'cic',
    1
);

7.3 两种情况共用的项目明细表

INSERT INTO outsourced_staff_onoffboard_application_project
(id, application_batch_id, project_id, business_line_code, project_year, project_num, available_num, request_num, remain_num, gmt_create, gmt_modified, modifier, creator, tenant_code, is_valid)
VALUES
(
    '项目明细流水号',
    '申请主表流水号',
    '项目编码,例如XM202600000112',
    '项目对应的business_line_code',
    '项目计划年度,例如2026',
    '该项目的历史迁移快照人数',
    0,
    '本次修正值:离场填正数、入场填负数',
    0,
    NOW(),
    NOW(),
    '1010001233',
    '1010001233',
    'cic',
    1
);

填写规则:

申请主表流水号:32位 UUID 或项目使用的流水号;主表 id、flow_req_id、flow_instance_id 必须相同。
项目明细流水号:另一个新的、不重复的流水号。
application_batch_id:必须等于申请主表流水号。
project_id:差异检查 SQL 返回的项目编码。
business_line_code:查询项目对应业务线后填写。
project_year:填写项目计划年度;不要填写完整日期。
project_num:按同项目既有历史迁移记录的口径填写,通常是该项目当时的迁移人员总数。
request_num:diff 的相反数。diff=-N 填 N;diff=N 填 -N。

执行后重新跑第 3 节的差异检查 SQL,确认该项目 diff=0。若不是 0,不要重复插入,先检查参数和在途申请。

7.4 具体示例:XM202600000112,diff=-1

该项目查询结果为实际人数 1、申请记录汇总 -2、diff=-1。确认无在途申请后,需要补 1 个正数,因此使用离场记录:offboard / request_num=1。

先在数据库生成两个不同的 32 位 UUID:

SELECT REPLACE(UUID(), '-', '');

示例生成的两个 ID:

申请主表流水号:e10b18db8fdc11f1acdd6805caca9fe8
项目明细流水号:ab3210138fde11f1acdd6805caca9fe8

执行以下两条 SQL:

INSERT INTO outsourced_staff_onoffboard_application
(
    id, application_type, application_date, remark,
    flow_req_id, flow_instance_id, status,
    gmt_create, gmt_modified, modifier, creator, tenant_code, is_valid
)
VALUES
(
    'e10b18db8fdc11f1acdd6805caca9fe8',
    'offboard',
    '2026-07-31',
    '历史数据迁移申请记录XM202600000112',
    'e10b18db8fdc11f1acdd6805caca9fe8',
    'e10b18db8fdc11f1acdd6805caca9fe8',
    'success',
    NOW(),
    NOW(),
    '1010001233',
    '1010001233',
    'cic',
    1
);
 
INSERT INTO outsourced_staff_onoffboard_application_project
(
    id, application_batch_id, project_id, business_line_code,
    project_year, project_num, available_num, request_num, remain_num,
    gmt_create, gmt_modified, modifier, creator, tenant_code, is_valid
)
VALUES
(
    'ab3210138fde11f1acdd6805caca9fe8',
    'e10b18db8fdc11f1acdd6805caca9fe8',
    'XM202600000112',
    'TX_LP',
    2029,
    3,
    0,
    1,
    0,
    NOW(),
    NOW(),
    '1010001233',
    '1010001233',
    'cic',
    1
);

本例字段对应关系:

主表 id = flow_req_id = flow_instance_id
项目明细 application_batch_id = 主表 id
request_num=1,所以 application_type 必须是 offboard

执行后重新跑第 3 节差异检查 SQL。预期该项目由:

1 + (-2) = -1

变为:

1 + (-2 + 1) = 0

示例中的 TX_LP、2029、3 来自该项目已确认的历史迁移口径;用于其他项目时必须替换为目标项目的实际值。两个 UUID 也不能重复使用。

8. 示例中的 28、27、-28、0 是怎么得到的

以项目 XM202600000150 为例:

项目额度:28
实际在场人员:27
申请记录净占用:-28
系统可用编制:28 + (-28) = 0

这四个数字来自两套不同的数据。实际人员数用于核对;入场申请是否允许,代码主要依据项目额度和入离场申请明细计算。

8.0 按顺序单独查询

以 XM202600000150 为例,按下面顺序执行,阅读和核对最直接:

-- 1. 项目额度
SELECT project_quota
FROM project_detail
WHERE project_code = 'XM202600000150'
ORDER BY version DESC
LIMIT 1;
-- 2. 实际在场人数
SELECT COUNT(*) AS actual_count
FROM outsourced_staff
WHERE project_id = 'XM202600000150'
  AND staff_status IN ('0', '2', '3', '-3', '-2');
-- 3. 申请记录净占用
SELECT COALESCE(SUM(request_num), 0) AS total_request_num
FROM outsourced_staff_onoffboard_application_project
WHERE project_id = 'XM202600000150';
-- 4. 手算可用编制
项目额度 + 申请记录净占用
28 + (-28) = 0

8.1 项目额度 28

数据来源:

project_detail.project_quota

查询 SQL:

SELECT project_quota
FROM project_detail
WHERE project_code = 'XM202600000150'
ORDER BY version DESC
LIMIT 1;

入场处理查询项目详情后,使用项目额度:

projectEntity.setProjectNum(projectYearAndQuotaDTO.getProjectQuota());

8.2 实际在场人员 27

来自第 3 节核对 SQL 的第一段:

SELECT COUNT(*) AS actual_count
FROM outsourced_staff
WHERE project_id = 'XM202600000150'
  AND staff_status IN ('0', '2', '3', '-3', '-2');

结果为 27,表示人员表中当前归属该项目、且状态符合条件的人员数量。

这个数字用于发现数据差异;入场申请处理不会直接用 COUNT(outsourced_staff) 来扣减额度。

8.3 申请记录净占用 -28

来自第 3 节核对 SQL 的第二段:

SELECT COALESCE(SUM(request_num), 0) AS total_request_num
FROM outsourced_staff_onoffboard_application_project
WHERE project_id = 'XM202600000150';

入场是负数、离场是正数。汇总为 -28,表示申请记录认为项目累计净入场并占用了 28 个编制。

入场处理代码也会按项目取出所有历史项目明细,并按 project_id 汇总 request_num:

Collectors.summingInt(
    OutsourcedStaffOnOffboardApplicationProjectEntity::getRequestNum
)

8.4 系统可用编制 0

入场处理中的计算公式:

projectEntity.setAvailableNum(
    projectYearAndQuotaDTO.getProjectQuota() + requestedNum
);

代入当前数据:

projectQuota = 28
requestedNum = -28
availableNum = 28 + (-28) = 0

新人再申请入场 1 人时,本次 request_num 会是 -1,申请后的剩余数会变成:

remainNum = 28 + (-28) + (-1) = -1

因此系统无法再分配编制,页面显示“项目人员已满”。

8.5 为什么补 offboard/+1 能释放名额

确认没有在途申请后,补一条离场修正记录:

application_type = offboard
request_num = 1

申请记录汇总从 -28 变为 -27:

修复后可用编制 = 28 + (-27) = 1

新人正常入场后再产生 -1:

实际人员 = 28
申请记录汇总 = -28
可用编制 = 0
diff = 28 + (-28) = 0

也就是说,补数不修改人员表的 27 人,而是修正申请明细表中多占用的 1 个编制。

9. 与项目模块的关联

项目模块不直接统计 outsourced_staff 表,而是通过项目编码查询入离场申请明细:

project_detail.project_code
=
outsourced_staff_onoffboard_application_project.project_id

项目审批发起时,将以下值写入流程中心:

play_remain_num     计划剩余额度
entrance_people_num 入离场申请记录汇总得到的已用编制
origin_play_num     原项目编制

项目处于 APPROVING 时读取流程快照;审批完成后重新实时汇总申请记录。因此审批中的项目,即使修复了外包人员数据,审批页面仍可能展示发起审批时的旧快照。

10. 最短操作清单

  1. 跑差异检查 SQL,拿到 diff。
  2. 查在途申请;有在途申请先处理申请,不补数。
  3. 查项目额度。
  4. diff 小于 0 补 offboard/+N;diff 大于 0 补 onboard/-N。
  5. 在事务内执行,验证 diff=0 后再提交。
  6. 同一项目的同一笔修正不能重复执行。