fujian_water_biz_doc/docs/superpowers/specs/2026-06-30-late-fee-start-and-daily-recalculation-design.md

9.4 KiB
Raw Blame History

违约金起算与每日重算设计

日期2026-06-30

背景

本设计用于明确违约金四种起算方式、逾期天数、金额计算、每日任务重算和历史账单处理口径。

本次范围同时覆盖:

  • 开账时违约金起算日计算与固化
  • 每日任务按未结清账单重算当前应收违约金
  • 当前未缴本金、已收违约金、已生效减免金额的扣减口径
  • 小数、封顶、配置非法值处理

本设计不改变违约金减免对象本身的提交/审批流程。注意区分:

  • CALC_LATE_FEE_TYPE:系统参数,表示违约金起算方式,取值 1/2/3/4
  • lateFeeType:账务调整/违约金减免字段,表示减免方式,例如按金额、按日期

相关配置

核心起算配置:

  • CALC_LATE_FEE_TYPE:违约金起算方式
    • 1:账单账务年月当月某日
    • 2:开账后若干天
    • 3:账单账务年月下月某日
    • 4:账单账务年月下下月某日
  • CALC_LATE_FEE_SETTING:起算参数值
    • type=1/3/4 时表示日号
    • type=2 时表示宽限天数

金额相关配置:

  • LATE_FEE_DECIMAL:违约金保留小数位数
  • LATE_FEE_MORE_PRINCIPAL:违约金封顶倍数
  • CostComponentDO.penaltyCoefficient:费用项违约金系数

账单字段:

  • ChargeDO.billMonth:账单账务年月,格式 YYYYMM
  • ChargeDO.billDate:开账时间
  • ChargeDO.lateFeeBeginDate:实际开始计算违约金的日期
  • ChargeDO.lateFee:当前应收违约金

开账起算日规则

开账时根据当时系统参数计算并固化 ChargeDO.lateFeeBeginDate。系统参数先计算出“宽限截止日”,宽限截止日当天不计违约金,次日才是第 1 个违约金计费日。

月份基准

CALC_LATE_FEE_TYPE=1/3/4 的月份最终以账单 billMonth 字段对应自然月为准。

  • type=1billMonth 当月某日为宽限截止日
  • type=3billMonth 下月某日为宽限截止日
  • type=4billMonth 下下月某日为宽限截止日

BILLINGMONTH_STARTDAY 负责账务年月生成和解释。本次违约金起算计算不在每日任务中重新按运行日期推导账务周期,而是使用账单已固化的 billMonth

四种起算方式

type=1/3/4

  1. 读取 CALC_LATE_FEE_SETTING 作为目标日号。
  2. 根据 billMonthtype 得到目标月份。
  3. 若目标月份没有该日号,例如 2 月没有 30/31 号,则目标日取该月最后一天。
  4. 目标日为宽限截止日。

type=2

  1. 读取 CALC_LATE_FEE_SETTING 作为宽限天数 N
  2. 开账日不算第 1 天。
  3. 宽限截止日为 billDate.toLocalDate() + N 天
  4. N=0 表示开账日就是宽限截止日,开账次日开始计违约金。

实际起算日

实际违约金起算日为:

lateFeeBeginDate = max(宽限截止日 + 1 天, billDate.toLocalDate() + 1 天)

因此,如果配置算出的宽限截止日早于开账日,也不能在开账日前产生违约金。

配置非法处理

CALC_LATE_FEE_SETTING 的处理规则:

  • type=1/3/4:有效日号范围为 1-31
  • type=2:有效天数范围为 >=0
  • settingValue 非法时回退 settingDefault
  • settingDefault 仍非法时,不生成 lateFeeBeginDate,并记录配置异常
  • type=1/3/4 中有效日号超过目标月份最大天数时,不视为非法,取目标月最后一天

billDate 为空时:

  • 不计算 lateFeeBeginDate
  • 记录数据异常
  • 不使用当前日期或 createTime 兜底

每日重算规则

每日任务只处理未结清账单:

  • 未缴账单:参与重算
  • 部分缴费账单:按当前未缴本金参与重算
  • 已结清账单:不再重算,避免覆盖历史违约金

每日任务必须幂等。每次根据当前账单状态重新计算并覆盖当前应收违约金,重复执行、失败重试、补跑都不得重复累加。

每日任务默认使用开账时固化的 lateFeeBeginDate。后续系统参数变化只影响新开账账单,不自动改变历史未结清账单的起算日。

历史未结清账单如果 lateFeeBeginDate 为空:

  • 不使用当前系统参数临时推导
  • 不计算违约金
  • 记录异常或告警,等待数据修正

逾期天数

  • 当前日期早于 lateFeeBeginDate:逾期天数为 0
  • 当前日期达到或晚于 lateFeeBeginDate:逾期天数包含当前日期

公式:

overdueDays = 当前日期 - lateFeeBeginDate.toLocalDate() + 1

金额计算规则

本次不判断通知状态。违约金产生的计算前提为:

  • 账单未结清
  • 存在当前未缴本金
  • 账单存在有效 lateFeeBeginDate
  • 当前日期达到或晚于 lateFeeBeginDate
  • 费用项 penaltyCoefficient > 0

本金基数

违约金本金基数使用当前未缴本金:

  • 已缴本金不再继续产生违约金
  • 部分缴费后,剩余未缴本金继续按完整逾期天数计算
  • 优先使用费用项级剩余未缴本金
  • 如果系统没有费用项级剩余未缴本金,则按各费用项原始本金比例分摊账单剩余未缴本金
  • 分摊后的费用项未缴本金之和必须等于账单剩余未缴本金
  • 分摊尾差落到最后一个参与项或金额最大的参与项

逐费用项计算

继续采用逐费用项计算后汇总的口径:

单项理论违约金 = 单项未缴本金 × 单项违约金系数 × 逾期天数

费用项违约金系数来源:

ChargeDetailDO.costComponentCode -> CostComponentDO.code -> CostComponentDO.penaltyCoefficient

每个费用项先按 LATE_FEE_DECIMAL 四舍五入,再汇总到账单理论违约金。封顶在账单理论违约金汇总后执行,封顶结果再按 LATE_FEE_DECIMAL 规范化一次。

小数位

LATE_FEE_DECIMAL 控制违约金保留小数位数:

  • 优先使用 settingValue
  • settingValue 为空、非数字、负数时回退 settingDefault
  • settingDefault 仍非法时按 0
  • 取整方式固定四舍五入
  • COUNTER_DECIMAL_WAY 不参与本次违约金计算

封顶

LATE_FEE_MORE_PRINCIPAL 控制违约金封顶:

  • 空值、非数字、负数按 1
  • =0 表示不设上限
  • >0 表示理论违约金不得超过当前未缴本金乘以配置倍数

处理顺序:

  1. 逐费用项计算原始违约金
  2. 每个费用项先按 LATE_FEE_DECIMAL 四舍五入
  3. 汇总得到账单理论违约金
  4. LATE_FEE_MORE_PRINCIPAL 对账单理论违约金封顶
  5. 封顶结果再按 LATE_FEE_DECIMAL 四舍五入后作为可扣减前理论违约金

已收与减免扣减

每日任务覆盖账单当前应收违约金时,必须扣减已收违约金和已审批生效的违约金减免金额。

公式:

当前应收违约金 = max(0, 理论违约金 - 已收违约金 - 已生效违约金减免金额)

已收违约金:

  • 从成功且未被撤销/红冲的收费或支付明细汇总
  • 只汇总实际违约金金额,例如 late_fee_amount
  • 不把本金支付误算为违约金支付

已生效违约金减免金额:

  • 只统计审批通过且已生效的违约金减免记录
  • 待审批、驳回、撤销的减免记录不参与扣减

主要影响范围

后端实现预计涉及:

  • 开账服务:生成账单时计算并保存 lateFeeBeginDate
  • 系统参数读取:读取 CALC_LATE_FEE_TYPECALC_LATE_FEE_SETTINGLATE_FEE_DECIMALLATE_FEE_MORE_PRINCIPAL
  • 违约金计算器:统一封装起算日和金额计算
  • 每日任务:扫描未结清账单,幂等重算并覆盖当前应收违约金
  • 账单明细读取:获取费用项本金、费用项编码和违约金系数
  • 支付明细汇总:汇总已收违约金
  • 违约金减免汇总:汇总已审批生效减免金额
  • 异常记录:配置异常、billDate 缺失、lateFeeBeginDate 缺失等

测试与验收点

开账起算日:

  • type=1 当月某日起算
  • type=2 开账后 N 天,开账日不算第 1 天
  • type=2 setting=0
  • type=3 下月某日起算
  • type=4 下下月某日起算
  • 配置日为 29/30/31 时遇到 2 月、4 月等短月
  • 开账日晚于宽限截止日时,起算日取 billDate + 1
  • billDate 为空时不生成起算日并记录异常
  • CALC_LATE_FEE_SETTING 非法时回退默认值
  • CALC_LATE_FEE_SETTING 默认值仍非法时不生成起算日并记录异常

每日重算:

  • 当前日期早于起算日,逾期天数为 0
  • 当前日期等于起算日,逾期天数为 1
  • 当前日期晚于起算日,逾期天数包含当前日期
  • 未缴账单参与重算
  • 部分缴费账单按当前未缴本金重算
  • 已结清账单不重算
  • lateFeeBeginDate 为空的历史未结清账单不计算并记录异常
  • 重复执行任务结果不变

金额计算:

  • 逐费用项按不同 penaltyCoefficient 计算并汇总
  • 没有费用项级剩余本金时按比例分摊账单剩余未缴本金
  • 分摊尾差不造成汇总不平
  • LATE_FEE_DECIMAL 控制逐项舍入和最终结果
  • LATE_FEE_DECIMAL 非法时回退默认值,默认值非法时按 0
  • LATE_FEE_MORE_PRINCIPAL 空值按 1
  • LATE_FEE_MORE_PRINCIPAL=0 不封顶
  • LATE_FEE_MORE_PRINCIPAL>0 按当前未缴本金倍数封顶
  • 逐项舍入、汇总、封顶、最终舍入的顺序固定
  • 已收违约金扣减
  • 已审批生效违约金减免扣减
  • 扣减后结果不小于 0

非目标

本次不覆盖:

  • 违约金减免流程重构
  • 通知状态判断
  • 已结清账单历史金额重算
  • 参数变更后自动追溯修改历史未结清账单起算日
  • 查询实时计算替代落库金额