From b328878d142b9c19514aea7c38adb6f883f717bd Mon Sep 17 00:00:00 2001 From: gaoqr <13665037151@163.com> Date: Thu, 25 Sep 2025 17:04:40 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E4=BC=98=E5=8C=96=E7=AC=AC=E4=B8=89?= =?UTF-8?q?=E6=96=B9=E6=94=AF=E4=BB=98=E9=80=8F=E4=BC=A0=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=AD=98=E5=9C=A8=E6=95=B0=E6=8D=AE=E5=BA=93?= =?UTF-8?q?=E4=B8=AD=EF=BC=9B2=E3=80=81=E6=96=B0=E5=A2=9E=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E6=94=AF=E4=BB=98=E5=8D=95=E7=9A=84=E8=BD=AE=E8=AE=AD?= =?UTF-8?q?=E5=A4=84=E7=90=86=E3=80=81=E5=A2=9E=E5=8A=A0=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E5=92=8C=E5=BE=85=E6=94=AF=E4=BB=98=E7=9A=84=E5=88=A4=E5=AE=9A?= =?UTF-8?q?=E5=92=8C=E5=AF=B9=E5=BA=94=E5=A4=84=E7=90=86=EF=BC=9B3?= =?UTF-8?q?=E3=80=81=E6=96=B0=E5=A2=9E=E6=9B=B4=E6=96=B0=E5=89=8D=E5=BA=8F?= =?UTF-8?q?=E8=B4=AD=E4=B9=B0=E8=AE=B0=E5=BD=95=E7=9A=84=E5=B7=B2=E6=94=AF?= =?UTF-8?q?=E4=BB=98=E5=B1=9E=E6=80=A7=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/dto/order/PayOrderRespDTO.java | 33 +--- .../dto/order/PayOrderUnifiedReqDTO.java | 5 - .../excel/core/convert/BaseBoolValEnum.java | 25 +++ .../core/convert/EnumBoolValConvert.java | 73 +++++++ .../system/enums/ErrorCodeConstants.java | 1 + .../funds/delay/vo/ProductDelayRespVO.java | 7 + .../admin/pay/vo/PayOrderUnifiedReqDTO.java | 5 - .../funds/delay/ProductDelayRecordDO.java | 5 + .../system/dal/dataobject/pay/PayOrderDO.java | 6 +- .../funds/delay/ProductDelayRecordMapper.java | 3 +- .../system/enums/common/WhetherEnum.java | 22 +++ .../module/system/enums/pay/PayConstants.java | 6 - .../system/enums/pay/PayOrderStatusEnum.java | 11 +- .../system/job/pay/PayOrderExpireJob.java | 9 +- .../funds/delay/ProductDelayService.java | 9 + .../funds/delay/ProductDelayServiceImpl.java | 10 + .../funds/purchase/PurchaseService.java | 12 +- .../funds/purchase/PurchaseServiceImpl.java | 23 +-- .../service/pay/PayOrderServiceImpl.java | 184 ++++++++++-------- .../service/pay/PayProductProcessor.java | 8 +- .../pay/factory/impl/AbstractPayHandler.java | 8 + .../impl/alipay/AlipayPcPayHandler.java | 33 +++- .../impl/wechat/WechatNativePayHandler.java | 50 ++++- .../src/main/resources/application-dev.yaml | 5 + .../src/main/resources/application-local.yaml | 5 + 25 files changed, 388 insertions(+), 170 deletions(-) create mode 100644 cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/BaseBoolValEnum.java create mode 100644 cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/EnumBoolValConvert.java create mode 100644 cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/enums/common/WhetherEnum.java diff --git a/cf-framework/cf-spring-boot-starter-biz-pay/src/main/java/com/cf/imes/framework/pay/core/client/dto/order/PayOrderRespDTO.java b/cf-framework/cf-spring-boot-starter-biz-pay/src/main/java/com/cf/imes/framework/pay/core/client/dto/order/PayOrderRespDTO.java index 63997f50c..2607c1baf 100644 --- a/cf-framework/cf-spring-boot-starter-biz-pay/src/main/java/com/cf/imes/framework/pay/core/client/dto/order/PayOrderRespDTO.java +++ b/cf-framework/cf-spring-boot-starter-biz-pay/src/main/java/com/cf/imes/framework/pay/core/client/dto/order/PayOrderRespDTO.java @@ -26,9 +26,13 @@ public class PayOrderRespDTO { private Integer status; /** - * 外部订单号 + * 第三方交易号 + */ + private String tradeNo; + + /** + * 系统内订单号 * - * 对应 PayOrderExtensionDO 的 no 字段 */ private String outTradeNo; @@ -76,11 +80,6 @@ public class PayOrderRespDTO { */ private String channelErrorMsg; - /** - * 发起支付给支付方后,异步回调原封不动回到调用方 - */ - private Map passbackParams; - /** * 支付渠道:0-支付宝、1-微信 */ @@ -105,13 +104,14 @@ public class PayOrderRespDTO { * 创建【WAITING】状态的订单返回 */ public static PayOrderRespDTO waitingOf(String displayMode, String displayContent, - String outTradeNo, Object rawData, Integer channelCode) { + String outTradeNo, String tradeNo, Object rawData, Integer channelCode) { PayOrderRespDTO respDTO = new PayOrderRespDTO(); respDTO.status = PayOrderStatusRespEnum.WAITING.getStatus(); respDTO.displayMode = displayMode; respDTO.displayContent = displayContent; // 相对通用的字段 respDTO.outTradeNo = outTradeNo; + respDTO.tradeNo = tradeNo; respDTO.rawData = rawData; respDTO.channelCode = channelCode; return respDTO; @@ -149,23 +149,6 @@ public class PayOrderRespDTO { return respDTO; } - /** - * 创建指定状态的订单返回,适合支付渠道回调时 - */ - public static PayOrderRespDTO of(Integer status, String channelOrderNo, String channelUserId, LocalDateTime successTime, - String outTradeNo, Object rawData, Map passbackParams) { - PayOrderRespDTO respDTO = new PayOrderRespDTO(); - respDTO.status = status; - respDTO.channelOrderNo = channelOrderNo; - respDTO.channelUserId = channelUserId; - respDTO.successTime = successTime; - // 相对通用的字段 - respDTO.outTradeNo = outTradeNo; - respDTO.rawData = rawData; - respDTO.passbackParams = passbackParams; - return respDTO; - } - /** * 创建【CLOSED】状态的订单返回,适合调用支付渠道失败时 */ diff --git a/cf-framework/cf-spring-boot-starter-biz-pay/src/main/java/com/cf/imes/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java b/cf-framework/cf-spring-boot-starter-biz-pay/src/main/java/com/cf/imes/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java index c0606a7e0..00c9dd44c 100644 --- a/cf-framework/cf-spring-boot-starter-biz-pay/src/main/java/com/cf/imes/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java +++ b/cf-framework/cf-spring-boot-starter-biz-pay/src/main/java/com/cf/imes/framework/pay/core/client/dto/order/PayOrderUnifiedReqDTO.java @@ -85,11 +85,6 @@ public class PayOrderUnifiedReqDTO { */ private Map channelExtras; - /** - * 发起支付给支付方后,异步回调原封不动回到调用方 - */ - private Map passbackParams; - /** * 展示模式 * diff --git a/cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/BaseBoolValEnum.java b/cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/BaseBoolValEnum.java new file mode 100644 index 000000000..adefc9ee4 --- /dev/null +++ b/cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/BaseBoolValEnum.java @@ -0,0 +1,25 @@ +package com.cf.imes.framework.excel.core.convert; + +/** + * 基础枚举接口,code为布尔类型 + *

+ * 用于easyexcel convert + * + * @author Gqr + * @since 2025/8/27 11:51 + */ +public interface BaseBoolValEnum { + /** + * 魔值 + * + * @return + */ + Boolean getCode(); + + /** + * 描述 + * + * @return + */ + String getDesc(); +} diff --git a/cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/EnumBoolValConvert.java b/cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/EnumBoolValConvert.java new file mode 100644 index 000000000..aa84b8b6f --- /dev/null +++ b/cf-framework/cf-spring-boot-starter-excel/src/main/java/com/cf/imes/framework/excel/core/convert/EnumBoolValConvert.java @@ -0,0 +1,73 @@ +package com.cf.imes.framework.excel.core.convert; + +import com.alibaba.excel.converters.Converter; +import com.alibaba.excel.converters.ReadConverterContext; +import com.alibaba.excel.converters.WriteConverterContext; +import com.alibaba.excel.enums.CellDataTypeEnum; +import com.alibaba.excel.metadata.data.WriteCellData; +import com.cf.imes.framework.excel.core.annotations.EnumFormat; +import lombok.extern.slf4j.Slf4j; + +import java.lang.reflect.Field; + +/** + * Excel 枚举转换器 + * + * @author Gqr + * @since 2025/8/27 11:02 + */ +@Slf4j +public class EnumBoolValConvert implements Converter { + @Override + public Class supportJavaTypeKey() { + return Boolean.class; // 泛型接口 + } + + @Override + public CellDataTypeEnum supportExcelTypeKey() { + return CellDataTypeEnum.BOOLEAN; // Excel 存字符串 + } + + // 写出:Integer -> 枚举描述 + @Override + public WriteCellData convertToExcelData(WriteConverterContext context) { + Boolean code = context.getValue(); + if (code == null) { + return new WriteCellData<>(""); + } + Class enumClass = getEnumClass(context.getContentProperty().getField()); + if (enumClass != null) { + for (Object e : enumClass.getEnumConstants()) { + BaseBoolValEnum baseEnum = (BaseBoolValEnum) e; + if (baseEnum.getCode().equals(code)) { + return new WriteCellData<>(baseEnum.getDesc()); + } + } + } + return new WriteCellData<>(""); + } + + // 读入:枚举描述 -> Integer code + @Override + public Boolean convertToJavaData(ReadConverterContext context) { + String desc = context.getReadCellData().getStringValue(); + if (desc == null) { + return Boolean.FALSE; + } + Class enumClass = getEnumClass(context.getContentProperty().getField()); + if (enumClass != null) { + for (Object e : enumClass.getEnumConstants()) { + BaseBoolValEnum baseEnum = (BaseBoolValEnum) e; + if (baseEnum.getDesc().equals(desc)) { + return baseEnum.getCode(); + } + } + } + return Boolean.FALSE; + } + + private Class getEnumClass(Field field) { + EnumFormat annotation = field.getAnnotation(EnumFormat.class); + return annotation != null ? annotation.value() : null; + } +} diff --git a/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/enums/ErrorCodeConstants.java b/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/enums/ErrorCodeConstants.java index 2e3856c58..15f2190f1 100644 --- a/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/enums/ErrorCodeConstants.java +++ b/cf-module-system/cf-module-system-api/src/main/java/com/cf/imes/module/system/enums/ErrorCodeConstants.java @@ -473,6 +473,7 @@ public class ErrorCodeConstants { public static final ErrorCode PRODUCTS_OUTPUT_NOT_SUPPORT = new ErrorCode(1_002_045_009, "暂不支持创建购买方式为【产量】的定价规则"); public static final ErrorCode RECHARGE_ACTIVE_NO_EXIST = new ErrorCode(1_002_045_010, "当前赠送金活动不存在"); public static final ErrorCode PRODUCTS_DETAIL_DURATION_UNIT_NOT_SUPPORT = new ErrorCode(1_002_045_011, "不支持的时长单位,请检查该定价规则的时长单位"); + public static final ErrorCode ORG_PRODUCT_PURCHASE_WAIT_PAY_ERROR = new ErrorCode(1_002_045_012, "组织下存在当前产品的待支付记录,请确认或取消支付后再次发起软件购买"); //=========== 广告相关 1-002-046-000 ============ public static final ErrorCode ADVERTISEMENT_POSITION_NO_EXIST = new ErrorCode(1_002_046_001, "广告所选放置位置不存在"); diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/funds/delay/vo/ProductDelayRespVO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/funds/delay/vo/ProductDelayRespVO.java index 6eb0763e4..02bd9b9e8 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/funds/delay/vo/ProductDelayRespVO.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/funds/delay/vo/ProductDelayRespVO.java @@ -3,7 +3,9 @@ package com.cf.imes.module.system.controller.admin.funds.delay.vo; import com.alibaba.excel.annotation.ExcelIgnore; import com.alibaba.excel.annotation.ExcelProperty; import com.cf.imes.framework.excel.core.annotations.EnumFormat; +import com.cf.imes.framework.excel.core.convert.EnumBoolValConvert; import com.cf.imes.framework.excel.core.convert.EnumConvert; +import com.cf.imes.module.system.enums.common.WhetherEnum; import com.cf.imes.module.system.enums.pay.DurationUnitEnum; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.v3.oas.annotations.media.Schema; @@ -60,4 +62,9 @@ public class ProductDelayRespVO { @Schema(description = "操作人") @ExcelProperty("操作人") private String creator; + + @Schema(description = "是否支付") + @ExcelProperty(value = "是否支付", converter = EnumBoolValConvert.class) + @EnumFormat(value = WhetherEnum.class) + private Boolean paid; } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/pay/vo/PayOrderUnifiedReqDTO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/pay/vo/PayOrderUnifiedReqDTO.java index 8b53e7fc5..d965bb69c 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/pay/vo/PayOrderUnifiedReqDTO.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/controller/admin/pay/vo/PayOrderUnifiedReqDTO.java @@ -90,11 +90,6 @@ public class PayOrderUnifiedReqDTO { */ private Map channelExtras; - /** - * 发起支付给支付方后,异步回调原封不动回到调用方 - */ - private Map passbackParams; - /** * 展示模式 * diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/funds/delay/ProductDelayRecordDO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/funds/delay/ProductDelayRecordDO.java index 517924665..2c4f51346 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/funds/delay/ProductDelayRecordDO.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/funds/delay/ProductDelayRecordDO.java @@ -77,6 +77,11 @@ public class ProductDelayRecordDO extends BaseDO { */ private Long purchaseId; + /** + * 是否支付 + */ + private Boolean paid; + /** * 备注 */ diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/pay/PayOrderDO.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/pay/PayOrderDO.java index 228a8388b..b83b72fb0 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/pay/PayOrderDO.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/dataobject/pay/PayOrderDO.java @@ -68,6 +68,11 @@ public class PayOrderDO extends BaseDO { */ private String payNo; + /** + * 交易类型 + */ + private Integer tradeType; + /** * 支付状态 */ @@ -99,7 +104,6 @@ public class PayOrderDO extends BaseDO { /** * 支付渠道的同步/异步通知的内容 - * */ private String channelNotifyData; } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/funds/delay/ProductDelayRecordMapper.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/funds/delay/ProductDelayRecordMapper.java index fdd08768a..6eb823453 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/funds/delay/ProductDelayRecordMapper.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/dal/mysql/funds/delay/ProductDelayRecordMapper.java @@ -26,6 +26,7 @@ public interface ProductDelayRecordMapper extends BaseMapperX 0) { + log.info("[execute][支付过期 ({}) 个]", count); + } + return CharSequenceUtil.format("支付过期 ({}) 个", count); } } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/delay/ProductDelayService.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/delay/ProductDelayService.java index 8444175a3..f563e7268 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/delay/ProductDelayService.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/delay/ProductDelayService.java @@ -47,4 +47,13 @@ public interface ProductDelayService { * @return */ PreviouProductDelayRespVO getActiveRecordsByProductId(Long productId, Long organId); + + + /** + * 更新购买记录下的延期记录为已支付 + * + * @param purchaseId + * @param organId + */ + void updatePurchaseDelayRecordsPaid(Long purchaseId, Long organId); } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/delay/ProductDelayServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/delay/ProductDelayServiceImpl.java index d16351b36..73784a9c2 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/delay/ProductDelayServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/delay/ProductDelayServiceImpl.java @@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.cf.imes.framework.common.pojo.PageResult; import com.cf.imes.framework.common.util.Assert.AssertUtils; import com.cf.imes.framework.common.util.object.BeanUtils; @@ -100,6 +101,7 @@ public class ProductDelayServiceImpl implements ProductDelayService { .endTime(endTime) .delayTime(delayTime) .purchaseId(purchaseId) + .paid(false) .build(); productDelayRecordMapper.insert(productDelayRecordDO); return productDelayRecordDO.getId(); @@ -183,4 +185,12 @@ public class ProductDelayServiceImpl implements ProductDelayService { } return previouProductDelayRespVO; } + + @Override + public void updatePurchaseDelayRecordsPaid(Long purchaseId, Long organId) { + productDelayRecordMapper.update(new LambdaUpdateWrapper() + .eq(ProductDelayRecordDO::getPurchaseId, purchaseId) + .eq(ProductDelayRecordDO::getOrganId, organId) + .set(ProductDelayRecordDO::getPaid, true)); + } } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/purchase/PurchaseService.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/purchase/PurchaseService.java index 68a9e5fb2..9bf3719d8 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/purchase/PurchaseService.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/purchase/PurchaseService.java @@ -117,13 +117,13 @@ public interface PurchaseService { PurchaseRecordDO getOrgInitialPurchaseRecord(Long organId, Long productId); /** - * 获取组织产品购买记录 + * 获取组织产品待支付的购买记录 * * @param organId * @param productId * @return */ - List getOrgProductPurchaseList(Long organId, Long productId); + List getOrgProductPendingPurchaseList(Long organId, Long productId); /** * 获取组织续费定价规则列表 @@ -133,14 +133,6 @@ public interface PurchaseService { */ List getOrgProductDetailList(Long organId, Long productId); - /** - * 更新历史组织产品购买记录为待支付 - * - * @param organId - * @param purchaseRecordId - */ - void pendingHistoryOrganProductPurchaseRecord(Long organId, Long purchaseRecordId); - /** * 作废历史组织产品购买记录 * diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/purchase/PurchaseServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/purchase/PurchaseServiceImpl.java index b9b16d9d9..79fb523d4 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/purchase/PurchaseServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/funds/purchase/PurchaseServiceImpl.java @@ -42,7 +42,6 @@ import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDate; -import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -83,7 +82,11 @@ public class PurchaseServiceImpl implements PurchaseService { @Override public void deleteRecord(Long id) { - purchaseRecordMapper.deleteById(id); + // 删除且作废 + purchaseRecordMapper.update(new LambdaUpdateWrapper() + .eq(PurchaseRecordDO::getId, id) + .set(PurchaseRecordDO::getDeleted, true) + .set(PurchaseRecordDO::getStatus, PurchaseRecordStatusEnum.INACTIVE.getStatus())); } @Override @@ -253,12 +256,12 @@ public class PurchaseServiceImpl implements PurchaseService { } @Override - public List getOrgProductPurchaseList(Long organId, Long productId) { + public List getOrgProductPendingPurchaseList(Long organId, Long productId) { return purchaseRecordMapper.selectList(new LambdaQueryWrapper() .eq(PurchaseRecordDO::getOrganId, organId) .eq(PurchaseRecordDO::getProductId, productId) - .eq(PurchaseRecordDO::getDeleted, false) - .ne(PurchaseRecordDO::getStatus, PurchaseRecordStatusEnum.PENDING.getStatus())); + .eq(PurchaseRecordDO::getStatus, PurchaseRecordStatusEnum.PENDING.getStatus()) + .eq(PurchaseRecordDO::getDeleted, false)); } @Override @@ -335,16 +338,6 @@ public class PurchaseServiceImpl implements PurchaseService { return resultList; } - @Override - public void pendingHistoryOrganProductPurchaseRecord(Long organId, Long purchaseRecordId) { - purchaseRecordMapper.update(new LambdaUpdateWrapper() - .eq(PurchaseRecordDO::getOrganId, organId) - .eq(PurchaseRecordDO::getId, purchaseRecordId) - .eq(PurchaseRecordDO::getStatus, PurchaseRecordStatusEnum.ACTIVE.getStatus()) - .eq(PurchaseRecordDO::getDeleted, false) - .set(PurchaseRecordDO::getStatus, PurchaseRecordStatusEnum.PENDING.getStatus())); - } - @Override public void invalidHistoryOrganProductPurchaseRecord(Long organId, Long purchaseRecordId) { purchaseRecordMapper.update(new LambdaUpdateWrapper() diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/PayOrderServiceImpl.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/PayOrderServiceImpl.java index ec57698d7..15b890c4e 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/PayOrderServiceImpl.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/PayOrderServiceImpl.java @@ -1,7 +1,7 @@ package com.cf.imes.module.system.service.pay; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.IdUtil; -import cn.hutool.core.util.NumberUtil; import cn.hutool.core.util.ObjectUtil; import com.alibaba.fastjson.JSON; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; @@ -67,6 +67,7 @@ import com.cf.imes.module.system.enums.pay.PayOrderStatusEnum; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.support.TransactionTemplate; import org.springframework.validation.annotation.Validated; import jakarta.annotation.Resource; @@ -76,7 +77,6 @@ import java.math.BigDecimal; import java.math.RoundingMode; import java.time.LocalDateTime; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Matcher; @@ -84,7 +84,7 @@ import java.util.regex.Pattern; import static com.cf.imes.framework.common.exception.util.ServiceExceptionUtil.exception; import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORG_PRODUCT_PURCHASE_LOCK_ERROR; -import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_NOTIFY_PASSBACK_NOT_EXISTS_ERROR; +import static com.cf.imes.module.system.enums.ErrorCodeConstants.ORG_PRODUCT_PURCHASE_WAIT_PAY_ERROR; import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR; import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_ORDER_CHANNEL_QUERY_NOT_EXIST; import static com.cf.imes.module.system.enums.ErrorCodeConstants.PAY_ORDER_NOTIFY_DATA_PARSE_ERROR; @@ -154,6 +154,9 @@ public class PayOrderServiceImpl implements PayOrderService { @Resource private ChenfengCacheProperties chenfengCacheProperties; + @Resource + private TransactionTemplate transactionTemplate; + @Override @Transactional(rollbackFor = Exception.class) public PayOrderRespDTO productPay(PayProductOrderUnifiedReqVO payProductOrderUnifiedReqVO) { @@ -179,6 +182,12 @@ public class PayOrderServiceImpl implements PayOrderService { // 获取渠道方处理器 Integer channelCode = PayProductChannelCodeEnum.isAliPay(payProductOrderUnifiedReqVO.getProductChannelCode()) ? PayChannelCodeEnum.ALIPAY_PC.getCode() : PayChannelCodeEnum.WECHAT_NATIVE.getCode(); try { + // 检查机构产品是否有待支付的购买记录 + List pendingPurchaseList = purchaseService.getOrgProductPendingPurchaseList(organId, productId); + if (CollUtil.isNotEmpty(pendingPurchaseList)) { + throw new ServiceException(ORG_PRODUCT_PURCHASE_WAIT_PAY_ERROR); + } + // 查询组织余额信息 OrganAmountDO organAmount = organAmountService.validOrganAmount(organId); @@ -215,15 +224,10 @@ public class PayOrderServiceImpl implements PayOrderService { if (ObjectUtil.isNotNull(payOrder)) { payOrderMapper.insert(payOrder); - // 拼接充值个性化参数 - Map passbackParams = new HashMap<>(); - passbackParams.put(PayConstants.PAY_PASSBACK_PARAMS_TRADE_TYPE_KEY, TradeTypeEnum.PRODUCT.getCode());// 交易类型 - passbackParams.put(PayConstants.PAY_PASSBACK_PARAMS_PURCHASE_RECORD_ID_KEY, productProcessorRespVO.getPurchaseRecordDO().getId());// 购买记录id,用于更新开票状态 - // 发起渠道方支付 AbstractPayHandler payHandler = payHandlerFactory.getPayHandler(channelCode); - payOrderUnifiedReqDTO = createPayOrderUnifiedReqDTO(payOrder, passbackParams); + payOrderUnifiedReqDTO = createPayOrderUnifiedReqDTO(payOrder); return payHandler.unifiedOrder(payOrderUnifiedReqDTO); } // 软件购买锁解锁 @@ -246,7 +250,7 @@ public class PayOrderServiceImpl implements PayOrderService { /** * 处理前序购买记录: - * 1、需要第三方支付,更新为等待支付,第三方支付回调后作废 + * 1、需要第三方支付,当前购买记录状态为待支付,等第三方回调/超期轮训处理到的时候标记为成功还是失败 * 2、不需要第三方支付,直接作废 * * @param previouProductDelayRespVO @@ -258,12 +262,11 @@ public class PayOrderServiceImpl implements PayOrderService { if (ObjectUtil.isNotNull(previouPurchaseRecord)) { Long previousId = previouPurchaseRecord.getId(); Long organId = previouPurchaseRecord.getOrganId(); - if (ObjectUtil.isNotNull(payOrder)) { - // 历史的购买记录更新为等待支付,需要第三方支付回调后作废前序 - purchaseService.pendingHistoryOrganProductPurchaseRecord(organId, previousId); - } else { + if (ObjectUtil.isNull(payOrder)) { // 作废历史的购买记录 purchaseService.invalidHistoryOrganProductPurchaseRecord(organId, previousId); + // 更新前置购买记录下的延期记录为已支付 + productDelayService.updatePurchaseDelayRecordsPaid(previousId, organId); } } } @@ -286,7 +289,7 @@ public class PayOrderServiceImpl implements PayOrderService { AbstractPayHandler payHandler = payHandlerFactory.getPayHandler(channelCode); // 创建支付订单 - PayOrderDO payOrder = createPayOrder(payRechargeOrderReqVO.getPrice(), "imes充值", channelCode); + PayOrderDO payOrder = createRechargePayOrder(payRechargeOrderReqVO.getPrice(), "imes充值", channelCode); // 设置赠送活动编号 Long rechargeActivityId = payRechargeOrderReqVO.getRechargeActivityId(); if (ObjectUtil.isNotNull(rechargeActivityId)) { @@ -296,14 +299,10 @@ public class PayOrderServiceImpl implements PayOrderService { } payOrderMapper.insert(payOrder); - // 拼接充值个性化参数 - Map passbackParams = new HashMap<>(); - passbackParams.put(PayConstants.PAY_PASSBACK_PARAMS_TRADE_TYPE_KEY, TradeTypeEnum.RECHARGE.getCode()); - // 发起渠道方支付 PayOrderUnifiedReqDTO payOrderUnifiedReqDTO = null; try { - payOrderUnifiedReqDTO = createPayOrderUnifiedReqDTO(payOrder, passbackParams); + payOrderUnifiedReqDTO = createPayOrderUnifiedReqDTO(payOrder); return payHandler.unifiedOrder(payOrderUnifiedReqDTO); } catch (Throwable e) { // 系统异常,则包装成 PayException 异常抛出 @@ -380,12 +379,12 @@ public class PayOrderServiceImpl implements PayOrderService { } /** - * 创建支付订单 + * 创建充值支付订单 * * @param * @return */ - private PayOrderDO createPayOrder(BigDecimal price, String subject, Integer channelCode) { + private PayOrderDO createRechargePayOrder(BigDecimal price, String subject, Integer channelCode) { PayOrderDO payOrderDO = new PayOrderDO(); Long payOrderId = IdUtil.getSnowflake(mybatisIdProperties.getWorkerId(), mybatisIdProperties.getDatacenterId()).nextId(); @@ -399,6 +398,7 @@ public class PayOrderServiceImpl implements PayOrderService { payOrderDO.setSubject(subject); payOrderDO.setChannelCode(PayChannelCodeEnum.fromType(channelCode)); payOrderDO.setOrganId(organId); + payOrderDO.setTradeType(TradeTypeEnum.RECHARGE.getCode()); return payOrderDO; } @@ -408,7 +408,7 @@ public class PayOrderServiceImpl implements PayOrderService { * @param payOrderDO * @return */ - private PayOrderUnifiedReqDTO createPayOrderUnifiedReqDTO(PayOrderDO payOrderDO, Map passbackParams) { + private PayOrderUnifiedReqDTO createPayOrderUnifiedReqDTO(PayOrderDO payOrderDO) { PayOrderUnifiedReqDTO payOrderUnifiedReqDTO = new PayOrderUnifiedReqDTO(); String subject = payOrderDO.getSubject(); @@ -418,7 +418,6 @@ public class PayOrderServiceImpl implements PayOrderService { payOrderUnifiedReqDTO.setPrice(payOrderDO.getPrice()); payOrderUnifiedReqDTO.setExpireTime(payOrderDO.getExpireTime()); payOrderUnifiedReqDTO.setNotifyUrl(String.format(alipayConfig.getNotifyUrl(), payOrderDO.getChannelCode().getCode())); - payOrderUnifiedReqDTO.setPassbackParams(passbackParams); return payOrderUnifiedReqDTO; } @@ -525,7 +524,7 @@ public class PayOrderServiceImpl implements PayOrderService { } // 如果已经是成功,直接返回,不用重复更新 if (PayOrderStatusEnum.isSuccess(order.getStatus().getStatus())) { - log.debug("[updateOrderExtensionSuccess][order({}) 已经是已支付,无需更新]", order.getId()); + log.debug("[notifyOrderSuccess][order({}) 已经是已支付,无需更新]", order.getId()); return; } if (!PayOrderStatusEnum.WAITING.equals(order.getStatus())) { // 校验状态,必须是待支付 @@ -545,21 +544,18 @@ public class PayOrderServiceImpl implements PayOrderService { if (updateCounts == 0) { // 校验状态,必须是待支付 throw exception(PAY_ORDER_STATUS_IS_NOT_WAITING); } - log.debug("[updateOrderExtensionSuccess][order({}) 更新为已支付]", order.getId()); + log.debug("[notifyOrderSuccess][order({}) 更新为已支付]", order.getId()); // 3. 根据交易类型做对应回调处理 - Map passbackParams = notify.getPassbackParams(); - Object passbackObj = passbackParams.get(PayConstants.PAY_PASSBACK_PARAMS_TRADE_TYPE_KEY); - AssertUtils.notEmpty(passbackObj, PAY_NOTIFY_PASSBACK_NOT_EXISTS_ERROR); - switch (TradeTypeEnum.fromCode(NumberUtil.parseInt(passbackObj.toString()))) { + switch (TradeTypeEnum.fromCode(order.getTradeType())) { case RECHARGE: notifyRechargeOrderSuccess(order, organId); break; case PRODUCT: - notifyProductPayOrderSuccess(organId, passbackParams); + notifyProductPayOrderSuccess(order); break; default: - throw ServiceExceptionUtil.exception(PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR, passbackObj.toString()); + throw ServiceExceptionUtil.exception(PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR, order.getTradeType()); } } @@ -603,21 +599,26 @@ public class PayOrderServiceImpl implements PayOrderService { /** * 产品购买成功回调 * - * @param organId - * @param passbackParams + * @param payOrderDO */ - private void notifyProductPayOrderSuccess(Long organId, Map passbackParams) { - Object purchaseRecordIdObj = passbackParams.get(PayConstants.PAY_PASSBACK_PARAMS_PURCHASE_RECORD_ID_KEY); - if (ObjectUtil.isNotNull(purchaseRecordIdObj)) { + private void notifyProductPayOrderSuccess(PayOrderDO payOrderDO) { + if (ObjectUtil.isNotNull(payOrderDO) && ObjectUtil.isNotNull(payOrderDO.getPurchaseId())) { + Long organId = payOrderDO.getOrganId(); + // 1、更新购买记录的开票状态 - long purchaseId = NumberUtil.parseLong(purchaseRecordIdObj.toString()); + long purchaseId = payOrderDO.getPurchaseId(); purchaseService.updateRecordInvoicable(purchaseId, organId); - // 2、作废前置购买记录 + // 2、激活本次的购买记录 + purchaseService.activateHistoryOrganProductPurchaseRecord(organId, purchaseId); + + // 3、作废前置购买记录 PurchaseRecordDO record = purchaseService.getRecord(purchaseId); Long previousId = record.getPreviousId(); if (ObjectUtil.isNotNull(previousId)) { purchaseService.invalidHistoryOrganProductPurchaseRecord(organId, previousId); + // 4、更新前置购买记录下的延期记录为已支付 + productDelayService.updatePurchaseDelayRecordsPaid(previousId, organId); } } @@ -657,7 +658,7 @@ public class PayOrderServiceImpl implements PayOrderService { .eq(PayOrderDO::getId, order.getId()) .eq(PayOrderDO::getStatus, PayOrderStatusEnum.WAITING) .eq(PayOrderDO::getOrganId, organId) - .set(PayOrderDO::getStatus, PayOrderStatusEnum.FAIL.getStatus()) + .set(PayOrderDO::getStatus, PayOrderStatusEnum.CLOSED.getStatus()) .set(PayOrderDO::getNotifyTime, LocalDateTime.now()) .set(PayOrderDO::getChannelErrorCode, notify.getChannelErrorCode()) .set(PayOrderDO::getChannelErrorMsg, notify.getChannelErrorMsg()) @@ -669,18 +670,15 @@ public class PayOrderServiceImpl implements PayOrderService { log.debug("[notifyOrderClosed][order({}) 更新为支付关闭]", order.getId()); // 3. 根据交易类型做对应回调处理 - Map passbackParams = notify.getPassbackParams(); - Object passbackObj = passbackParams.get(PayConstants.PAY_PASSBACK_PARAMS_TRADE_TYPE_KEY); - AssertUtils.notEmpty(passbackObj, PAY_NOTIFY_PASSBACK_NOT_EXISTS_ERROR); - switch (TradeTypeEnum.fromCode(NumberUtil.parseInt(passbackObj.toString()))) { + switch (TradeTypeEnum.fromCode(order.getTradeType())) { case RECHARGE: notifyRechargeOrderFail(order, organId); break; case PRODUCT: - notifyProductPayOrderFail(organId, passbackParams); + notifyProductPayOrderFail(order); break; default: - throw ServiceExceptionUtil.exception(PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR, passbackObj.toString()); + throw ServiceExceptionUtil.exception(PAY_NOTIFY_PASSBACK_TRADETYPE_NOT_SUPPORT_ERROR, order.getTradeType()); } } @@ -697,41 +695,41 @@ public class PayOrderServiceImpl implements PayOrderService { /** * 产品购买失败回调 * - * @param organId - * @param passbackParams + * @param orderDO */ - private void notifyProductPayOrderFail(Long organId, Map passbackParams) { - Object purchaseRecordIdObj = passbackParams.get(PayConstants.PAY_PASSBACK_PARAMS_PURCHASE_RECORD_ID_KEY); - if (ObjectUtil.isNotNull(purchaseRecordIdObj)) { - long purchaseId = NumberUtil.parseLong(purchaseRecordIdObj.toString()); + private void notifyProductPayOrderFail(PayOrderDO orderDO) { + if (ObjectUtil.isNotNull(orderDO) && ObjectUtil.isNotNull(orderDO.getPurchaseId())) { + Long organId = orderDO.getOrganId(); + Long purchaseId = orderDO.getPurchaseId(); PurchaseRecordDO record = purchaseService.getRecord(purchaseId); // 1、产品购买第三方支付失败 删除购买记录 purchaseService.deleteRecord(purchaseId); - // 2、混合支付失败,退回扣除的现金和赠送金 + BigDecimal zero = BigDecimal.ZERO; BigDecimal accountBalanceSpent = record.getAccountBalanceSpent(); BigDecimal giftMoneySpent = record.getGiftMoneySpent(); - OrganRechargeAmountRespVO organRefundProductRespVO = organAmountService.refundProductOrganAmountBalanceAndGift(organId, accountBalanceSpent, giftMoneySpent); + // 如果现金花费和赠送金花费任一不为0,则代表有要退回的操作 + if (accountBalanceSpent.compareTo(zero) > 0 || giftMoneySpent.compareTo(zero) > 0) { + // 2、混合支付失败,退回扣除的现金和赠送金 + OrganRechargeAmountRespVO organRefundProductRespVO = organAmountService.refundProductOrganAmountBalanceAndGift(organId, accountBalanceSpent, giftMoneySpent); - // 3、新增退回的收支记录 - IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder() - .organId(organId) - .orderNo(String.valueOf(record.getId())) - .businessNo(LocalDateTimeUtils.formatNow()) - .tradeType(TradeTypeEnum.PRODUCT.getCode()) - .incomeExpenseType(IncomeExpenseTypeEnum.INCOME.getCode()) - .cashAmountChange(accountBalanceSpent) - .giftAmountChange(giftMoneySpent) - .availableAmount(organRefundProductRespVO.getAvailableAmount()) - .amount(organRefundProductRespVO.getAmount()) - .giftAmount(organRefundProductRespVO.getGiftAmount()) - .build(); - incomeExpenseDetailsDO.setCreator(record.getCreator()); - incomeExpenseDetailsDO.setUpdater(record.getUpdater()); - incomeExpenseService.createDetail(incomeExpenseDetailsDO); - - // 4、回退前序购买记录的状态 - purchaseService.activateHistoryOrganProductPurchaseRecord(organId, purchaseId); + // 3、新增退回的收支记录 + IncomeExpenseDetailsDO incomeExpenseDetailsDO = IncomeExpenseDetailsDO.builder() + .organId(organId) + .orderNo(String.valueOf(record.getId())) + .businessNo(LocalDateTimeUtils.formatNow()) + .tradeType(TradeTypeEnum.PRODUCT.getCode()) + .incomeExpenseType(IncomeExpenseTypeEnum.INCOME.getCode()) + .cashAmountChange(accountBalanceSpent) + .giftAmountChange(giftMoneySpent) + .availableAmount(organRefundProductRespVO.getAvailableAmount()) + .amount(organRefundProductRespVO.getAmount()) + .giftAmount(organRefundProductRespVO.getGiftAmount()) + .build(); + incomeExpenseDetailsDO.setCreator(record.getCreator()); + incomeExpenseDetailsDO.setUpdater(record.getUpdater()); + incomeExpenseService.createDetail(incomeExpenseDetailsDO); + } } } @@ -778,18 +776,48 @@ public class PayOrderServiceImpl implements PayOrderService { // 2、调用三方接口,查询支付单状态,是不是已支付 PayOrderRespDTO payOrderRespDTO = payHandler.queryPayOrder(order.getPayNo()); - if (PayOrderStatusEnum.isSuccess(payOrderRespDTO.getStatus())) { - notifyOrder(payOrderRespDTO); - return false; + if (PayOrderStatusEnum.isSuccess(payOrderRespDTO.getStatus()) || PayOrderStatusEnum.isClosed(payOrderRespDTO.getStatus())) { + notifyOrderWithTx(payOrderRespDTO); + log.info("[expireOrder][order({}) 更新为支付关闭成功]", order.getId()); + return true; } - log.info("[expireOrder][order({}) 更新为支付关闭失败]", order.getId()); - return true; + + // 3、系统内超期未支付状态是NOTPAY,主动关单后再次查询状态 + if (PayOrderStatusEnum.WAITING.getStatus().equals(payOrderRespDTO.getStatus())) { + boolean closeOrderResult = payHandler.closeOrder(order.getPayNo()); + if (closeOrderResult) { + payOrderRespDTO = payHandler.queryPayOrder(order.getPayNo()); + if (PayOrderStatusEnum.isSuccess(payOrderRespDTO.getStatus()) || PayOrderStatusEnum.isClosed(payOrderRespDTO.getStatus())) { + notifyOrderWithTx(payOrderRespDTO); + log.info("[expireOrder][order({}) 更新为支付关闭成功]", order.getId()); + return true; + } + } + } + + return false; } catch (Throwable e) { log.error("[expireOrder][order({}) 过期订单异常]", order.getId(), e); return false; } } + /** + * 手动控制支付回调中更新数据库操作的事务 + * + * @param notify + */ + private void notifyOrderWithTx(PayOrderRespDTO notify) { + transactionTemplate.executeWithoutResult(status -> { + try { + notifyOrder(notify); + } catch (Exception e) { + status.setRollbackOnly(); + throw e; + } + }); + } + @Override public PageResult getRechargeDetailsPage(RechargeDetailsPageReqVO reqVO) { // 分页查询 diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/PayProductProcessor.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/PayProductProcessor.java index 564f3047a..4afc1ab46 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/PayProductProcessor.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/PayProductProcessor.java @@ -299,7 +299,7 @@ public class PayProductProcessor { .monthAverageAmount(monthAverageAmount) .alipaySpent(price) .initial(initialized) - .status(PurchaseRecordStatusEnum.ACTIVE.getStatus()) + .status(PurchaseRecordStatusEnum.PENDING.getStatus()) .build(); purchaseRecordDO.setEndTime(calculateEndTime(purchaseRecordDO.getStartTime(), purchaseRecordDO.getPurchaseDuration(), purchaseRecordDO.getPurchaseDurationUnit(), @@ -340,7 +340,7 @@ public class PayProductProcessor { .monthAverageAmount(monthAverageAmount) .wechatSpent(price) .initial(initialized) - .status(PurchaseRecordStatusEnum.ACTIVE.getStatus()) + .status(PurchaseRecordStatusEnum.PENDING.getStatus()) .build(); purchaseRecordDO.setEndTime(calculateEndTime(purchaseRecordDO.getStartTime(), purchaseRecordDO.getPurchaseDuration(), purchaseRecordDO.getPurchaseDurationUnit(), @@ -507,6 +507,7 @@ public class PayProductProcessor { } else { purchaseRecordDO.setWechatSpent(remaining); } + purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus()); } // 更新余额 @@ -587,6 +588,7 @@ public class PayProductProcessor { } else { purchaseRecordDO.setWechatSpent(remaining); } + purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus()); } // 更新余额 @@ -677,6 +679,7 @@ public class PayProductProcessor { } else { purchaseRecordDO.setWechatSpent(remaining); } + purchaseRecordDO.setStatus(PurchaseRecordStatusEnum.PENDING.getStatus()); } // 更新余额 @@ -708,6 +711,7 @@ public class PayProductProcessor { payOrderDO.setChannelCode(payChannelCodeEnum); payOrderDO.setOrganId(organId); payOrderDO.setPurchaseId(purchaseId); + payOrderDO.setTradeType(TradeTypeEnum.PRODUCT.getCode()); return payOrderDO; } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/AbstractPayHandler.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/AbstractPayHandler.java index 89af0276c..a276a1173 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/AbstractPayHandler.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/AbstractPayHandler.java @@ -37,4 +37,12 @@ public abstract class AbstractPayHandler { * @return */ public abstract PayOrderRespDTO doParseOrderNotify(Map params, String body, Map headers) throws Throwable; + + /** + * 关闭订单 + * + * @param outTradeNo 系统内部交易号 + * @return 订单是否关闭成功 + */ + public abstract boolean closeOrder(String outTradeNo) throws Throwable; } diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/alipay/AlipayPcPayHandler.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/alipay/AlipayPcPayHandler.java index 95d3c1595..8798abcaa 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/alipay/AlipayPcPayHandler.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/alipay/AlipayPcPayHandler.java @@ -8,11 +8,14 @@ import cn.hutool.http.Method; import com.alibaba.fastjson.JSON; import com.alipay.api.AlipayApiException; import com.alipay.api.DefaultAlipayClient; +import com.alipay.api.domain.AlipayTradeCloseModel; import com.alipay.api.domain.AlipayTradePagePayModel; import com.alipay.api.domain.AlipayTradeQueryModel; import com.alipay.api.internal.util.AlipaySignature; +import com.alipay.api.request.AlipayTradeCloseRequest; import com.alipay.api.request.AlipayTradePagePayRequest; import com.alipay.api.request.AlipayTradeQueryRequest; +import com.alipay.api.response.AlipayTradeCloseResponse; import com.alipay.api.response.AlipayTradePagePayResponse; import com.alipay.api.response.AlipayTradeQueryResponse; import com.cf.imes.framework.common.util.object.ObjectUtils; @@ -21,7 +24,6 @@ import com.cf.imes.framework.pay.core.client.dto.order.PayOrderRespDTO; import com.cf.imes.module.system.controller.admin.pay.vo.PayOrderUnifiedReqDTO; import com.cf.imes.framework.pay.core.enums.order.PayOrderStatusRespEnum; import com.cf.imes.module.system.enums.pay.PayChannelCodeEnum; -import com.cf.imes.module.system.enums.pay.PayConstants; import com.cf.imes.module.system.service.pay.factory.impl.AbstractPayHandler; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -30,7 +32,6 @@ import jakarta.annotation.Resource; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; -import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; @@ -63,7 +64,6 @@ public class AlipayPcPayHandler extends AbstractPayHandler { model.setTimeExpire(LocalDateTimeUtil.format(reqDTO.getExpireTime(), NORM_DATETIME_FORMATTER)); model.setProductCode(alipayConfig.getProductCode()); model.setQrPayMode(alipayConfig.getQrPayMode()); - model.setPassbackParams(JSON.toJSONString(reqDTO.getPassbackParams())); // 1.2 构建 AlipayTradePagePayRequest 请求 AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); @@ -81,7 +81,7 @@ public class AlipayPcPayHandler extends AbstractPayHandler { outTradeNo, response); } return PayOrderRespDTO.waitingOf(null, response.getBody(), - outTradeNo, response, PayChannelCodeEnum.ALIPAY_PC.getCode()); + outTradeNo, response.getTradeNo(), response, PayChannelCodeEnum.ALIPAY_PC.getCode()); } @Override @@ -118,15 +118,30 @@ public class AlipayPcPayHandler extends AbstractPayHandler { Map bodyObj = HttpUtil.decodeParamMap(body, StandardCharsets.UTF_8); Integer status = parseStatus(bodyObj.get("trade_status")); - // 3.解析创建支付时候的个性化参数 - String passbackParamsStr = bodyObj.get(PayConstants.PAY_PASSBACK_PARAMS_KEY); - Map passbackParams = JSON.parseObject(passbackParamsStr, Map.class); - Assert.notNull(status, (Supplier) () -> { throw new IllegalArgumentException(CharSequenceUtil.format("body({}) 的 trade_status 不正确", body)); }); return PayOrderRespDTO.of(status, bodyObj.get("trade_no"), bodyObj.get("seller_id"), parseTime(params.get("gmt_payment")), - bodyObj.get("out_trade_no"), body, passbackParams); + bodyObj.get("out_trade_no"), body); + } + + @Override + public boolean closeOrder(String outTradeNo) throws AlipayApiException { + // 构造请求参数以调用接口 + AlipayTradeCloseRequest request = new AlipayTradeCloseRequest(); + AlipayTradeCloseModel model = new AlipayTradeCloseModel(); + model.setOutTradeNo(outTradeNo); + request.setBizModel(model); + + // 请求关单 + DefaultAlipayClient client = new DefaultAlipayClient(alipayConfig); + AlipayTradeCloseResponse response = client.execute(request); + + if (response.isSuccess()) { + return true; + } else { + return false; + } } /** diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/wechat/WechatNativePayHandler.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/wechat/WechatNativePayHandler.java index dfd5c4b18..431b6c801 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/wechat/WechatNativePayHandler.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/service/pay/factory/impl/wechat/WechatNativePayHandler.java @@ -3,7 +3,6 @@ package com.cf.imes.module.system.service.pay.factory.impl.wechat; import cn.hutool.core.date.LocalDateTimeUtil; import cn.hutool.core.date.TemporalAccessorUtil; import cn.hutool.core.text.CharSequenceUtil; -import com.alibaba.fastjson.JSON; import com.cf.imes.framework.common.util.json.JsonUtils; import com.cf.imes.framework.pay.config.ChenfengWechatPayConfig; import com.cf.imes.framework.pay.core.client.dto.order.PayOrderRespDTO; @@ -14,7 +13,9 @@ import com.cf.imes.module.system.enums.pay.PayOrderStatusEnum; import com.cf.imes.module.system.service.pay.factory.impl.AbstractPayHandler; import com.github.binarywang.wxpay.bean.notify.SignatureHeader; import com.github.binarywang.wxpay.bean.notify.WxPayNotifyV3Result; +import com.github.binarywang.wxpay.bean.request.WxPayOrderQueryV3Request; import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; +import com.github.binarywang.wxpay.bean.result.WxPayOrderQueryV3Result; import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; import com.github.binarywang.wxpay.exception.WxPayException; import com.github.binarywang.wxpay.service.WxPayService; @@ -54,7 +55,6 @@ public class WechatNativePayHandler extends AbstractPayHandler { request.setTimeExpire(formatDateV3(reqDTO.getExpireTime())); request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(getClientIP())); request.setNotifyUrl(reqDTO.getNotifyUrl()); - request.setAttach(JSON.toJSONString(reqDTO.getPassbackParams())); // 加载配置,创建client WxPayService client = new WxPayServiceImpl(); @@ -65,12 +65,28 @@ public class WechatNativePayHandler extends AbstractPayHandler { // 转换结果 return PayOrderRespDTO.waitingOf(PayOrderDisplayModeEnum.APP.getMode(), JsonUtils.toJsonString(response), - reqDTO.getOutTradeNo(), response, PayChannelCodeEnum.WECHAT_NATIVE.getCode()); + reqDTO.getOutTradeNo(), null, response, PayChannelCodeEnum.WECHAT_NATIVE.getCode()); } @Override - public PayOrderRespDTO queryPayOrder(String outTradeNo) { - return null; + public PayOrderRespDTO queryPayOrder(String outTradeNo) throws WxPayException { + // 加载配置,创建client + WxPayService client = new WxPayServiceImpl(); + client.setConfig(chenfengWechatPayConfig); + //todo 可能是 wxjava 的 bug:https://github.com/binarywang/WxJava/issues/1557 + client.getConfig().setApiV3HttpClient(null); + + // 构建 WxPayUnifiedOrderRequest 对象 + WxPayOrderQueryV3Request request = new WxPayOrderQueryV3Request() + .setOutTradeNo(outTradeNo); + // 执行请求 + WxPayOrderQueryV3Result response = client.queryOrderV3(request); + + // 转换结果 + Integer status = parseStatus(response.getTradeState()); + String openid = response.getPayer() != null ? response.getPayer().getOpenid() : null; + return PayOrderRespDTO.of(status, response.getTransactionId(), openid, parseDateV3(response.getSuccessTime()), + outTradeNo, response); } @Override @@ -87,13 +103,33 @@ public class WechatNativePayHandler extends AbstractPayHandler { Integer status = parseStatus(result.getTradeState()); String openid = result.getPayer() != null ? result.getPayer().getOpenid() : null; return PayOrderRespDTO.of(status, result.getTransactionId(), openid, parseDateV3(result.getSuccessTime()), - result.getOutTradeNo(), body, JSON.parseObject(result.getAttach(), Map.class)); + result.getOutTradeNo(), body); + } + + @Override + public boolean closeOrder(String outTradeNo) { + // 加载配置,创建client + WxPayService client = new WxPayServiceImpl(); + client.setConfig(chenfengWechatPayConfig); + //todo 可能是 wxjava 的 bug:https://github.com/binarywang/WxJava/issues/1557 + client.getConfig().setApiV3HttpClient(null); + + // 执行请求 + try { + client.closeOrderV3(outTradeNo); + } catch (WxPayException wxPayException) { + log.error("微信关闭订单失败:{}", wxPayException); + return false; + } + return true; } static String formatDateV3(LocalDateTime time) { return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), UTC_WITH_XXX_OFFSET_PATTERN); } + + /** * 组装请求头重的签名信息 * @@ -120,7 +156,7 @@ public class WechatNativePayHandler extends AbstractPayHandler { case "CLOSED": case "REVOKED": // 已撤销(刷卡支付独有) case "PAYERROR": // 支付失败(其它原因,如银行返回失败) - return PayOrderStatusEnum.FAIL.getStatus(); + return PayOrderStatusEnum.CLOSED.getStatus(); default: throw new IllegalArgumentException(CharSequenceUtil.format("未知的支付状态({})", tradeState)); } diff --git a/cf-module-system/cf-module-system-biz/src/main/resources/application-dev.yaml b/cf-module-system/cf-module-system-biz/src/main/resources/application-dev.yaml index 4b97c3f98..86111a279 100644 --- a/cf-module-system/cf-module-system-biz/src/main/resources/application-dev.yaml +++ b/cf-module-system/cf-module-system-biz/src/main/resources/application-dev.yaml @@ -236,6 +236,11 @@ chenfeng: - advertisement - system_advertisement_task - gift_money_details + - pay_order + - purchase_record + - income_expense_details + - system_organization_amount + - product_delay_record use-data-code: imes_prod #动态数据源标识,后期添加的数据源需要修改此值 xss: diff --git a/cf-module-system/cf-module-system-biz/src/main/resources/application-local.yaml b/cf-module-system/cf-module-system-biz/src/main/resources/application-local.yaml index 1b9b6d150..db7ab3d46 100644 --- a/cf-module-system/cf-module-system-biz/src/main/resources/application-local.yaml +++ b/cf-module-system/cf-module-system-biz/src/main/resources/application-local.yaml @@ -252,6 +252,11 @@ chenfeng: - advertisement - system_advertisement_task - gift_money_details + - pay_order + - purchase_record + - income_expense_details + - system_organization_amount + - product_delay_record use-data-code: imes_prod #动态数据源标识,后期添加的数据源需要修改此值 env: # 多环境的配置项