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/domain/entity/User.java b/src/main/java/com/example/demo/domain/entity/User.java index 1dda6e9..1fee4db 100644 --- a/src/main/java/com/example/demo/domain/entity/User.java +++ b/src/main/java/com/example/demo/domain/entity/User.java @@ -28,6 +28,7 @@ public class User implements Serializable { private String name; // 客户姓名 @ExcelProperty("所属地区") private String market; // 所属地区 + @ExcelIgnore private String marketName; // 所属地区 @ExcelIgnore private BigDecimal sumPermanentGold; // 历史永久金币 diff --git a/src/main/java/com/example/demo/domain/vo/cash/CashCollection.java b/src/main/java/com/example/demo/domain/vo/cash/CashCollection.java index 322615d..7d06b5e 100644 --- a/src/main/java/com/example/demo/domain/vo/cash/CashCollection.java +++ b/src/main/java/com/example/demo/domain/vo/cash/CashCollection.java @@ -30,6 +30,7 @@ public class CashCollection implements Serializable { @ExcelProperty("序号") private Integer id; //订单信息 + @ExcelIgnore private Integer orderType; // 订单类型:1-收款,2-退款 @ExcelProperty("精网号") private Integer jwcode; // 精网号 @@ -68,7 +69,7 @@ public class CashCollection implements Serializable { private BigDecimal receivedAmount; // 到账金额 @ExcelProperty("手续费") private BigDecimal handlingCharge; // 手续费 - @ExcelProperty("到账币种") + @ExcelProperty("到账地区") private String receivedMarket; //到账地区 // 支付信息 @ExcelProperty("支付方式") @@ -90,7 +91,7 @@ public class CashCollection implements Serializable { private String submitterName; // 提交人 姓名 @ExcelProperty("转账凭证") private String voucher; // 转账凭证 - @ExcelIgnore + @ExcelProperty("备注") private String remark; // 备注 @ExcelIgnore private String receivedRemark; //到账备注 diff --git a/src/main/java/com/example/demo/domain/vo/cash/CashRecordDone.java b/src/main/java/com/example/demo/domain/vo/cash/CashRecordDone.java index 170f203..41215e6 100644 --- a/src/main/java/com/example/demo/domain/vo/cash/CashRecordDone.java +++ b/src/main/java/com/example/demo/domain/vo/cash/CashRecordDone.java @@ -119,6 +119,9 @@ public class CashRecordDone { @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") private Date eTime; // 结束时间 private Integer relatedId; + private BigDecimal NewRefundGold; + private BigDecimal NewRefundFree; + private Integer adminId; } \ No newline at end of file diff --git a/src/main/java/com/example/demo/domain/vo/coin/GoldUser.java b/src/main/java/com/example/demo/domain/vo/coin/GoldUser.java index 8fa5110..515f82d 100644 --- a/src/main/java/com/example/demo/domain/vo/coin/GoldUser.java +++ b/src/main/java/com/example/demo/domain/vo/coin/GoldUser.java @@ -1,5 +1,6 @@ package com.example.demo.domain.vo.coin; +import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelProperty; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; @@ -56,5 +57,6 @@ public class GoldUser { @ExcelProperty("首充日期") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "Asia/Shanghai") private Date firstRecharge; // 首充日期 + @ExcelIgnore private List markets; // 地区列表 } diff --git a/src/main/java/com/example/demo/domain/vo/coin/RechargeActivity.java b/src/main/java/com/example/demo/domain/vo/coin/RechargeActivity.java index 97a2ae3..763eb2e 100644 --- a/src/main/java/com/example/demo/domain/vo/coin/RechargeActivity.java +++ b/src/main/java/com/example/demo/domain/vo/coin/RechargeActivity.java @@ -1,5 +1,6 @@ package com.example.demo.domain.vo.coin; +import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelProperty; import com.fasterxml.jackson.annotation.JsonFormat; import lombok.AllArgsConstructor; @@ -30,7 +31,7 @@ public class RechargeActivity { @ExcelProperty("业绩归属地") private String businessBelong; // 业绩归属地 - + @ExcelIgnore private String area; // 地区 @ExcelProperty("地区") diff --git a/src/main/java/com/example/demo/mapper/cash/CashCollectionMapper.java b/src/main/java/com/example/demo/mapper/cash/CashCollectionMapper.java index ecbb39f..976a803 100644 --- a/src/main/java/com/example/demo/mapper/cash/CashCollectionMapper.java +++ b/src/main/java/com/example/demo/mapper/cash/CashCollectionMapper.java @@ -51,7 +51,7 @@ public interface CashCollectionMapper { //根据精网号获取市场名 String getMarketNameByJwcode(Integer jwcode); //获取收款活动列表 - List getActivityList(); + List getActivityList(@Param("now")LocalDateTime now); //查找未同步的订单 ListgetUnSync(@Param("size")int size); //给同步过去的gOrder设置同步状态 diff --git a/src/main/java/com/example/demo/serviceImpl/bean/BeanConsumeServiceImpl.java b/src/main/java/com/example/demo/serviceImpl/bean/BeanConsumeServiceImpl.java index 6b50165..44cb3e4 100644 --- a/src/main/java/com/example/demo/serviceImpl/bean/BeanConsumeServiceImpl.java +++ b/src/main/java/com/example/demo/serviceImpl/bean/BeanConsumeServiceImpl.java @@ -158,7 +158,7 @@ public class BeanConsumeServiceImpl implements BeanConsumeService { //筛选查询直播消费 @Override public Object selectLiveBy(Integer pageNum, Integer pageSize, BeanConsumeLive beanConsumeLive) { - PageHelper.startPage(pageNum, pageSize); + String channel=roleMapper.getChannel(beanConsumeLive.getRoleId()); if (channel==null){ return "角色频道有误"; @@ -166,7 +166,7 @@ public class BeanConsumeServiceImpl implements BeanConsumeService { if (!channel.equals("全部")){ beanConsumeLive.setLiveChannel(channel); } - + PageHelper.startPage(pageNum, pageSize); List beanConsumeLives = liveMapper.selectLiveBy(beanConsumeLive); //int total = liveMapper.selectLiveCount(beanConsumeLive); return new PageInfo<>(beanConsumeLives); @@ -174,7 +174,7 @@ public class BeanConsumeServiceImpl implements BeanConsumeService { //筛选查询铁粉消费 @Override public Object selectFanBy(Integer pageNum, Integer pageSize, BeanConsumeFan beanConsumeFan) { - PageHelper.startPage(pageNum, pageSize); + String channel=roleMapper.getChannel(beanConsumeFan.getRoleId()); if (channel==null){ return "角色频道有误"; @@ -182,6 +182,7 @@ public class BeanConsumeServiceImpl implements BeanConsumeService { if (!channel.equals("全部")){ beanConsumeFan.setChannel(channel); } + PageHelper.startPage(pageNum, pageSize); List beanConsumeFans = beanConsumeMapper.selectFanBy(beanConsumeFan); return new PageInfo<>(beanConsumeFans); } 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..6e0e9bc 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()); @@ -326,7 +326,8 @@ public User getNameAndMarket(Integer jwcode) { //获取收款活动列表 @Override public List getActivityList() { - return cashCollectionMapper.getActivityList(); + LocalDateTime now = LocalDateTime.now(); + return cashCollectionMapper.getActivityList(now); } //同步g_order订单到cash_record表 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 bce68b5..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()); @@ -232,8 +232,28 @@ public class CashRefundServiceImpl implements RefundService { if (cashRecordDone.getRefundReason()== null) { throw new RuntimeException("请填写退款理由"); } + if(cashRecordDone.getNewRefundGold()== null){ + cashRecordDone.setNewRefundGold(BigDecimal.valueOf(0)); + } + if(cashRecordDone.getNewRefundFree()== null){ + 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 @@ -263,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(); @@ -346,7 +365,7 @@ CashRecordDone cashRecordDone1 = new CashRecordDone(); userGoldRecord.setGoodsName(cashRecordDone.getGoodsName()); userGoldRecord.setPayPlatform("金币系统"); userGoldRecord.setRemark(cashRecordDone.getRemark()); - userGoldRecord.setAdminId(cashRecordDone.getAuditId()); + userGoldRecord.setAdminId(cashRecordDone.getAdminId()); userGoldRecord.setAuditStatus(1); userGoldRecord.setTaskGold(0); userGoldRecord.setCreateTime(new Date()); @@ -372,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(); } diff --git a/src/main/java/com/example/demo/serviceImpl/cash/MessageServiceImpl.java b/src/main/java/com/example/demo/serviceImpl/cash/MessageServiceImpl.java index cffadd4..ff3e85a 100644 --- a/src/main/java/com/example/demo/serviceImpl/cash/MessageServiceImpl.java +++ b/src/main/java/com/example/demo/serviceImpl/cash/MessageServiceImpl.java @@ -24,6 +24,9 @@ public class MessageServiceImpl implements MessageService { private MessageMapper messageMapper; @Override public List getMessage(List markets, List status) { + if(status== null|| status.size()==0){ + status.add(99); + } return messageMapper.getMessage(markets, status) ; } diff --git a/src/main/resources/cashMapper/CashCollectionMapper.xml b/src/main/resources/cashMapper/CashCollectionMapper.xml index ab2423c..61119b2 100644 --- a/src/main/resources/cashMapper/CashCollectionMapper.xml +++ b/src/main/resources/cashMapper/CashCollectionMapper.xml @@ -222,7 +222,7 @@ select ra.id,ra.activity_name,ra.business_belong,m.name as area,ra.status from recharge_activity ra left join market m on m.id=ra.area - where ra.flag=1 + where ra.flag=1 and ra.status=1 and #{now} between start_time and end_time