瀏覽代碼

支付宝每日限单

zoujiajian 1 年之前
父節點
當前提交
8ddf928a95

+ 18 - 0
netflix-dao/src/main/java/com/cyksj/mapper/ZfbShopOrderDayLimitMapper.java

@@ -0,0 +1,18 @@
+package com.cyksj.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.cyksj.model.entity.ZfbShopOrderDayLimit;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: ZfbShopOrderDayLimitMapper
+ * 创建者: JavaZou
+ * 创建时间:2024/11/26 11:40
+ */
+public interface ZfbShopOrderDayLimitMapper extends BaseMapper<ZfbShopOrderDayLimit> {
+	Integer decrRemainOrdersById(Long id);
+
+	void incrDayLimitRemainOrders(String shopId);
+
+	void incrHourLimitRemainOrders(String shopId);
+}

+ 58 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/ZfbShopOrderDayLimit.java

@@ -0,0 +1,58 @@
+package com.cyksj.model.entity;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: ZfbShopOrderDayLimit
+ * 创建者: JavaZou
+ * 创建时间:2024/11/26 11:40
+ */
+@Getter
+@Setter
+public class ZfbShopOrderDayLimit extends BaseEntity {
+	/**
+	 * 支付宝appid
+	 */
+	private String appId;
+
+	/**
+	 * 每日限制单数
+	 */
+	private Integer dayLimitOrders;
+
+	/**
+	 * 每日剩余可支付单数
+	 */
+	private Integer dayRemainOrders;
+
+	/**
+	 * 每小时随机单数 (小时重置)
+	 */
+	private Integer hourRandOrders;
+
+	/**
+	 * 每小时限制单数
+	 */
+	private Integer hourLimitOrders;
+
+	/**
+	 * 每小时剩余可支付单数
+	 */
+	private Integer hourRemainOrders;
+
+	/**
+	 * 限制开始时间
+	 */
+	private Integer hourStart;
+
+	/**
+	 * 限制结束时间
+	 */
+	private Integer hourEnd;
+
+	private String remark;
+
+	private Boolean status;
+}

+ 16 - 0
netflix-dao/src/main/resources/mapper/ZfbShopOrderDayLimitMapper.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.cyksj.mapper.ZfbShopOrderDayLimitMapper">
+
+    <update id="decrRemainOrdersById">
+        update zfb_shop_order_day_limit set day_remain_orders = day_remain_orders - 1,hour_remain_orders = hour_remain_orders - 1 where id = #{id} and day_remain_orders > 0 and hour_remain_orders > 0
+    </update>
+
+    <update id="incrDayLimitRemainOrders">
+        update zfb_shop_order_day_limit set day_remain_orders = day_remain_orders + 1 where app_id = #{shopId} and day_remain_orders >= 0 and day_remain_orders &lt; day_limit_orders;
+    </update>
+
+    <update id="incrHourLimitRemainOrders">
+        update zfb_shop_order_day_limit set hour_remain_orders = hour_remain_orders + 1 where app_id = #{shopId} and hour_remain_orders >= 0 and hour_remain_orders &lt; hour_limit_orders;
+    </update>
+</mapper>

+ 10 - 0
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderDonServiceImpl.java

@@ -350,6 +350,8 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 
 	private final UserMirrorShopDistributeWaitingSendPointsMapper userMirrorShopDistributeWaitingSendPointsMapper;
 
+	private final ZfbShopOrderDayLimitMapper zfbShopOrderDayLimitMapper;
+
 	private final List<String> noChekGoodsTemp = List.of("Apple One", "Youtube", "Spotify");
 
 	@Transactional(rollbackFor = Throwable.class)
@@ -992,6 +994,14 @@ public class OrderDonServiceImpl extends ServiceImpl<OrderDonMapper, OrderDon> i
 				if (gcCash != null && gcCash.compareTo(BigDecimal.ZERO) > 0) {
 					giftCardOrderUseRecordService.returnGiftCardCash(orderDon.getId());
 				}
+				//恢复支付宝 每日限单数
+				if (DateTime.now().toDateStr().equals(DateUtil.format(orderDon.getCreatedTime(), "YYYY-MM-dd"))) {
+					zfbShopOrderDayLimitMapper.incrDayLimitRemainOrders(orderDon.getShopId());
+				}
+				//当前时间短可恢复 小时限单数
+				if (DateUtil.hour(orderDon.getCreatedTime(), true) == DateUtil.hour(DateTime.now(), true)) {
+					zfbShopOrderDayLimitMapper.incrHourLimitRemainOrders(orderDon.getShopId());
+				}
 			}
 		}
 	}

+ 33 - 2
netflix-service/src/main/java/com/cyksj/service/pay/impl/AliPayCommonServiceImpl.java

@@ -1,5 +1,6 @@
 package com.cyksj.service.pay.impl;
 
+import cn.hutool.core.date.DateTime;
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.StrUtil;
 import com.alipay.api.AlipayApiException;
@@ -38,6 +39,7 @@ import org.springframework.stereotype.Service;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.util.Date;
+import java.util.List;
 import java.util.Optional;
 
 /*
@@ -76,6 +78,8 @@ public class AliPayCommonServiceImpl implements AliPayCommonService {
 
 	private final UserMirrorShopFeeOrderDonMapper feeOrderDonMapper;
 
+	private final ZfbShopOrderDayLimitMapper zfbShopOrderDayLimitMapper;
+
 	private static final Sequence SEQUENCE = new Sequence(0);
 
 	@Override
@@ -116,14 +120,24 @@ public class AliPayCommonServiceImpl implements AliPayCommonService {
 		Long orderId = re.getOrderId();
 		String returnUrl = re.getReturnUrl();
 		OrderDon orderDon = orderDonMapper.selectById(orderId);
-		//获取支付宝默认商户
-		String appId = sysConfigService.getDefaultShopConfigByType("zfb");
+		String appId = null;
 		if (orderDon.getPopularizeId() != null) {
 			ShopConfig shopConfig = shopConfigMapper.getByChannelPopularizeId(orderDon.getPopularizeId(), "zfb");
 			if (shopConfig != null) {
 				appId = shopConfig.getAppId();
 			}
 		}
+		//排除推广链接
+		if (StrUtil.isEmpty(appId)) {
+			//获取每日订单限制 支付宝商户
+			ZfbShopOrderDayLimit dayLimit = getZfbShopOrdersDayLimit();
+			if (dayLimit == null) {
+				//获取支付宝默认商户
+				appId = sysConfigService.getDefaultShopConfigByType("zfb");
+			} else {
+				appId = dayLimit.getAppId();
+			}
+		}
 		if (OrderDon.Type.ALI_PAY != orderDon.getType()) {
 			orderDon.setType(OrderDon.Type.ALI_PAY);
 		}
@@ -187,6 +201,23 @@ public class AliPayCommonServiceImpl implements AliPayCommonService {
 		return aliPayParams;
 	}
 
+	private ZfbShopOrderDayLimit getZfbShopOrdersDayLimit() {
+		int hour = DateUtil.hour(DateTime.now(), true);
+		List<ZfbShopOrderDayLimit> orderDayLimits = zfbShopOrderDayLimitMapper.selectList(Wrappers.lambdaQuery(ZfbShopOrderDayLimit.class)
+				.le(ZfbShopOrderDayLimit::getHourStart, hour)
+				.ge(ZfbShopOrderDayLimit::getHourEnd, hour)
+				.gt(ZfbShopOrderDayLimit::getDayRemainOrders, 0)
+				.gt(ZfbShopOrderDayLimit::getHourRemainOrders, 0)
+				.eq(ZfbShopOrderDayLimit::getStatus, 1));
+		for (ZfbShopOrderDayLimit orderDayLimit : orderDayLimits) {
+			Integer update = zfbShopOrderDayLimitMapper.decrRemainOrdersById(orderDayLimit.getId());
+			if (update == 1) {
+				return orderDayLimit;
+			}
+		}
+		return null;
+	}
+
 	public AliPayParams builderPayParams(Long orderId, String orderNo, String shopId, BigDecimal money, Date orderCreatedTime, String subject, String productCode, String payMode, String notifyUrl, String returnUrl) throws Exception {
 		OrderPayBuilderDto orderPayBuilderDto = new OrderPayBuilderDto();
 		orderPayBuilderDto.setOrderId(orderId);

+ 47 - 0
netflix-web/src/main/java/com/cyksj/web/controller/manage/shop/ShopConfigController.java

@@ -1,12 +1,15 @@
 package com.cyksj.web.controller.manage.shop;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.util.RandomUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
+import com.cyksj.mapper.ZfbShopOrderDayLimitMapper;
 import com.cyksj.model.entity.Channel;
 import com.cyksj.model.entity.ShopConfig;
+import com.cyksj.model.entity.ZfbShopOrderDayLimit;
 import com.cyksj.model.request.ChannelPayReq;
 import com.cyksj.service.mange.channel.ChannelService;
 import com.cyksj.service.mange.shop.ShopConfigService;
@@ -18,6 +21,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import java.util.List;
 
 /**
  * @author chan
@@ -37,6 +41,8 @@ public class ShopConfigController {
 
     private final ChannelService channelService;
 
+    private final ZfbShopOrderDayLimitMapper zfbShopOrderDayLimitMapper;
+
     @GetMapping
     @SaCheckPermission("merchant:view")
     public Result<SearchResult<ShopConfig>> get(){
@@ -81,4 +87,45 @@ public class ShopConfigController {
                 .eq(StrUtil.isNotBlank(zfbShopId), Channel::getZfbShopId, zfbShopId));
         return GatewayResponse.SUCCESS.newBuilder().toResult();
     }
+
+    /**
+     * 支付宝每日限单数 配置列表
+     */
+    @GetMapping("/get/zfb/limit")
+    public Result<List<ZfbShopOrderDayLimit>> getZfbShopOrderDayLimit() {
+        List<ZfbShopOrderDayLimit> orderDayLimits = beanSearcher.searchAll(ZfbShopOrderDayLimit.class, MapUtils.flatBuilder(request.getParameterMap()).build());
+        return GatewayResponse.SUCCESS.newBuilder().toResult(orderDayLimits);
+    }
+
+    /**
+     * 编辑支付宝限制单数配置
+     */
+    @PutMapping("/update/zfb/limit")
+    public Result<String> updateZfbShopOrderDayLimit(ZfbShopOrderDayLimit zfbShopOrderDayLimit) {
+        zfbShopOrderDayLimitMapper.updateById(zfbShopOrderDayLimit);
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
+
+    /**
+     * 新增支付宝限制单数配置
+     */
+    @PostMapping("/save/zfb/limit")
+    public Result<String> saveZfbShopOrderDayLimit(@RequestBody ZfbShopOrderDayLimit zfbShopOrderDayLimit) {
+        Integer hourRandHourOrders = zfbShopOrderDayLimit.getHourRandOrders();
+        zfbShopOrderDayLimit.setHourLimitOrders(RandomUtil.randomInt(hourRandHourOrders) + 1);
+        zfbShopOrderDayLimit.setHourRemainOrders(zfbShopOrderDayLimit.getHourLimitOrders());
+        zfbShopOrderDayLimit.setDayRemainOrders(zfbShopOrderDayLimit.getDayLimitOrders());
+        zfbShopOrderDayLimitMapper.insert(zfbShopOrderDayLimit);
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
+
+
+    /**
+     * 新增支付宝限制单数配置
+     */
+    @DeleteMapping("/delete/zfb/limit/{id}")
+    public Result<String> saveZfbShopOrderDayLimit(@PathVariable Long id) {
+        zfbShopOrderDayLimitMapper.deleteById(id);
+        return GatewayResponse.SUCCESS.newBuilder().toResult();
+    }
 }

+ 102 - 0
netflix-web/src/main/java/com/cyksj/web/controller/zfb/ZfbAsyncNotifyInfoController.java

@@ -0,0 +1,102 @@
+package com.cyksj.web.controller.zfb;
+
+import cn.hutool.json.JSONObject;
+import com.alipay.api.internal.util.AlipaySignature;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.cyksj.common.util.Jsons;
+import com.cyksj.config.zfb.BaseZfbConfig;
+import com.cyksj.mapper.ZfbShopOrderDayLimitMapper;
+import com.cyksj.mapper.channel.ShopConfigMapper;
+import com.cyksj.model.entity.ZfbShopOrderDayLimit;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * 项目名: yhlxj2
+ * 文件名: ZfbAsyncNotifyInfoController
+ * 创建者: JavaZou
+ * 创建时间:2024/11/29 9:56
+ * 支付宝异步通知信息
+ */
+@Slf4j
+@RequestMapping("/alipay/async/notify")
+@RestController
+@RequiredArgsConstructor
+public class ZfbAsyncNotifyInfoController {
+	private final ShopConfigMapper shopConfigMapper;
+
+	private final ZfbShopOrderDayLimitMapper zfbShopOrderDayLimitMapper;
+
+	@PostMapping("/handle")
+	public String alipayAsyncNotify(HttpServletRequest request) throws Exception {
+		Map<String, String> params = convertRequestParamsToMap(request);
+		String appId = params.get("app_id");
+		log.info("支付宝异步信息回调信息:{}", params);
+		boolean signVerified = AlipaySignature.rsaCheckV1(params, shopConfigMapper.getByAppId(appId).getPublicKey(), BaseZfbConfig.charset, BaseZfbConfig.signType);
+		if (!signVerified) {
+			log.error("{}异步信息回调校验签名错误,自定义入参", appId);
+			return "fail";
+		}
+		String notifyType = params.get("notify_type");
+		//非商户变更通知回调 不处理
+		if (!"merchant_status_update".equals(notifyType)) {
+			return "success";
+		}
+		log.info("开始处理商户:{}状态变更回调信息", appId);
+		String bizContent = request.getParameter("biz_content");
+
+		JSONObject re = Jsons.parseObject(bizContent, JSONObject.class);
+
+		/**
+		 * NORMAL	正常状态,商户账户功能可用,无限制。
+		 * FROZEN	冻结状态,商户账户受到限制,可能是由于违规操作、账户风险等。
+		 * CLOSED	关闭状态,商户账户已被关闭,不可使用。
+		 * LIMITED	限制状态,商户账户部分功能受限。
+		 * AUDITING	审核中,商户信息或账户正在审核中,暂时不可用。
+		 * AUDIT_REJECT	审核未通过,商户信息审核失败,账户不可用。
+		 * TERMINATED	商户终止合作,账户已永久关闭。
+		 */
+		String status = re.getStr("status");
+		log.info("{}商户目前状态:{}", appId, status);
+		if (!"NORMAL".equals(status)) {
+			ZfbShopOrderDayLimit dayLimit = zfbShopOrderDayLimitMapper.selectOne(Wrappers.lambdaQuery(ZfbShopOrderDayLimit.class)
+					.eq(ZfbShopOrderDayLimit::getAppId, appId)
+					.last("limit 1"));
+			if (dayLimit != null) {
+				dayLimit.setStatus(false);
+				zfbShopOrderDayLimitMapper.updateById(dayLimit);
+			}
+		}
+		return "success";
+	}
+
+	private static Map<String, String> convertRequestParamsToMap(HttpServletRequest request) {
+		Map<String, String> retMap = new HashMap<>();
+		Set<Map.Entry<String, String[]>> entrySet = request.getParameterMap().entrySet();
+		for (Map.Entry<String, String[]> entry : entrySet) {
+			String name = entry.getKey();
+			String[] values = entry.getValue();
+			int valLen = values.length;
+			if (valLen == 1) {
+				retMap.put(name, values[0]);
+			} else if (valLen > 1) {
+				StringBuilder sb = new StringBuilder();
+				for (String val : values) {
+					sb.append(",").append(val);
+				}
+				retMap.put(name, sb.substring(1));
+			} else {
+				retMap.put(name, "");
+			}
+		}
+		return retMap;
+	}
+}