积分数仓怎么建模?发放、消耗、过期和撤销能只存余额吗?
面试怎么回答
我会用变更流水、发放批次和余额快照三层建模。流水记录发放、消耗、过期与撤销,批次保存有效期,消耗明细说明扣了哪批积分。余额用于快速查询与核对,但不能代替明细。撤销和过期要结合剩余权益,重复事件按稳定业务ID处理,并用批次余额与总余额交叉验证,避免同一份积分被扣两次。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
用户问:“我明明只用了30积分,为什么今天少了50?”如果数仓只保留昨天和今天两个余额,最多知道差了多少,解释不了这20积分究竟是过期、撤销还是被重复扣了。
积分余额是结果,变更流水和发放批次才解释来源与去向;过期和撤销必须关联原始权益,避免同一份积分重复扣减。
余额能回答多少,不能回答为什么
最新余额适合查询当前权益,但不能完整解释每一次变化。只存每日余额,也会把一天内的发放和消耗抵消掉,无法分析用户获得、使用和失效的过程。
因此需要独立的变更事实,粒度可以是一笔已确认积分动作。保留动作ID、用户、动作类型、变更数量、业务发生时间、来源单据和处理状态。
待确认发放、冻结积分与可用积分也要区分。如果不同状态之间只是转移,不能重复当成新发放。积分数仓首先要复刻业务账本的语义,而不是把所有正数叫收入、负数叫支出。
有效期通常属于发放批次
同一个用户的一百积分,可能来自不同活动,有不同有效期、使用范围和撤销规则。总余额相同,不代表权益组成相同。
发放批次应保存来源动作、发放数量、有效期与规则版本。消耗发生时,如果业务按最早到期优先,就需要保留实际消耗分配,而不只是给用户总额减去一个数字。
一笔消耗可以使用多个批次,一个批次也可能被多次消耗,因此消耗动作与发放批次之间可能是多对多关系。分配明细的粒度应明确到“消耗动作加发放批次”,并校验分配合计等于该动作消耗量。

过期扣的是剩余权益
假设某批次发放一百积分,用户已经使用三十,到期时通常需要处理剩余七十,而不是再次扣掉原始一百。具体规则以业务系统为准,但必须说明是原始发放量还是剩余量。
撤销同样复杂。原订单退款后,需要撤销相关积分,但这批积分可能已经被消耗。业务可能允许负余额、追扣后续发放,或者采用其他补偿规则;数仓不能自行选择一种看起来方便的处理方式。
需要保留撤销与原始发放的关联,以及已经发生的补偿动作。已经过期的部分是否还允许再次撤销,也要由业务规则决定,避免两个任务对同一份权益重复扣减。
一个典型场景:过期任务让余额变成负数
比如一个假设积分系统发放一百、消耗三十,过期任务却按原发放量再扣一百。余额最终变成负三十,团队最初怀疑消耗消息重复。
检查动作ID没有重复,真正的问题是过期依据选错了基数。旧验收只验证每笔动作都有来源单据,既没有核对批次剩余,也没有把消耗和过期放在同一条权益链上。
修正时按批次重建已发放、已分配、已过期和已撤销的关系,重算剩余权益,再与源系统确认补偿范围。不能简单把负余额截成零,否则差异只是被隐藏。
回归测试要包含部分消耗后过期、全额消耗后过期、退款撤销、重复事件与迟到动作,让每一种扣减都能解释它针对哪份权益。
对账要分流水层和余额层
流水层检查动作唯一、类型合法、来源关联和分配守恒;余额层检查期初余额加已确认变更是否等于期末余额。涉及冻结时,还要分别核对可用与冻结账户的转移。
余额快照是按时点定义的,不能沿时间简单相加。发放量、消耗量属于期间流量,当前剩余属于时点存量,两者的汇总方式不同。
迟到流水进入后,需要明确修正历史余额还是生成后续调整记录,并保留账本版本。分析数仓负责还原与解释,不应绕过源业务系统直接替用户改积分。
把来源、去向和剩余三件事连起来,积分模型才不只是一个增减数字的表,而是一份能被客服、运营和开发共同核对的权益记录。