Redis分布式锁使用以及对接支付宝,paypal,strip跨境支付

本章重点在于如何使用redis的分布式锁来锁定库存。减少超卖,同时也对接了支付宝,paypal,strip跨境支付

第一步先建立一个商品表
 

CREATE TABLE `sys_product` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键',`code` varchar(60) DEFAULT NULL COMMENT '商品编码',`name` varchar(60) DEFAULT NULL COMMENT '商品名字',`image` varchar(200) DEFAULT NULL COMMENT '商品图片',`inventory` int(11) DEFAULT NULL COMMENT '商品库存',`points` int(11) DEFAULT NULL COMMENT '商品积分',`amount` decimal(10,0) DEFAULT NULL COMMENT '商品金额',`create_time` datetime NOT NULL COMMENT '创建时间',`create_user` varchar(60) DEFAULT NULL COMMENT '创建人',`update_time` datetime DEFAULT NULL COMMENT '修改时间',`update_user` varchar(60) DEFAULT NULL COMMENT '修改人',`is_delete` int(2) DEFAULT NULL COMMENT '是否删除',PRIMARY KEY (`id`),UNIQUE KEY `id` (`id`) USING BTREE COMMENT '主键'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='产品表';

对应的sql文件里面有各个信息,有名字,库存,还有积分。这几个概念很重要

页面如下

代码如下

 /*** 新增产品** @param sysProduct 产品* @return 结果*/@Overridepublic int insertSysProduct(SysProduct sysProduct) {// 然后生成产品编码,生成一个6位的英文字母sysProduct.setCreateTime(DateUtils.getNowDate());// 首先先生成一个随机数,然后调用方法,如果生成重复则会一直生成直接到不重复String getProductCode = getProductCode();SysProduct one = this.lambdaQuery().eq(SysProduct::getCode, getProductCode).one();if (StringUtils.isNotNull(one)) {// 说明重复了,需要重新生成递归getProductCode = getProductCode();}// 同时也要判定一下商品名字是否重复,如果名字重复则无法添加Long countName = this.lambdaQuery().eq(SysProduct::getName, sysProduct.getName()).count();if (countName > 0) {throw new ServiceException("名字已经重复,无法添加");}// 如果是空的则直接插入数据sysProduct.setCode(getProductCode);// 创建人sysProduct.setCreateUser(SecurityUtils.getLoginUser().getUserId());// 修改人sysProduct.setUpdateUser(SecurityUtils.getLoginUser().getUserId());// 修改时间return sysProductMapper.insertSysProduct(sysProduct);}/*** 生产一个随机数** @return 返回值*/public String getProductCode() {// 生成6位随机英文字母StringBuilder sb = new StringBuilder();for (int i = 0; i < NUMBER_6; i++) {sb.append(ALPHABET.charAt(RandomUtils.nextInt(0, ALPHABET.length())));}return sb.toString();}/*** 修改产品** @param sysProduct 产品* @return 结果*/@Overridepublic int updateSysProduct(SysProduct sysProduct) {// 同时也要判定一下商品名字是否重复,如果名字重复则无法添加Long countName = this.lambdaQuery().eq(SysProduct::getName, sysProduct.getName()).ne(SysProduct::getId, sysProduct.getId()).count();if (countName > 0) {throw new ServiceException("名字已经重复,无法添加");}sysProduct.setUpdateTime(DateUtils.getNowDate());return sysProductMapper.updateSysProduct(sysProduct);}

第二步创建订单表

CREATE TABLE `sys_order` (`id` bigint(20) NOT NULL COMMENT '主键',`order_id` bigint(20) DEFAULT NULL COMMENT '订单id',`points` int(11) DEFAULT NULL COMMENT '订单积分',`amount` decimal(2,0) DEFAULT NULL COMMENT '订单金额',`number` int(11) DEFAULT NULL COMMENT '数量',`status` int(11) DEFAULT '0' COMMENT '订单状态(0 未支付 1支付中 2支付成功)',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`create_user` bigint(20) DEFAULT NULL COMMENT '创建人',`update_time` datetime DEFAULT NULL COMMENT '修改时间',`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',`is_delete` int(2) DEFAULT NULL COMMENT '是否删除',PRIMARY KEY (`id`),UNIQUE KEY `id` (`id`) USING BTREE COMMENT '主键',KEY `order_id` (`order_id`) USING BTREE COMMENT '订单id'
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单表';CREATE TABLE `sys_order_item` (`id` bigint(20) NOT NULL COMMENT '主键id',`order_id` bigint(20) DEFAULT NULL COMMENT '订单id',`order_item_id` bigint(20) DEFAULT NULL COMMENT '订单详情id',`product_name` varchar(30) DEFAULT NULL COMMENT '商品名字',`product_number` int(11) DEFAULT NULL COMMENT '商品数量',`product_amount` decimal(2,0) DEFAULT NULL COMMENT '商品金额',`product_code` varchar(30) DEFAULT NULL COMMENT '商品编码',`product_image` varchar(255) DEFAULT NULL COMMENT '商品图片',`product_points` varchar(30) DEFAULT NULL COMMENT '商品积分',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`create_user` bigint(20) DEFAULT NULL COMMENT '创建人',`update_time` datetime DEFAULT NULL COMMENT '创建时间',`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',`is_delete` int(2) DEFAULT NULL COMMENT '是否删除',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单详情表';

第三步我们要生成一个订单提前准备往rabbitmq按照延迟队列插件

作用:就是给订单加一个过期时间,当这个订单创建后,有一个过期时间当这个时间过期后自动关闭订单,修改状态,可以利用rabbitmq的延迟插件可以很好的实现,当然也有其他方案,比如redis,定时任务。

第一步先看rabbimq版本。

第二步到gitHub上找到对应的版本的延迟队列

Releases · rabbitmq/rabbitmq-delayed-message-exchange

第三步直接将这个拖到服务器上

第四步:我的rabbitmq安装在docker里面,所以直接在docker按照延迟插件

执行这个命令是copy这个路径到docker中rabbitmq的内部容器的plugins

docker cp /usr/rabbitmq_delayed_message/rabbitmq_delayed_message_exchange-v4.0.7.ez rabbit:/plugins/

执行完毕后出现

Successfully copied 44kB to rabbit:/plugins/

然后到docker内部容器看是否安装成功

 docker exec -it rabbit /bin/bash

cd plugins

进到plugins

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

然后执行exit退出容器,在重启rabbitmq

 docker restart fe46628fec68

然后到rabbitmq控制界面查看是否有也有这个插件

说明OK了

第四步创建订单代码

点击创建订单,传递一个库存,和产品id。然后添加订单表有一个主表和明细表

主表存订单总金额,总积分,总库存。

明细表存在一些商品的信息。

前端代码如下:

<template><div class="app-container"><el-form:model="queryParams"ref="queryForm"size="small":inline="true"v-show="showSearch"label-width="68px"><el-form-item label="商品编码" prop="code"><el-inputv-model="queryParams.code"placeholder="请输入商品编码"clearable@keyup.enter.native="handleQuery"/></el-form-item><el-form-item label="商品名字" prop="name"><el-inputv-model="queryParams.name"placeholder="请输入商品名字"clearable@keyup.enter.native="handleQuery"/></el-form-item><el-form-item><el-buttontype="primary"icon="el-icon-search"size="mini"@click="handleQuery">搜索</el-button><el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button></el-form-item></el-form><el-row :gutter="10" class="mb8"><el-col :span="1.5"><el-buttontype="primary"plainicon="el-icon-plus"size="mini"@click="handleAdd"v-hasPermi="['system:product:add']">新增</el-button></el-col><el-col :span="1.5"><el-buttontype="success"plainicon="el-icon-edit"size="mini":disabled="single"@click="handleUpdate"v-hasPermi="['system:product:edit']">修改</el-button></el-col><el-col :span="1.5"><el-buttontype="danger"plainicon="el-icon-delete"size="mini":disabled="multiple"@click="handleDelete"v-hasPermi="['system:product:remove']">删除</el-button></el-col><el-col :span="1.5"><el-buttontype="warning"plainicon="el-icon-download"size="mini"@click="handleExport"v-hasPermi="['system:product:export']">导出</el-button></el-col><el-dialogtitle="确认订单":visible.sync="orderDialogVisible"width="25%"custom-class="custom-order-dialog"><el-table :data="selectedProducts" border><el-table-columnproperty="name"label="商品名称"width="200"></el-table-column><el-table-column property="inventory" label="商品库存" width="200"><template slot-scope="scope"><el-input v-model="scope.row.inventory" size="small"></el-input></template></el-table-column><!-- 可以根据需要添加更多列 --></el-table><span slot="footer" class="dialog-footer"><el-button @click="orderDialogVisible = false">取消</el-button><el-button type="primary" @click="submitOrder">确定</el-button></span></el-dialog><!-- 创建订单按钮 --><el-col :span="1.5"><el-button @click="handleCreateOrder">创建订单</el-button></el-col><right-toolbar:showSearch.sync="showSearch"@queryTable="getList"></right-toolbar></el-row><el-tablev-loading="loading":data="productList"@selection-change="handleSelectionChange"><el-table-column type="selection" width="55" align="center" /><el-table-column label="商品编码" align="center" prop="code" /><el-table-column label="商品名字" align="center" prop="name" /><el-table-column label="商品图片" align="center" prop="image" width="100"><template slot-scope="scope"><image-preview :src="scope.row.image" :width="50" :height="50" /></template></el-table-column><el-table-column label="商品库存" align="center" prop="inventory" /><el-table-column label="商品积分" align="center" prop="points" /><el-table-column label="商品金额" align="center" prop="amount" /><el-table-columnlabel="操作"align="center"class-name="small-padding fixed-width"><template slot-scope="scope"><el-buttonsize="mini"type="text"icon="el-icon-edit"@click="handleUpdate(scope.row)"v-hasPermi="['system:product:edit']">修改</el-button><el-buttonsize="mini"type="text"icon="el-icon-delete"@click="handleDelete(scope.row)"v-hasPermi="['system:product:remove']">删除</el-button></template></el-table-column></el-table><paginationv-show="total > 0":total="total":page.sync="queryParams.pageNum":limit.sync="queryParams.pageSize"@pagination="getList"/><!-- 添加或修改产品对话框 --><el-dialog :title="title" :visible.sync="open" width="500px" append-to-body><el-form ref="form" :model="form" :rules="rules" label-width="80px"><el-form-item label="商品名字" prop="name"><el-input v-model="form.name" placeholder="请输入商品名字" /></el-form-item><el-form-item label="商品图片" prop="image"><image-upload v-model="form.image" /></el-form-item><el-form-item label="商品库存" prop="inventory"><el-input v-model="form.inventory" placeholder="请输入商品库存" /></el-form-item><el-form-item label="商品积分" prop="points"><el-input v-model="form.points" placeholder="请输入商品积分" /></el-form-item><el-form-item label="商品金额" prop="amount"><el-input v-model="form.amount" placeholder="请输入商品金额" /></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button type="primary" @click="submitForm">确 定</el-button><el-button @click="cancel">取 消</el-button></div></el-dialog></div>
</template><script>
import {listProduct,getProduct,delProduct,addProduct,updateProduct,
} from "@/api/system/product";
import { createOrder } from "@/api/system/order";export default {name: "Product",data() {return {// 其他已有属性...orderDialogVisible: false, // 控制订单对话框的可见性selectedProducts: [], // 存储选中的产品// 遮罩层loading: true,// 选中数组ids: [],// 非单个禁用single: true,// 非多个禁用multiple: true,// 显示搜索条件showSearch: true,// 总条数total: 0,// 产品表格数据productList: [],// 弹出层标题title: "",// 是否显示弹出层open: false,// 查询参数queryParams: {pageNum: 1,pageSize: 10,code: null,name: null,image: null,inventory: null,points: null,amount: null,updateUser: null,},// 表单参数form: {},// 表单校验rules: {createTime: [{ required: true, message: "创建时间不能为空", trigger: "blur" },],},};},created() {this.getList();},methods: {/** 查询产品列表 */getList() {this.loading = true;listProduct(this.queryParams).then((response) => {this.productList = response.rows;this.total = response.total;this.loading = false;});},// 取消按钮cancel() {this.open = false;this.reset();},// 表单重置reset() {this.form = {id: null,code: null,name: null,image: null,inventory: null,points: null,amount: null,createTime: null,createUser: null,updateTime: null,updateUser: null,isDelete: null,};this.resetForm("form");},/** 搜索按钮操作 */handleQuery() {this.queryParams.pageNum = 1;this.getList();},/** 重置按钮操作 */resetQuery() {this.resetForm("queryForm");this.handleQuery();},// 多选框选中数据handleSelectionChange(selection) {this.ids = selection.map((item) => item.id);this.single = selection.length !== 1;this.multiple = !selection.length;this.selectedProducts = selection; // 直接赋值选中的产品列表},/** 新增按钮操作 */handleAdd() {this.reset();this.open = true;this.title = "添加产品";},/** 修改按钮操作 */handleUpdate(row) {this.reset();const id = row.id || this.ids;getProduct(id).then((response) => {this.form = response.data;this.open = true;this.title = "修改产品";});},/** 提交按钮 */submitForm() {this.$refs["form"].validate((valid) => {if (valid) {if (this.form.id != null) {updateProduct(this.form).then((response) => {this.$modal.msgSuccess("修改成功");this.open = false;this.getList();});} else {addProduct(this.form).then((response) => {this.$modal.msgSuccess("新增成功");this.open = false;this.getList();});}}});},/** 删除按钮操作 */handleDelete(row) {const ids = row.id || this.ids;this.$modal.confirm('是否确认删除产品编号为"' + ids + '"的数据项?').then(function () {return delProduct(ids);}).then(() => {this.getList();this.$modal.msgSuccess("删除成功");}).catch(() => {});},/** 导出按钮操作 */handleExport() {this.download("system/product/export",{...this.queryParams,},`product_${new Date().getTime()}.xlsx`);},/** 创建订单 */handleCreateOrder() {console.log("参数" + this.selectedProducts.length);if (this.selectedProducts.length === 0) {this.$message({message: "请选择至少一个商品!",type: "warning",});return;}this.orderDialogVisible = true; // 打开订单对话框},/** 提交订单 */submitOrder() {const sysCreateOrderDTO = this.selectedProducts.map((product) => ({productId: product.id,inventory: product.inventory,}));createOrder(sysCreateOrderDTO).then(() => {this.$modal.msgSuccess("订单创建成功");this.orderDialogVisible = false; // 关闭订单对话框this.getList();// 刷新产品列表}).catch((error) => {this.$modal.msgError(error);});},},
};
</script>
<style scoped>
/* 自定义订单对话框样式 */
.custom-order-dialog {/* 父容器样式 */
}
.custom-order-dialog .product-list {list-style: none;padding: 0;margin: 0;
}
.custom-order-dialog .product-item {display: flex;align-items: center;margin-bottom: 10px;
}
.custom-order-dialog .product-item i {margin-right: 8px;color: #67c23a; /* 或者选择其他颜色 */
}
</style>
// 新增订单
export function createOrder(sysCreateOrderDTO) {return request({url: '/system/order',method: 'post',data: sysCreateOrderDTO})
}
import request from '@/utils/request'// 查询产品列表
export function listProduct(query) {return request({url: '/system/product/list',method: 'get',params: query})
}// 查询产品详细
export function getProduct(id) {return request({url: '/system/product/' + id,method: 'get'})
}// 新增产品
export function addProduct(data) {return request({url: '/system/product',method: 'post',data: data})
}// 修改产品
export function updateProduct(data) {return request({url: '/system/product',method: 'put',data: data})
}// 删除产品
export function delProduct(id) {return request({url: '/system/product/' + id,method: 'delete'})
}

后端代码如下

controller

 /*** 新增订单* @param sysCreateOrderDTO 入参* @return 结果*/@PreAuthorize("@ss.hasPermi('system:order:add')")@Log(title = "订单", businessType = BusinessType.INSERT)@PostMappingpublic AjaxResult add(@RequestBody List<SysCreateOrderDTO> sysCreateOrderDTO) {return toAjax(sysOrderService.insertSysOrder(sysCreateOrderDTO));}

 entity

package com.ruoyi.system.domain.dto;import lombok.Data;/*** @author hu* @desc 创建订单的DTO*/
@Data
public class SysCreateOrderDTO {/*** 产品id*/private Long productId;/*** 库存*/private Long inventory;
}

service

 /*** 新增订单** @param sysCreateOrderDTO 添加的订单信息* @return 结果*/@Override@Transactional(rollbackFor = Exception.class)public Boolean insertSysOrder(List<SysCreateOrderDTO> sysCreateOrderDTO) {// 第一步拿到川产品id进行逗号拆分if (StringUtils.isNull(sysCreateOrderDTO)) {throw new ServiceException("缺少必要的参数");}// 拿到对应的产品idList<Long> productIds = sysCreateOrderDTO.stream().map(item -> item.getProductId()).collect(Collectors.toList());// 拿到全部产品id// 然后查询产品是否存在如果产品存在,则开始存入到数据库并且订单是状态是未支付。List<SysProduct> sysProducts = sysProductService.lambdaQuery().in(SysProduct::getId, productIds).eq(SysProduct::getIsDelete, 0).list();// 然后判定是否存在if (sysProducts.size() != productIds.size()) {throw new ServiceException("请确保产品是否存在");}// 然后判定一下传递的库存是否大于商品原来的库存// 获取积分AtomicLong points = new AtomicLong(0);AtomicLong amounts = new AtomicLong(0);AtomicLong numbers = new AtomicLong(0);sysProducts.forEach(item -> {sysCreateOrderDTO.forEach(info -> {if (item.getId().equals(info.getProductId())) {if (item.getInventory() < info.getInventory()) {throw new ServiceException("请确保库存是否充足");}// 积分等于库存*单个积分Long point = info.getInventory() * item.getPoints();// 金额,等于单价*出库Long amount = info.getInventory() * item.getAmount();points.set(points.get() + point);amounts.set(amounts.get() + amount);numbers.set(numbers.get() + info.getInventory());}});});// 如果产品存在,这时候开始创建订单,并且给一个过期时间SysOrder sysOrder = new SysOrder();// 订单idSnowFlakeGenerateIdWorker snowFlakeGenerateIdWorker =new SnowFlakeGenerateIdWorker(0L, 0L);String id = snowFlakeGenerateIdWorker.generateNextId();// 订单idsysOrder.setOrderId(Long.valueOf(id));// 积分 不是空的sysOrder.setPoints(points.get());// 金额sysOrder.setAmount(amounts.get());// 数量sysOrder.setNumber(numbers.get());// 订单状态sysOrder.setStatus(0);// 创建时间sysOrder.setCreateTime(DateUtils.getNowDate());// 修改时间sysOrder.setUpdateTime(DateUtils.getNowDate());// 创建人sysOrder.setCreateUser(SecurityUtils.getLoginUser().getUserId());// 修改人sysOrder.setUpdateUser(SecurityUtils.getLoginUser().getUserId());// 然后创建订单详情表List<SysOrderItem> orderItems = new ArrayList<>();sysProducts.forEach(productInfo -> {SysOrderItem sysOrderItem = new SysOrderItem();// 订单idsysOrderItem.setOrderId(sysOrder.getOrderId());// 订单详情idsysOrderItem.setOrderItemId(Long.valueOf(snowFlakeGenerateIdWorker.generateNextId()));// 商品名字sysOrderItem.setProductName(productInfo.getName());// 商品数量sysOrderItem.setProductNumber(sysCreateOrderDTO.stream().filter(item -> item.getProductId().equals(productInfo.getId())).findFirst().get().getInventory());// 商品金额sysOrderItem.setProductAmount(productInfo.getAmount());// 商品编码sysOrderItem.setProductCode(productInfo.getCode());// 商品图片sysOrderItem.setProductImage(productInfo.getImage());// 商品积分sysOrderItem.setProductPoints(productInfo.getPoints());// 创建人sysOrderItem.setCreateUser(SecurityUtils.getLoginUser().getUserId());// 修改人sysOrderItem.setUpdateUser(SecurityUtils.getLoginUser().getUserId());// 修改时间sysOrderItem.setUpdateTime(DateUtils.getNowDate());// 创建时间sysOrderItem.setCreateTime(DateUtils.getNowDate());// 放到集合里面orderItems.add(sysOrderItem);});Boolean result = this.save(sysOrder) && sysOrderItemService.saveBatch(orderItems);// 创建一个订单过期时间,直接放到redis里面默认1个小时吧,如果一个小时到了,然后修改一下订单状态,这时候同时发送给rabbitmq做延迟队列redisTemplate.opsForValue().set(Constants.ORDER_PREFIX + sysOrder.getOrderId(), "0", 1, TimeUnit.HOURS);// 如果是空这时候同时需要给rabbitmq发送消息,这个消息是延迟消息,所以需要创建队列rabbitTemplate.convertAndSend(IsoMqConstant.ORDER_DELAY_EXCHANGE, IsoMqConstant.ORDER_DELAY_ROUTING_KEY, sysOrder.getOrderId(), message -> {message.getMessageProperties().setDelay(1 * DAYS_PER_SECOND);return message;});return result;}

注意使用了rabbitmq有延迟队列,就是主要用于处理订单过期时间的

package com.ruoyi.system.mq;import com.rabbitmq.client.Channel;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.SysOrder;
import com.ruoyi.system.enums.SysOrderStatusEnum;
import com.ruoyi.system.service.ISysOrderService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;import java.io.IOException;import static com.ruoyi.common.mq.IsoMqConstant.ORDER_DELAY_QUEUE;/*** @author hu* @desc 订单消费者* @date 2025/4/27 15:06*/
@Component
@Slf4j
@AllArgsConstructor
public class SysOrderConsumer {private final ISysOrderService sysOrderService;/*** 延迟消费者** @param channel* @param message* @param orderId*/@RabbitListener(queues = ORDER_DELAY_QUEUE)public void orderPayDelayConsumer(Channel channel, Message message, Long orderId) {try {log.info("订单延迟队列开始了:{}", orderId);// 然后拿到订单状态,这时候需要查询一下,如果订单状态是未支付,则修改订单状态为已取消SysOrder sysOrder = sysOrderService.lambdaQuery().eq(SysOrder::getOrderId, orderId).eq(SysOrder::getIsDelete, 0).eq(SysOrder::getStatus, 0).one();if (StringUtils.isNotNull(sysOrder)) {sysOrderService.lambdaUpdate().set(SysOrder::getStatus, SysOrderStatusEnum.CANCELLED.getCode()).eq(SysOrder::getOrderId, orderId).update();}channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);} catch (Exception e) {log.error("发送了异常", e.getMessage());try {channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);} catch (IOException ex) {throw new RuntimeException(ex);}}}
}
package com.ruoyi.system.config;import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import static com.ruoyi.common.mq.IsoMqConstant.*;/*** @author hu* @desc rabbitmq  配置*/
@Configuration
public class RabbitmqConfig {/*** 延迟交换机** @return 返回*/@Beanpublic DirectExchange delayExchange() {return ExchangeBuilder.directExchange(ORDER_DELAY_EXCHANGE).delayed() //设置delay属性为true 默认是true.durable(true).build();}/*** 延迟队列** @return 返回*/@Beanpublic Queue delayQueue() {return new Queue(ORDER_DELAY_QUEUE);}/*** 绑定** @param delayQueue    延迟队列* @param delayExchange 延迟交换机* @return 返回值*/@Beanpublic Binding delayBinding(Queue delayQueue, DirectExchange delayExchange) {return BindingBuilder.bind(delayQueue).to(delayExchange).with(ORDER_DELAY_ROUTING_KEY);}
}

结果点击确定就能实现结果如下

未完待续。

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

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

相关文章

使用frpc链接内网的mysql

以下是配置 frpc 连接内网 MySQL 服务的详细步骤&#xff1a; 1. 准备工作 frps 服务器&#xff1a;已部署在公网 IP 11.117.11.245&#xff0c;假设 frps 的默认端口为 7000。 内网 MySQL 服务&#xff1a;运行在内网机器的 3306 端口。 目标&#xff1a;通过公网 IP 11.117…

2025信息安全网络安全意识培训资料汇编(24份)

最新整理&#xff1a;2025信息安全网络安全意识培训资料汇编&#xff0c;共24份资料&#xff0c;供学习参考。 互联网信息安全意识培训.pptx100个网络安全风险防范知识.pptx亚信信息安全意识培训.pptx网络安全法规及意识培训.pptx网络安全意识与案例分析.pptx绿盟-安全意识培训…

JAVA:使用 XStream 实现对象与XML转换的技术指南

1、简述 XStream 是一个简单便捷的 Java 库,用于对象与 XML 的相互转换。其主要特点是: 易于使用:无需复杂的配置即可直接使用。支持自定义:可以灵活地定制对象的序列化和反序列化规则。强大的功能:支持注解、自定义转换器等。本文将详细介绍 XStream 的基本使用方法,并…

VITA STANDARDS LIST,VITA 标准清单下载

VITA STANDARDS LIST&#xff0c;VITA 标准清单下载 DesignationTitleAbstractStatusVMEbus Handbook, 4th EditionA users guide to the VME, VME64 and VME64x bus specifications - features over 70 product photos and over 160 circuit diagrams, tables and graphs. The…

Assetto Corsa 神力科莎 [DLC 解锁] [Steam] [Windows]

Assetto Corsa 神力科莎 [DLC 解锁] [Steam] [Windows] 需要有游戏正版基础本体&#xff0c;安装路径不能带有中文&#xff0c;或其它非常规拉丁字符&#xff1b; DLC 版本 至最新全部 DLC 后续可能无法及时更新文章&#xff0c;具体最新版本见下载文件说明 DLC 解锁列表&…

【Java idea配置】

IntelliJ IDEA创建类时自动生成注释 /** * program: ${PROJECT_NAME} * * since: jdk1.8 * * description: ${description} * * author: ${USER} * * create: ${YEAR}-${MONTH}-${DAY} ${HOUR}:${MINUTE} **/自动导入和自动移除无用导入 idea彩色日志不生效 调试日志输出 在…

计算方法实验六 数值积分

【实验性质】综合性实验。 【实验目的】理解插值型积分法&#xff1b;掌握复化积分法算法。 【实验内容】 1对 &#xff0c;用复化梯形积分和变步长梯形积分求值&#xff08;截断误差不超过&#xff09;。 【理论基础】 积分在工程中有重要的应用&#xff0c;数值积分…

Webug4.0靶场通关笔记11- 第15关任意文件下载与第16关MySQL配置文件下载

目录 一、文件下载 二、第15关 任意文件下载 1.打开靶场 2.源码分析 3.渗透实战 三、第16关 MySQL配置文件下载 1.打开靶场 2.源码分析 3.渗透实战 &#xff08;1&#xff09;Windows系统 &#xff08;2&#xff09;Linux系统 四、渗透防御 一、文件下载 本文通过…

小土堆pytorch--tensorboard的使用

小土堆pytorch--tensorboard的使用 小土堆pytorch--tensorboard的使用0.介绍1.使用tensorboard绘制 y x 等简单函数1.1 相应的代码1.2 对上述代码的解释1.3 可能遇到的问题1.3.1 问题1.3.2 解决方法 2.使用tensorboard加载数据集中的图片2.1 相应代码2.2 对上述代码的解释2.2.…

大模型(LLMs)RAG 版面分析——文本分块面

大模型&#xff08;LLMs&#xff09;RAG 版面分析——文本分块面 一、为什么需要对文本分块&#xff1f; 二、能不能介绍一下常见的文本分块方法&#xff1f; 2.1 一般的文本分块方法 2.2 正则拆分的文本分块方法 2.3 Spacy Text Splitter 方法 2.4 基于 langchain 的 Cha…

解构区块链身份认证:从ID到零知识证明的实战指南

引言 在数字经济高速发展的今天&#xff0c;数字身份已成为个人与数字世界交互的核心凭证。传统中心化身份系统存在数据孤岛、隐私泄露、单点故障等痛点&#xff0c;而区块链技术凭借​​去中心化、不可篡改、可追溯​​的特性&#xff0c;为数字身份验证提供了革命性解决方案…

c#数据结构 线性表篇 非常用线性集合总结

本人能力有限,使用了一些Ai的结论,如有不足还请斧正 目录 1.HashSet <> Dictionary 2.SortedSet <>提供升序方法的List 3.ArrayList<>List 4.BitArray <> Bit[] array 5.StringCollection <>List 6.StringDictionary<>Dictionary 1…

爬虫管理平台-最新版本发布

TaskPyro 是什么&#xff1f; TaskPyro 是一个轻量级的 Python 任务调度平台&#xff0c;专注于提供简单易用的任务管理和爬虫调度解决方案。它能够帮助您轻松管理和调度 Python 任务&#xff0c;特别适合需要定时执行的爬虫任务和数据处理任务。 官方文档&#xff1a;https:/…

过采样处理

一、数据读取与初步观察 首先&#xff0c;使用pandas库读取信用卡交易数据集&#xff1a; data pd.read_csv(r"./creditcard.csv") print(data.head())通过head()方法查看数据集的前几行&#xff0c;初步了解数据的结构和内容。该数据集包含交易时间、交易金额、多…

潮乎盲盒商城系统全开源多级分销推广海报奖品兑换试玩概率OSS云存储多端源码

一、源码描述 这是一套潮乎盲盒商城源码&#xff0c;仿小叮当盲盒商城&#xff0c;后端Laravel框架前端uniappvue&#xff0c;前后端数据库分离&#xff0c;支持四端同步数据&#xff08;H5小程序等&#xff09;&#xff0c;测试环境: php7.4&#xff0c;mysql5.6&#xff0c;…

c++环境和vscode常用的一些有用插件

环境 WSL需要安装cmake 编译器g14 应该是包含了所有std:c23把好像包含部分c26 vscode 需要插件cmake vscode clangd 方便提示吧 File Watch 插件目的在保存.h/.cpp文件时候自动执行vscode 的cmake吧 error lens 方便每次显示错误和警告的提示懒得每次点击去看错误 Edit Sugge…

Spring 转发 form-data 文件上传请求时中文文件名乱码

Spring 转发 form-data 文件上传请求时中文文件名乱码 复现问题找原因解决问题参考 复现问题 后端有两个接口&#xff1a; /upload 是文件上传的接口。 /forward 是转发文件上传请求的接口。 RequestMapping RestController public class FileUploadController {/*** 直接调…

MySQL 8.4.4 安全升级指南:从漏洞修复到版本升级全流程解析

目录 二、升级前关键注意事项 1. 数据安全与备份 2. 版本兼容性与路径规划 三、分步升级操作流程 1. 环境预检与准备 2. 安装包部署 3. 强制升级组件 4. 验证与启动 一、背景与必要性 近期安全扫描发现生产环境的 MySQL 数据库存在多个高危漏洞(CVE 详情参见Oracle 官…

vulkanscenegraph显示倾斜模型(6.4)-多线程下的记录与提交

前言 上章深入分析了帧循环中呈现阶段的具体实现。本章将分析多线程下的记录与提交&#xff0c;进一步剖析vsg帧循环过程中的同步机制&#xff0c;并揭露信号量(VkSemaphore)和围栏(VkFence)以及vsg::FrameBlock与vsg::Barrier在其中的作用。 目录 1 信号量(VkSemaphore)、栅栏…

Python爬虫实战:获取扇贝单词数据并分析,为用户高效学习单词做参考

一、引言 随着互联网的迅猛发展,在线学习资源日益丰富多样。扇贝单词作为一款备受欢迎的在线英语学习平台,积累了海量的单词学习数据。借助 Python 强大的爬虫技术获取这些数据,并运用数据分析和机器学习方法进行深度挖掘,能够为用户量身定制更个性化、更高效的单词学习方…