perf 消息通知功能调试和流水记录

This commit is contained in:
bootx
2024-02-25 01:43:08 +08:00
parent f1828d89f1
commit f26806816b
40 changed files with 979 additions and 144 deletions

View File

@@ -15,10 +15,11 @@
> DaxPay是一套基于Bootx-Platform脚手架构建的开源支付网关系统已经对接支付宝、微信支付相关的接口以及扩展了钱包支付、储值卡支付、现金支付等新的支付方式。
> 可以独立部署,提供接口供业务系统进行调用,不对原有系统产生影响
> 当前处于功能开发阶段,部分功能可能会有调整,`V2.1.0`时将作为正式生产可用版本进行发布之后会保证系统版本非大版本升级时API接口和数据接口向前兼容
## 🧭 特色功能
- 封装各类支付通道的接口为统一的接口,方便业务系统进行调用,简化对接多种支付方式的复杂度
- 已对接`微信支付`相关的接口,目前已经支持`V2`版本的接口,后续版本将支持`V3`版本的接口
- 已对接`支付宝`相关的接口,目前已经支持`V2`版本的接口,后续版本将支持`V3`版本的接口
- 已对接`微信支付``支付宝`相关的接口,目前已经支持`V2`版本的接口,后续版本将支持`V3`版本的接口
- 支持组合支付,满足用户系统需要多种方式同时进行支付的场景。
- 提供`HTTP`方式接口调用能力,和`Java`版本的`SDK`,方便业务系统进行对接
- 接口请求和响应数据支持启用签名机制,可根据实际需要进行开关,保证交易安全可靠
@@ -162,17 +163,10 @@ public class SimplePayOrderTest {
### 2.0.X版本:
- [ ] 对账比对功能实现
- [ ] 支持转账操作
- [ ] 支持转账、分账操作
- [ ] 云闪付支付支持
- [ ] 微信增加V3版本接口支持
- [ ] 支付宝增加V3版本接口支持
- [x] 钱包功能完善
- [x] 储值卡功能完善
- [x] 现金支付功能完善
- [ ] 支付流水记录功能
- [ ] 支付宝和微信增加V3版本接口支持
- [ ] 消息通知支持消息中间件模式
- [ ] 增加验签调试等功能的页面
- [ ] 支付宝进行关闭时,支持通过撤销模式进行订单关闭
### 2.1.X版本:
- [ ] 增加账户金额表

View File

@@ -1,6 +1,17 @@
# CHANGELOG
## [v2.0.1]
- 增加支付、退款时客户通知功能,支持多次重发
- 增加客户通知任务记录功能
- 支持钱包支付、流水记录、各类操作等功能
- 支持储值卡支付、流水记录、各类操作等功能
- 支持现金支付和流水记录功能
- 增加支付宝流水记录功能
- 增加微信流水记录功能
- 变更:废弃调用接口时的`version`字段调用时不再进行传输SDK中同步进行删除
- 优化:订单支持关闭时间记录
- 优化:增加退款订单扩展记录
- fix: 同步支付通道订单不能正确生成
## [v2.0.0]
- 支持支付宝支付扫码支付、付款码支付、PC支付、H5支付
- 支持微信支付扫码支付、WAP支付、公众号支付

View File

@@ -4,6 +4,10 @@
- [x] 钱包支付, 钱包流水
- [x] 储值卡支付, 储值卡流水
- [ ] 支付回调通知和退款回调通知功能
- [x] 回调任务发起
- [ ] 回调调用记录
- [x] SDK接收对象编写
- [ ] Demo模块接收回调演示
- [x] 通道配置启用停用配置生效
- [x] 支付对账的序列化生成器有问题待platform更新

View File

@@ -0,0 +1,59 @@
package cn.bootx.platform.daxpay.demo.controller;
import cn.bootx.platform.common.core.annotation.IgnoreAuth;
import cn.bootx.platform.daxpay.sdk.model.notice.PayNoticeModel;
import cn.bootx.platform.daxpay.sdk.model.notice.RefundNoticeModel;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
/**
*
* @author xxm
* @since 2024/2/24
*/
@Slf4j
@IgnoreAuth
@Tag(name = "回调测试")
@RestController
@RequestMapping("/demo/callback")
@RequiredArgsConstructor
public class ClientNoticeReceiveController {
@Operation(summary = "支付消息(map接收)")
@PostMapping("/pay")
public String pay(@RequestBody Map<String,Object> map){
log.info("接收到支付回调消息: {}",map);
return "SUCCESS";
}
@Operation(summary = "支付消息(对象接收)")
@PostMapping("/payObject")
public String pay(@RequestBody PayNoticeModel model){
log.info("接收到支付回调消息: {}",model);
return "SUCCESS";
}
@Operation(summary = "退款消息")
@PostMapping("/refund")
public String refund(@RequestBody Map<String,Object> map) {
log.info("接收到退款回调消息: {}",map);
return "SUCCESS";
}
@Operation(summary = "退款消息()")
@PostMapping("/refundObject")
public String refund(@RequestBody RefundNoticeModel map) {
log.info("接收到退款回调消息: {}",map);
return "SUCCESS";
}
}

View File

@@ -0,0 +1,33 @@
package cn.bootx.platform.daxpay.sdk.model.notice;
import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum;
import cn.bootx.platform.daxpay.sdk.code.PayWayEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* 支付通道信息
* @author xxm
* @since 2024/1/7
*/
@Getter
@Setter
@ToString
public class PayChannelModel {
/**
* 支付通道编码
* @see PayChannelEnum#getCode()
*/
private String channel;
/**
* 支付方式编码
* @see PayWayEnum
*/
private String way;
/** 支付金额 */
private Integer amount;
}

View File

@@ -0,0 +1,63 @@
package cn.bootx.platform.daxpay.sdk.model.notice;
import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum;
import cn.bootx.platform.daxpay.sdk.code.PayStatusEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
/**
* 支付异步通知类
* @author xxm
* @since 2024/1/7
*/
@Getter
@Setter
@ToString
public class PayNoticeModel {
/** 支付ID */
private Long paymentId;
/** 业务号 */
private String businessNo;
/** 是否是异步支付 */
private boolean asyncPay;
/**
* 异步支付通道
* @see PayChannelEnum
*/
private String asyncChannel;
/** 支付金额 */
private Integer amount;
/** 支付通道信息 */
private List<PayChannelModel> payChannels;
/**
* 支付状态
* @see PayStatusEnum
*/
private String status;
/** 支付创建时间 */
private Long createTime;
/** 支付成功时间 */
private Long payTime;
/** 支付关闭时间 */
private Long closeTime;
/** 商户扩展参数,回调时会原样返回 */
private String attach;
/** 签名 */
private String sign;
}

View File

@@ -0,0 +1,28 @@
package cn.bootx.platform.daxpay.sdk.model.notice;
import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
/**
* 退款通道信息
* @author xxm
* @since 2024/2/22
*/
@Getter
@Setter
@ToString
public class RefundChannelModel {
/**
* 通道编码
* @see PayChannelEnum#getCode()
*/
private String channel;
/**
* 退款金额
*/
private Integer amount;
}

View File

@@ -0,0 +1,59 @@
package cn.bootx.platform.daxpay.sdk.model.notice;
import cn.bootx.platform.daxpay.sdk.code.PayChannelEnum;
import cn.bootx.platform.daxpay.sdk.code.RefundStatusEnum;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.util.List;
/**
* 退款通知消息
* @author xxm
* @since 2024/2/22
*/
@Getter
@Setter
@ToString
public class RefundNoticeModel {
/** 退款ID */
private Long refundId;
/** 退款号 */
private String refundNo;
/** 是否含有异步通道 */
private boolean asyncPay;
/**
* 异步通道
* @see PayChannelEnum
*/
private String asyncChannel;
/** 退款金额 */
private Integer amount;
/** 退款通道信息 */
private List<RefundChannelModel> refundChannels;
/**
* 退款状态
* @see RefundStatusEnum
*/
private String status;
/** 退款成功时间 */
private Long refundTime;
/** 退款创建时间 */
private Long createTime;
/** 商户扩展参数,回调时会原样返回 */
private String attach;
/** 签名 */
private String sign;
}

View File

@@ -67,7 +67,6 @@ public class DaxPayKit {
}
String body = execute.body();
log.debug("响应参数:{}", body);
return request.toModel(body);
}

View File

@@ -7,6 +7,7 @@ import cn.bootx.platform.daxpay.sdk.net.DaxPayConfig;
import cn.bootx.platform.daxpay.sdk.net.DaxPayKit;
import cn.bootx.platform.daxpay.sdk.param.pay.SimplePayParam;
import cn.bootx.platform.daxpay.sdk.response.DaxPayResult;
import cn.hutool.core.util.RandomUtil;
import org.junit.Before;
import org.junit.Test;
@@ -27,6 +28,9 @@ public class SimplePayOrderTest {
DaxPayKit.initConfig(config);
}
/**
* 异步通道测试
*/
@Test
public void simplePay() {
// 简单支付参数
@@ -44,4 +48,24 @@ public class SimplePayOrderTest {
PayOrderModel data = execute.getData();
System.out.println(data);
}
/**
* 同步通道+回调测试
*/
@Test
public void simplePayCallback() {
SimplePayParam param = new SimplePayParam();
param.setBusinessNo("P"+ RandomUtil.randomNumbers(5));
param.setAmount(12);
param.setTitle("测试接口支付");
param.setChannel(PayChannelEnum.CASH.getCode());
param.setPayWay(PayWayEnum.NORMAL.getCode());
param.setClientIp("127.0.0.1");
param.setNotifyUrl("http://127.0.0.1:9000/demo/callback/pay");
param.setNotifyUrl("http://127.0.0.1:9000/demo/callback/payObject");
DaxPayResult<PayOrderModel> execute = DaxPayKit.execute(param);
System.out.println(execute);
PayOrderModel data = execute.getData();
System.out.println(data);
}
}

View File

@@ -0,0 +1,64 @@
package cn.bootx.platform.daxpay.admin.controller.task;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.daxpay.service.core.task.notice.service.ClientNoticeTaskService;
import cn.bootx.platform.daxpay.service.dto.record.notice.ClientNoticeRecordDto;
import cn.bootx.platform.daxpay.service.dto.record.notice.ClientNoticeTaskDto;
import cn.bootx.platform.daxpay.service.param.record.ClientNoticeRecordQuery;
import cn.bootx.platform.daxpay.service.param.record.ClientNoticeTaskQuery;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 客户通知任务
* @author xxm
* @since 2024/2/24
*/
@Tag(name = "客户系统通知任务")
@RestController
@RequestMapping("/task/notice")
@RequiredArgsConstructor
public class ClientNoticeTaskController {
private final ClientNoticeTaskService clientNoticeTaskService;
@Operation(summary = "重新发送消息通知")
@PostMapping("/resetSend")
public ResResult<Void> resetSend(Long taskId){
clientNoticeTaskService.sendData(taskId);
return Res.ok();
}
@Operation(summary = "分页查询")
@GetMapping("/page")
public ResResult<PageResult<ClientNoticeTaskDto>> page(PageParam pageParam, ClientNoticeTaskQuery query){
return Res.ok(clientNoticeTaskService.taskPage(pageParam, query));
}
@Operation(summary = "查询单条")
@GetMapping("/findById")
public ResResult<ClientNoticeTaskDto> findById(Long id){
return Res.ok(clientNoticeTaskService.findTaskById(id));
}
@Operation(summary = "分页查询")
@GetMapping("/record/page")
public ResResult<PageResult<ClientNoticeRecordDto>> recordPage(PageParam pageParam, ClientNoticeRecordQuery query){
return Res.ok(clientNoticeTaskService.recordPage(pageParam, query));
}
@Operation(summary = "查询单条")
@GetMapping("/record/findById")
public ResResult<ClientNoticeRecordDto> findRecordById(Long id){
return Res.ok(clientNoticeTaskService.findRecordById(id));
}
}

View File

@@ -0,0 +1,22 @@
package cn.bootx.platform.daxpay.service.code;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 消息通知发送类型
* @author xxm
* @since 2024/2/25
*/
@Getter
@AllArgsConstructor
public enum ClientNoticeSendTypeEnum {
/** 自动发送 */
AUTO("auto", "自动发送"),
/** 手动发送 */
MANUAL("manual", "手动发送");
private final String type;
private final String name;
}

View File

@@ -1,10 +1,14 @@
package cn.bootx.platform.daxpay.service.common.context;
import cn.bootx.platform.daxpay.code.PayWayEnum;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
/**
* 异步支付信息
@@ -13,7 +17,7 @@ import java.time.LocalDateTime;
*/
@Data
@Accessors(chain = true)
public class AsyncPayLocal {
public class PayLocal {
/**
* 异步支付方式
@@ -36,4 +40,13 @@ public class AsyncPayLocal {
/** 订单失效时间, */
private LocalDateTime expiredTime;
/** 支付订单 */
private PayOrder payOrder;
/** 支付订单扩展 */
private PayOrderExtra payOrderExtra;
/** 通道支付订单 */
private List<PayChannelOrder> payChannelOrders;
}

View File

@@ -18,8 +18,8 @@ public class PaymentContext {
/** 平台全局配置 */
private final PlatformLocal platformInfo = new PlatformLocal();
/** 异步支付相关信息 */
private final AsyncPayLocal asyncPayInfo = new AsyncPayLocal();
/** 支付相关信息 */
private final PayLocal payInfo = new PayLocal();
/** 退款相关信息 */
private final RefundLocal refundInfo = new RefundLocal();

View File

@@ -6,7 +6,7 @@ import cn.bootx.platform.daxpay.param.channel.AliPayParam;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.service.code.AliPayCode;
import cn.bootx.platform.daxpay.service.code.AliPayWay;
import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.service.common.context.PayLocal;
import cn.bootx.platform.daxpay.service.common.context.NoticeLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
@@ -66,7 +66,7 @@ public class AliPayService {
Integer amount = payChannelParam.getAmount();
String payBody = null;
// 异步线程存储
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
// wap支付
if (Objects.equals(payChannelParam.getWay(), PayWayEnum.WAP.getCode())) {
payBody = this.wapPay(amount, payOrder, alipayConfig);
@@ -207,7 +207,7 @@ public class AliPayService {
* 付款码支付
*/
public void barCode(int amount, PayOrder payOrder, AliPayParam aliPayParam, AliPayConfig alipayConfig) {
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
AlipayTradePayModel model = new AlipayTradePayModel();
model.setSubject(payOrder.getTitle());

View File

@@ -10,7 +10,7 @@ import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.result.pay.SyncResult;
import cn.bootx.platform.daxpay.service.code.WeChatPayCode;
import cn.bootx.platform.daxpay.service.code.WeChatPayWay;
import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.service.common.context.PayLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
@@ -79,7 +79,7 @@ public class WeChatPayService {
Integer amount = payChannelParam.getAmount();
String totalFee = String.valueOf(amount);
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();;
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();;
String payBody = null;
PayWayEnum payWayEnum = PayWayEnum.findByCode(payChannelParam.getWay());
@@ -173,7 +173,7 @@ public class WeChatPayService {
* 付款码支付
*/
private void barCode(String amount, PayOrder payment, String authCode, WeChatPayConfig weChatPayConfig) {
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
Map<String, String> params = MicroPayModel.builder()
.appid(weChatPayConfig.getWxAppId())

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.result.pay.PayResult;
import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.service.common.context.PayLocal;
import cn.bootx.platform.daxpay.service.common.context.NoticeLocal;
import cn.bootx.platform.daxpay.service.common.context.PlatformLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
@@ -37,7 +37,7 @@ public class PayBuilder {
public PayOrder buildPayOrder(PayParam payParam) {
// 订单超时时间
LocalDateTime expiredTime = PaymentContextLocal.get()
.getAsyncPayInfo()
.getPayInfo()
.getExpiredTime();
// 计算总价
int sumAmount = payParam.getPayChannels().stream()
@@ -118,7 +118,7 @@ public class PayBuilder {
paymentResult.setStatus(payOrder.getStatus());
// 设置异步支付参数
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();;
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();;
if (StrUtil.isNotBlank(asyncPayInfo.getPayBody())) {
paymentResult.setPayBody(asyncPayInfo.getPayBody());
}

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.common.core.util.ResultConvertUtil;
import cn.bootx.platform.daxpay.code.RefundStatusEnum;
import cn.bootx.platform.daxpay.code.PayStatusEnum;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.service.common.context.PayLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
@@ -54,7 +54,7 @@ public class PayChannelOrderService {
*/
@Transactional(rollbackFor = Exception.class)
public void switchAsyncPayChannel(PayOrder payOrder, PayChannelParam payChannelParam){
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
// 是否支付完成
PayStatusEnum payStatus = asyncPayInfo.isPayComplete() ? PayStatusEnum.SUCCESS : PayStatusEnum.PROGRESS;
// 判断新发起的

View File

@@ -1,18 +0,0 @@
package cn.bootx.platform.daxpay.service.core.payment.notice.dao;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.daxpay.service.core.payment.notice.entity.ClientNoticeTask;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
*
* @author xxm
* @since 2024/2/20
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ClientNoticeTaskManager extends BaseManager<ClientNoticeTaskMapper, ClientNoticeTask> {
}

View File

@@ -1,17 +0,0 @@
package cn.bootx.platform.daxpay.service.core.payment.notice.entity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 消息通知任务记录表
* @author xxm
* @since 2024/2/20
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
public class ClientNoticeRecord extends MpCreateEntity {
}

View File

@@ -3,8 +3,10 @@ package cn.bootx.platform.daxpay.service.core.payment.notice.service;
import cn.bootx.platform.common.core.exception.RepetitiveOperationException;
import cn.bootx.platform.common.core.util.CollUtil;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.common.jackson.util.JacksonUtil;
import cn.bootx.platform.common.redis.RedisClient;
import cn.bootx.platform.daxpay.code.PaySignTypeEnum;
import cn.bootx.platform.daxpay.service.code.ClientNoticeSendTypeEnum;
import cn.bootx.platform.daxpay.service.code.ClientNoticeTypeEnum;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayOrderExtraManager;
@@ -12,24 +14,23 @@ import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.refund.dao.RefundOrderExtraManager;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.refund.entity.RefundOrder;
import cn.bootx.platform.daxpay.service.core.payment.notice.dao.ClientNoticeTaskManager;
import cn.bootx.platform.daxpay.service.core.payment.notice.entity.ClientNoticeTask;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.PayChannelResult;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.PayNoticeResult;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.RefundChannelResult;
import cn.bootx.platform.daxpay.service.core.payment.notice.result.RefundNoticeResult;
import cn.bootx.platform.daxpay.service.core.system.config.entity.PlatformConfig;
import cn.bootx.platform.daxpay.service.core.system.config.service.PlatformConfigService;
import cn.bootx.platform.daxpay.service.core.task.notice.dao.ClientNoticeTaskManager;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeRecord;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeTask;
import cn.bootx.platform.daxpay.service.core.task.notice.service.ClientNoticeRecordService;
import cn.bootx.platform.daxpay.util.PaySignUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.HttpException;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.lock.LockInfo;
import com.baomidou.lock.LockTemplate;
import lombok.RequiredArgsConstructor;
@@ -43,7 +44,7 @@ import java.util.stream.Collectors;
/**
* 消息通知任务服务
* 总共会发起15次通知通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h
* 如果失败总共会重新发起15次通知通知频率为15s/15s/30s/3m/10m/20m/30m/30m/30m/60m/3h/3h/3h/6h/6h
* @author xxm
* @since 2024/2/20
*/
@@ -56,14 +57,14 @@ public class ClientNoticeService {
private final PayChannelOrderManager payChannelOrderManager;
private final RefundOrderExtraManager refundOrderExtraManager;
private final RefundChannelOrderManager refundChannelOrderManager;
private final PlatformConfigService configService;
private final ClientNoticeTaskManager taskManager;
private final ClientNoticeRecordService recordService;
private final RedisClient redisClient;
private final LockTemplate lockTemplate;
@@ -129,12 +130,8 @@ public class ClientNoticeService {
log.error("注册支付消息通知任务失败数据错误订单ID{}",order.getId());
throw new RuntimeException(e);
}
try {
// 同时触发一次通知, 如果成功发送, 任务结束
this.sendData(task, LocalDateTime.now());
} catch (HttpException e) {
this.failHandler(task, LocalDateTime.now());
}
// 同时触发一次通知, 如果成功发送, 任务结束
this.sendData(task, LocalDateTime.now());
}
/**
@@ -170,7 +167,8 @@ public class ClientNoticeService {
}
return new ClientNoticeTask()
.setUrl(orderExtra.getNotifyUrl())
.setContent(JSONUtil.toJsonStr(payNoticeResult))
// 时间序列化进行了重写
.setContent(JacksonUtil.toJson(payNoticeResult))
.setType(ClientNoticeTypeEnum.PAY.getType())
.setSendCount(0)
.setOrderId(order.getId());
@@ -209,14 +207,11 @@ public class ClientNoticeService {
taskManager.save(task);
} catch (Exception e) {
log.error("注册退款消息通知任务失败数据错误订单ID{}",order.getId());
log.error("错误内容",e);
throw new RuntimeException(e);
}
try {
// 同时触发一次通知, 如果成功发送, 任务结束
this.sendData(task, LocalDateTime.now());
} catch (HttpException e) {
this.failHandler(task, LocalDateTime.now());
}
// 同时触发一次通知, 如果成功发送, 任务结束
this.sendData(task, LocalDateTime.now());
}
/**
@@ -252,7 +247,8 @@ public class ClientNoticeService {
}
return new ClientNoticeTask()
.setUrl(orderExtra.getNotifyUrl())
.setContent(JSONUtil.toJsonStr(payNoticeResult))
// 时间序列化进行了重写
.setContent(JacksonUtil.toJson(payNoticeResult))
.setType(ClientNoticeTypeEnum.PAY.getType())
.setSendCount(0)
.setOrderId(order.getId());
@@ -269,9 +265,9 @@ public class ClientNoticeService {
// 发起一个异步任务,
for (String taskId : taskIds) {
this.run(Long.valueOf(taskId));
// 删除Redis中任务
redisClient.zremByMembers(KEY,taskId);
}
// 删除Redis中任务
redisClient.zremRangeByScore(KEY, start, end);
}
/**
@@ -285,16 +281,19 @@ public class ClientNoticeService {
throw new RepetitiveOperationException("支付同步处理中,请勿重复操作");
}
// 查询任务, 进行发送
ClientNoticeTask task = taskManager.findById(taskId).orElse(null);
ClientNoticeTask task = null;
try {
task = taskManager.findById(taskId).orElse(null);
// 不存在任务直接跳过
if (Objects.isNull(task)) {
return;
}
// 已经发送成功则不进行发送
if (task.isSuccess()){
return;
}
// 执行发送逻辑
this.sendData(task, now);
} catch (HttpException e) {
//noinspection DataFlowIssue
this.failHandler(task, now);
} finally {
lockTemplate.releaseLock(lock);
}
@@ -304,17 +303,37 @@ public class ClientNoticeService {
* 发送通知数据, 如果失败, 注册下次重发的任务
*/
private void sendData(ClientNoticeTask task, LocalDateTime now){
HttpResponse execute = HttpUtil.createPost(task.getUrl())
.body(task.getContent(), ContentType.JSON.getValue())
.timeout(5000)
.execute();
String body = execute.body();
// 创建记录
ClientNoticeRecord record = new ClientNoticeRecord()
.setTaskId(task.getId())
.setType(ClientNoticeSendTypeEnum.AUTO.getType())
.setReqCount(task.getSendCount()+1);
String body = null;
try {
HttpResponse execute = HttpUtil.createPost(task.getUrl())
.body(task.getContent(), ContentType.JSON.getValue())
.timeout(5000)
.execute();
body = execute.body();
} catch (Exception e) {
log.error("发送通知失败数据错误任务ID{}",task.getOrderId());
log.error("错误内容",e);
record.setErrorMsg(e.getMessage());
}
// 如果响应值等于SUCCESS, 说明发送成功, 进行成功处理
if (Objects.equals(body, "SUCCESS")){
this.successHandler(task);
record.setSuccess(true);
} else {
this.failHandler(task,now);
// 如果响应地址不为空, 将错误响应写到记录中
if (Objects.nonNull(body)){
// 预防返回值过长, 只保留100位
record.setErrorMsg(StrUtil.sub(body,0,100));
}
}
// 保存请求记录
recordService.save(record);
}
/**
@@ -331,8 +350,13 @@ public class ClientNoticeService {
* 失败处理, 首先发送次数+1, 然后注册后推指定时间的重试任务
*/
private void failHandler(ClientNoticeTask task, LocalDateTime now){
// 为空不进行处理
if (Objects.isNull(task)){
return;
}
// 次数+1
task.setSendCount(task.getSendCount() + 1);
task.setSendCount(task.getSendCount() + 1).setLatestTime(now);
// 判断发送次数是否未超过15次, 注册任务到redis中
if (task.getSendCount() < 16){
// 根据当前次数和时间计算出毫秒值, 然后写入到Redis中

View File

@@ -17,6 +17,7 @@ import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderQueryService;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
import cn.bootx.platform.daxpay.service.core.payment.sync.service.PaySyncService;
import cn.bootx.platform.daxpay.service.func.AbsPayStrategy;
import cn.bootx.platform.daxpay.util.PayUtil;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
@@ -27,6 +28,7 @@ import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.bootx.platform.daxpay.code.PayStatusEnum.*;
@@ -71,7 +73,7 @@ public class PayAssistService {
if (PayUtil.isNotSync(payParam.getPayChannels())){
return;
}
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
PlatformLocal platform = PaymentContextLocal.get().getPlatformInfo();
// 支付订单是非为空
if (Objects.nonNull(order)){
@@ -100,18 +102,18 @@ public class PayAssistService {
// 首先读取请求参数
noticeInfo.setNotifyUrl(payParam.getNotifyUrl());
// 读取接口配置
if (StrUtil.isNotBlank(noticeInfo.getNotifyUrl())){
if (StrUtil.isBlank(noticeInfo.getNotifyUrl())){
noticeInfo.setNotifyUrl(apiInfo.getNoticeUrl());
}
// 读取平台配置
if (StrUtil.isNotBlank(noticeInfo.getNotifyUrl())){
if (StrUtil.isBlank(noticeInfo.getNotifyUrl())){
noticeInfo.setNotifyUrl(platform.getNotifyUrl());
}
}
// 同步回调
noticeInfo.setNotifyUrl(payParam.getReturnUrl());
if (StrUtil.isNotBlank(noticeInfo.getNotifyUrl())){
noticeInfo.setNotifyUrl(platform.getNotifyUrl());
noticeInfo.setReturnUrl(payParam.getReturnUrl());
if (StrUtil.isBlank(noticeInfo.getReturnUrl())){
noticeInfo.setReturnUrl(platform.getNotifyUrl());
}
// 退出回调地址
noticeInfo.setQuitUrl(payParam.getQuitUrl());
@@ -141,16 +143,31 @@ public class PayAssistService {
}
/**
* 创建支付订单/附加表/支付通道表并保存返回支付订单
* 创建支付订单并保存, 返回支付订单
*/
public PayOrder createPayOrder(PayParam payParam) {
PayLocal payInfo = PaymentContextLocal.get().getPayInfo();
// 构建支付订单并保存
PayOrder payOrder = PayBuilder.buildPayOrder(payParam);
payOrderService.save(payOrder);
PayOrder order = PayBuilder.buildPayOrder(payParam);
payOrderService.save(order);
// 构建支付订单扩展表并保存
PayOrderExtra payOrderExtra = PayBuilder.buildPayOrderExtra(payParam, payOrder.getId());
PayOrderExtra payOrderExtra = PayBuilder.buildPayOrderExtra(payParam, order.getId());
payOrderExtraManager.save(payOrderExtra);
return payOrder;
payInfo.setPayOrder(order).setPayOrderExtra(payOrderExtra);
return order;
}
/**
* 创建并保存通道支付订单
*/
public void createPayChannelOrder(List<AbsPayStrategy> payStrategies) {
PayLocal payInfo = PaymentContextLocal.get().getPayInfo();
List<PayChannelOrder> channelOrders = payStrategies.stream()
.map(AbsPayStrategy::getChannelOrder)
.filter(Objects::nonNull)
.collect(Collectors.toList());
payChannelOrderManager.saveAll(channelOrders);
payInfo.setPayChannelOrders(channelOrders);
}
/**
@@ -165,8 +182,7 @@ public class PayAssistService {
PayOrderExtra payOrderExtra = payOrderExtraManager.findById(paymentId)
.orElseThrow(() -> new DataNotExistException("支付订单不存在"));
NoticeLocal noticeInfo = PaymentContextLocal.get()
.getNoticeInfo();
NoticeLocal noticeInfo = PaymentContextLocal.get().getNoticeInfo();
String notifyUrl = noticeInfo.getNotifyUrl();
String returnUrl = noticeInfo.getReturnUrl();

View File

@@ -6,11 +6,10 @@ import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.param.pay.PayParam;
import cn.bootx.platform.daxpay.param.pay.SimplePayParam;
import cn.bootx.platform.daxpay.result.pay.PayResult;
import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.service.common.context.PayLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.order.pay.builder.PayBuilder;
import cn.bootx.platform.daxpay.service.core.order.pay.dao.PayChannelOrderManager;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayChannelOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrderExtra;
import cn.bootx.platform.daxpay.service.core.order.pay.service.PayOrderService;
@@ -33,7 +32,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import static cn.bootx.platform.daxpay.code.PayStatusEnum.*;
@@ -144,31 +142,27 @@ public class PayService {
private void firstPayHandler(PayParam payParam, PayOrder payOrder) {
// 1.获取支付方式,通过工厂生成对应的策略组
List<AbsPayStrategy> payStrategyList = PayStrategyFactory.createAsyncLast(payParam.getPayChannels());
if (CollectionUtil.isEmpty(payStrategyList)) {
List<AbsPayStrategy> strategies = PayStrategyFactory.createAsyncLast(payParam.getPayChannels());
if (CollectionUtil.isEmpty(strategies)) {
throw new PayUnsupportedMethodException();
}
// 2.初始化支付的参数
for (AbsPayStrategy paymentStrategy : payStrategyList) {
paymentStrategy.initPayParam(payOrder, payParam);
for (AbsPayStrategy strategy : strategies) {
strategy.initPayParam(payOrder, payParam);
}
// 3.1 执行支付前处理动作
payStrategyList.forEach(AbsPayStrategy::doBeforePayHandler);
strategies.forEach(AbsPayStrategy::doBeforePayHandler);
// 3.2 执行通道支付单的生成动作
payStrategyList.forEach(AbsPayStrategy::generateChannelOrder);
strategies.forEach(AbsPayStrategy::generateChannelOrder);
// 4.1 支付操作
payStrategyList.forEach(AbsPayStrategy::doPayHandler);
strategies.forEach(AbsPayStrategy::doPayHandler);
// 4.2 支付调用成功操作, 进行扣款、创建记录类类似的操作
payStrategyList.forEach(AbsPayStrategy::doSuccessHandler);
// 4.3 获取通道支付订单进行保存, 异步支付通道的订单单独处理
List<PayChannelOrder> channelOrders = payStrategyList.stream()
.map(AbsPayStrategy::getChannelOrder)
.filter(Objects::nonNull)
.collect(Collectors.toList());
payChannelOrderManager.saveAll(channelOrders);
strategies.forEach(AbsPayStrategy::doSuccessHandler);
// 4.3 保存通道支付订单
payAssistService.createPayChannelOrder(strategies);
// 5.1 如果没有异步支付, 直接进行订单完成处理
if (PayUtil.isNotSync(payParam.getPayChannels())) {
@@ -178,7 +172,7 @@ public class PayService {
payOrderService.updateById(payOrder);
}
// 5.2 如果异步支付完成, 进行订单完成处理, 同时发送回调消息
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
if (asyncPayInfo.isPayComplete()) {
payOrder.setGatewayOrderNo(asyncPayInfo.getGatewayOrderNo())
.setStatus(SUCCESS.getCode())
@@ -188,9 +182,9 @@ public class PayService {
// 如果支付完成 发送通知
if (Objects.equals(payOrder.getStatus(), SUCCESS.getCode())){
clientNoticeService.registerPayNotice(payOrder, null, channelOrders);
PayLocal payInfo = PaymentContextLocal.get().getPayInfo();
clientNoticeService.registerPayNotice(payOrder, payInfo.getPayOrderExtra(), payInfo.getPayChannelOrders());
}
}
/**
@@ -222,7 +216,7 @@ public class PayService {
payStrategyList.forEach(AbsPayStrategy::doSuccessHandler);
// 6.1 如果异步支付完成, 进行订单完成处理
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
if (asyncPayInfo.isPayComplete()) {
payOrder.setGatewayOrderNo(asyncPayInfo.getGatewayOrderNo())
.setStatus(SUCCESS.getCode())

View File

@@ -5,7 +5,7 @@ import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.param.channel.AliPayParam;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.service.common.context.PayLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.alipay.entity.AliPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.alipay.service.AliPayConfigService;
@@ -93,7 +93,7 @@ public class AliPayStrategy extends AbsPayStrategy {
*/
@Override
public void doSuccessHandler() {
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
channelOrderService.switchAsyncPayChannel(this.getOrder(), this.getPayChannelParam());
// 支付完成, 保存记录
if (asyncPayInfo.isPayComplete()) {

View File

@@ -3,7 +3,7 @@ package cn.bootx.platform.daxpay.service.core.payment.pay.strategy;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayAmountAbnormalException;
import cn.bootx.platform.daxpay.param.pay.PayChannelParam;
import cn.bootx.platform.daxpay.service.common.context.AsyncPayLocal;
import cn.bootx.platform.daxpay.service.common.context.PayLocal;
import cn.bootx.platform.daxpay.service.common.local.PaymentContextLocal;
import cn.bootx.platform.daxpay.service.core.channel.wechat.entity.WeChatPayConfig;
import cn.bootx.platform.daxpay.service.core.channel.wechat.service.WeChatPayConfigService;
@@ -92,7 +92,7 @@ public class WeChatPayStrategy extends AbsPayStrategy {
public void doSuccessHandler() {
channelOrderService.switchAsyncPayChannel(this.getOrder(), this.getPayChannelParam());
this.getOrder().setAsyncChannel(this.getChannel().getCode());
AsyncPayLocal asyncPayInfo = PaymentContextLocal.get().getAsyncPayInfo();
PayLocal asyncPayInfo = PaymentContextLocal.get().getPayInfo();
// 是否支付完成, 保存流水记录
if (asyncPayInfo.isPayComplete()){
weChatPayRecordService.pay(this.getOrder(), this.getChannelOrder());

View File

@@ -0,0 +1,23 @@
package cn.bootx.platform.daxpay.service.core.task.notice.convert;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeRecord;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeTask;
import cn.bootx.platform.daxpay.service.dto.record.notice.ClientNoticeRecordDto;
import cn.bootx.platform.daxpay.service.dto.record.notice.ClientNoticeTaskDto;
import org.mapstruct.Mapper;
import org.mapstruct.factory.Mappers;
/**
*
* @author xxm
* @since 2024/2/23
*/
@Mapper
public interface ClientNoticeConvert {
ClientNoticeConvert CONVERT = Mappers.getMapper(ClientNoticeConvert.class);
ClientNoticeRecordDto convert(ClientNoticeRecord in);
ClientNoticeTaskDto convert(ClientNoticeTask in);
}

View File

@@ -0,0 +1,35 @@
package cn.bootx.platform.daxpay.service.core.task.notice.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.query.generator.QueryGenerator;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeRecord;
import cn.bootx.platform.daxpay.service.param.record.ClientNoticeRecordQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Repository;
/**
*
* @author xxm
* @since 2024/2/23
*/
@Slf4j
@Repository
@RequiredArgsConstructor
public class ClientNoticeRecordManager extends BaseManager<ClientNoticeRecordMapper, ClientNoticeRecord> {
/**
* 分页
*/
public Page<ClientNoticeRecord> page(PageParam pageParam, ClientNoticeRecordQuery query){
QueryWrapper<ClientNoticeRecord> generator = QueryGenerator.generator(query);
Page<ClientNoticeRecord> mpPage = MpUtil.getMpPage(pageParam, ClientNoticeRecord.class);
return this.page(mpPage, generator);
}
}

View File

@@ -0,0 +1,14 @@
package cn.bootx.platform.daxpay.service.core.task.notice.dao;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeRecord;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
*
* @author xxm
* @since 2024/2/23
*/
@Mapper
public interface ClientNoticeRecordMapper extends BaseMapper<ClientNoticeRecord> {
}

View File

@@ -0,0 +1,35 @@
package cn.bootx.platform.daxpay.service.core.task.notice.dao;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.impl.BaseManager;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.common.query.generator.QueryGenerator;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeTask;
import cn.bootx.platform.daxpay.service.param.record.ClientNoticeTaskQuery;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
*
* @author xxm
* @since 2024/2/20
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ClientNoticeTaskManager extends BaseManager<ClientNoticeTaskMapper, ClientNoticeTask> {
/**
* 分页
*/
public Page<ClientNoticeTask> page(PageParam pageParam, ClientNoticeTaskQuery query){
QueryWrapper<ClientNoticeTask> generator = QueryGenerator.generator(query);
Page<ClientNoticeTask> mpPage = MpUtil.getMpPage(pageParam, ClientNoticeTask.class);
return this.page(mpPage, generator);
}
}

View File

@@ -1,6 +1,6 @@
package cn.bootx.platform.daxpay.service.core.payment.notice.dao;
package cn.bootx.platform.daxpay.service.core.task.notice.dao;
import cn.bootx.platform.daxpay.service.core.payment.notice.entity.ClientNoticeTask;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeTask;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;

View File

@@ -0,0 +1,57 @@
package cn.bootx.platform.daxpay.service.core.task.notice.entity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.daxpay.service.code.ClientNoticeSendTypeEnum;
import cn.bootx.platform.daxpay.service.core.task.notice.convert.ClientNoticeConvert;
import cn.bootx.platform.daxpay.service.dto.record.notice.ClientNoticeRecordDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 消息通知任务记录表
* @author xxm
* @since 2024/2/20
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@DbTable(comment = "消息通知任务记录")
@TableName("pay_client_notice_record")
public class ClientNoticeRecord extends MpCreateEntity implements EntityBaseFunction<ClientNoticeRecordDto> {
/** 任务ID */
@DbColumn(comment = "任务ID")
private Long taskId;
/** 请求次数 */
@DbColumn(comment = "请求次数")
private Integer reqCount;
/** 发送是否成功 */
@DbColumn(comment = "发送是否成功")
private boolean success;
/**
* 发送类型, 自动发送, 手动发送
* @see ClientNoticeSendTypeEnum
*/
@DbColumn(comment = "发送类型")
private String type;
/** 错误信息 */
@DbColumn(comment = "错误信息")
private String errorMsg;
/**
* 转换
*/
@Override
public ClientNoticeRecordDto toDto() {
return ClientNoticeConvert.CONVERT.convert(this);
}
}

View File

@@ -1,7 +1,10 @@
package cn.bootx.platform.daxpay.service.core.payment.notice.entity;
package cn.bootx.platform.daxpay.service.core.task.notice.entity;
import cn.bootx.platform.common.mybatisplus.base.MpCreateEntity;
import cn.bootx.platform.common.core.function.EntityBaseFunction;
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
import cn.bootx.platform.daxpay.service.code.ClientNoticeTypeEnum;
import cn.bootx.platform.daxpay.service.core.task.notice.convert.ClientNoticeConvert;
import cn.bootx.platform.daxpay.service.dto.record.notice.ClientNoticeTaskDto;
import cn.bootx.table.modify.annotation.DbColumn;
import cn.bootx.table.modify.annotation.DbTable;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -9,6 +12,8 @@ import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 消息通知任务
* @author xxm
@@ -18,18 +23,18 @@ import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
@DbTable(comment = "消息通知任务")
@TableName("tb_client_notice_task")
public class ClientNoticeTask extends MpCreateEntity {
@TableName("pay_client_notice_task")
public class ClientNoticeTask extends MpBaseEntity implements EntityBaseFunction<ClientNoticeTaskDto> {
/** 本地订单ID */
@DbColumn(comment = "本地订单ID")
private Long orderId;
/**
* 回调类型
* 消息类型
* @see ClientNoticeTypeEnum
*/
@DbColumn(comment = "回调类型")
@DbColumn(comment = "消息类型")
private String type;
/** 消息内容 */
@@ -47,4 +52,16 @@ public class ClientNoticeTask extends MpCreateEntity {
/** 发送地址 */
@DbColumn(comment = "发送地址")
private String url;
/** 最后发送时间 */
@DbColumn(comment = "最后发送时间")
private LocalDateTime latestTime;
/**
* 转换
*/
@Override
public ClientNoticeTaskDto toDto() {
return ClientNoticeConvert.CONVERT.convert(this);
}
}

View File

@@ -0,0 +1,29 @@
package cn.bootx.platform.daxpay.service.core.task.notice.service;
import cn.bootx.platform.daxpay.service.core.task.notice.dao.ClientNoticeRecordManager;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeRecord;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
/**
* 消息通知发送记录
* @author xxm
* @since 2024/2/23
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ClientNoticeRecordService {
private final ClientNoticeRecordManager recordManager;
/**
* 保存
*/
@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)
public void save(ClientNoticeRecord record){
recordManager.save(record);
}
}

View File

@@ -0,0 +1,70 @@
package cn.bootx.platform.daxpay.service.core.task.notice.service;
import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.PageResult;
import cn.bootx.platform.common.core.rest.param.PageParam;
import cn.bootx.platform.common.mybatisplus.util.MpUtil;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeRecord;
import cn.bootx.platform.daxpay.service.core.task.notice.entity.ClientNoticeTask;
import cn.bootx.platform.daxpay.service.core.payment.notice.service.ClientNoticeService;
import cn.bootx.platform.daxpay.service.core.task.notice.dao.ClientNoticeRecordManager;
import cn.bootx.platform.daxpay.service.core.task.notice.dao.ClientNoticeTaskManager;
import cn.bootx.platform.daxpay.service.dto.record.notice.ClientNoticeRecordDto;
import cn.bootx.platform.daxpay.service.dto.record.notice.ClientNoticeTaskDto;
import cn.bootx.platform.daxpay.service.param.record.ClientNoticeRecordQuery;
import cn.bootx.platform.daxpay.service.param.record.ClientNoticeTaskQuery;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 通知任务查询类
* @author xxm
* @since 2024/2/23
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ClientNoticeTaskService {
private final ClientNoticeService clientNoticeService;
private final ClientNoticeTaskManager taskManager;
private final ClientNoticeRecordManager recordManager;
/**
* 手动触发消息发送
*/
public void sendData(Long taskId){
}
/**
* 任务分页查询
*/
public PageResult<ClientNoticeTaskDto> taskPage(PageParam pageParam, ClientNoticeTaskQuery query){
return MpUtil.convert2DtoPageResult(taskManager.page(pageParam, query));
}
/**
* 任务详情
*/
public ClientNoticeTaskDto findTaskById(Long id){
return taskManager.findById(id).map(ClientNoticeTask::toDto).orElseThrow(DataNotExistException::new);
}
/**
* 记录分页
*/
public PageResult<ClientNoticeRecordDto> recordPage(PageParam pageParam, ClientNoticeRecordQuery query){
return MpUtil.convert2DtoPageResult(recordManager.page(pageParam, query));
}
/**
* 记录详情
*/
public ClientNoticeRecordDto findRecordById(Long id){
return recordManager.findById(id).map(ClientNoticeRecord::toDto).orElseThrow(DataNotExistException::new);
}
}

View File

@@ -0,0 +1,44 @@
package cn.bootx.platform.daxpay.service.dto.record.notice;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.service.code.ClientNoticeSendTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 消息通知记录
* @author xxm
* @since 2024/2/23
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "消息通知记录")
public class ClientNoticeRecordDto extends BaseDto {
/** 任务ID */
@Schema(description = "任务ID")
private Long taskId;
/**
* 发送类型, 自动发送, 手动发送
* @see ClientNoticeSendTypeEnum
*/
@Schema(description = "发送类型")
private String type;
/** 请求次数 */
@Schema(description = "请求次数")
private Integer reqCount;
/** 发送是否成功 */
@Schema(description = "发送是否成功")
private boolean success;
/** 错误信息 */
@Schema(description = "错误信息")
private String errorMsg;
}

View File

@@ -0,0 +1,53 @@
package cn.bootx.platform.daxpay.service.dto.record.notice;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.service.code.ClientNoticeTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 消息通知任务
* @author xxm
* @since 2024/2/23
*/
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "消息通知任务")
public class ClientNoticeTaskDto extends BaseDto {
/** 本地订单ID */
@Schema(description = "本地订单ID")
private Long orderId;
/**
* 回调类型
* @see ClientNoticeTypeEnum
*/
@Schema(description = "回调类型")
private String type;
/** 消息内容 */
@Schema(description = "消息内容")
private String content;
/** 是否发送成功 */
@Schema(description = "是否发送成功")
private boolean success;
/** 发送次数 */
@Schema(description = "发送次数")
private Integer sendCount;
/** 发送地址 */
@Schema(description = "发送地址")
private String url;
/** 最后发送时间 */
@Schema(description = "最后发送时间")
private LocalDateTime latestTime;
}

View File

@@ -0,0 +1,37 @@
package cn.bootx.platform.daxpay.service.param.record;
import cn.bootx.platform.common.core.annotation.QueryParam;
import cn.bootx.platform.common.core.rest.param.QueryOrder;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 客户通知记录查询
* @author xxm
* @since 2024/2/24
*/
@QueryParam
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "客户通知记录查询")
public class ClientNoticeRecordQuery extends QueryOrder {
/** 任务ID */
@Schema(description ="任务ID")
private Long taskId;
/** 请求次数 */
@Schema(description ="请求次数")
private Integer reqCount;
/** 发送是否成功 */
@Schema(description ="发送是否成功")
private Boolean success;
/** 错误信息 */
@Schema(description ="错误信息")
private String errorMsg;
}

View File

@@ -0,0 +1,49 @@
package cn.bootx.platform.daxpay.service.param.record;
import cn.bootx.platform.common.core.annotation.QueryParam;
import cn.bootx.platform.common.core.rest.param.QueryOrder;
import cn.bootx.platform.daxpay.service.code.ClientNoticeTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 发送通知任务查询
* @author xxm
* @since 2024/2/24
*/
@QueryParam
@EqualsAndHashCode(callSuper = true)
@Data
@Accessors(chain = true)
@Schema(title = "发送通知任务查询")
public class ClientNoticeTaskQuery extends QueryOrder {
/** 本地订单ID */
@Schema(description ="本地订单ID")
private Long orderId;
/**
* 通知类型
* @see ClientNoticeTypeEnum
*/
@Schema(description ="通知类型")
private String type;
/** 消息内容 */
@Schema(description ="消息内容")
private String content;
/** 是否发送成功 */
@Schema(description ="是否发送成功")
private Boolean success;
/** 发送次数 */
@Schema(description ="发送次数")
private Integer sendCount;
/** 发送地址 */
@Schema(description ="发送地址")
private String url;
}

View File

@@ -10,7 +10,7 @@ import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
* 消息通知任务
* 发送消息通知任务
* @author xxm
* @since 2024/2/22
*/
@@ -19,7 +19,7 @@ import java.time.LocalDateTime;
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
@RequiredArgsConstructor
public class ClientNoticeTask implements Job {
public class ClientNoticeSendTask implements Job {
private final ClientNoticeService clientNoticeService;

View File

@@ -33,7 +33,7 @@ public class PayReconcileTaskService {
reconcileService.downAndSave(reconcileOrder);
// 3. 执行账单比对, 生成差异单
}
}