商城预订单;分账与分账退款;添加微信支付异步任务类

This commit is contained in:
DB 2023-11-06 11:47:08 +08:00
parent 5984409601
commit 212110afc8
20 changed files with 849 additions and 188 deletions

View File

@ -119,4 +119,6 @@ wx:
#支付通知地址 #支付通知地址
notifyUrl: https://frp-oak.top:11899/Cpop-Mall/wxPay/callback/notify/order notifyUrl: https://frp-oak.top:11899/Cpop-Mall/wxPay/callback/notify/order
#退款通知地址 #退款通知地址
notifyRefund: https://frp-oak.top:11899/Cpop-Mall/wxPay/callback/notify/refund notifyRefund: https://frp-oak.top:11899/Cpop-Mall/wxPay/callback/notify/refund
#分账通知地址
notifySharing: https://frp-oak.top:11899/Cpop-Mall/wxPay/callback/notify/profitSharing

View File

@ -106,7 +106,7 @@ task:
# 设置空闲线程存活时间(秒 # 设置空闲线程存活时间(秒
keepAliveSeconds: 300 keepAliveSeconds: 300
# 设置队列容量 # 设置队列容量
queueCapacity: 10240 queueCapacity: 1024
# 设置线程名称前缀 # 设置线程名称前缀
threadNamePrefix: "Cpop-Mall-AsyncNotify-" threadNamePrefix: "Cpop-Mall-AsyncNotify-"
# 设置线程池等待终止时间(秒) # 设置线程池等待终止时间(秒)

View File

@ -1,20 +1,29 @@
package com.cpop.mall.web; package com.cpop.mall.web;
import com.alibaba.fastjson.JSONObject;
import com.cpop.core.utils.SpringUtils; import com.cpop.core.utils.SpringUtils;
import com.cpop.core.utils.uuid.IdUtils; import com.cpop.core.utils.uuid.IdUtils;
import com.cpop.mall.business.entity.Order; import com.cpop.mall.business.entity.Order;
import com.cpop.mall.business.service.OrderService; import com.cpop.mall.business.service.OrderService;
import com.cpop.mall.framework.config.wxPay.WxPayProperties; import com.cpop.mall.framework.config.wxPay.WxPayProperties;
import com.cpop.system.business.entity.Brand; import com.cpop.system.business.entity.Brand;
import com.cpop.system.business.entity.ProfitSharing;
import com.cpop.system.business.service.BrandService; import com.cpop.system.business.service.BrandService;
import com.cpop.system.business.service.ProfitSharingService;
import com.github.binarywang.wxpay.bean.profitsharing.ProfitSharingReceiverRequest;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingReturnRequest;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request; import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result; import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.config.WxPayConfig;
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;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest;
import java.util.HashMap;
import java.util.Map;
/** /**
* @author DB * @author DB
* @createTime 2023/11/01 18:15 * @createTime 2023/11/01 18:15
@ -38,7 +47,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("77602361596874752"); Order order = SpringUtils.getBean(OrderService.class).getById("78954327589658624");
WxPayRefundV3Request request = new WxPayRefundV3Request(); WxPayRefundV3Request request = new WxPayRefundV3Request();
WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount(); WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
//退款金额(单位分) //退款金额(单位分)
@ -57,4 +66,49 @@ public class CpopWxPayTests {
WxPayRefundV3Result result = wxPayService.refundV3(request); WxPayRefundV3Result result = wxPayService.refundV3(request);
System.out.println(result); System.out.println(result);
} }
/**
* @descriptions 分账退款
* @author DB
* @date 2023/11/03 9:57
* @return: void
*/
@Test
public void profitSharingRefund() throws WxPayException {
ProfitSharingReturnRequest profitSharingReturnRequest = new ProfitSharingReturnRequest();
//ProfitSharing profitSharing = SpringUtils.getBean(ProfitSharingService.class).getById("77860920238751744");
//profitSharingReturnRequest.setOrderId(profitSharing.getOutProfitSharingId());
//profitSharingReturnRequest.setOutReturnNo(profitSharing.getId());
profitSharingReturnRequest.setOrderId("30001200512023110656192114311");
profitSharingReturnRequest.setOutReturnNo("4200002030202311064693113248");
profitSharingReturnRequest.setDescription("分账退款");
profitSharingReturnRequest.setSubMchId("1650816616");
profitSharingReturnRequest.setReturnMchid("1618884922");
//profitSharingReturnRequest.setAmount(profitSharing.getAmount());
profitSharingReturnRequest.setAmount(1L);
wxPayService.getProfitSharingV3Service().profitSharingReturn(profitSharingReturnRequest);
}
/**
* @descriptions 添加分账接收方
* @author DB
* @date 2023/11/03 10:17
* @param
* @return: void
*/
@Test
public void addReceiver() throws WxPayException {
//固定商户信息
Map<String, Object> mapReceiver = new HashMap<>(4);
mapReceiver.put("type", "MERCHANT_ID");
//mapReceiver.put("sub_mchid", "1650816616");
mapReceiver.put("sub_mchid", "1618884922");
mapReceiver.put("account", "1618884922");
mapReceiver.put("relation_type", "SERVICE_PROVIDER");
mapReceiver.put("name","果酱盒子");
//添加分账接收方
ProfitSharingReceiverRequest profitSharingReceiver = new ProfitSharingReceiverRequest();
profitSharingReceiver.setReceiver(JSONObject.toJSONString(mapReceiver));
wxPayService.getProfitSharingService().addReceiver(profitSharingReceiver);
}
} }

View File

@ -0,0 +1,72 @@
package com.cpop.mall.business.bo;
import com.cpop.core.annontation.StringArrayConvert;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* @author DB
* @createTime 2023/11/06 11:13
* @description
*/
@Data
@Accessors(chain = true)
@ApiModel(value = "商城预订单对象")
public class AdvanceOrderBo implements Serializable {
/**
* 主键
*/
@ApiModelProperty("主键")
private String id;
/**
* 总金额
*/
@NotNull(message = "总金额不能为空")
@ApiModelProperty(value = "总金额",required = true)
private BigDecimal totalAmount;
/**
* 总积分
*/
@NotNull(message = "总积分不能为空")
@ApiModelProperty(value = "总积分",required = true)
private Long totalPoint;
/**
* 当前订单所有产品名
*/
@StringArrayConvert
@ApiModelProperty(value = "当前订单所有产品名")
private String productNames;
/**
* 支付类型
*/
@NotNull(message = "支付类型不能为空")
@ApiModelProperty(value = "支付方式(0:微信支付;1:积分支付)",required = true)
private Integer payType;
/**
* 支付用户名
*/
@ApiModelProperty(value = "支付用户名")
private String payUserName;
/**
* 订单详情
*/
@NotEmpty(message = "订单详情不能为空")
@ApiModelProperty(value = "订单详情",required = true)
private List<OrderDetailBo> orderDetailList;
}

View File

@ -0,0 +1,55 @@
package com.cpop.mall.business.bo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author DB
* @createTime 2023/11/06 11:21
* @description
*/
@Data
@ApiModel(value = "商城订单下单详情请求对象")
public class OrderDetailBo implements Serializable {
/**
* 商品记录id
*/
@NotBlank(message = "商品规格记录id不能为空")
@ApiModelProperty(value = "商品规格记录id",required = true)
private String productRecordId;
/**
* 金额
*/
@NotNull(message = "金额不能为空")
@ApiModelProperty(value = "金额",required = true)
private BigDecimal amount;
/**
* 积分
*/
@NotNull(message = "积分不能为空")
@ApiModelProperty(value = "积分",required = true)
private Integer point;
/**
* 下单数量
*/
@NotNull(message = "下单数量不能为空")
@ApiModelProperty(value = "下单数量",required = true)
private Integer number;
/**
* 店铺(校区)id
*/
@NotBlank(message = "店铺(校区)id不能为空")
@ApiModelProperty(value = "店铺(校区)id",required = true)
private String storeId;
}

View File

@ -29,26 +29,6 @@ public class PlaceOrderBo implements Serializable {
@ApiModelProperty("主键") @ApiModelProperty("主键")
private String id; private String id;
/**
* 总金额
*/
@NotNull(message = "总金额不能为空")
@ApiModelProperty(value = "总金额",required = true)
private BigDecimal totalAmount;
/**
* 总积分
*/
@NotNull(message = "总积分不能为空")
@ApiModelProperty(value = "总积分",required = true)
private Long totalPoint;
/**
* 下单人真实姓名
*/
@ApiModelProperty("下单人真实姓名")
private String payUserName;
/** /**
* 收货人名 * 收货人名
*/ */
@ -63,13 +43,6 @@ public class PlaceOrderBo implements Serializable {
@ApiModelProperty(value = "收货人电话",required = true) @ApiModelProperty(value = "收货人电话",required = true)
private String receivePhone; private String receivePhone;
/**
* 当前订单所有产品名
*/
@StringArrayConvert
@ApiModelProperty(value = "当前订单所有产品名")
private String productNames;
/** /**
* 收货地址 * 收货地址
*/ */
@ -90,53 +63,4 @@ public class PlaceOrderBo implements Serializable {
@ApiModelProperty(value = "支付方式(0:微信支付;1:积分支付)",required = true) @ApiModelProperty(value = "支付方式(0:微信支付;1:积分支付)",required = true)
private Integer payType; private Integer payType;
/**
* 订单详情
*/
@NotEmpty(message = "订单详情不能为空")
@ApiModelProperty(value = "订单详情",required = true)
private List<PlaceOrderDetail> placeOrderDetailList;
/**
* 下单详情
*/
@Data
@ApiModel(value = "商城订单下单详情请求对象")
public static class PlaceOrderDetail implements Serializable{
/**
* 商品记录id
*/
@NotBlank(message = "商品规格记录id不能为空")
@ApiModelProperty(value = "商品规格记录id",required = true)
private String productRecordId;
/**
* 金额
*/
@NotNull(message = "金额不能为空")
@ApiModelProperty(value = "金额",required = true)
private BigDecimal amount;
/**
* 积分
*/
@NotNull(message = "积分不能为空")
@ApiModelProperty(value = "积分",required = true)
private Integer point;
/**
* 下单数量
*/
@NotNull(message = "下单数量不能为空")
@ApiModelProperty(value = "下单数量",required = true)
private Integer number;
/**
* 店铺(校区)id
*/
//TODO:@NotBlank(message = "店铺(校区)id不能为空")
@ApiModelProperty(value = "店铺(校区)id",required = true)
private String storeId;
}
} }

View File

@ -52,4 +52,17 @@ public class WxPayCallbackController {
return WxPayNotifyResponse.success("成功"); return WxPayNotifyResponse.success("成功");
} }
/**
* @descriptions 微信支付分账通知
* @author DB
* @date 2023/10/27 16:04
* @param xmlData 数据
* @return: java.lang.String
*/
@PostMapping("/notify/profitSharing")
public String parseProfitSharingNotifyResult(@RequestBody String xmlData){
orderService.parseProfitSharingNotifyResult(xmlData);
return WxPayNotifyResponse.success("成功");
}
} }

View File

@ -3,13 +3,11 @@ package com.cpop.mall.business.controller.mini;
import com.cpop.common.utils.bean.BeanUtils; import com.cpop.common.utils.bean.BeanUtils;
import com.cpop.core.base.R; import com.cpop.core.base.R;
import com.cpop.core.utils.SpringUtils; import com.cpop.core.utils.SpringUtils;
import com.cpop.mall.business.bo.OrderApplyRefundBo; import com.cpop.mall.business.bo.*;
import com.cpop.mall.business.bo.OrderEvaluateBo;
import com.cpop.mall.business.bo.OrderPageBo;
import com.cpop.mall.business.bo.PlaceOrderBo;
import com.cpop.mall.business.entity.OrderEvaluate; import com.cpop.mall.business.entity.OrderEvaluate;
import com.cpop.mall.business.service.OrderEvaluateService; import com.cpop.mall.business.service.OrderEvaluateService;
import com.cpop.mall.business.service.OrderService; import com.cpop.mall.business.service.OrderService;
import com.cpop.mall.business.vo.OrderInfoVo;
import com.cpop.mall.business.vo.OrderPageVo; import com.cpop.mall.business.vo.OrderPageVo;
import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.paginate.Page;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
@ -49,6 +47,19 @@ public class MiniOrderController {
return R.ok(page); return R.ok(page);
} }
/**
* @descriptions 生成预订单
* @author DB
* @date 2023/11/06 11:12
* @param bo 请求参数
* @return: com.cpop.core.base.R<java.lang.Object>
*/
@PostMapping("/createAdvanceOrder")
@ApiOperation("商城-订单-生成预订单")
public R<String> createAdvanceOrder(@RequestBody @Validated @ApiParam("商城-预订单参数") AdvanceOrderBo bo) {
return R.ok(orderService.createAdvanceOrder(bo));
}
/** /**
* @descriptions 商城-订单-下单 * @descriptions 商城-订单-下单
* @author DB * @author DB
@ -103,9 +114,24 @@ public class MiniOrderController {
*/ */
@PutMapping("/cancelOrder") @PutMapping("/cancelOrder")
@ApiOperation("取消订单") @ApiOperation("取消订单")
public R<Object> cancelOrder(@RequestParam("orderId") @ApiParam("商城-订单Id") String orderId) { public R<Void> cancelOrder(@RequestParam("orderId") @ApiParam("商城-订单Id") String orderId) {
orderService.cancelOrder(orderId); orderService.cancelOrder(orderId);
return R.ok(); return R.ok();
} }
/**
* @descriptions 小程序-重新支付订单
* @author DB
* @date 2023/11/03 15:53
* @param orderId 订单id
* @return: com.cpop.core.base.R<com.mybatisflex.core.paginate.Page<com.cpop.mall.business.vo.OrderPageVo>>
*/
@GetMapping("/rePayOrder/{orderId}")
@ApiOperation("小程序-重新支付订单")
public R<Object> rePayOrder(@PathVariable @ApiParam(value = "订单id", required = true) String orderId) {
return R.ok(orderService.rePayOrder(orderId));
}
} }

View File

@ -0,0 +1,33 @@
package com.cpop.mall.business.dto;
import com.mybatisflex.annotation.Id;
import lombok.Data;
/**
* @author DB
* @createTime 2023/11/03 12:56
* @description
*/
@Data
public class ProductAndRecordDto {
/**
* 主键
*/
private String id;
/**
* 商品名
*/
private String productName;
/**
* 产品描述
*/
private String description;
/**
* 产品记录id
*/
private String productRecordId;
}

View File

@ -34,12 +34,12 @@ public class Order extends BaseEntity implements Serializable {
private String id; private String id;
/** /**
* 订单状态(0:待付款;1:待发货;2:待确认;3:已完成;4:退款/售后中) * 订单状态(0:待付款;1:待发货;2:待确认;3:已完成;4:退款/售后中:5:订单取消;6:过期)
*/ */
private Integer orderStatus; private Integer orderStatus;
/** /**
* 上一个状态(0:待付款;1:待发货;2:待确认;3:已完成;4:退款/售后中:5:订单取消) * 上一个状态(0:待付款;1:待发货;2:待确认;3:已完成;4:退款/售后中:5:订单取消;6:过期)
*/ */
private Integer previousStatus; private Integer previousStatus;

View File

@ -1,10 +1,8 @@
package com.cpop.mall.business.service; package com.cpop.mall.business.service;
import com.cpop.mall.business.bo.LogisticsOrderBo; import com.cpop.mall.business.bo.*;
import com.cpop.mall.business.bo.OrderApplyRefundBo;
import com.cpop.mall.business.bo.OrderPageBo;
import com.cpop.mall.business.bo.PlaceOrderBo;
import com.cpop.mall.business.entity.Order; import com.cpop.mall.business.entity.Order;
import com.cpop.mall.business.vo.OrderInfoVo;
import com.cpop.mall.business.vo.OrderPageVo; import com.cpop.mall.business.vo.OrderPageVo;
import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.service.IService; import com.mybatisflex.core.service.IService;
@ -26,6 +24,15 @@ public interface OrderService extends IService<Order> {
*/ */
Page<OrderPageVo> getOrderPage(OrderPageBo bo); Page<OrderPageVo> getOrderPage(OrderPageBo bo);
/**
* @descriptions 生成预订单
* @author DB
* @date 2023/11/06 11:17
* @param bo 请求参数
* @return: java.lang.Object
*/
String createAdvanceOrder(AdvanceOrderBo bo);
/** /**
* @descriptions 商城-订单-下单 * @descriptions 商城-订单-下单
* @author DB * @author DB
@ -53,6 +60,15 @@ public interface OrderService extends IService<Order> {
*/ */
void wxPayNotifyOrder(String xmlData); void wxPayNotifyOrder(String xmlData);
/**
* @descriptions 分账结果通知
* @author DB
* @date 2023/11/03 9:50
* @param xmlData 加密数据
* @return: void
*/
void parseProfitSharingNotifyResult(String xmlData);
/** /**
* @descriptions 取消订单 * @descriptions 取消订单
* @author DB * @author DB
@ -69,4 +85,14 @@ public interface OrderService extends IService<Order> {
* @Date: 2023/10/28 15:27 * @Date: 2023/10/28 15:27
*/ */
void inputLogisticsOrder(LogisticsOrderBo bo); void inputLogisticsOrder(LogisticsOrderBo bo);
/**
* @descriptions 小程序-重新支付订单
* @author DB
* @date 2023/11/03 16:14
* @param orderId 订单id
* @return: java.lang.Object
*/
Object rePayOrder(String orderId);
} }

View File

@ -5,6 +5,7 @@ import com.cpop.core.base.enums.OrderSource;
import com.cpop.core.base.exception.ServiceException; import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.utils.SpringUtils; import com.cpop.core.utils.SpringUtils;
import com.cpop.core.utils.sql.SqlUtils; import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.core.utils.uuid.IdUtils;
import com.cpop.mall.business.bo.OrderRefundPageBo; import com.cpop.mall.business.bo.OrderRefundPageBo;
import com.cpop.mall.business.bo.OrderRejectRefundBo; import com.cpop.mall.business.bo.OrderRejectRefundBo;
import com.cpop.mall.business.entity.Order; import com.cpop.mall.business.entity.Order;
@ -15,9 +16,13 @@ import com.cpop.mall.business.service.OrderService;
import com.cpop.mall.business.vo.OrderRefundPageVo; import com.cpop.mall.business.vo.OrderRefundPageVo;
import com.cpop.mall.framework.config.wxPay.WxPayProperties; import com.cpop.mall.framework.config.wxPay.WxPayProperties;
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.WxPayRefundNotifyResult; import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult;
import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyV3Result;
import com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingReturnRequest;
import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request; import com.github.binarywang.wxpay.bean.request.WxPayRefundV3Request;
import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result; import com.github.binarywang.wxpay.bean.result.WxPayRefundV3Result;
import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.exception.WxPayException;
@ -63,17 +68,26 @@ public class OrderRefundServiceImpl extends ServiceImpl<OrderRefundMapper, Order
OrderRefund orderRefund = this.queryChain().where(ORDER_REFUND.ID.eq(id)).one(); OrderRefund orderRefund = this.queryChain().where(ORDER_REFUND.ID.eq(id)).one();
Order order = SpringUtils.getBean(OrderService.class).queryChain().where(ORDER.ID.eq(orderRefund.getOrderId())).one(); Order order = SpringUtils.getBean(OrderService.class).queryChain().where(ORDER.ID.eq(orderRefund.getOrderId())).one();
//分账记录 //分账记录
ProfitSharing profitSharing = SpringUtils.getBean(ProfitSharingService.class).queryChain().where(PROFIT_SHARING.ORDER_ID.eq(order.getId())) ProfitSharingService profitSharingService = SpringUtils.getBean(ProfitSharingService.class);
.and(PROFIT_SHARING.ORDER_SOURCE.eq(OrderSource.MALL.toString())).one(); ProfitSharing profitSharing = profitSharingService.queryChain().where(PROFIT_SHARING.ORDER_ID.eq(order.getId()))
//TODO:可能还需要先从分账退款 .and(PROFIT_SHARING.ORDER_SOURCE.eq(OrderSource.MALL.toString()))
/*ProfitSharingReturnRequest profitSharingReturnRequest = new ProfitSharingReturnRequest(); .and(PROFIT_SHARING.PROFIT_SHARING_STATUS.eq(1))
profitSharingReturnRequest.setOrderId(profitSharing.getOutProfitSharingId()); .one();
profitSharingReturnRequest.setReturnMchid(profitSharing.getPayAccount()); if (null != profitSharing){
profitSharingReturnRequest.setAmount(profitSharing.getAmount()); ProfitSharingReturnRequest profitSharingReturnRequest = new ProfitSharingReturnRequest();
profitSharingReturnRequest.setOutOrderNo(profitSharing.getId()); profitSharingReturnRequest.setOrderId(profitSharing.getOutProfitSharingId());
profitSharingReturnRequest.setDescription("订单退款"); profitSharingReturnRequest.setOutReturnNo(profitSharing.getId());
profitSharingReturnRequest.setSubMchId("1618436087"); profitSharingReturnRequest.setDescription("分账退款");
ProfitSharingReturnResult profitSharingReturnResult = wxPayService.getProfitSharingV3Service().profitSharingReturn(profitSharingReturnRequest);*/ profitSharingReturnRequest.setSubMchId(profitSharing.getPayAccount());
profitSharingReturnRequest.setReturnMchid(wxPayProperties.getSharingAccount());
profitSharingReturnRequest.setAmount(profitSharing.getAmount());
wxPayService.getProfitSharingV3Service().profitSharingReturn(profitSharingReturnRequest);
//分账退款记录设置退款
profitSharingService.updateChain()
.set(PROFIT_SHARING.PROFIT_SHARING_STATUS, 2)
.where(PROFIT_SHARING.ID.eq(profitSharing.getId()))
.update();
}
WxPayRefundV3Request request = new WxPayRefundV3Request(); WxPayRefundV3Request request = new WxPayRefundV3Request();
WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount(); WxPayRefundV3Request.Amount amount = new WxPayRefundV3Request.Amount();
//退款金额(单位分) //退款金额(单位分)
@ -87,8 +101,7 @@ public class OrderRefundServiceImpl extends ServiceImpl<OrderRefundMapper, Order
.setOutRefundNo(id) .setOutRefundNo(id)
.setReason(orderRefund.getRefundReason()) .setReason(orderRefund.getRefundReason())
.setAmount(amount); .setAmount(amount);
WxPayRefundV3Result result = wxPayService.refundV3(request); wxPayService.refundV3(request);
this.updateChain().set(ORDER_REFUND.REFUND_STATUS, 1).where(ORDER_REFUND.OUT_REFUND_ID.eq(result.getRefundId())).update();
} catch (WxPayException e) { } catch (WxPayException e) {
throw new ServiceException(e.getMessage()); throw new ServiceException(e.getMessage());
} }
@ -156,12 +169,13 @@ public class OrderRefundServiceImpl extends ServiceImpl<OrderRefundMapper, Order
@Override @Override
public void wxPayNotifyRefund(String xmlData) { public void wxPayNotifyRefund(String xmlData) {
try { try {
WxPayRefundNotifyResult result = wxPayHandler.getWxPayService().parseRefundNotifyResult(xmlData); WxPayRefundNotifyV3Result result = wxPayHandler.getWxPayService().parseRefundNotifyV3Result(xmlData, null);
WxPayRefundNotifyResult.ReqInfo reqInfo = result.getReqInfo(); WxPayRefundNotifyV3Result.DecryptNotifyResult reqInfo = result.getResult();
//获取退款单号
this.updateChain().set(ORDER_REFUND.REFUND_STATUS, 1) this.updateChain().set(ORDER_REFUND.REFUND_STATUS, 1)
.set(ORDER_REFUND.OUT_REFUND_ID, reqInfo.getRefundId()) .set(ORDER_REFUND.OUT_REFUND_ID, reqInfo.getRefundId())
.where(ORDER_REFUND.ID.eq(reqInfo.getOutRefundNo())).update(); .where(ORDER_REFUND.ID.eq(reqInfo.getOutRefundNo()))
//TODO:回滚库存 .update();
} catch (WxPayException e) { } catch (WxPayException e) {
throw new ServiceException(e.getMessage()); throw new ServiceException(e.getMessage());
} }

View File

@ -1,45 +1,43 @@
package com.cpop.mall.business.service.impl; package com.cpop.mall.business.service.impl;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.cpop.common.utils.DateUtils;
import com.cpop.common.utils.StringUtils; import com.cpop.common.utils.StringUtils;
import com.cpop.common.utils.bean.BeanUtils; import com.cpop.common.utils.bean.BeanUtils;
import com.cpop.common.utils.ip.IpUtils; import com.cpop.common.utils.ip.IpUtils;
import com.cpop.core.base.entity.PageDomain; import com.cpop.core.base.entity.PageDomain;
import com.cpop.core.base.enums.OrderSource;
import com.cpop.core.base.enums.UserType; import com.cpop.core.base.enums.UserType;
import com.cpop.core.base.exception.ServiceException; import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.service.RedisService; import com.cpop.core.service.RedisService;
import com.cpop.core.utils.QuartzUtils;
import com.cpop.core.utils.SecurityUtils; import com.cpop.core.utils.SecurityUtils;
import com.cpop.core.utils.SpringUtils; import com.cpop.core.utils.SpringUtils;
import com.cpop.core.utils.sql.SqlUtils; import com.cpop.core.utils.sql.SqlUtils;
import com.cpop.mall.business.bo.LogisticsOrderBo; import com.cpop.mall.business.bo.*;
import com.cpop.mall.business.bo.OrderApplyRefundBo;
import com.cpop.mall.business.bo.OrderPageBo;
import com.cpop.mall.business.bo.PlaceOrderBo;
import com.cpop.mall.business.dto.WxPayGoodsDetailDto; import com.cpop.mall.business.dto.WxPayGoodsDetailDto;
import com.cpop.mall.business.entity.*; import com.cpop.mall.business.entity.*;
import com.cpop.mall.business.mapper.OrderMapper; import com.cpop.mall.business.mapper.OrderMapper;
import com.cpop.mall.business.service.*; import com.cpop.mall.business.service.*;
import com.cpop.mall.business.task.OrderOverTimeUnPayTask;
import com.cpop.mall.business.task.ProductRecordSyncStockTask; import com.cpop.mall.business.task.ProductRecordSyncStockTask;
import com.cpop.mall.business.task.WxPayAsyncTask;
import com.cpop.mall.business.vo.OrderInfoVo;
import com.cpop.mall.business.vo.OrderPageVo; import com.cpop.mall.business.vo.OrderPageVo;
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.enums.QuartzEnums;
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.Brand;
import com.cpop.system.business.entity.ProfitSharing;
import com.cpop.system.business.service.BrandService; 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.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;
import com.mybatisflex.core.paginate.Page; import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper; import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl; import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.quartz.*;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@ -71,7 +69,10 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
private WxPayHandler wxPayHandler; private WxPayHandler wxPayHandler;
@Autowired @Autowired
private WxPayProperties wxPayProperties; private Scheduler scheduler;
@Autowired
private WxPayAsyncTask wxPayAsyncTask;
/** /**
* @descriptions 分页查询商城-订单 * @descriptions 分页查询商城-订单
@ -96,6 +97,12 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
//查询此用户订单 //查询此用户订单
queryWrapper.where(ORDER.PAY_USER_ID.eq(loginUserInfo.getString("userId"))); queryWrapper.where(ORDER.PAY_USER_ID.eq(loginUserInfo.getString("userId")));
} }
//订单状态
if (null != bo.getOrderStatus()) {
queryWrapper.and(ORDER.ORDER_STATUS.eq(bo.getOrderStatus()));
} else {
queryWrapper.and(ORDER.ORDER_STATUS.ne(6));
}
PageDomain pageDomain = SqlUtils.getInstance().getPageDomain(); PageDomain pageDomain = SqlUtils.getInstance().getPageDomain();
return this.mapper.paginateAs(Page.of(pageDomain.getPageNum(),pageDomain.getPageSize()), return this.mapper.paginateAs(Page.of(pageDomain.getPageNum(),pageDomain.getPageSize()),
queryWrapper queryWrapper
@ -105,8 +112,6 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
.and(ORDER.PAY_USER_NAME.like(bo.getPayUserName())) .and(ORDER.PAY_USER_NAME.like(bo.getPayUserName()))
.and(ORDER.RECEIVE_PHONE.like(bo.getReceivePhone())) .and(ORDER.RECEIVE_PHONE.like(bo.getReceivePhone()))
.and(ORDER.PRODUCT_NAMES.like(bo.getProductName())) .and(ORDER.PRODUCT_NAMES.like(bo.getProductName()))
//订单状态
.and(ORDER.ORDER_STATUS.eq(bo.getOrderStatus()))
.orderBy(ORDER.CREATE_TIME.desc()), .orderBy(ORDER.CREATE_TIME.desc()),
OrderPageVo.class, OrderPageVo.class,
//子查询 //子查询
@ -128,27 +133,26 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
} }
/** /**
* @descriptions 商城-订单- * @descriptions 生成预订
* @author DB * @author DB
* @date 2023/10/25 9:32 * @date 2023/11/06 11:17
* @param bo 下单请求对象 * @param bo 请求参数
* @return: void * @return: java.lang.Object
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) public String createAdvanceOrder(AdvanceOrderBo bo) {
public Object placeOrder(PlaceOrderBo bo) {
RedisService redisService = SpringUtils.getBean(RedisService.class); RedisService redisService = SpringUtils.getBean(RedisService.class);
//获取当前下单用户信息 //获取当前下单用户信息
JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo(); JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
//分布式锁进行幂等处理 //分布式锁进行幂等处理
Lock userIdLock = redisService.distributedLock(MallRedisConstant.IDEMPOTENT_LOCK_USER_PAY + loginUserInfo.getString("userId")); Lock userIdLock = redisService.distributedLock(MallRedisConstant.IDEMPOTENT_LOCK_CREATE_ADVANCE_ORDER + loginUserInfo.getString("userId"));
if (userIdLock.tryLock()) { if (userIdLock.tryLock()) {
//检查库存 //检查库存
Map<String, Integer> recordNumIsEnough = null; Map<String, Integer> recordNumIsEnough = null;
try { try {
recordNumIsEnough= recordNumIsEnough(bo.getPlaceOrderDetailList());
Order order = BeanUtils.mapToClass(bo, Order.class); Order order = BeanUtils.mapToClass(bo, Order.class);
List<OrderDetail> orderDetails = BeanUtils.mapToList(bo.getPlaceOrderDetailList(), OrderDetail.class); List<OrderDetail> orderDetails = BeanUtils.mapToList(bo.getOrderDetailList(), OrderDetail.class);
recordNumIsEnough= recordNumIsEnough(orderDetails);
//规格记录ids //规格记录ids
Set<String> recordIds = orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet()); Set<String> recordIds = orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet());
//获取涉及到的商品 //获取涉及到的商品
@ -170,11 +174,76 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
order.setPayUserName(loginUserInfo.getString("nickName")); order.setPayUserName(loginUserInfo.getString("nickName"));
} }
this.save(order); this.save(order);
// 通过JobBuilder构建JobDetail实例JobDetail规定其job只能是实现Job接口的实例
JobDetail jobDetail = JobBuilder.newJob(OrderOverTimeUnPayTask.class).withIdentity(QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getName() + order.getId(),
QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getGroup())
.usingJobData("orderId", order.getId())
.build();
//添加定时修改订单状态任务
QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
//设置15分钟后
String cron = quartzUtils.convertToCron(DateUtils.addMinutes(new Date(), 15));
CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
// CronTrigger表达式触发器 继承于TriggerTriggerBuilder 用于构建触发器实例
CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getName() + order.getId(),
QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getGroup())
.withSchedule(cronScheduleBuilder).build();
scheduler.scheduleJob(jobDetail, cronTrigger);
//保存订单详情 //保存订单详情
orderDetails.forEach(item -> { orderDetails.forEach(item -> {
item.setOrderId(order.getId()); item.setOrderId(order.getId());
}); });
SpringUtils.getBean(OrderDetailService.class).saveBatch(orderDetails); SpringUtils.getBean(OrderDetailService.class).saveBatch(orderDetails);
return order.getId();
} catch (Exception e) {
//回滚库存
if (recordNumIsEnough != null){
recordNumIsEnough.forEach((key, value) -> {
redisService.longIncrement(MallRedisConstant.STOCK_RECORD_NUM + key, value.longValue());
});
}
throw new ServiceException(e.getMessage());
} finally {
//释放锁
userIdLock.unlock();
}
}else {
//获取锁失败直接返回空
throw new ServiceException("请勿重复下单");
}
}
/**
* @descriptions 商城-订单-下单
* @author DB
* @date 2023/10/25 9:32
* @param bo 下单请求对象
* @return: void
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Object placeOrder(PlaceOrderBo bo) {
//获取当前下单用户信息
JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
//分布式锁进行幂等处理
RedisService redisService = SpringUtils.getBean(RedisService.class);
//分布式锁进行幂等处理
Lock userIdLock = redisService.distributedLock(MallRedisConstant.IDEMPOTENT_LOCK_USER_PAY + loginUserInfo.getString("userId"));
if (userIdLock.tryLock()){
try {
//获取订单信息
Order order = this.getById(bo.getId());
//获取预订单详情信息
OrderDetailService orderDetailService = SpringUtils.getBean(OrderDetailService.class);
List<OrderDetail> orderDetails = orderDetailService.queryChain().where(ORDER_DETAIL.ORDER_ID.eq(order.getId())).list();
//获取涉及到的商品
ProductService productService = SpringUtils.getBean(ProductService.class);
List<Product> productList = productService.queryChain()
.select(PRODUCT.ID, PRODUCT.PRODUCT_NAME, PRODUCT.DESCRIPTION)
.from(PRODUCT)
.leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.PRODUCT_ID.eq(PRODUCT.ID))
.where(PRODUCT_RECORD.ID.in(orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet())))
.list();
//微信支付 //微信支付
if (bo.getPayType() == 0) { if (bo.getPayType() == 0) {
//微信支付统一下单 //微信支付统一下单
@ -191,31 +260,37 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
.setOpenid(loginUserInfo.getString("openId")) .setOpenid(loginUserInfo.getString("openId"))
//商品描述 //商品描述
.setBody(productList.stream().map(Product::getDescription).collect(Collectors.joining(","))) .setBody(productList.stream().map(Product::getDescription).collect(Collectors.joining(",")))
//商品详情 //商品详情,如果涉及到商品优惠需要重新涉及
.setDetail(JSONObject.toJSONString(BeanUtils.mapToList(bo.getPlaceOrderDetailList(), WxPayGoodsDetailDto.class))) .setDetail(JSONObject.toJSONString(BeanUtils.mapToList(orderDetails, WxPayGoodsDetailDto.class)))
.setOutTradeNo(order.getId()) .setOutTradeNo(order.getId())
//元转分 //元转分
.setTotalFee(bo.getTotalAmount().scaleByPowerOfTen(2).intValue()) .setTotalFee(order.getTotalAmount().scaleByPowerOfTen(2).intValue())
.setTradeType("JSAPI"); .setTradeType("JSAPI");
return wxPayService.createOrder(orderRequest); Object result = wxPayService.createOrder(orderRequest);
//删除定时器任务
QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
quartzUtils.deleteJob(QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getName() + order.getId(),
QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getGroup());
return result;
} else { } else {
return pointPay(recordNumIsEnough, order.getId()); //统计支付商品情况
Map<String, Integer> recordNumIsEnough = orderDetails.stream().collect(Collectors.toMap(OrderDetail::getProductRecordId, OrderDetail::getNumber));
Object result = pointPay(recordNumIsEnough, order.getId());
//删除定时器任务
QuartzUtils quartzUtils = SpringUtils.getBean(QuartzUtils.class);
quartzUtils.deleteJob(QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getName() + order.getId(),
QuartzEnums.ORDER_OVERTIME_UN_PAY_TASK.getGroup());
return result;
} }
} catch (Exception e) { } catch (Exception e) {
//回滚库存
if (recordNumIsEnough != null){
recordNumIsEnough.forEach((key, value) -> {
redisService.longIncrement(MallRedisConstant.STOCK_RECORD_NUM + key, value.longValue());
});
}
throw new ServiceException(e.getMessage()); throw new ServiceException(e.getMessage());
} finally { } finally {
//释放锁 //释放锁
userIdLock.unlock(); userIdLock.unlock();
} }
}else { } else {
//获取锁失败直接返回空 //获取锁失败直接返回空
throw new ServiceException("请勿重复下单"); throw new ServiceException("请勿重复支付");
} }
} }
@ -246,7 +321,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
* @param list 订单详情 * @param list 订单详情
* @return: Map<String, Integer> 预库存记录 * @return: Map<String, Integer> 预库存记录
*/ */
private Map<String, Integer> recordNumIsEnough(List<PlaceOrderBo.PlaceOrderDetail> list) throws ServiceException { private Map<String, Integer> recordNumIsEnough(List<OrderDetail> list) throws ServiceException {
RedisService redisService = SpringUtils.getBean(RedisService.class); RedisService redisService = SpringUtils.getBean(RedisService.class);
Map<String, Integer> stockNumMap = new HashMap<>(); Map<String, Integer> stockNumMap = new HashMap<>();
//遍历库存 //遍历库存
@ -334,7 +409,7 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
Brand brand = SpringUtils.getBean(BrandService.class).getById(order.getBrandId()); Brand brand = SpringUtils.getBean(BrandService.class).getById(order.getBrandId());
if (StringUtils.isBlank(brand.getWxAppId()) || !StringUtils.equals(brand.getWxAppId(), wxPayService.getConfig().getAppId())) { if (StringUtils.isBlank(brand.getWxAppId()) || !StringUtils.equals(brand.getWxAppId(), wxPayService.getConfig().getAppId())) {
//需要分账 //需要分账
wxPayProfitSharing(orderId, notifyResult, wxPayService); wxPayAsyncTask.asyncWxPayProfitSharing(orderId, notifyResult, wxPayService);
} }
//异步更新 //异步更新
SpringUtils.getBean(ProductRecordSyncStockTask.class).asyncUpdateRecords(orderNumMap); SpringUtils.getBean(ProductRecordSyncStockTask.class).asyncUpdateRecords(orderNumMap);
@ -344,48 +419,28 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
} }
/** /**
* @descriptions 微信支付分账 * @descriptions 分账结果通知
* @author DB * @author DB
* @date 2023/11/02 15:26 * @date 2023/11/03 9:50
* @param orderId 订单id * @param xmlData 加密数据
* @param notifyResult 微信支付结果
* @param wxPayService 微信支付
* @return: void * @return: void
*/ */
private void wxPayProfitSharing(String orderId, WxPayOrderNotifyResult notifyResult, WxPayService wxPayService) throws WxPayException { @Override
ProfitSharing profitSharing = new ProfitSharing(); public void parseProfitSharingNotifyResult(String xmlData) {
profitSharing.setOrderId(orderId) WxPayService wxPayService = wxPayHandler.getWxPayService();
.setProfitSharingStatus(0) try {
.setOrderSource(OrderSource.MALL.toString()); WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(xmlData);
ProfitSharingService profitSharingService = SpringUtils.getBean(ProfitSharingService.class); //更新分账订单
profitSharingService.save(profitSharing); SpringUtils.getBean(ProfitSharingService.class).updateChain().set(PROFIT_SHARING.OUT_PROFIT_SHARING_ID, notifyResult.getTransactionId())
Double ceil = Math.ceil(notifyResult.getTotalFee() * OrderSource.MALL.getRate()); .set(PROFIT_SHARING.AMOUNT, notifyResult.getTotalFee())
//固定商户信息 .set(PROFIT_SHARING.PROFIT_SHARING_STATUS, 1)
Map<String, Object> mapReceiver = new HashMap<>(4); //当前分账账户
mapReceiver.put("type", "MERCHANT_ID"); .set(PROFIT_SHARING.PAY_ACCOUNT, wxPayService.getConfig().getSubMchId())
mapReceiver.put("account", wxPayProperties.getSharingAccount()); .where(PROFIT_SHARING.ID.eq(notifyResult.getOutTradeNo()))
mapReceiver.put("relation_type", "SERVICE_PROVIDER"); .update();
mapReceiver.put("amount", ceil.longValue()); } catch (WxPayException e) {
mapReceiver.put("name","果酱盒子"); throw new ServiceException(e.getMessage());
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();
} }
/** /**
@ -423,4 +478,77 @@ public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements
.set(ORDER.LOGISTICS_ORDER, bo.getLogisticsOrder()) .set(ORDER.LOGISTICS_ORDER, bo.getLogisticsOrder())
.where(ORDER.ID.eq(bo.getId())).update(); .where(ORDER.ID.eq(bo.getId())).update();
} }
/**
* @descriptions 小程序-重新支付订单
* @author DB
* @date 2023/11/03 16:14
* @param orderId 订单id
* @return: java.lang.Object
*/
@Override
public Object rePayOrder(String orderId) {
Order order = this.getById(orderId);
//获取订单详情
List<OrderDetail> orderDetails = SpringUtils.getBean(OrderDetailService.class).queryChain().where(ORDER_DETAIL.ORDER_ID.eq(orderId)).list();
//获取当前下单用户信息
JSONObject loginUserInfo = SecurityUtils.getInstance().getLoginUserInfo();
List<Product> productList = SpringUtils.getBean(ProductService.class).queryChain()
.select(PRODUCT.ID, PRODUCT.PRODUCT_NAME, PRODUCT.DESCRIPTION)
.from(PRODUCT)
.leftJoin(PRODUCT_RECORD).on(PRODUCT_RECORD.PRODUCT_ID.eq(PRODUCT.ID))
.where(PRODUCT_RECORD.ID.in(orderDetails.stream().map(OrderDetail::getProductRecordId).collect(Collectors.toSet())))
.list();
//分布式锁进行幂等处理
RedisService redisService = SpringUtils.getBean(RedisService.class);
Lock userIdLock = redisService.distributedLock(MallRedisConstant.IDEMPOTENT_LOCK_USER_PAY + loginUserInfo.getString("userId"));
if (userIdLock.tryLock()) {
//检查库存
Map<String, Integer> recordNumIsEnough = null;
try {
recordNumIsEnough= recordNumIsEnough(orderDetails);
//微信支付
if (order.getPayType() == 0) {
//微信支付统一下单
WxPayService wxPayService = wxPayHandler.getWxPayService();
WxPayUnifiedOrderRequest orderRequest = new WxPayUnifiedOrderRequest();
//查询是否是服务商系品牌
String brandId = loginUserInfo.getString("brandId");
Brand brand = SpringUtils.getBean(BrandService.class).getById(brandId);
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(",")))
//商品详情,如果涉及到商品优惠需要重新涉及
.setDetail(JSONObject.toJSONString(BeanUtils.mapToList(orderDetails, WxPayGoodsDetailDto.class)))
.setOutTradeNo(order.getId())
//元转分
.setTotalFee(order.getTotalAmount().scaleByPowerOfTen(2).intValue())
.setTradeType("JSAPI");
return wxPayService.createOrder(orderRequest);
} else {
return pointPay(recordNumIsEnough, order.getId());
}
} catch (Exception e){
//回滚库存
if (recordNumIsEnough != null){
recordNumIsEnough.forEach((key, value) -> {
redisService.longIncrement(MallRedisConstant.STOCK_RECORD_NUM + key, value.longValue());
});
}
throw new ServiceException(e.getMessage());
}finally {
userIdLock.unlock();
}
}else {
//获取锁失败直接返回空
throw new ServiceException("请勿重复下单");
}
}
} }

View File

@ -0,0 +1,32 @@
package com.cpop.mall.business.task;
import com.cpop.core.utils.SpringUtils;
import com.cpop.mall.business.entity.Order;
import com.cpop.mall.business.service.OrderService;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import static com.cpop.mall.business.entity.table.OrderTableDef.ORDER;
/**
* @author DB
* @createTime 2023/11/03 14:56
* @description 订单超时未支付任务
*/
public class OrderOverTimeUnPayTask implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//获取工单id
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
String orderId = jobDataMap.getString("orderId");
OrderService orderService = SpringUtils.getBean(OrderService.class);
Order order = orderService.getById(orderId);
//订单存在并且订单仍然是未支付修改订单为超时
if (null != order && order.getOrderStatus() == 0) {
orderService.updateChain().set(ORDER.ORDER_STATUS, 6).set(ORDER.PREVIOUS_STATUS, 0).where(ORDER.ID.eq(orderId)).update();
}
}
}

View File

@ -12,9 +12,7 @@ import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.util.Iterator; import java.util.*;
import java.util.List;
import java.util.Map;
import static com.cpop.mall.business.entity.table.ProductRecordTableDef.PRODUCT_RECORD; import static com.cpop.mall.business.entity.table.ProductRecordTableDef.PRODUCT_RECORD;
@ -41,7 +39,7 @@ public class ProductRecordSyncStockTask {
RedisService redisService = SpringUtils.getBean(RedisService.class); RedisService redisService = SpringUtils.getBean(RedisService.class);
productRecords.forEach(item->{ productRecords.forEach(item->{
redisService.setCacheObject(MallRedisConstant.STOCK_RECORD_NUM + item.getId(), item.getRecordNum()); redisService.setCacheObject(MallRedisConstant.STOCK_RECORD_NUM + item.getId(), item.getRecordNum());
log.info("商品记录id为:{};商品库存为:{}", item.getProductId(), item.getRecordNum()); log.info("商品记录id为:{};商品库存为:{}", item.getId(), item.getRecordNum());
}); });
log.info("===============================结束<--库存信息结束-->结束==============================="); log.info("===============================结束<--库存信息结束-->结束===============================");
} }

View File

@ -0,0 +1,137 @@
package com.cpop.mall.business.task;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.cpop.core.base.enums.OrderSource;
import com.cpop.core.base.exception.ServiceException;
import com.cpop.core.base.exception.UtilException;
import com.cpop.core.utils.SpringUtils;
import com.cpop.mall.framework.config.wxPay.WxPayProperties;
import com.cpop.system.business.entity.ProfitSharing;
import com.cpop.system.business.service.ProfitSharingService;
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.config.WxPayConfig;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.cpop.system.business.entity.table.ProfitSharingTableDef.PROFIT_SHARING;
/**
* @author DB
* @createTime 2023/11/03 11:13
* @description 微信支付异步处理类
*/
@Component
public class WxPayAsyncTask {
@Autowired
private WxPayProperties wxPayProperties;
/**
* @descriptions 异步分账
* @author DB
* @date 2023/11/03 11:14
* @param orderId 订单id
* @param notifyResult 通知结果
* @param wxPayService 微信支付处理类
* @return: void
*/
@Async("customAsyncThreadPool")
public void asyncWxPayProfitSharing(String orderId, WxPayOrderNotifyResult notifyResult, WxPayService wxPayService) {
ProfitSharingRequest profitSharingRequest = buildProfitSharingRequest(orderId, notifyResult);
try {
Thread.sleep(5000);
wxPayProfitSharing(profitSharingRequest, wxPayService, 0);
} catch (InterruptedException e) {
throw new ServiceException(e.getMessage());
}
}
/**
* @descriptions 构建分账请求参数
* @author DB
* @date 2023/11/03 11:12
* @param orderId 订单号
* @param notifyResult 通知结果
* @return: com.github.binarywang.wxpay.bean.profitsharingV3.ProfitSharingRequest
*/
private ProfitSharingRequest buildProfitSharingRequest(String orderId, WxPayOrderNotifyResult notifyResult) {
Double ceil = Math.ceil(notifyResult.getTotalFee() * OrderSource.MALL.getRate());
ProfitSharing profitSharing = new ProfitSharing();
profitSharing.setOrderId(orderId)
.setProfitSharingStatus(0)
.setOrderSource(OrderSource.MALL.toString())
.setAmount(ceil.longValue())
.setPayAccount(notifyResult.getSubMchId());
ProfitSharingService profitSharingService = SpringUtils.getBean(ProfitSharingService.class);
profitSharingService.save(profitSharing);
//固定商户信息
Map<String, Object> mapReceiver = new HashMap<>(4);
mapReceiver.put("type", "MERCHANT_ID");
mapReceiver.put("account", wxPayProperties.getSharingAccount());
mapReceiver.put("amount", ceil.longValue());
mapReceiver.put("description","分账到服务商");
//TODO:V3版本目前发送失败
/*
ProfitSharingReceiverRequest profitSharingReceiver = new ProfitSharingReceiverRequest();
profitSharingReceiver.setReceiver();
ProfitSharingReceiver profitSharingReceiver = new ProfitSharingReceiver();
profitSharingReceiver.setType("MERCHANT_ID");
profitSharingReceiver.setAccount(wxPayProperties.getSharingAccount());
profitSharingReceiver.setRelationType("SERVICE_PROVIDER");
profitSharingReceiver.setAmount(ceil.longValue());
profitSharingReceiver.setName("果酱盒子");
profitSharingReceiver.setDescription("分账到服务商");
*/
List<Map<String, Object>> receivers = new ArrayList<>();
receivers.add(mapReceiver);
//分账请求参数
ProfitSharingRequest profitSharingRequest = new ProfitSharingRequest();
profitSharingRequest.setReceivers(JSONObject.toJSONString(receivers));
profitSharingRequest.setOutOrderNo(profitSharing.getId());
profitSharingRequest.setTransactionId(notifyResult.getTransactionId());
profitSharingRequest.setSubMchId(notifyResult.getSubMchId());
return profitSharingRequest;
}
/**
* @descriptions 微信支付分账
* @author DB
* @date 2023/11/03 11:09
* @param profitSharingRequest 请求参数
* @param wxPayService 微信服务类
* @param flag 标记
* @return: void
*/
private void wxPayProfitSharing(ProfitSharingRequest profitSharingRequest, WxPayService wxPayService, Integer flag) {
try {
ProfitSharingResult profitSharingResult = wxPayService.getProfitSharingService().profitSharing(profitSharingRequest);
//存入系统
SpringUtils.getBean(ProfitSharingService.class).updateChain()
.set(PROFIT_SHARING.OUT_PROFIT_SHARING_ID,profitSharingResult.getOrderId())
.set(PROFIT_SHARING.PROFIT_SHARING_STATUS,1)
.where(PROFIT_SHARING.ID.eq(profitSharingResult.getOutOrderNo()))
.update();
} catch (WxPayException e) {
if (flag > 5) {
throw new ServiceException(e.getMessage());
} else {
//重复调用
flag = flag + 1;
wxPayProfitSharing(profitSharingRequest, wxPayService, flag);
}
}
}
}

View File

@ -0,0 +1,102 @@
package com.cpop.mall.business.vo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;
/**
* @author DB
* @createTime 2023/11/03 15:54
* @description
*/
@Data
@Accessors(chain = true)
@ApiModel(value = "商城订单详情返回对象")
public class OrderInfoVo implements Serializable {
/**
* 主键
*/
@ApiModelProperty("主键")
private String id;
/**
* 订单状态(0:待付款;1:待发货;2:待确认;3:已完成;4:退款/售后中)
*/
@ApiModelProperty("订单状态(0:待付款;1:待发货;2:待确认;3:已完成;4:退款/售后中)")
private Integer orderStatus;
/**
* 总金额
*/
@ApiModelProperty("总金额")
private BigDecimal totalAmount;
/**
* 总积分
*/
@ApiModelProperty("总积分")
private Long totalPoint;
/**
* 支付类型
*/
@ApiModelProperty("支付类型")
private Integer payType;
/**
* 下单用户名
*/
@ApiModelProperty("下单用户名")
private String payUserName;
/**
* 收货人名
*/
@ApiModelProperty("收货人名")
private String receiveName;
/**
* 收货人电话
*/
@ApiModelProperty("收货人电话")
private String receivePhone;
/**
* 收货地址
*/
@ApiModelProperty("收货地址")
private String receiveAddress;
/**
* 商品名
*/
@ApiModelProperty("商品名")
private String productNames;
/**
* 备注
*/
@ApiModelProperty("备注")
private String remarks;
/**
* 下单时间
*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss" , timezone = "GMT+8")
@ApiModelProperty("下单时间")
private LocalDateTime createTime;
/**
* 订单详情vo
*/
@ApiModelProperty("订单详情")
private List<OrderDetailVo> detailList;
}

View File

@ -65,4 +65,9 @@ public class WxPayProperties {
* 分账账号 * 分账账号
*/ */
private String sharingAccount; private String sharingAccount;
/**
* 分账通知
*/
private String notifySharing;
} }

View File

@ -10,6 +10,11 @@ public interface MallRedisConstant {
*/ */
String STOCK_RECORD_NUM = "mall:stock:recordNum:"; String STOCK_RECORD_NUM = "mall:stock:recordNum:";
/**
* 用户创建预订单(用户id)
*/
String IDEMPOTENT_LOCK_CREATE_ADVANCE_ORDER = "mall:idempotentLock:createAdvanceOrder:";
/** /**
* 用户下单幂等锁(用户id) * 用户下单幂等锁(用户id)
*/ */

View File

@ -0,0 +1,35 @@
package com.cpop.mall.framework.enums;
import lombok.Getter;
@Getter
public enum QuartzEnums {
/**
* 订单超时未支付过期定时任务
*/
ORDER_OVERTIME_UN_PAY_TASK("OrderOvertimeUnPayTask:", "Order");
QuartzEnums(String name, String group) {
this.name = name;
this.group = group;
}
/**
*
*/
private String name;
/**
*
*/
private String group;
public void setName(String name) {
this.name = name;
}
public void setGroup(String group) {
this.group = group;
}
}