添加测试controller;调整库存调用;添加分账

This commit is contained in:
DB 2023-11-02 16:31:45 +08:00
parent 068b90ed89
commit 63bacf0cd8
12 changed files with 158 additions and 62 deletions

View File

@ -42,7 +42,7 @@ public class AsyncScheduledTaskConfig {
//允许线程的空闲时间30秒 //允许线程的空闲时间30秒
executor.setKeepAliveSeconds(config.getKeepAliveSeconds()); executor.setKeepAliveSeconds(config.getKeepAliveSeconds());
//设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
executor.setWaitForTasksToCompleteOnShutdown(true); executor.setWaitForTasksToCompleteOnShutdown(false);
//设置线程池中任务的等待时间如果超过这个时候还没有销毁就强制销毁以确保应用最后能够被关闭而不是阻塞住 //设置线程池中任务的等待时间如果超过这个时候还没有销毁就强制销毁以确保应用最后能够被关闭而不是阻塞住
executor.setAwaitTerminationSeconds(config.getAwaitTerminationSeconds()); executor.setAwaitTerminationSeconds(config.getAwaitTerminationSeconds());
/** /**

View File

@ -4,7 +4,7 @@ cpop:
profile: E:/Cpop/uploadPath profile: E:/Cpop/uploadPath
jwt: jwt:
#白名单 #白名单
whiteList: /login,/miniLogin,/wxPay/callback/notify/**,/mini/brand/jamboxBrandIsInMall/*,/profile/**,/doc.html,/webjars/**,/favicon.ico,/v2/api-docs/**,/swagger-resources whiteList: /login,/miniLogin,/test/**,/wxPay/callback/notify/**,/mini/brand/jamboxBrandIsInMall/*,/profile/**,/doc.html,/webjars/**,/favicon.ico,/v2/api-docs/**,/swagger-resources
gateway: gateway:
rsa-keypair: rsa-keypair:
# 公钥文件 # 公钥文件
@ -85,11 +85,17 @@ knife4j:
api-rule-resources: api-rule-resources:
- com.cpop.mall.business.controller.backstage - com.cpop.mall.business.controller.backstage
Mall-Mini: Mall-Mini:
#后台 #小程序
group-name: Mall-Mini group-name: Mall-Mini
api-rule: package api-rule: package
api-rule-resources: api-rule-resources:
- com.cpop.mall.business.controller.mini - com.cpop.mall.business.controller.mini
Mall-Test:
#测试
group-name: Mall-Test
api-rule: package
api-rule-resources:
- com.cpop.mall.business.controller.test
#系统 #系统
System: System:
group-name: System group-name: System

View File

@ -4,7 +4,7 @@ cpop:
profile: /root/cpop-union/cpop-mall/upload profile: /root/cpop-union/cpop-mall/upload
jwt: jwt:
#白名单 #白名单
whiteList: /login,/miniLogin,/wxPay/callback/notify/**,/profile/** whiteList: /login,/miniLogin,/mini/brand/jamboxBrandIsInMall/*,/wxPay/callback/notify/**,/profile/**
#拦截 #拦截
gateway: gateway:
rsa-keypair: rsa-keypair:

View File

@ -4,7 +4,7 @@ cpop:
profile: /root/cpop-union/cpop-mall/upload profile: /root/cpop-union/cpop-mall/upload
jwt: jwt:
#白名单 #白名单
whiteList: /login,/miniLogin,/wxPay/callback/notify/**,/profile/**,/doc.html,/webjars/**,/favicon.ico,/v2/api-docs/**,/swagger-resources,/wxOpen/receiveTicket,/wxOpen/*/callback,/wxOpen/bindOpenAccount/*,/wxCp/portal/* whiteList: /login,/miniLogin,/mini/brand/jamboxBrandIsInMall/*,/wxPay/callback/notify/**,/profile/**,/doc.html,/webjars/**,/favicon.ico,/v2/api-docs/**,/swagger-resources,/wxOpen/receiveTicket,/wxOpen/*/callback,/wxOpen/bindOpenAccount/*,/wxCp/portal/*
#拦截 #拦截
gateway: gateway:
rsa-keypair: rsa-keypair:

View File

@ -31,7 +31,7 @@ spring:
max-file-size: 1024MB max-file-size: 1024MB
max-request-size: 300MB max-request-size: 300MB
profiles: profiles:
active: local,core,mall,system active: dev,core,mall,system
datasource: datasource:
type: com.zaxxer.hikari.HikariDataSource type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver driver-class-name: com.mysql.cj.jdbc.Driver
@ -106,7 +106,7 @@ task:
# 设置空闲线程存活时间(秒 # 设置空闲线程存活时间(秒
keepAliveSeconds: 300 keepAliveSeconds: 300
# 设置队列容量 # 设置队列容量
queueCapacity: 100 queueCapacity: 10240
# 设置线程名称前缀 # 设置线程名称前缀
threadNamePrefix: "Cpop-Mall-AsyncNotify-" threadNamePrefix: "Cpop-Mall-AsyncNotify-"
# 设置线程池等待终止时间(秒) # 设置线程池等待终止时间(秒)
@ -131,11 +131,11 @@ wx:
#微信支付 #微信支付
pay: pay:
#微信公众号或者小程序等的appid #微信公众号或者小程序等的appid
appId: wx1eb0e5fb7dac3c05 appId: wx20853d18c455e874
#微信支付商户号 #微信支付商户号
mchId: 1618884922 mchId: 1618884922
#微信支付商户密钥 #微信支付商户密钥
mchKey: JamBox20230919174000000000000002 mchKey: JamBox20230919174000000000000002
apiV3Key: JamBox20230919174000000000000002 apiV3Key: JamBox20230919174000000000000002
#服务商账号 #分账服务商账号
sharingAccount: 1618436087 sharingAccount: 1618884922

View File

@ -38,7 +38,7 @@ public class CpopWxPayTests {
@Test @Test
public void refund() throws WxPayException { public void refund() throws WxPayException {
Brand brand = SpringUtils.getBean(BrandService.class).getById("75140168047210496"); Brand brand = SpringUtils.getBean(BrandService.class).getById("75140168047210496");
Order order = SpringUtils.getBean(OrderService.class).getById("77257142464598016"); Order order = SpringUtils.getBean(OrderService.class).getById("77602361596874752");
WxPayRefundV3Request request = new WxPayRefundV3Request(); WxPayRefundV3Request request = new WxPayRefundV3Request();
WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount(); WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
//退款金额(单位分) //退款金额(单位分)

View File

@ -0,0 +1,55 @@
package com.cpop.mall.business.controller.test;
import com.cpop.core.base.R;
import com.cpop.mall.business.bo.MallRoleBo;
import com.cpop.mall.business.bo.MallRolePageBo;
import com.cpop.mall.business.service.RoleBrandService;
import com.cpop.mall.business.task.ProductRecordSyncStockTask;
import com.cpop.mall.business.vo.MallRolePageVo;
import com.cpop.system.business.bo.MenuListBo;
import com.cpop.system.business.bo.RoleStatusBo;
import com.cpop.system.business.service.MenuService;
import com.cpop.system.business.vo.MenuVo;
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.mybatisflex.core.paginate.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author DB
* @createTime 2023/10/19 18:14
* @description
*/
@RestController
@Api(tags = "商城测试接口")
@RequestMapping("/test")
public class TestController {
@Autowired
private ProductRecordSyncStockTask productRecordSyncStockTask;
/**
* @descriptions 并发批量扣减库存测试
* @author DB
* @date 2023/11/02 9:47
* @return: java.lang.String
*/
@ApiOperation("并发批量扣减库存测试")
@PostMapping("/concurrencyDeductionStockTest")
public R<Void> concurrencyDeductionStockTest(){
for (int i = 0; i < 10000; i++) {
Map<String, Integer> map = new HashMap<>(1);
map.put("77212443863334912", 1);
productRecordSyncStockTask.asyncUpdateRecords(map);
}
return R.ok();
}
}

View File

@ -25,12 +25,15 @@ import com.cpop.mall.business.vo.OrderPageVo;
import com.cpop.mall.framework.config.wxPay.WxPayProperties; import com.cpop.mall.framework.config.wxPay.WxPayProperties;
import com.cpop.mall.framework.constant.MallRedisConstant; import com.cpop.mall.framework.constant.MallRedisConstant;
import com.cpop.mall.framework.handler.WxPayHandler; import com.cpop.mall.framework.handler.WxPayHandler;
import com.cpop.system.business.entity.Brand;
import com.cpop.system.business.entity.ProfitSharing; import com.cpop.system.business.entity.ProfitSharing;
import com.cpop.system.business.service.BrandService;
import com.cpop.system.business.service.ProfitSharingService; import com.cpop.system.business.service.ProfitSharingService;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingReceiverRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingRequest;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingResult;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingReceiver; import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingReceiver;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingRequest;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService; import com.github.binarywang.wxpay.service.WxPayService;
@ -136,9 +139,9 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
public Object placeOrder(PlaceOrderBo bo) { public Object placeOrder(PlaceOrderBo bo) {
RedisService redisService = SpringUtils.getBean(RedisService.class); RedisService redisService = SpringUtils.getBean(RedisService.class);
//获取当前下单用户信息 //获取当前下单用户信息
JSONObject loginStaffInfo = SecurityUtils.getInstance().getLoginUserInfo(); JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
//分布式锁进行幂等处理 //分布式锁进行幂等处理
Lock userIdLock = redisService.distributedLock(MallRedisConstant.IDEMPOTENT_LOCK_USER_PAY + loginStaffInfo.getString("userId")); Lock userIdLock = redisService.distributedLock(MallRedisConstant.IDEMPOTENT_LOCK_USER_PAY + loginUserInfo.getString("userId"));
if (userIdLock.tryLock()) { if (userIdLock.tryLock()) {
//检查库存 //检查库存
Map<String, Integer> recordNumIsEnough = recordNumIsEnough(bo.getPlaceOrderDetailList()); Map<String, Integer> recordNumIsEnough = recordNumIsEnough(bo.getPlaceOrderDetailList());
@ -159,11 +162,11 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
order.setOrderStatus(0) order.setOrderStatus(0)
.setPreviousStatus(0) .setPreviousStatus(0)
//获取当前用户所属品牌 //获取当前用户所属品牌
.setBrandId(loginStaffInfo.getString("brandId")) .setBrandId(loginUserInfo.getString("brandId"))
.setProductNames(productList.stream().map(Product::getProductName).collect(Collectors.joining(","))) .setProductNames(productList.stream().map(Product::getProductName).collect(Collectors.joining(",")))
.setPayUserId(loginStaffInfo.getString("userId")); .setPayUserId(loginUserInfo.getString("userId"));
if (StringUtils.isBlank(bo.getPayUserName())) { if (StringUtils.isBlank(bo.getPayUserName())) {
order.setPayUserName(loginStaffInfo.getString("nickName")); order.setPayUserName(loginUserInfo.getString("nickName"));
} }
this.save(order); this.save(order);
//保存订单详情 //保存订单详情
@ -173,11 +176,18 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
SpringUtils.getBean(OrderDetailService.class).saveBatch(orderDetails); SpringUtils.getBean(OrderDetailService.class).saveBatch(orderDetails);
//微信支付 //微信支付
if (bo.getPayType() == 0) { if (bo.getPayType() == 0) {
//微信支付统一下单
WxPayService wxPayService = wxPayHandler.getWxPayService();
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest(); WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
//需要分账 //查询是否是服务商系品牌
orderRequest.setProfitSharing("Y") String brandId = loginUserInfo.getString("brandId");
.setSpbillCreateIp(IpUtils.getHostIp()) Brand brand = SpringUtils.getBean(BrandService.class).getById(brandId);
.setSubOpenid(loginStaffInfo.getString("openId")) if (StringUtils.isBlank(brand.getWxAppId()) || !StringUtils.equals(brand.getWxAppId(), wxPayService.getConfig().getAppId())) {
//需要分账
orderRequest.setProfitSharing("Y");
}
orderRequest.setSpbillCreateIp(IpUtils.getHostIp())
.setOpenid(loginUserInfo.getString("openId"))
//商品描述 //商品描述
.setBody(productList.stream().map(Product::getDescription).collect(Collectors.joining(","))) .setBody(productList.stream().map(Product::getDescription).collect(Collectors.joining(",")))
//商品详情 //商品详情
@ -186,8 +196,6 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
//元转分 //元转分
.setTotalFee(bo.getTotalAmount().scaleByPowerOfTen(2).intValue()) .setTotalFee(bo.getTotalAmount().scaleByPowerOfTen(2).intValue())
.setTradeType("JSAPI"); .setTradeType("JSAPI");
//微信支付统一下单
WxPayService wxPayService = wxPayHandler.getWxPayService();
return wxPayService.createOrder(orderRequest); return wxPayService.createOrder(orderRequest);
} else { } else {
return pointPay(recordNumIsEnough, order.getId()); return pointPay(recordNumIsEnough, order.getId());
@ -317,34 +325,14 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
.where(ORDER_DETAIL.ORDER_ID.eq(orderId)).list(); .where(ORDER_DETAIL.ORDER_ID.eq(orderId)).list();
//订单数量集合 //订单数量集合
Map<String, Integer> orderNumMap = orderDetails.stream().collect(Collectors.toMap(OrderDetail::getProductRecordId, OrderDetail::getNumber)); Map<String, Integer> orderNumMap = orderDetails.stream().collect(Collectors.toMap(OrderDetail::getProductRecordId, OrderDetail::getNumber));
//TODO:分账先注释 //查询订单信息
ProfitSharing profitSharing = new ProfitSharing(); Order order = this.getById(orderId);
profitSharing.setOrderId(orderId) //获取订单品牌信息
.setProfitSharingStatus(0) Brand brand = SpringUtils.getBean(BrandService.class).getById(order.getBrandId());
.setOrderSource(OrderSource.MALL.toString()); if (StringUtils.isBlank(brand.getWxAppId()) || !StringUtils.equals(brand.getWxAppId(), wxPayService.getConfig().getAppId())) {
ProfitSharingService profitSharingService = SpringUtils.getBean(ProfitSharingService.class); //需要分账
profitSharingService.save(profitSharing); wxPayProfitSharing(orderId, notifyResult, wxPayService);
ProfitSharingRequest sharingRequest = new ProfitSharingRequest(); }
ProfitSharingReceiver profitSharingReceiver = new ProfitSharingReceiver();
List<ProfitSharingReceiver> profitSharingReceivers = new ArrayList<>();
Double ceil = Math.ceil(notifyResult.getTotalFee() * OrderSource.MALL.getRate());
profitSharingReceiver.setAmount(ceil.longValue());
profitSharingReceiver.setDescription("向服务商分账");
profitSharingReceiver.setType("MERCHANT_ID");
profitSharingReceiver.setRelationType("SERVICE_PROVIDER");
profitSharingReceiver.setAccount(wxPayProperties.getSharingAccount());
profitSharingReceivers.add(profitSharingReceiver);
sharingRequest.setTransactionId(notifyResult.getTransactionId());
sharingRequest.setOutOrderNo(profitSharing.getOrderId());
//分账结束
sharingRequest.setUnfreezeUnsplit(true);
sharingRequest.setAppid(wxPayProperties.getAppId());
sharingRequest.setReceivers(profitSharingReceivers);
ProfitSharingResult profitSharingResult = wxPayService.getProfitSharingV3Service().profitSharing(sharingRequest);
//更新分账订单
profitSharingService.updateChain().set(PROFIT_SHARING.OUT_PROFIT_SHARING_ID,profitSharingResult.getOrderId())
.set(PROFIT_SHARING.AMOUNT, ceil.longValue()).set(PROFIT_SHARING.PROFIT_SHARING_STATUS, 1).set(PROFIT_SHARING.PAY_ACCOUNT, wxPayProperties.getSharingAccount())
.where(PROFIT_SHARING.ID.eq(profitSharing.getId())).update();
//异步更新 //异步更新
SpringUtils.getBean(ProductRecordSyncStockTask.class).asyncUpdateRecords(orderNumMap); SpringUtils.getBean(ProductRecordSyncStockTask.class).asyncUpdateRecords(orderNumMap);
} catch (WxPayException e) { } catch (WxPayException e) {
@ -352,6 +340,51 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
} }
} }
/**
* @descriptions 微信支付分账
* @author DB
* @date 2023/11/02 15:26
* @param orderId 订单id
* @param notifyResult 微信支付结果
* @param wxPayService 微信支付
* @return: void
*/
private void wxPayProfitSharing(String orderId, WxPayOrderNotifyResult notifyResult, WxPayService wxPayService) throws WxPayException {
ProfitSharing profitSharing = new ProfitSharing();
profitSharing.setOrderId(orderId)
.setProfitSharingStatus(0)
.setOrderSource(OrderSource.MALL.toString());
ProfitSharingService profitSharingService = SpringUtils.getBean(ProfitSharingService.class);
profitSharingService.save(profitSharing);
Double ceil = Math.ceil(notifyResult.getTotalFee() * OrderSource.MALL.getRate());
//固定商户信息
Map<String, Object> mapReceiver = new HashMap<>(4);
mapReceiver.put("type", "MERCHANT_ID");
mapReceiver.put("account", wxPayProperties.getSharingAccount());
mapReceiver.put("relation_type", "SERVICE_PROVIDER");
mapReceiver.put("amount", ceil.longValue());
mapReceiver.put("name","果酱盒子");
mapReceiver.put("description", "分账到服务商");
/*
//添加分账接收方
ProfitSharingReceiverRequest profitSharingReceiver = new ProfitSharingReceiverRequest();
profitSharingReceiver.setReceiver(JSONObject.toJSONString(mapReceiver));
wxPayService.getProfitSharingService().addReceiver(profitSharingReceiver);*/
//分账
ProfitSharingRequest profitSharingRequest = new ProfitSharingRequest();
List<Map<String, Object>> receivers = new ArrayList<>(1);
receivers.add(mapReceiver);
profitSharingRequest.setReceivers(JSONObject.toJSONString(receivers));
profitSharingRequest.setSignType("HMAC-SHA256");
profitSharingRequest.setOutOrderNo(profitSharing.getId());
profitSharingRequest.setTransactionId(notifyResult.getTransactionId());
ProfitSharingResult profitSharingResult = wxPayService.getProfitSharingService().profitSharing(profitSharingRequest);
//更新分账订单
profitSharingService.updateChain().set(PROFIT_SHARING.OUT_PROFIT_SHARING_ID, profitSharingResult.getOrderId())
.set(PROFIT_SHARING.AMOUNT, ceil.longValue()).set(PROFIT_SHARING.PROFIT_SHARING_STATUS, 1).set(PROFIT_SHARING.PAY_ACCOUNT, wxPayProperties.getSharingAccount())
.where(PROFIT_SHARING.ID.eq(profitSharing.getId())).update();
}
/** /**
* @descriptions 取消订单 * @descriptions 取消订单
* @author DB * @author DB

View File

@ -26,16 +26,6 @@ public class WxPayProperties {
*/ */
private String mchKey; private String mchKey;
/**
* 服务商模式下的子商户公众账号ID普通模式请不要配置请在配置文件中将对应项删除
*/
private String subAppId;
/**
* 服务商模式下的子商户号普通模式请不要配置最好是请在配置文件中将对应项删除
*/
private String subMchId;
/** /**
* apiclient_cert.p12文件的绝对路径或者如果放在项目中请以classpath:开头指定 * apiclient_cert.p12文件的绝对路径或者如果放在项目中请以classpath:开头指定
*/ */

View File

@ -32,6 +32,12 @@ public class BrandBo {
@ApiModelProperty("微信商户号") @ApiModelProperty("微信商户号")
private String wxMchId; private String wxMchId;
/**
* 微信Appid
*/
@ApiModelProperty("微信Appid")
private String wxAppId;
/** /**
* 微信支付密钥 * 微信支付密钥
*/ */

View File

@ -40,6 +40,11 @@ public class Brand extends BaseEntity implements Serializable {
*/ */
private String brandName; private String brandName;
/**
* 微信appid
*/
private String wxAppId;
/** /**
* 微信商户号 * 微信商户号
*/ */

View File

@ -76,6 +76,7 @@ public class BrandServiceImpl extends ServiceImpl<BrandMapper, Brand> implements
} }
Brand sysBrand = new Brand(); Brand sysBrand = new Brand();
sysBrand.setBrandName(brand.getString("name")) sysBrand.setBrandName(brand.getString("name"))
.setWxAppId(brand.getString("appid"))
.setWxMchId(brand.getString("wxMchId")) .setWxMchId(brand.getString("wxMchId"))
.setWxMchKey(brand.getString("wxMchKey")) .setWxMchKey(brand.getString("wxMchKey"))
.setWxKeyPath(brand.getString("wxKeyPath")) .setWxKeyPath(brand.getString("wxKeyPath"))