《分布式中间件技术实战:Java版》学习笔记(一):抢红包

news2024/11/16 15:26:15

数据库建表

(1)red_send_record
记录用户发送了若干总金额的若干个红包。

CREATE TABLE `red_send_record`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `user_id` int(0) NOT NULL COMMENT '用户id',
  `red_packet` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '红包全局唯一标识串',
  `total` int(0) NOT NULL COMMENT '人数',
  `amount` decimal(10, 2) NULL DEFAULT NULL COMMENT '总金额(单位分)',
  `enable_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1' COMMENT '是否有效',
  `create_time` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (`id`) USING BTREE
) 

(2)red_detail
记录用户发送的红包被分成的小红包金额。

CREATE TABLE `red_detail`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `red_packet` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `amount` decimal(8, 2) NULL DEFAULT NULL,
  `enable_flag` char(1) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1',
  `create_time` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`) USING BTREE
)

(3)red_rob_record
记录用户抢到的红包金额。

CREATE TABLE `red_rob_record`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `user_id` int(0) NOT NULL,
  `red_packet` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `amount` decimal(8, 2) NOT NULL,
  `create_time` timestamp(0) NULL DEFAULT NULL,
  `enable_flag` char(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT '1',
  PRIMARY KEY (`id`) USING BTREE
)

随机生成红包金额

红包金额的最小单位是分,将红包金额放大100倍到int类型(为了方便生成随机数),保证红包金额至少是1。

第一种分红包的方式是:红包金额先按照红包数均分,再拿2份数量的金额生成随机数,这种方式生成的金额方差小。
第二种分红包的方式是:直接拿红包总金额生成随机数,这种方式生成的金额方差大。

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class RedPacketUtil {

    public static Random random = new Random();

    public static int min = 1;

    /**
     * 随机生成红包金额
     * @param totalAmount 总金额
     * @param totalNum 红包个数
     * @return
     */
    public static List<Double> devideRedPacket(int totalAmount, int totalNum) {
        List<Double> result = new ArrayList();

        while(totalNum > 1) {
            int max = totalAmount / totalNum * 2;
            int randomAmount = min + random.nextInt(max);
            double amountResult = (double)randomAmount / 100;
            result.add(amountResult);

            totalAmount -= randomAmount;
            totalNum--;
        }

        result.add((double)totalAmount / 100);

        return result;
    }

    public static List<Double> devideRedPacket2(int totalAmount, int totalNum) {
        List<Double> result = new ArrayList();

        int splitAmount = totalAmount - totalNum * min;

        while(totalNum > 1) {

            int randomAmount = random.nextInt(splitAmount);
            double amountResult = (double)(randomAmount + min) / 100;
            result.add(amountResult);

            splitAmount -= randomAmount;
            totalNum--;
        }

        result.add((double)(splitAmount + min) / 100);

        return result;
    }   
}

单元测试:

@Test
public void testRedPacket() {
    List<Double> result = RedPacketUtil.devideRedPacket(2000, 10);
    AtomicReference<Double> total = new AtomicReference<>((double) 0);
    result.stream().forEach(data -> {
        System.out.println(data);
        total.updateAndGet(v -> new Double((double) (v + data)));
    });
    System.out.println("total = " + total);
}

发红包

发红包的请求参数是用户唯一标识、红包总金额和红包个数。

import lombok.Data;
import java.io.Serializable;

@Data
public class RedSendRecordDTO implements Serializable {
    private int userId;
    private int total;
    private double amount;
}

将分好的随机红包金额放入redis的List集合中,设置24小时失效。异步将用户发红包记录和随机红包金额写入数据库。

数据库操作直接引入Mybatis-plus

public String sendRedPacket(RedSendRecordDTO redSendRecordDTO) {
    String redPacket = UUID.randomUUID().toString();

    List<Double> amountPerRedPacket = RedPacketUtil.devideRedPacket((int)redSendRecordDTO.getAmount() * 100, redSendRecordDTO.getTotal());

    amountPerRedPacket.stream().forEach(amount -> {
        redisTemplate.opsForList().rightPush(redPacket, amount);
    });
    redisTemplate.expire(redPacket, 24, TimeUnit.HOURS);

    CompletableFuture.runAsync(new Runnable() {
        @Override
        public void run() {
            RedSendRecord redSendRecord = new RedSendRecord();
            redSendRecord.setRedPacket(redPacket);
            redSendRecord.setAmount(redSendRecordDTO.getAmount());
            redSendRecord.setUserId(redSendRecordDTO.getUserId());
            redSendRecord.setTotal(redSendRecordDTO.getTotal());
            save(redSendRecord);

            amountPerRedPacket.stream().forEach(amount -> {
                RedDetail redDetail = new RedDetail();
                redDetail.setRedPacket(redPacket);
                redDetail.setAmount(amount);
                redDetailService.save(redDetail);
            });
        }
    });

    return redPacket;
}

在这里插入图片描述
在这里插入图片描述

抢红包

抢红包入参是用户唯一标识、红包唯一标识。

import lombok.Data;
import java.io.Serializable;

@Data
public class RedRobRecordDTO implements Serializable {
    private int userId;
    private String redPacket;

}

抢红包要防止用户重复抢红包和一个红包被多个用户抢到。

(1)为了方便jmeter测试,在没有传userId参数时,生成一个随机用户标识。

(2)用redis的map存放用户标识和抢到的红包金额。

(3)先判断该用户之前有没有抢过红包,抢过直接返回抢到的红包金额。如果没有抢过,向redis添加一个key-value,如果添加成功,标识该用户可以抢红包,如果添加失败,标识该用户重复抢红包了。

(4)从redis存放随机红包金额的集合弹出一个红包给可以抢红包的用户,并保存到数据库。如果随机红包金额集合弹出的元素为空,表示红包抢完了。

@Override
public String robRedPacket(RedRobRecordDTO redRobRecordDTO) {
    if(0 == redRobRecordDTO.getUserId()) {
        int userId = RedPacketUtil.random.nextInt(1000);
        redRobRecordDTO.setUserId(userId);
    }
    log.info("robRedPacket redRobRecordDTO is:{}", JSONUtil.toJsonStr(redRobRecordDTO));

    //标识用户正在抢红包
    String userRobKey = redRobRecordDTO.getRedPacket() + ":" + redRobRecordDTO.getUserId();
    //用户抢到的红包金额
    String userAmountKey = redRobRecordDTO.getRedPacket() + ":amount";

    //判断用户是否抢过
    boolean isRobed = redisTemplate.opsForHash().hasKey(userAmountKey, redRobRecordDTO.getUserId());
    if(isRobed) {
        double amountPerUserId = (double) redisTemplate.opsForHash().get(userAmountKey, redRobRecordDTO.getUserId());
        return String.valueOf(amountPerUserId);
    }

    boolean isSet = redisTemplate.opsForValue().setIfAbsent(userRobKey, redRobRecordDTO.getUserId(), 1, TimeUnit.MINUTES);
    if(!isSet) {
        return "抢过了";
    }

    //抢到红包
    Double value = (Double) redisTemplate.opsForList().rightPop(redRobRecordDTO.getRedPacket());
    if(value == null) {
        return "红包被抢完了";
    }
    redisTemplate.opsForHash().put(userAmountKey, redRobRecordDTO.getUserId(), value);
    RedRobRecord redRobRecord = new RedRobRecord();
    redRobRecord.setUserId(redRobRecordDTO.getUserId());
    redRobRecord.setAmount(value);
    redRobRecord.setRedPocket(redRobRecordDTO.getRedPacket());
    redRobRecordService.save(redRobRecord);
   return String.valueOf(value);
}

在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/656897.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

笔记本电脑介绍:记录生活,激发灵感

笔记本电脑是一种轻便、便携的电脑&#xff0c;它的出现改变了人们的工作和生活方式&#xff0c;它的优势在于它的小巧、轻便、便携性&#xff0c;可以满足用户的不同需求。本文将从笔记本电脑的结构、功能、优势和应用四个方面进行详细阐述。 一、笔记本电脑的结构 笔记本电…

mysql常见错误汇总

mysql常见错误汇总 列别名问题 可以在查询选择列表中使用别名为列提供 不同名称。可以使用 、 或子句中的别名来引用该列&#xff1a;GROUP BY ORDER BY HAVING SELECT SQRT(a*b) AS root FROM tbl_nameGROUP BY root HAVING root > 0;SELECT id, COUNT(*) AS cnt FROM t…

Grafana安装和实现可视化和告警

1、Grafana安装和实现可视化和告警 Prometheus UI 提供了快速验证 PromQL 以及临时可视化支持的能力&#xff0c;但其可视化能力却比较弱。一般情况下&#xff0c; 我们都用 Grafana 来实现对 Prometheus 的可视化实现。 1.1 什么是 Grafana Grafana 是一个用来展示各种各样…

【Linux】可重入函数

文章目录 前言一. 场景二. 可重入与线程安全结束语 前言 在Linux中&#xff0c;进程/线程可能因为时间片到达&#xff0c;或者其他中断&#xff0c;或者调用系统&#xff0c;需要从用户态切换到内核态&#xff0c;而内核空间会保存切换前&#xff0c;用户代码执行处的上下文&a…

环境搭建【1】VM和ubuntun 环境搭建

1.安装VMware 1.1 下载安装包 &#xff08;1&#xff09;官网下载&#xff1a;https://customerconnect.vmware.com/en/downloads/info/slug/desktop_end_user_computing/vmware_workstation_pro/16_0 &#xff08;2&#xff09;百度网盘&#xff1a;https://pan.baidu.com/s/…

5.pixi.js编写的塔防游戏(类似保卫萝卜)-子弹跟随精灵移动

游戏说明 一个用pixi.js编写的h5塔防游戏&#xff0c;可以用electron打包为exe&#xff0c;支持移动端&#xff0c;也可以用webview控件打包为app在移动端使用 环境说明 cnpm6.2.0 npm6.14.13 node12.22.7 npminstall3.28.0 yarn1.22.10 npm config list electron_mirr…

【详解】篮球记分牌硬件及代码

篮球记分牌设计 1 系统设计1.1 设计任务 1.2 性能指标要求1.2 设计思路及设计框图1.2.1设计思路1.2.2总体设计框图1.2.3电路原理图1.2.3 PCB布线图 2 主要程序模块的设计及原理2.1 外部中断0 2.2 菜单2.3 两队比分及两队犯规次数显示及修改2.3.1选择功能2.3.2修改功能2.3.3显示…

golang vscode环境报错gopls was not able to find modules in your workspace的解决方式

目录 错误提示 分析 解决方式 方法一&#xff1a;将workspace与项目路径保持一致 方案二&#xff1a;使用go work指明纳入工作区的的module 总结 错误提示 golang从老版本升级到go1.20.5后打开vscode&#xff0c;发现代码不能自动补全了&#xff0c;而且vscode跳出一下的…

dolphinscheduler创建创建租户错误

报错信息 [ERROR] 2023-06-17 17:04:10.282 org.apache.dolphinscheduler.api.exceptions.ApiExceptionHandler:[45] - 创建租户错误 org.apache.hadoop.security.AccessControlException: Permission denied: userhdfs, accessWRITE, inode"/":root:supergroup:drw…

搭建自己的 new bing网站----nginx反代(群晖,全docker版)

文章目录 1.前言&#xff1a;2. 部署过程&#xff1a;2.1.部署go-proxy-bing 项目&#xff1a;2.1.部署nginx反代&#xff1a;这两步完成实际上就可以访问了&#xff0c; 3.部署聊天服务器&#xff1a;代表链接成功&#xff01; 1.前言&#xff1a; 本篇教程全萌新向&#xff…

Qt for Android 调试遇到问题总结

一、首次编译Downloading “Gradle” 无法下载导致编译无法完成问题 解决办法&#xff1a; 1、通过地址&#xff1a;https://services.gradle.org/distributions/gradle-5.5.1-bin.zip单独下载 2、再将下载的gradle-5.5.1-bin.zip放到“C:\Users\hp\.gradle\wrapper\dists\gr…

软件工程导论期末急救包(上)

目录 什么是软件工程&#xff1f;它的目标和内容是什么&#xff1f; 软件文档作用及包含 软件过程模型 瀑布模型 快速原型模型 增量模型 螺旋模型 喷泉模型 软件生存周期 需求分析阶段的基本任务是什么&#xff1f; 可行性研究的任务是什么&#xff1f; 软件是什…

MySQL——MySQL的图形化界面工具安装与使用

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 写在前面&#xff1a; 一.DataGrip安装使用 1). 找到下载准备好的安装包&am…

2023 最新 1200 道 JAVA 面试题,囊括面试所有硬核技能

Java 面试八股文有必要背吗&#xff1f; 我的回答是&#xff1a;很有必要。你可以讨厌这种模式&#xff0c;但你一定要去背&#xff0c;因为不背你就进不了大厂。现如今&#xff0c;Java 面试的本质就是八股文&#xff0c;把八股文面试题背好&#xff0c;面试才有可能表现好。…

power gating/power switch/level shift/IR-drop

目录 1.power gating 2.level shift 3.IR-drop 1.power gating power gating 的设计重点如下&#xff1a; 1)Power Switch的设计&#xff1b; 2)Power Gating controller的设计&#xff1b; 3)retention register和isolation cell的选择及插入&#xff1b; 4)Power Gati…

【Pandas】pandas用法解析(一)

目录 一、生成数据表 1.导入pandas库 2.导入CSV或者xlsx文件 3.用pandas创建数据表 二、数据表信息查看 1.维度查看 2.数据表基本信息&#xff08;维度、列名称、数据格式、所占空间等&#xff09; 3.每一列数据的格式 4.某一列格式 5.空值判断 6.查看某一列空值 7…

使用torch的自动微分实现自定义函数优化

前言 目的是利用torch已经有的自动微分机制&#xff0c;进行参数迭代更新&#xff0c;就不用自己写代码算了。 文章目录 前言1. 待优化函数1.1 解释 2. 代码3. 结果 1. 待优化函数 y 10 ( x 1 x 2 − 5 ) 2 ( x 1 − x 2 ) 2 y10\times(x_1x_2-5)^2(x_1-x_2)^2 y10(x1​x…

oracle中如何修改日期类型的字段值

系列文章目录 文章目录 系列文章目录前言一、数据库表方式进行修改二、sql方式步骤总结 前言 Oracle是一家全球领先的数据库管理系统&#xff08;DBMS&#xff09;和企业软件公司。Oracle Corporation成立于1977年&#xff0c;总部位于美国加利福尼亚州的红木城&#xff08;Re…

SpringBoot整合篇

SpringBoot整合第三方技术 1、整合缓存 何为缓存&#xff1f; 缓存是一种介于数据永久存储介质与数据应用之间的数据临时存储介质使用缓存可以有效的减少低速数据读取过程的次数&#xff08;例如磁盘IO&#xff09;&#xff0c;提高系统性能缓存不仅可以用于提高永久性存储介…

腾讯云最新社招面经出炉(面试原题+答案解析)

前言 大家好&#xff0c;今天给大家分享一篇腾讯云的面经&#xff0c;以下是面试题和答案。加油&#xff0c;一起卷。 聊聊项目&#xff0c;好的设计&#xff0c;好的代码 谈谈什么是零拷贝&#xff1f; 一共有几种 IO 模型&#xff1f;NIO 和多路复用的区别&#xff1f; F…