应收核定:百万人缴费的批量算法
应收核定遍历机构→遍历人员→匹配参数→计算缴费的批量算法文章目录应收核定遍历机构→遍历人员→匹配参数→计算缴费的批量算法一、问题二、算法结构三、为什么用嵌套循环而不是一条 SQL四、关键的几个判断判断是否可以缴费获取匹配的缴费参数计算缴费五、两种通知六、性能考虑七、总结每个月都要跑一次。全省几百万人遍历两层循环。错了就要重跑。一、问题城乡居民养老保险是按年缴费的。每年社保系统要做一次应收核定——给每一个参保人算出他今年该缴多少钱。全省几百万人每个人缴费档次不一样、缴费参数不一样、开始缴费年份不一样、是否有政府补贴不一样。要全算一遍。这不是一个 SQLUPDATE能搞定的。是一个嵌套循环 参数匹配 累加计算的批处理程序。二、算法结构遍历机构市县 → 乡镇 → 村 │ └→ 遍历人员该机构下所有参保人 │ ▼ 判断是否可以缴费年龄、参保状态、是否已缴 │ ├─ 不能缴费 → 记录错误信息 → 继续下一个 │ ▼ 获取匹配的缴费参数年度、地区、人群类别 │ ▼ 获取缴费账户 │ ▼ 获取参保信息缴费开始年份、已缴年数 │ ▼ 获取缴费档次自己选的档 / 默认档 │ ▼ 计算累加缴费之前已缴的 今年的 │ ▼ 计算缴费金额 档次 × 费率 × 月数如果有政府补贴加补贴抵扣 │ ▼ 保存缴费记录 │ ▼ 生成缴费通知单号 │ ▼ 下一人 → 下一机构 → 完成伪代码实现function 应收核定(年度): errorList [] // 并行每个区县一个线程 for 机构 in 获取所有统筹区(并行): conn 获取数据库连接() count 0 try: for 人员 in 获取该机构下参保人员(机构ID): // 判断能不能缴费 if not 可缴费(人员): errorList.append({人员ID, 原因不可缴费原因(人员)}) continue // 获取参数年度 地区 人群类别 params 获取缴费参数(年度, 人员.地区, 人员.人群类别) // 获取缴费档次 档次 人员.自选档次 or params.默认档次 // 获取参保信息 参保 获取参保信息(人员.ID) // 获取账户 账户 获取缴费账户(人员.ID) // 计算缴费 金额 档次.金额 - 人员.政府补贴 计算利息(账户, 年度) // 如果有中断补缴 if 参保.有中断: for 中断年度 in 参保.中断年度列表: 补缴参数 获取历史缴费参数(中断年度, 人员.地区) 补缴金额 补缴参数.基数 × 补缴参数.费率 金额 补缴金额 // 保存 INSERT INTO 缴费记录(人员ID,年度,金额,缴费类型,状态待缴费) INSERT INTO 缴费通知单(人员ID,年度,金额,通知单号生成通知单号()) count 1 if count % 1000 0: conn.commit() // 分批提交 conn.commit() // 最后一批 catch Exception as e: conn.rollback() errorList.append({机构ID, 错误e.message}) return {成功人数总人数-len(errorList), 错误清单errorList} function 可缴费(人员): if 人员.年龄 16 or 人员.年龄 60: return false // 原因: 年龄不符 if 人员.是否死亡: return false // 原因: 已死亡 if EXISTS(SELECT 1 FROM 待遇发放 WHERE 人员ID人员.ID): return false // 原因: 已领取待遇 if EXISTS(SELECT 1 FROM 缴费记录 WHERE 人员ID人员.ID AND 年度年度): return false // 原因: 已缴费 return true三、为什么用嵌套循环而不是一条 SQLSQL 能算一个字段——UPDATE ... SET amount base * rate。但算不出这些这个人的档次是今年选的还是沿用去年的他是不是低保对象享受政府代缴他上个月刚满 60 岁这个月还要不要缴他去年中断了一年今年补缴的基数按哪年算这些规则不是一条 SQL 能表达的。每一个判断都是一次数据库查询或内存计算。所以只能用循环——逐人逐条判断。四、关键的几个判断判断是否可以缴费if年龄16or年龄60:不可缴费if已死亡:不可缴费if已领取待遇:不可缴费if已缴费本年度:不可缴费重复核定获取匹配的缴费参数缴费参数是按年度 地区 人群类别三个维度查的。比如2025年 海口市 普通居民 → 缴费基数 100 元2025年 海口市 低保对象 → 政府代缴个人 0 元2025年 三亚市 普通居民 → 缴费基数 200 元参数错了全省几百万人的缴费金额全错。所以参数表变更要走审批。计算缴费缴费金额 缴费档次金额 - 政府补贴金额 利息如果有中断补缴还要往前查中断年份的参数。不是简单用今年的标准往前套。五、两种通知算完之后生成两种单据缴费通知单每年生成告诉参保人你该缴多少。有通知单号手机端可查。缴费错误信息不能缴费的人记录不能缴费的原因——年龄不符、已死亡、已领取待遇。不是报错就完了要能被人看懂、能被社区工作人员拿去核实。六、性能考虑几百万人一天跑完需要优化机构分组并行一个县一个线程并行算分批提交每 1000 人 commit 一次不全放在一个事务里断点续跑跑到一半断了可以从上次断点的机构重新开始不是分布式计算。就是在单机上把循环跑得高效一点。七、总结应收核定是社保系统的每月必考科目。不是技术难度最高的模块但是容错率最低的模块——算错一个人的缴费金额他可能会少领养老金。所以这个模块的代码十几年没大改。不是因为写得好。是因为不敢改。