From 7e9e5e164b5ef65ef9094354f0273c72f555e9d8 Mon Sep 17 00:00:00 2001
From: huangqizhen <15552608129@163.com>
Date: Sun, 23 Nov 2025 16:59:18 +0800
Subject: [PATCH] =?UTF-8?q?11.23=20=E6=B6=88=E6=81=AF=E6=8E=A8=E9=80=81?=
=?UTF-8?q?=E5=AE=8C=E5=96=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
pom.xml | 6 +++
.../com/example/demo/config/RateLimitUtil.java | 39 +++++++++++++++
.../demo/controller/cash/CashRefundController.java | 58 ++++++++++++++++++++--
.../serviceImpl/cash/CashAuditServiceImpl.java | 20 ++++----
.../cash/CashCollectionServiceImpl.java | 2 +-
.../serviceImpl/cash/CashRefundServiceImpl.java | 28 +++++++----
6 files changed, 130 insertions(+), 23 deletions(-)
create mode 100644 src/main/java/com/example/demo/config/RateLimitUtil.java
diff --git a/pom.xml b/pom.xml
index a329465..26116b3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -70,6 +70,12 @@
hutool-all
5.8.24
+
+
+ com.google.guava
+ guava
+ 32.1.3-jre
+
org.springframework.boot
spring-boot-starter-web
diff --git a/src/main/java/com/example/demo/config/RateLimitUtil.java b/src/main/java/com/example/demo/config/RateLimitUtil.java
new file mode 100644
index 0000000..12453b5
--- /dev/null
+++ b/src/main/java/com/example/demo/config/RateLimitUtil.java
@@ -0,0 +1,39 @@
+package com.example.demo.config;
+
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * 本地限流工具类(基于Guava Cache)
+ */
+public class RateLimitUtil {
+ // 缓存:key=限流标识(用户ID/IP),value=占位符(无实际意义)
+ private static final Cache RATE_LIMIT_CACHE = CacheBuilder.newBuilder()
+ .expireAfterWrite(3, TimeUnit.SECONDS) // 3秒后自动过期(时间窗口)
+ .maximumSize(5000) // 最大缓存容量(防止内存溢出,根据用户量调整)
+ .build();
+
+ /**
+ * 判断是否允许请求
+ * @param key 限流唯一标识(如用户ID、IP)
+ * @return true=允许请求,false=限流中
+ */
+ public static boolean isAllowed(String key) {
+ // 1. 尝试从缓存获取key,存在则说明3秒内已请求过(限流)
+ if (RATE_LIMIT_CACHE.getIfPresent(key) != null) {
+ return false;
+ }
+ // 2. 缓存中不存在,存入缓存(占位符用new Object()即可)
+ RATE_LIMIT_CACHE.put(key, new Object());
+ return true;
+ }
+
+ /**
+ * 手动移除限流标识(如接口执行失败时,释放限流)
+ * @param key 限流唯一标识
+ */
+ public static void removeKey(String key) {
+ RATE_LIMIT_CACHE.invalidate(key);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/example/demo/controller/cash/CashRefundController.java b/src/main/java/com/example/demo/controller/cash/CashRefundController.java
index d3d0281..6f0339e 100644
--- a/src/main/java/com/example/demo/controller/cash/CashRefundController.java
+++ b/src/main/java/com/example/demo/controller/cash/CashRefundController.java
@@ -2,6 +2,7 @@ package com.example.demo.controller.cash;
import com.example.demo.Util.JWTUtil;
+import com.example.demo.config.RateLimitUtil;
import com.example.demo.domain.entity.Admin;
import com.example.demo.domain.vo.cash.CashRecordDTO;
import com.example.demo.domain.vo.cash.CashRecordDone;
@@ -26,6 +27,7 @@ import org.springframework.web.context.request.ServletRequestAttributes;
import java.util.Arrays;
import java.util.List;
+import java.util.Objects;
/**
* @program: GOLD
@@ -196,9 +198,38 @@ public class CashRefundController {
}
}
@PostMapping("/finalReview")
- public Result finalReview(@RequestBody CashRecordDone cashRecordDone) {
- return Result.success(refundService.finalreview(cashRecordDone));
- }
+ public Result finalReview(@RequestBody CashRecordDone cashRecordDone,HttpServletRequest request) {
+ {
+ // --------------- 限流逻辑开始 ---------------
+ String limitKey = null;
+ try {
+ // 1. 优先用「用户ID」作为限流标识(从token解析,精准限流)
+ HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
+ String token = req.getHeader("token");
+ Admin admin = (Admin) JWTUtil.getUserDetailsList(String.valueOf(token), Admin.class);
+ if (admin != null && admin.getId() != null) {
+ limitKey = "finalReview_" + admin.getId(); // 格式:接口名_用户ID
+ }
+ } catch (Exception e) {
+ // token解析失败(用户未登录),降级用「IP地址」限流
+ limitKey = "finalReview_" + getIpAddress(request); // 格式:接口名_IP
+ }
+
+ // 2. 校验限流:3秒内同一key不允许重复请求
+ if (Objects.isNull(limitKey) || !RateLimitUtil.isAllowed(limitKey)) {
+ return Result.error("3秒内只能请求一次,请稍后再试"); // 限流提示
+ }
+ // --------------- 限流逻辑结束 ---------------
+
+ try {
+ // 原有业务逻辑:执行最终审核
+ return Result.success(refundService.finalreview(cashRecordDone));
+ } catch (Exception e) {
+ // 接口执行失败时,移除限流标识(允许用户重新尝试)
+ RateLimitUtil.removeKey(limitKey);
+ return Result.error("审核失败:" + e.getMessage());
+ }
+ }}
@PostMapping("/executor")
public Result executor(@RequestBody CashRecordDone cashRecordDone) throws Exception {
try {
@@ -239,4 +270,25 @@ public class CashRefundController {
public Result ceshi() {
return Result.success("测试消息");
}
+
+ /**
+ * 辅助方法:获取用户真实IP(处理反向代理/负载均衡场景)
+ */
+ private String getIpAddress(HttpServletRequest request) {
+ String ip = request.getHeader("x-forwarded-for");
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getHeader("WL-Proxy-Client-IP");
+ }
+ if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
+ ip = request.getRemoteAddr();
+ }
+ // 多IP场景(如多层代理),取第一个非unknown的IP
+ if (ip != null && ip.contains(",")) {
+ ip = ip.split(",")[0].trim();
+ }
+ return ip;
+ }
}
diff --git a/src/main/java/com/example/demo/serviceImpl/cash/CashAuditServiceImpl.java b/src/main/java/com/example/demo/serviceImpl/cash/CashAuditServiceImpl.java
index 69f2a40..328c554 100644
--- a/src/main/java/com/example/demo/serviceImpl/cash/CashAuditServiceImpl.java
+++ b/src/main/java/com/example/demo/serviceImpl/cash/CashAuditServiceImpl.java
@@ -6,6 +6,7 @@ import com.example.demo.domain.entity.CashRecord;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.entity.UserGoldRecord;
import com.example.demo.domain.vo.cash.CashCollectionMessage;
+import com.example.demo.domain.vo.coin.Messages;
import com.example.demo.mapper.cash.CashAuditMapper;
import com.example.demo.mapper.cash.CashCollectionMapper;
import com.example.demo.mapper.coin.AuditMapper;
@@ -130,17 +131,16 @@ public class CashAuditServiceImpl implements CashAuditService {
//更新订单
cashAuditMapper.updateOrder(updateOrder);
// 创建消息队列,用于发送审核结果通知
- CashCollectionMessage message = new CashCollectionMessage();
- message.setId(order.getId());
- message.setOrderCode(orderCode);
+ Messages message = new Messages();
+ message.setJwcode(order.getJwcode());
+ message.setName(order.getName());
message.setStatus(updateOrder.getStatus());
- message.setStatusDescription(action == 1 ? "线下财务审核通过待填手续费" : "线下财务审核驳回");
- message.setMessage(action == 1 ? "收款订单审核通过" : "收款订单审核驳回");
- message.setSubmitterId(order.getSubmitterId());
- message.setAuditId(auditId);
- message.setAuditName(auditName);
- message.setTimestamp(LocalDateTime.now());
-
+ message.setDesc(order.getJwcode() + action==1?"收款记录需补充手续费,前往填写":"现金收款申请已被驳回,前往查看驳回理");
+ message.setTitle(action==1?"收款订单审核通过":"收款订单审核驳回");
+ message.setType(1);
+ message.setTypeId(order.getId());
+ message.setMarket(Integer.valueOf(order.getMarket()));
+ rabbitTemplate.convertAndSend(RabbitMQConfig.CASH_COLLECTION_EXCHANGE, "cash.collection.save", message);
// 根据审核结果发送不同的消息
if (action == 1) {
// 发送审核通过消息
diff --git a/src/main/java/com/example/demo/serviceImpl/cash/CashCollectionServiceImpl.java b/src/main/java/com/example/demo/serviceImpl/cash/CashCollectionServiceImpl.java
index 891dd33..53628c0 100644
--- a/src/main/java/com/example/demo/serviceImpl/cash/CashCollectionServiceImpl.java
+++ b/src/main/java/com/example/demo/serviceImpl/cash/CashCollectionServiceImpl.java
@@ -132,7 +132,7 @@ public class CashCollectionServiceImpl implements CashCollectionService {
message.setJwcode(cashRecord.getJwcode());
message.setName(cashRecord.getName());
message.setStatus(cashRecord.getStatus());
- message.setDesc(cashRecord.getJwcode()+"用户有条收款订单需审核");
+ message.setDesc(cashRecord.getJwcode()+"用户的现金收款申请待审核,请前往审核");
message.setTitle("现金收款--新增收款");
message.setType(1);
message.setTypeId(cashRecord.getId());
diff --git a/src/main/java/com/example/demo/serviceImpl/cash/CashRefundServiceImpl.java b/src/main/java/com/example/demo/serviceImpl/cash/CashRefundServiceImpl.java
index 1cad9dd..cf01b1b 100644
--- a/src/main/java/com/example/demo/serviceImpl/cash/CashRefundServiceImpl.java
+++ b/src/main/java/com/example/demo/serviceImpl/cash/CashRefundServiceImpl.java
@@ -204,7 +204,7 @@ public class CashRefundServiceImpl implements RefundService {
message.setJwcode(cashRecordRefund.getJwcode());
message.setName(cashRecordRefund.getName());
message.setStatus(cashRecordRefund.getStatus());
- message.setDesc(cashRecordRefund.getJwcode()+"用户有条退款订单需审核");
+ message.setDesc(cashRecordRefund.getJwcode()+"用户的客服退款申请待审核,前往处理");
message.setTitle("现金退款--新增退款");
message.setType(0);
message.setTypeId(cashRecordRefund.getId());
@@ -239,7 +239,21 @@ public class CashRefundServiceImpl implements RefundService {
cashRecordDone.setNewRefundFree(BigDecimal.valueOf(0));
}
int result = cashRefundMapper.update(cashRecordDone);
- return (result > 0 ? Result.success("提交成功") : Result.error("提交失败")).getCode();
+ CashRecordDTO cashRecordDTO = cashRefundMapper.selectById(cashRecordDone.getId());
+ if (result > 0) {
+ // 发送审核消息
+ Messages message = new Messages();
+ message.setJwcode(cashRecordDTO.getJwcode());
+ message.setName(cashRecordDTO.getName());
+ message.setStatus(cashRecordDTO.getStatus());
+ message.setDesc(cashRecordDTO.getJwcode() + "用户的退款申请待审核,前往处理");
+ message.setTitle("现金退款--当地退款审核(编辑后提交)");
+ message.setType(1);
+ message.setTypeId(cashRecordDTO.getId());
+ message.setMarket(cashRecordDTO.getMarket());
+ rabbitTemplate.convertAndSend(RabbitMQConfig.CASH_REFUND_EXCHANGE, "cash.refund.save", message);
+ }
+ return (result > 0 ? Result.success("提交成功") : Result.error("提交失败")).getCode();
}
@Override
@@ -269,15 +283,14 @@ CashRecordDone cashRecordDone1 = new CashRecordDone();
message.setJwcode(cashRecordDTO.getJwcode());
message.setName(cashRecordDTO.getName());
message.setStatus(cashRecordDTO.getStatus());
- message.setDesc(cashRecordDTO.getJwcode()+"用户有条退款订单需审核");
+ message.setDesc(cashRecordDTO.getJwcode()+cashRecordDTO.getStatus()!=12|| cashRecordDTO.getStatus()!=22?"用户的退款申请待审核,前往处理":"用户的现金退款申请已被驳回,前往查看详情");
message.setTitle("现金退款--当地退款审核");
message.setType(1);
message.setTypeId(cashRecordDTO.getId());
message.setMarket(cashRecordDTO.getMarket());
- if (cashRecordDTO.getStatus() != 12 || cashRecordDTO.getStatus() != 22) {
rabbitTemplate.convertAndSend(RabbitMQConfig.CASH_REFUND_EXCHANGE, "cash.refund.save", message);
- }
+
}
return (result > 0 ? Result.success("提交成功") : Result.error("提交失败")).getCode();
@@ -378,15 +391,12 @@ CashRecordDone cashRecordDone1 = new CashRecordDone();
message.setJwcode(cashRecordDTO.getJwcode());
message.setName(cashRecordDTO.getName());
message.setStatus(cashRecordDTO.getStatus());
- message.setDesc(cashRecordDTO.getJwcode()+"用户有条退款订单需审核");
+ message.setDesc(cashRecordDTO.getJwcode()+cashRecordDTO.getStatus()!=32?"用户的退款申请待审核,前往处理":"用户的现金退款申请已被驳回,前往查看详情");
message.setTitle("现金退款--执行人退款提交");
message.setType(1);
message.setTypeId(cashRecordDTO.getId());
message.setMarket(cashRecordDTO.getMarket());
- if (cashRecordDTO.getStatus() != 32) {
- rabbitTemplate.convertAndSend(RabbitMQConfig.CASH_REFUND_EXCHANGE, "cash.refund.save", message);
- }
}
return (result > 0 ? Result.success("提交成功") : Result.error("提交失败")).getCode();
}