当前位置: 首页 > java >正文

【JAVA】支付,积分相关代码开发总结

一、前言

   如果做支付系统或者积分系统我们都知道,里面的数字类型和计算规则是我们平时需要注意的。

如金钱的数据类型,金钱的加减乘除,积分的数目,积分的计算规则等等。比如商城的页面金额0.01元,是否我们数据库里就是存储的是0.01呢?有时候我们也碰到过0.0001元的,那么在JAVA系统里怎么处理这些数据类型,下面根据实践经验进行简单的总结。

二、数据类型

1、积分数量我们可以用Long类型去表示,如果使用Integer类型可能不够。

2、金额的页面类型我们可以使用BigDecimal去表示,  这里特别需要注意的事BigDecimal的加减乘除

在Java中,‌BigDecimal的加减乘除运算需通过add()subtract()multiply()divide()方法实现‌,直接使用算术运算符会导致精度丢失或计算错误。‌‌

(1)加法‌:使用add()方法。

BigDecimal num1 = new BigDecimal("2");
BigDecimal num2 = new BigDecimal("6");
BigDecimal sum = num1.add(num2); // 结果:8

(2)减法‌:使用subtract()方法。

BigDecimal difference = num2.subtract(num1); // 结果:4

BigDecimal difference = num2.subtract(num1); // 结果:4


(3)乘法‌:使用multiply()方法。

BigDecimal product = num1.multiply(num2); // 结果:12

(4)除法‌:使用divide()方法(需指定精度和舍入模式)

BigDecimal quotient = num2.divide(num1, 2, RoundingMode.HALF_UP); // 结果:3.00

注意事项

构造方法选择‌:优先使用String参数构造,避免double参数导致精度问题

// 正确示例
new BigDecimal("0.1"); // 精确
// 错误示例
new BigDecimal(0.1); // 存在精度误差

使用Hutool工具类库运算BigDecimal的加减乘除、保留两位小数。


@Test
public void operationBigDecimal()
{BigDecimal bigDecimal1 = new BigDecimal("10.2567");BigDecimal bigDecimal2 = new BigDecimal("2.236");//加法BigDecimal addResult = NumberUtil.add(bigDecimal1,bigDecimal2);System.out.println("加法运算结果:" + addResult);//减法BigDecimal subResult = NumberUtil.sub(bigDecimal1,bigDecimal2);System.out.println("减法运算结果:" + subResult);//乘法BigDecimal mulResult = NumberUtil.mul(bigDecimal1,bigDecimal2);System.out.println("乘法运算结果:" + mulResult);//除法BigDecimal divResult = NumberUtil.div(bigDecimal1,bigDecimal2);System.out.println("除法运算结果:" + divResult);//保留两位小数BigDecimal roundResult = NumberUtil.round(bigDecimal1,2);System.out.println("保留两位小数:" + roundResult);
}

BigDecimal的四舍五入,并保留两位小数。

 
@Test
public void roundTest()
{BigDecimal bigDecimal1 = new BigDecimal("12.233");BigDecimal bigDecimal2 = new BigDecimal("12.288");//四舍五入,并保留两位小数BigDecimal round1 = bigDecimal1.setScale(2, BigDecimal.ROUND_HALF_UP);BigDecimal round2 = bigDecimal2.setScale(2, BigDecimal.ROUND_HALF_UP);System.out.println("数值1:" + bigDecimal1);System.out.println("四舍五入:" + round1);System.out.println("-----------------");System.out.println("数值2:" + bigDecimal2);System.out.println("四舍五入:" + round2);
}

如果是互联网金融行业,所有在进行计算的时候尽量使用 ROUND_HALF_EVEN

以上都是页面的数据类型

在数据库里我们可以通过存Long类型,单位是 毫来存储金额

三、处理订单的时候,我们往往经常需要一个唯一的订单号和交易流水号,怎么生成这两个号码更好?

博主建议使用雪花算法就行,上一节里面写有,使用雪花算法注意使用每台部署的服务器标识就是生成,以防生成号码重复

package cn.ctg.zlt.framework.common.util.string;import lombok.Data;
import org.springframework.context.annotation.Configuration;/*** @ClassName SnowflakeIdWorker* @Description TODO* @Author jiwenjian* @Date 2024/8/13 下午3:55*/
@Configuration
@Data
public class SnowflakeIdWorkerUtils {/** 工作机器ID(0~31) */private long workerId;/** 数据中心ID(0~31) */private long datacenterId;/** 订单支付单号 1开头 */private static final String PAY_CODE = "1";/** 订单退款单号 2开头 */private static final String REFUND_CODE = "2";/** 云卡充值单号 3开头 */private static final String ACCOUNT_CODE = "3";/** 积分批次号 4开头 */private static final String POINT_CODE = "4";/** 积分费用入账批次号 5开头 */private static final String POINT_EXPENSE_CODE = "5";/** 首次关注公众号发放积分订单号 6开头 */private static final String FIRST_FOLLOW_ISSUE_POINT_CODE = "6";/** 新用户注册发放积分订单号 7开头 */private static final String ISSUE_REGISTER_TASK_POINT_CODE = "7";// ==============================Fields===========================================/** 开始时间截 (2015-01-01) */private final long twepoch = 1420041600000L;/** 机器id所占的位数 */private final long workerIdBits = 5L;/** 数据标识id所占的位数 */private final long datacenterIdBits = 5L;/** 支持的最大机器id,结果是31 (这个移位算法可以很快的计算出几位二进制数所能表示的最大十进制数) */private final long maxWorkerId = -1L ^ (-1L << workerIdBits);/** 支持的最大数据标识id,结果是31 */private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);/** 序列在id中占的位数 */private final long sequenceBits = 12L;/** 机器ID向左移12位 */private final long workerIdShift = sequenceBits;/** 数据标识id向左移17位(12+5) */private final long datacenterIdShift = sequenceBits + workerIdBits;/** 时间截向左移22位(5+5+12) */private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;/** 生成序列的掩码,这里为4095 (0b111111111111=0xfff=4095) */private final long sequenceMask = -1L ^ (-1L << sequenceBits);/** 毫秒内序列(0~4095) */private long sequence = 0L;/** 上次生成ID的时间截 */private long lastTimestamp = -1L;//==============================Constructors=====================================/*** 构造函数*/public SnowflakeIdWorkerUtils() {}/*** 支付单号* @return*/public String getOrderNo() {return PAY_CODE+ nextId();}/*** 退款单号* @return*/public String getRefundCode() {return REFUND_CODE+ nextId();}/*** 云卡充值单号* @return*/public String getAccountCode() {return ACCOUNT_CODE+ nextId();}/*** 积分批次号* @return*/public String getPointNo() {return POINT_CODE+ nextId();}/*** 积分费用入账批次号* @return*/public String getPointExpenseNo() {return POINT_EXPENSE_CODE+ nextId();}/*** 首次关注公众号发放积分订单号* @return*/public String getFirstFollowIssuePointNo() {return FIRST_FOLLOW_ISSUE_POINT_CODE+ nextId();}/*** 新用户注册发放积分订单号* @return*/public String getIssueRegisterTaskPointNo() {return ISSUE_REGISTER_TASK_POINT_CODE+ nextId();}/*** 构造函数* @param workerId 工作ID (0~31)* @param datacenterId 数据中心ID (0~31)*/public SnowflakeIdWorkerUtils(long workerId, long datacenterId) {if (workerId > maxWorkerId || workerId < 0) {throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));}if (datacenterId > maxDatacenterId || datacenterId < 0) {throw new IllegalArgumentException(String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));}this.workerId = workerId;this.datacenterId = datacenterId;}// ==============================Methods==========================================/*** 获得下一个ID (该方法是线程安全的)* @return SnowflakeId*/public synchronized long nextId() {long timestamp = timeGen();//如果当前时间小于上一次ID生成的时间戳,说明系统时钟回退过这个时候应当抛出异常if (timestamp < lastTimestamp) {throw new RuntimeException(String.format("Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));}//如果是同一时间生成的,则进行毫秒内序列if (lastTimestamp == timestamp) {sequence = (sequence + 1) & sequenceMask;//毫秒内序列溢出if (sequence == 0) {//阻塞到下一个毫秒,获得新的时间戳timestamp = tilNextMillis(lastTimestamp);}}//时间戳改变,毫秒内序列重置else {sequence = 0L;}//上次生成ID的时间截lastTimestamp = timestamp;//移位并通过或运算拼到一起组成64位的IDreturn ((timestamp - twepoch) << timestampLeftShift) //| (datacenterId << datacenterIdShift) //| (workerId << workerIdShift) //| sequence;}/*** 阻塞到下一个毫秒,直到获得新的时间戳* @param lastTimestamp 上次生成ID的时间截* @return 当前时间戳*/protected long tilNextMillis(long lastTimestamp) {long timestamp = timeGen();while (timestamp <= lastTimestamp) {timestamp = timeGen();}return timestamp;}/*** 返回以毫秒为单位的当前时间* @return 当前时间(毫秒)*/protected long timeGen() {return System.currentTimeMillis();}//==============================Test=============================================/** 测试 */public static void main(String[] args) {SnowflakeIdWorkerUtils idWorker = new SnowflakeIdWorkerUtils(1, 1);for (int i = 0; i < 10; i++) {String id = idWorker.getPointNo();System.out.println(id);}}
}

http://www.xdnf.cn/news/14423.html

相关文章:

  • Linux信号机制:进程异步通信的秘密
  • 黑马python(六)
  • 【Canvas与艺术】三只堂前燕
  • DAY49 超大力王爱学Python
  • 深度解析Vue路由原理与实战指南
  • AUTOSAR Adaptive系统如何进行时间同步与延迟分析?
  • YOLOv11改进 | Conv/卷积篇 | 2024最新ECCV最新大感受野的小波卷积WTConv助力YOLOv11有效涨点(二次创新C3k2)
  • 【 C++ 模板中 `template<typename T>` 与 `template<class T>` 的深度解析】
  • springboot测试类原理
  • AI编程:正在拉开新一轮“人与人”的差距
  • Kafka多副本机制
  • python 将字典的值替换为键名作为变量名的形式(带缩进)
  • 基于51单片机的直流电动控制速度系统proteus仿真
  • leetcode 分割回文串 java
  • 总结用ubuntu一直以来遇到的问题
  • 加盐加密算法
  • 浏览器基础及缓存
  • 【Linux】Linux 信号驱动I/O
  • Git 配置 SSH 密钥与私钥教程(跨平台完整指南)
  • 京东API接口最新指南:店铺所有商品接口的接入与使用
  • 易语言模拟真人鼠标轨迹算法 - 非贝塞尔曲线
  • 大模型的开发应用(十一):对话风格微调项目(下):微调与部署
  • 《AI辅助编程:从零掌握核心逻辑》工作坊开业
  • mysql修改密码笔记
  • 基于51单片机的智能小车:按键调速、障碍跟踪、红外循迹与数码管显示(一个合格的单片机课设)
  • 浙江康冠锁业携智能锁具亮相2025上海国际快递物流展
  • 山东大学软件学院创新项目实训开发日志——第十七周(二)
  • 【C语言扩展识别实数负数】2022-5-29
  • Web第二次方向考核复盘
  • OpenHarmony 5.0读取文件并写入到另一份文件(公共文件夹),并保持原先的格式以及编码类型