当前位置: 代码网 > it编程>编程语言>Java > Java使用record关键词统计余额的示例代码

Java使用record关键词统计余额的示例代码

2026年04月23日 Java 我要评论
java record 关键词+ map 汇总统计实战:一段余额统计代码背后的设计思想很多 java 程序员在日常开发中都会遇到类似需求:统计一批用户当前可用余额但当代码写出来后,往往会变成一坨:if

java record 关键词+ map 汇总统计实战:一段余额统计代码背后的设计思想

很多 java 程序员在日常开发中都会遇到类似需求:

统计一批用户当前可用余额

但当代码写出来后,往往会变成一坨:

  • if/else 到处都是
  • bigdecimal 累加混乱
  • map 使用不规范
  • dto 写一堆模板代码

最近在项目中看到一段非常典型的实现代码,涉及:

  • java record
  • map.getordefault
  • 批次余额统计
  • bigdecimal 累加
  • mybatis lambdaquerywrapper

虽然代码不长,但里面隐藏着很多 值得学习的设计思想

本文就带大家 逐行拆解这段代码,并分析背后的实现逻辑

一、完整代码

先看完整方法:

public map<long, batchbalancesummary> summarizeavailablebalances(list<long> userids) {
    if (userids == null || userids.isempty()) {
        return map.of();
    }

    list<long> distinctuserids = userids.stream()
        .filter(java.util.objects::nonnull)
        .distinct()
        .tolist();

    if (distinctuserids.isempty()) {
        return map.of();
    }

    for (long userid : distinctuserids) {
        ensureuserbatchwallet(userid);
        expireifneeded(userid);
    }

    map<long, batchbalancesummary> result = new linkedhashmap<>();

    for (long userid : distinctuserids) {
        result.put(userid, new batchbalancesummary(bigdecimal.zero, bigdecimal.zero));
    }

    list<accountbatchentity> batches = accountbatchmapper.selectlist(
        new lambdaquerywrapper<accountbatchentity>()
            .in(accountbatchentity::getuserid, distinctuserids)
            .eq(accountbatchentity::getstatus, status_active)
            .gt(accountbatchentity::getremainingamount, bigdecimal.zero)
    );

    for (accountbatchentity batch : batches) {

        batchbalancesummary current =
            result.getordefault(batch.getuserid(),
                new batchbalancesummary(bigdecimal.zero, bigdecimal.zero));

        if (account_type_promo.equalsignorecase(batch.getaccounttype())) {

            result.put(batch.getuserid(),
                new batchbalancesummary(
                    current.cashbalance(),
                    current.promobalance()
                        .add(normalize(batch.getremainingamount()))
                ));

        } else {

            result.put(batch.getuserid(),
                new batchbalancesummary(
                    current.cashbalance()
                        .add(normalize(batch.getremainingamount())),
                    current.promobalance()
                ));
        }
    }

    return result;
}

这段代码的核心作用其实就是:

统计一批用户当前可用余额(现金 + 赠送金)

返回结构:

userid -> 余额汇总

二、余额汇总对象:java record

代码中使用了 record 定义余额对象:

public record batchbalancesummary(
        bigdecimal cashbalance,
        bigdecimal promobalance) {
}

record 是 java 16 引入的 数据类语法糖

它会自动生成:

  • 构造函数
  • getter
  • equals
  • hashcode
  • tostring

传统写法:

public class batchbalancesummary {

    private bigdecimal cashbalance;
    private bigdecimal promobalance;

}

至少要写几十行代码。

而 record:1 行搞定

record 编译后结构

batchbalancesummary

├── cashbalance
├── promobalance
├── equals()
├── hashcode()
└── tostring()

三、整体执行流程

这段代码整体流程如下:

可以看到整个过程非常清晰:

  • 输入用户列表
  • 数据清洗
  • 账户状态校验
  • 查询批次余额
  • 汇总统计

四、第一步:参数防御

if (userids == null || userids.isempty()) {
    return map.of();
}

这是典型的 防御式编程

如果用户列表为空:

直接返回:

{}

避免后面逻辑执行。

map.of() 是 java 9 新特性:

返回 不可变 map

五、第二步:数据清洗

list<long> distinctuserids = userids.stream()
        .filter(objects::nonnull)
        .distinct()
        .tolist();

作用:

  • 去掉 null
  • 去重

例如:

输入:

[1001,1002,null,1001]

输出:

[1001,1002]

这样可以避免:

  • 重复查询数据库
  • 重复统计余额

六、第三步:确保钱包存在

ensureuserbatchwallet(userid);

这一步通常是:

初始化用户钱包

如果用户第一次使用余额系统:

就创建钱包记录。

例如:

user_wallet

表中插入一条数据。

七、第四步:处理余额过期

expireifneeded(userid);

余额系统通常都有:

  • 赠送金
  • 优惠金
  • 过期时间

所以需要在统计前:

把过期余额标记为失效

否则会统计到错误金额。

八、第五步:初始化结果 map

map<long, batchbalancesummary> result = new linkedhashmap<>();

为什么用 linkedhashmap

因为:可以保持插入顺序

初始化结果:

1001 -> (0,0)
1002 -> (0,0)

代码:

result.put(userid,
    new batchbalancesummary(bigdecimal.zero, bigdecimal.zero));

九、第六步:查询数据库批次

list<accountbatchentity> batches =
    accountbatchmapper.selectlist(...)

sql 等价:

select *
from account_batch
where user_id in (...)
and status = active
and remaining_amount > 0

查询条件:

条件含义
user_id指定用户
status批次有效
remaining_amount > 0还有余额

十、第七步:余额累加

核心代码:

batchbalancesummary current =
    result.getordefault(...)

作用:

获取当前用户已经累计的余额。

如果不存在:

返回默认:

(0,0)

判断余额类型

account_type_promo

如果是:

赠送余额

就加到:

promobalance

否则:

cashbalance

现金余额累加

current.cashbalance().add(amount)

赠送余额累加

current.promobalance().add(amount)

十一、余额统计示例

假设数据库数据:

useridtypeamount
1001cash100
1001promo20
1001cash30
1002promo50

统计过程:

1001 -> (0,0)

+100 cash
1001 -> (100,0)

+20 promo
1001 -> (100,20)

+30 cash
1001 -> (130,20)

最终:

1001 -> (130,20)
1002 -> (0,50)

十二、余额统计架构图

整个余额系统一般是这样设计:

十三、record 的优势

使用 record 后代码变得非常干净:

batchbalancesummary

只负责:

数据承载

优点:

  • 不可变对象
  • 线程安全
  • 减少模板代码
  • 更清晰的领域模型

十四、几个可以优化的地方

1 使用 computeifabsent

可以替换:

getordefault

写法更优雅:

result.computeifabsent(
    userid,
    k -> new batchbalancesummary(bigdecimal.zero, bigdecimal.zero)
);

2 sql 聚合优化

如果数据量很大:

可以用 sql 聚合:

select user_id,
       account_type,
       sum(remaining_amount)
from account_batch
group by user_id,account_type

减少 java 层循环。

3 批量过期处理

目前代码:

expireifneeded(userid)

如果用户很多:

可能产生大量 sql。

可以改成:

批量过期处理

十五、总结

这段代码虽然只有几十行,但实际上包含了很多优秀的设计思想:

  • record 数据对象
  • map 汇总统计
  • 防御式编程
  • bigdecimal 安全计算
  • 批次余额设计

一句话总结:

通过批次账户模型,实现用户余额的安全、可扩展统计。

这种设计在很多系统中都会出现:

  • 钱包系统
  • 积分系统
  • 余额系统
  • 账户系统

如果你在做 支付 / 钱包 / 优惠金系统,这种设计模式基本是必备技能。

到此这篇关于java使用record关键词统计余额的示例代码的文章就介绍到这了,更多相关java统计余额内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2026  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com