zoujiajian пре 2 година
родитељ
комит
55c703c067

+ 4 - 0
netflix-dao/src/main/java/com/cyksj/mapper/GoodsWholesaleOrderDonMapper.java

@@ -2,6 +2,9 @@ package com.cyksj.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.cyksj.model.entity.GoodsWholesaleOrderDon;
+import org.apache.ibatis.annotations.Param;
+
+import java.util.List;
 
 /**
  * 项目名: yhlxj
@@ -10,4 +13,5 @@ import com.cyksj.model.entity.GoodsWholesaleOrderDon;
  * 创建时间:2024/8/26 12:24
  */
 public interface GoodsWholesaleOrderDonMapper extends BaseMapper<GoodsWholesaleOrderDon> {
+	Integer getUserWholesaleNumByUserIds(@Param("list") List<Long> userIdList);
 }

+ 26 - 0
netflix-dao/src/main/java/com/cyksj/model/dto/GoodsWholesaleUserDepositDto.java

@@ -0,0 +1,26 @@
+package com.cyksj.model.dto;
+
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: GoodsWholesaleUserDepositDto
+ * 创建者: JavaZou
+ * 创建时间:2024/8/26 14:06
+ */
+@Getter
+@Setter
+public class GoodsWholesaleUserDepositDto {
+	/**
+	 * 押金
+	 */
+	private BigDecimal deposit;
+
+	/**
+	 * 笔数
+	 */
+	private Integer num;
+}

+ 11 - 0
netflix-dao/src/main/java/com/cyksj/model/entity/GoodsWholesaleUserDeposit.java

@@ -44,6 +44,17 @@ public class GoodsWholesaleUserDeposit extends BaseEntity{
 	 */
 	private String operator;
 
+	/**
+	 * 商户id
+	 */
+	private String shopId;
+
+	private String orderNo;
+
+	private String refundOutNo;
+
+	private String transactionId;
+
 
 	@Getter
 	@AllArgsConstructor

+ 65 - 0
netflix-dao/src/main/java/com/cyksj/model/views/GoodsWholesaleOrderView.java

@@ -0,0 +1,65 @@
+package com.cyksj.model.views;
+
+import com.ejlchina.searcher.bean.DbField;
+import com.ejlchina.searcher.bean.SearchBean;
+import lombok.Getter;
+import lombok.Setter;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * 项目名: yhlxj
+ * 文件名: GoodsWholesaleOrderView
+ * 创建者: JavaZou
+ * 创建时间:2024/8/26 14:51
+ */
+@Getter
+@Setter
+@SearchBean(tables = "goods_wholesale_order_don go left join goods_don_sku sku on sku.id = go.sku_id" +
+		" left join goods_don g on g.id = go.goods_id " +
+		" left join user u on u.id = go.user_id")
+public class GoodsWholesaleOrderView {
+
+	@DbField("go.id")
+	private Long id;
+
+	@DbField("go.goods_id")
+	private Long goodsId;
+
+	@DbField("go.sku_id")
+	private Long skuId;
+
+	@DbField("g.title")
+	private String title;
+
+	@DbField("sku.spec_val")
+	private String specVal;
+
+	@DbField("go.num")
+	private Integer num;
+
+	@DbField("u.id")
+	private Long userId;
+
+	@DbField("u.nickname")
+	private String nickname;
+
+	@DbField("go.order_no")
+	private String orderNo;
+
+	@DbField("go.transaction_id")
+	private String transactionId;
+
+	@DbField("go.money")
+	private BigDecimal money;
+
+	@DbField("go.pay_time")
+	private Date payTime;
+
+	@DbField("go.source")
+	private String source;
+
+	@DbField("go.created_time")
+	private Date createdTime;
+}

+ 13 - 0
netflix-dao/src/main/resources/mapper/GoodsWholesaleOrderDonMapper.xml

@@ -0,0 +1,13 @@
+<?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.GoodsWholesaleOrderDonMapper">
+
+
+    <select id="getUserWholesaleNumByUserIds" resultType="java.lang.Integer">
+        select sum(num) from goods_wholesale_user_account where status not in ('close','noPayment','refund') user_id in
+        <foreach collection="list" item="item" open="(" separator="," close=")">
+            #{item}
+        </foreach>
+    </select>
+</mapper>

+ 5 - 0
netflix-service/src/main/java/com/cyksj/service/goods/GoodsWholesaleService.java

@@ -3,6 +3,8 @@ package com.cyksj.service.goods;
 import com.baomidou.mybatisplus.extension.service.IService;
 import com.cyksj.model.entity.GoodsWholesale;
 
+import java.util.Map;
+
 /**
  * 项目名: yhlxj
  * 文件名: GoodsWholesaleService
@@ -10,4 +12,7 @@ import com.cyksj.model.entity.GoodsWholesale;
  * 创建时间:2024/8/26 11:49
  */
 public interface GoodsWholesaleService extends IService<GoodsWholesale> {
+	void depositRefund(Long id, Long adminId) throws Exception;
+
+	void wxRefundNotify(Map<String, String> map) throws Exception;
 }

+ 73 - 0
netflix-service/src/main/java/com/cyksj/service/goods/impl/GoodsWholesaleServiceImpl.java

@@ -1,12 +1,26 @@
 package com.cyksj.service.goods.impl;
 
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.Codec;
+import com.cyksj.common.util.Xmls;
 import com.cyksj.mapper.GoodsWholesaleMapper;
+import com.cyksj.mapper.GoodsWholesaleUserDepositMapper;
+import com.cyksj.mapper.channel.ShopConfigMapper;
+import com.cyksj.mapper.manage.cms.CmsUserMapper;
 import com.cyksj.model.entity.GoodsWholesale;
+import com.cyksj.model.entity.GoodsWholesaleUserDeposit;
 import com.cyksj.service.goods.GoodsWholesaleService;
+import com.cyksj.service.order.OrderRefundService;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
+import java.util.Map;
+import java.util.Optional;
+
 /**
  * 项目名: yhlxj
  * 文件名: GoodsWholesaleServiceImpl
@@ -15,6 +29,65 @@ import org.springframework.stereotype.Service;
  */
 @Service
 @RequiredArgsConstructor
+@Slf4j
 public class GoodsWholesaleServiceImpl extends ServiceImpl<GoodsWholesaleMapper, GoodsWholesale> implements GoodsWholesaleService {
+	private final GoodsWholesaleUserDepositMapper goodsWholesaleUserDepositMapper;
+
+	private final OrderRefundService orderRefundService;
+
+	private final ShopConfigMapper shopConfigMapper;
+
+	private final CmsUserMapper cmsUserMapper;
+
+	@Override
+	public void depositRefund(Long id, Long adminId) throws Exception {
+		GoodsWholesaleUserDeposit goodsWholesaleUserDeposit = goodsWholesaleUserDepositMapper.selectById(id);
+		if (goodsWholesaleUserDeposit == null) {
+			throw BusinessRuntimeException.getInstance("记录不存在");
+		}
+		if (!goodsWholesaleUserDeposit.getStatus()) {
+			throw BusinessRuntimeException.getInstance("押金已退");
+		}
+
+		int update = goodsWholesaleUserDepositMapper.update(null, Wrappers.lambdaUpdate(GoodsWholesaleUserDeposit.class)
+				.set(GoodsWholesaleUserDeposit::getStatus, 0)
+				.eq(GoodsWholesaleUserDeposit::getId, id)
+				.eq(GoodsWholesaleUserDeposit::getStatus, 1));
+		if (update == 0) {
+			throw BusinessRuntimeException.getInstance("押金已退");
+		}
+		String outRefundNo = orderRefundService.refundWholesaleUserDeposit(goodsWholesaleUserDeposit);
+		goodsWholesaleUserDeposit.setRefundOutNo(outRefundNo);
+		Optional.ofNullable(cmsUserMapper.selectById(adminId)).ifPresent(cu -> goodsWholesaleUserDeposit.setOperator(cu.getNickname()));
+		goodsWholesaleUserDepositMapper.updateById(goodsWholesaleUserDeposit);
+	}
 
+	@Override
+	public void wxRefundNotify(Map<String, String> map) throws Exception {
+		// 对退款数据 解密
+		String md5Key = Codec.DoDigest.custom().setAlgorithm(Codec.DoDigest.Algorithm.MD5).setStringData(shopConfigMapper.getByAppId(map.get("mch_id")).getSecret()).toHexString();
+		String reqInfoXml = Codec.DoCipher.custom().setAlgorithm(Codec.DoCipher.Trans.AES_ECB_PKCS7Padding).setBase64Data(map.get("req_info")).setStringKey(md5Key).ofDecrypt().toText();
+		log.info("=== 退款数据解密: \n {}", reqInfoXml);
+		Map<String, String> reqInfoMap = Xmls.toMap(reqInfoXml);
+		// 退款失败
+		if (StrUtil.equals("CHANGE", reqInfoMap.get("refund_status"))) {
+			throw BusinessRuntimeException.getInstance("退款异常.");
+		} else if (StrUtil.equals("REFUNDCLOSE", reqInfoMap.get("refund_status"))) {
+			throw BusinessRuntimeException.getInstance("退款关闭.");
+		}
+		/* 此处处理业务逻辑 */
+		String outRefundNo = reqInfoMap.get("out_refund_no");
+		String transactionId = reqInfoMap.get("transaction_id");
+		String refundFee = reqInfoMap.get("refund_fee");
+		GoodsWholesaleUserDeposit userDeposit = goodsWholesaleUserDepositMapper.selectOne(Wrappers.lambdaQuery(GoodsWholesaleUserDeposit.class).eq(GoodsWholesaleUserDeposit::getTransactionId, transactionId).last("limit 1"));
+		if (userDeposit == null) {
+			log.error("退款回调,transactionId:{}订单不存在", transactionId);
+			return;
+		}
+		log.info("微信退款: 退款单号[{}] 退款成功.", outRefundNo);
+		if (userDeposit.getStatus()) {
+			userDeposit.setStatus(false);
+			goodsWholesaleUserDepositMapper.updateById(userDeposit);
+		}
+	}
 }

+ 1 - 0
netflix-service/src/main/java/com/cyksj/service/order/OrderRefundService.java

@@ -25,4 +25,5 @@ public interface OrderRefundService {
 	 */
 	void refundUpgradeRecord(UpgradeOrderDon orderDon, BigDecimal refundMoney, UpgradePackage upgradePackage, String operator, String outRefundNo, String refundType, String chatImg);
 
+	String refundWholesaleUserDeposit(GoodsWholesaleUserDeposit goodsWholesaleUserDeposit) throws Exception;
 }

+ 68 - 0
netflix-service/src/main/java/com/cyksj/service/order/impl/OrderRefundServiceImpl.java

@@ -145,6 +145,74 @@ public class OrderRefundServiceImpl implements OrderRefundService {
 		refundUpgradeOrderDonMapper.insert(refund);
 	}
 
+	@Override
+	public String refundWholesaleUserDeposit(GoodsWholesaleUserDeposit goodsWholesaleUserDeposit) throws Exception {
+		GoodsWholesaleUserDeposit.Type type = goodsWholesaleUserDeposit.getType();
+		if (type == GoodsWholesaleUserDeposit.Type.WeChat) {
+			return wxRefundWholesaleUserDeposit(goodsWholesaleUserDeposit);
+		}
+		if (type == GoodsWholesaleUserDeposit.Type.ALI_PAY) {
+			return zfbRefundWholesaleUserDeposit(goodsWholesaleUserDeposit);
+		}
+		return StrUtil.EMPTY;
+	}
+
+	private String zfbRefundWholesaleUserDeposit(GoodsWholesaleUserDeposit goodsWholesaleUserDeposit) throws Exception {
+		BigDecimal money = goodsWholesaleUserDeposit.getMoney();
+		if (money.compareTo(BigDecimal.ZERO) <=0){
+			return StrUtil.EMPTY;
+		}
+		AlipayTradeRefundResponse response;
+		//支付宝退款
+		AlipayClient alipayClient = AliPayClientFactory.getAlipayClient(Optional.ofNullable(goodsWholesaleUserDeposit.getShopId()).orElse(ShopConfig.ZFB_DEFAULT_APP_ID));
+		AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
+		AlipayTradeRefundModel model = new AlipayTradeRefundModel();
+		//支付宝交易号
+		model.setTradeNo(goodsWholesaleUserDeposit.getTransactionId());
+		model.setRefundAmount(money.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_EVEN).toString());
+		model.setQueryOptions(List.of("gmt_refund_pay"));
+		long time = System.currentTimeMillis();
+		//退款订单号
+		String outRefundNo = Codec.DoDigest.custom()
+				.setAlgorithm(Codec.DoDigest.Algorithm.MD5)
+				.setStringData(time + String.valueOf(goodsWholesaleUserDeposit.getId()))
+				.toHexString();
+		//退款请求号
+		model.setOutRequestNo(outRefundNo);
+		request.setBizModel(model);
+		String shopId = goodsWholesaleUserDeposit.getShopId();
+		ShopConfig shopConfig = shopConfigMapper.selectOne(Wrappers.lambdaQuery(ShopConfig.class).eq(ShopConfig::getAppId, goodsWholesaleUserDeposit.getShopId()));
+		if (shopId != null && shopConfig != null && shopConfig.getIsCert()) {
+			response = alipayClient.certificateExecute(request);
+		} else {
+			response = alipayClient.execute(request);
+		}
+		if (!response.isSuccess()) throw BusinessRuntimeException.getInstance("退款失败,e:{0}", response.getSubMsg());
+		return outRefundNo;
+	}
+
+	private String wxRefundWholesaleUserDeposit(GoodsWholesaleUserDeposit goodsWholesaleUserDeposit) throws Exception {
+		BigDecimal money = goodsWholesaleUserDeposit.getMoney();
+		if (money.compareTo(BigDecimal.ZERO) <=0){
+			return StrUtil.EMPTY;
+		}
+		long time = System.currentTimeMillis();
+		//退款订单号
+		String outRefundNo = Codec.DoDigest.custom()
+				.setAlgorithm(Codec.DoDigest.Algorithm.MD5)
+				.setStringData(time + String.valueOf(goodsWholesaleUserDeposit.getId()))
+				.toHexString();
+		log.info("退押金退款订单号:{}", outRefundNo);
+		weChatService.refundWxOrder(weChatConfig.getAppid(),
+				Optional.ofNullable(goodsWholesaleUserDeposit.getShopId()).orElse(ShopConfig.WX_DEFAULT_APP_ID),
+				goodsWholesaleUserDeposit.getTransactionId(),
+				outRefundNo,
+				money,
+				money,
+				weChatConfig.getDomain() + "manage/goods/whole/sale/deposit/refund/notify");
+		//记录退款信息
+		return outRefundNo;
+	}
 
 	public String wxRefund(OrderRefundReq refundReq, OrderDon orderDon) throws Exception {
 		if (orderDon.getMoney().compareTo(BigDecimal.ZERO) <=0){

+ 2 - 1
netflix-web/src/main/java/com/cyksj/config/SaTokenConfigure.java

@@ -41,7 +41,8 @@ public class SaTokenConfigure implements WebMvcConfigurer {
 						"/manage/account/put/account",
 						"/manage/coupon/get/classificationList",
 						"/manage/coupon/get/couponList",
-						"manage/order/wx/refund/notify"));
+						"manage/order/wx/refund/notify",
+						"/manage/goods/whole/sale/deposit/refund/notify"));
 		//注解权限校验
 		registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");
 	}

+ 62 - 3
netflix-web/src/main/java/com/cyksj/web/controller/manage/goods/GoodsDonWholeSaleController.java

@@ -1,5 +1,10 @@
 package com.cyksj.web.controller.manage.goods;
 
+import cn.dev33.satoken.stp.StpUtil;
+import cn.hutool.core.util.StrUtil;
+import com.cyksj.common.exception.BusinessRuntimeException;
+import com.cyksj.common.util.IoKit;
+import com.cyksj.common.util.Xmls;
 import com.cyksj.dto.Result;
 import com.cyksj.enums.GatewayResponse;
 import com.cyksj.mapper.GoodsWholesaleOrderDonMapper;
@@ -11,10 +16,15 @@ import com.ejlchina.searcher.BeanSearcher;
 import com.ejlchina.searcher.SearchResult;
 import com.ejlchina.searcher.util.MapUtils;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.OutputStream;
 import java.util.List;
+import java.util.Map;
 
 /**
  * 项目名: yhlxj
@@ -25,6 +35,7 @@ import java.util.List;
 @RequestMapping("/manage/goods/whole/sale")
 @RestController
 @RequiredArgsConstructor
+@Slf4j
 public class GoodsDonWholeSaleController {
 	private final GoodsWholesaleService goodsWholesaleService;
 
@@ -87,11 +98,59 @@ public class GoodsDonWholeSaleController {
 	@GetMapping("/get/deposit")
 	public Result<SearchResult<GoodsWholesaleUserDeposit>> getDeposit() {
 		SearchResult<GoodsWholesaleUserDeposit> search = beanSearcher.search(GoodsWholesaleUserDeposit.class, MapUtils.flatBuilder(request.getParameterMap()).build());
-		search.getDataList().forEach(data->{
+		search.getDataList().forEach(data -> {
 			List<Long> userIdList = userBindRelationService.getRelationUserIdList(data.getUserId(), null);
-			goodsWholesaleOrderDonMapper.getUserWholesa leNumByUserIds();
-			data.setTotal();
+			Integer total = goodsWholesaleOrderDonMapper.getUserWholesaleNumByUserIds(userIdList);
+			data.setTotal(total);
 		});
 		return GatewayResponse.SUCCESS.newBuilder().toResult(search);
 	}
+
+	/**
+	 * 押金退款
+	 */
+	@PostMapping("/deposit/refund/{id}")
+	public Result<String> depositRefund(@PathVariable Long id) throws Exception {
+		long adminId = StpUtil.getLoginIdAsLong();
+		goodsWholesaleService.depositRefund(id, adminId);
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 押金退款回调
+	 */
+	@PostMapping("/deposit/refund/notify")
+	public Result<String> depositRefundWxNotify(HttpServletResponse response) throws Exception {
+		String xml = IoKit.toString(request.getInputStream());
+		log.info("=== 押金退款成功的回调: \n {}", xml);
+
+		// 解析xml
+		Map<String, String> map = Xmls.toMap(xml);
+
+		// 退款失败
+		if (!StrUtil.equals("SUCCESS", map.get("return_code"))) {
+			throw BusinessRuntimeException.getInstance("退款失败.");
+		}
+
+		goodsWholesaleService.wxRefundNotify(map);
+
+		String toWechatXmlResponse = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>";
+
+		OutputStream out = null;
+		try {
+			out = response.getOutputStream();
+			IoKit.write(toWechatXmlResponse, out);
+		} catch (Throwable e) {
+			throw BusinessRuntimeException.getInstance(e == null ? null : e.getMessage());
+		} finally {
+			IoKit.close(out);
+		}
+		return GatewayResponse.SUCCESS.newBuilder().toResult();
+	}
+
+	/**
+	 * 批发订单
+	 */
+	@GetMapping("/get/order/list")
+	public Result<>
 }