首页 > 其他分享 >来了,资金类交易业务(如电商交易、支付)中,经常提到的Money类!

来了,资金类交易业务(如电商交易、支付)中,经常提到的Money类!

时间:2025-01-07 15:35:40浏览次数:1  
标签:yuan return BigDecimal fen Money feeRatePercentage 电商 交易

资金类交易业务中 经常提到的Money类,大家了解一下。 了解了Money类,就会对资金类业务如电商交易、支付更了解。

资金类业务中,金额如果处理得不好,带来的直接后果就是资金损失(资损风险)。

对于研发经验不足的团队而言,经常会犯以下几种错误:

不统一,存在各系统使用BigDecimal、double、long等数据类型来定义金额。

手动对金额进行加、减、乘、除运算,单位(元与分)换算。

带来的后果,通常就是资金损失,再细化一下,最常见的情况有下面3种:

1)手动做单位换算导致金额被放大或缩小100倍。比如大家规定传的是元,但是其中有位同学忘记了,以为传的是分,外部渠道要求传元,就手动乘以100。或者反过来。

2)1分钱归属问题。比如结算给商家,或计算手续费时,碰到除不尽时,使用四舍五入,还是向零舍入,还是银行家舍入?这取决于财务策略。

3)精度丢失。在大金额时,double有可能会有精度丢失问题。

这时候,解决方案就是定义统一的 Money 类。并且,要明确统一使用 Money 类来处理金额数据。

1. 统一的Money 类

talk is cheap , show you the code directly.

package com.emax.trans;

import cn.hutool.core.lang.Assert;
import lombok.Getter;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 资金类交易业务(如电商交易、支付)中,经常提到的Money类。
 * <p>该类是不可变的(immutable),并且实现了Comparable和Serializable接口。
 * <p><a href="https://mp.weixin.qq.com/s/34JEFRrV_b6J_8Z_O3MbLg">原始文章</a>
 *
 * @author zhangguozhan
 * @date 2025-01-07 14:40
 */
@Getter
public class Money implements Serializable, Comparable<Money> {
    private static final long serialVersionUID = 1L;

    private BigDecimal yuan;
    private long fen;


    /**
     * 私有构造函数,确保通过工厂方法{@link #of}来创建实例。
     *
     * @param yuan
     * @param fen
     */
    private Money(BigDecimal yuan, long fen) {
        this.yuan = yuan;
        this.fen = fen;
    }

    public static Money of(BigDecimal yuan) {
        Assert.notNull(yuan, "yuan is null");
        return new Money(yuan, yuan.movePointRight(2).longValue());
    }

    public static Money of(Long fen) {
        Assert.notNull(fen, "fen is null");
        return new Money(BigDecimal.valueOf(fen * 100), fen);
    }

    /**
     * 加法操作,返回新的Money实例。(注意Money类是不可变类,此操作不会修改当前Money对象的属性值)
     *
     * @param other
     * @return
     */
    public Money add(Money other) {
        Assert.notNull(other, "other is null");
        Assert.notNull(other.yuan, "other.yuan is null");
        BigDecimal sum = this.yuan.add(other.yuan);
        return of(sum);
    }

    /**
     * (按百分比费率)计算手续费
     *
     * @param feeRatePercentage 百分比 的手续费费率比例,例如 5.5% 传 5.5
     * @return
     */
    public BigDecimal calculateFeeByPercentageRate(BigDecimal feeRatePercentage) {
        return calculateFeeByPercentageRate(feeRatePercentage, RoundingMode.HALF_UP);
    }

    /**
     * (按百分比费率)计算手续费
     *
     * @param feeRatePercentage 百分比 的手续费费率比例,例如 5.5% 传 5.5
     * @param roundingMode
     * @return
     */
    public BigDecimal calculateFeeByPercentageRate(BigDecimal feeRatePercentage, RoundingMode roundingMode) {
        Assert.notNull(feeRatePercentage, "feeRatePercentage is null");
        return yuan.multiply(feeRatePercentage).divide(BigDecimal.valueOf(100), 2, roundingMode);
    }

    /**
     * (按<0的实际费率)计算手续费
     *
     * @param feeRateAbs <0 的手续费费率,例如 0.55
     * @return
     */
    public BigDecimal calculateFeeByAbsRate(BigDecimal feeRateAbs) {
        return calculateFeeByAbsRate(feeRateAbs, RoundingMode.HALF_UP);
    }

    /**
     * (按<0的实际费率)计算手续费
     *
     * @param feeRateAbs   <0 的手续费费率,例如 0.55
     * @param roundingMode
     * @return
     */
    public BigDecimal calculateFeeByAbsRate(BigDecimal feeRateAbs, RoundingMode roundingMode) {
        Assert.notNull(feeRateAbs, "feeRateAbs is null");
        return yuan.multiply(feeRateAbs).setScale(2, roundingMode);
    }


    @Override
    public int compareTo(Money o) {
        return Long.compare(fen, o.fen);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == this) return true;
        if (obj instanceof Money) return compareTo((Money) obj) == 0;
        return false;
    }

    @Override
    public String toString() {
        return String.format("%s元(%s分)", yuan, fen);
    }
}




测试:

    public static void main(String[] args) {
        Money money = Money.of(BigDecimal.valueOf(100));
        Money money2 = Money.of(BigDecimal.valueOf(200));
        System.out.println(money.compareTo(money2));
        System.out.println(money.equals(money2));
        System.out.println(money.add(money2));
        System.out.println(money.calculateFeeByPercentageRate(BigDecimal.valueOf(5.5)));
        System.out.println(money.calculateFeeByAbsRate(BigDecimal.valueOf(0.065)));
    }

输出:

-1
false
300元(30000分)
5.50
6.50

2. 如何统一使用 Money 类

待补充~


金额如果处理得不好,带来的直接后果就是资金损失,哪怕不是今天,早晚也得出事。
如果你是研发同学,发现内部还没有使用Money类处理金额,建议早点对内部系统做改造。如果你是产品经理,建议转给内部研发工程师,避免踩资损的坑。

标签:yuan,return,BigDecimal,fen,Money,feeRatePercentage,电商,交易
From: https://www.cnblogs.com/buguge/p/18657723

相关文章

  • 量化交易选择券商有何讲究?从交易成本到技术支持全面解析
    炒股自动化:申请官方API接口,散户也可以python炒股自动化(0),申请券商API接口python炒股自动化(1),量化交易接口区别Python炒股自动化(2):获取股票实时数据和历史数据Python炒股自动化(3):分析取回的实时数据和历史数据Python炒股自动化(4):通过接口向交易所发送订单Python炒股自动化(5):......
  • 基于SpringBoot的生鲜交易系统设计与实现
    1.1项目研究的背景困扰交易市场的许多问题当中,生鲜交易管理一定是交易市场不敢忽视的一块。但是管理好生鲜交易又面临很多麻烦需要解决,例如有几个方面:第一,生鲜市场往往人数都比较多,如何保证能够管理到每一个商家,如何在工作琐碎,记录繁多的情况下将生鲜交易的当前情况反......
  • 20个电商主题模版及案例预览
    本篇主要介绍20个适合做跨境电商网站的WordPress主题。该主题需要考虑几个方面:响应式设计、与WooCommerce插件兼容、用户体验、加载速度、SEO优化、多语言支持。如有技术问题,可以联系我们。跨境电商主题应该具备的特点响应式设计:确保主题在不同设备(如手机、平板和桌......
  • Java高级开发面试题,在设计一个高并发的在线交易系统时,如何确保线程安全和数据一致性?
    在设计一个高并发的在线交易系统时,如何确保线程安全和数据一致性?考虑一个类似于电子商务平台的商品秒杀活动。在这种场景中,大量用户会在短时间内尝试购买有限数量的商品。因此,系统的挑战在于处理高并发请求,同时确保每个订单的数据完整性和一致性。1.锁机制与性能考量传......
  • 构建电商数据采集系统初定位
    构建电商数据采集系统是一个复杂的过程,需要综合考虑多个方面。以下是构建电商数据采集系统的一般步骤与策略:一:步骤 需求分析确定目标:明确数据采集的目的,例如分析市场趋势、了解竞争对手、优化商品推荐等。确定数据范围:确定需要采集的数据类型,如商品信息、价格、评论、销......
  • 分享几个好用的电商API接口(可测试)
    以下是一些好用的电商API接口,涵盖了商品、订单、支付、用户等多个方面:获取APIkey测试一、商品相关API接口商品详情接口功能:根据商品ID查询商品的详细信息,包括SKU信息、详情主图、库存、销量等。示例接口名:item_get。应用平台:1688、淘宝等。商品搜索接口功能:根据用户输......
  • 直播电商运营工具如何选?看这一篇就够了
    近年来,直播电商迅速崛起,成为电商行业的新风口。无论是头部主播还是中小商家,都希望通过直播带货实现销售增长。然而,随着市场竞争的加剧,直播电商的运营难度也在不断增加。商家们面临着选品、数据分析、用户互动、流量获取等多重挑战。如何高效管理直播流程、精准分析数据、优化用户......
  • 古玩玉器交易系统|Java|SSM|VUE| 前后端分离
                 【技术栈】1⃣️:架构:B/S、MVC2⃣️:系统环境:Windowsh/Mac3⃣️:开发环境:IDEA、JDK1.8、Maven、Mysql5.7+4⃣️:技术栈:Java、Mysql、SSM、Mybatis-Plus、VUE、jquery,html5⃣️数据库可视化工具:navicat6⃣️服务器:SpringBoot自带apachetomcat......
  • 【案例实操】Stable Diffusion 将产品白底图变成电商模特场景图全流程思路分享
    大家好我是AIGC阿道夫前言:在电商的视觉战场上,产品展示图的吸引力关乎成败。白底图虽简洁,却难在竞争中脱颖而出。本案例实操将为你揭秘如何利用StableDiffusion扭转乾坤,把单调白底图转化为吸睛的电商模特场景图,开启创意无限的电商视觉营销新篇章。今天分享一下SD模特......
  • 基于SpringBoot+Vue+UniApp的涪陵区特色农产品交易系统(毕业设计源码+论文+PPT+部署)
    文章目录前言详细视频演示系统介绍后台管理技术框架后端采用SpringBoot框架前端框架Vue选题推荐(部分)系统测试系统测试的目的系统功能测试代码参考为什么选择我?获取源码前言......