Commit e40c0e3057c4e89b42a93cebdf4e39e107aa2ab8
Merge branch 'feature/mall_product' of https://gitee.com/zhijiantianya/ruoyi-vue-pro
# Conflicts: # pom.xml # sql/mysql/pay_wallet.sql # yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
Showing
50 changed files
with
718 additions
and
158 deletions
sql/mysql/pay_wallet.sql
| @@ -246,3 +246,11 @@ VALUES ( | @@ -246,3 +246,11 @@ VALUES ( | ||
| 246 | '转账订单', '', 2, 3, 1117, | 246 | '转账订单', '', 2, 3, 1117, |
| 247 | 'transfer', 'ep:credit-card', 'pay/transfer/index', 0, 'PayTransfer' | 247 | 'transfer', 'ep:credit-card', 'pay/transfer/index', 0, 'PayTransfer' |
| 248 | ); | 248 | ); |
| 249 | + | ||
| 250 | +-- 转账通知脚本 | ||
| 251 | + | ||
| 252 | +ALTER TABLE `pay_app` | ||
| 253 | + ADD COLUMN `transfer_notify_url` varchar(1024) NOT NULL COMMENT '转账结果的回调地址' AFTER `refund_notify_url`; | ||
| 254 | +ALTER TABLE `pay_notify_task` | ||
| 255 | + MODIFY COLUMN `merchant_order_id` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci COMMENT '商户订单编号' AFTER `status`, | ||
| 256 | + ADD COLUMN `merchant_transfer_id` varchar(64) COMMENT '商户转账单编号' AFTER `merchant_order_id`; | ||
| 249 | \ No newline at end of file | 257 | \ No newline at end of file |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
| @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; | @@ -6,6 +6,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundRespDTO; | ||
| 6 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; | 6 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; |
| 7 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; | 7 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; |
| 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; | 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; |
| 9 | +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; | ||
| 9 | 10 | ||
| 10 | import java.util.Map; | 11 | import java.util.Map; |
| 11 | 12 | ||
| @@ -86,4 +87,12 @@ public interface PayClient { | @@ -86,4 +87,12 @@ public interface PayClient { | ||
| 86 | */ | 87 | */ |
| 87 | PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO); | 88 | PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO); |
| 88 | 89 | ||
| 90 | + /** | ||
| 91 | + * 获得转账订单信息 | ||
| 92 | + * | ||
| 93 | + * @param outTradeNo 外部订单号 | ||
| 94 | + * @param type 转账类型 | ||
| 95 | + * @return 转账信息 | ||
| 96 | + */ | ||
| 97 | + PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type); | ||
| 89 | } | 98 | } |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/transfer/PayTransferRespDTO.java
| @@ -53,11 +53,24 @@ public class PayTransferRespDTO { | @@ -53,11 +53,24 @@ public class PayTransferRespDTO { | ||
| 53 | /** | 53 | /** |
| 54 | * 创建【WAITING】状态的转账返回 | 54 | * 创建【WAITING】状态的转账返回 |
| 55 | */ | 55 | */ |
| 56 | - public static PayTransferRespDTO waitingOf(String channelOrderNo, | 56 | + public static PayTransferRespDTO waitingOf(String channelTransferNo, |
| 57 | String outTransferNo, Object rawData) { | 57 | String outTransferNo, Object rawData) { |
| 58 | PayTransferRespDTO respDTO = new PayTransferRespDTO(); | 58 | PayTransferRespDTO respDTO = new PayTransferRespDTO(); |
| 59 | respDTO.status = PayTransferStatusRespEnum.WAITING.getStatus(); | 59 | respDTO.status = PayTransferStatusRespEnum.WAITING.getStatus(); |
| 60 | - respDTO.channelTransferNo = channelOrderNo; | 60 | + respDTO.channelTransferNo = channelTransferNo; |
| 61 | + respDTO.outTransferNo = outTransferNo; | ||
| 62 | + respDTO.rawData = rawData; | ||
| 63 | + return respDTO; | ||
| 64 | + } | ||
| 65 | + | ||
| 66 | + /** | ||
| 67 | + * 创建【IN_PROGRESS】状态的转账返回 | ||
| 68 | + */ | ||
| 69 | + public static PayTransferRespDTO dealingOf(String channelTransferNo, | ||
| 70 | + String outTransferNo, Object rawData) { | ||
| 71 | + PayTransferRespDTO respDTO = new PayTransferRespDTO(); | ||
| 72 | + respDTO.status = PayTransferStatusRespEnum.IN_PROGRESS.getStatus(); | ||
| 73 | + respDTO.channelTransferNo = channelTransferNo; | ||
| 61 | respDTO.outTransferNo = outTransferNo; | 74 | respDTO.outTransferNo = outTransferNo; |
| 62 | respDTO.rawData = rawData; | 75 | respDTO.rawData = rawData; |
| 63 | return respDTO; | 76 | return respDTO; |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
| @@ -188,11 +188,11 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen | @@ -188,11 +188,11 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen | ||
| 188 | 188 | ||
| 189 | @Override | 189 | @Override |
| 190 | public final PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { | 190 | public final PayTransferRespDTO unifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { |
| 191 | + validatePayTransferReqDTO(reqDTO); | ||
| 191 | PayTransferRespDTO resp; | 192 | PayTransferRespDTO resp; |
| 192 | - try{ | ||
| 193 | - validatePayTransferReqDTO(reqDTO); | 193 | + try { |
| 194 | resp = doUnifiedTransfer(reqDTO); | 194 | resp = doUnifiedTransfer(reqDTO); |
| 195 | - }catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可 | 195 | + } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可 |
| 196 | throw ex; | 196 | throw ex; |
| 197 | } catch (Throwable ex) { | 197 | } catch (Throwable ex) { |
| 198 | // 系统异常,则包装成 PayException 异常抛出 | 198 | // 系统异常,则包装成 PayException 异常抛出 |
| @@ -219,9 +219,25 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen | @@ -219,9 +219,25 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen | ||
| 219 | } | 219 | } |
| 220 | } | 220 | } |
| 221 | 221 | ||
| 222 | + @Override | ||
| 223 | + public final PayTransferRespDTO getTransfer(String outTradeNo, PayTransferTypeEnum type) { | ||
| 224 | + try { | ||
| 225 | + return doGetTransfer(outTradeNo, type); | ||
| 226 | + } catch (ServiceException ex) { // 业务异常,都是实现类已经翻译,所以直接抛出即可 | ||
| 227 | + throw ex; | ||
| 228 | + } catch (Throwable ex) { | ||
| 229 | + log.error("[getTransfer][客户端({}) outTradeNo({}) type({}) 查询转账单异常]", | ||
| 230 | + getId(), outTradeNo, type, ex); | ||
| 231 | + throw buildPayException(ex); | ||
| 232 | + } | ||
| 233 | + } | ||
| 234 | + | ||
| 222 | protected abstract PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) | 235 | protected abstract PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) |
| 223 | throws Throwable; | 236 | throws Throwable; |
| 224 | 237 | ||
| 238 | + protected abstract PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) | ||
| 239 | + throws Throwable; | ||
| 240 | + | ||
| 225 | // ========== 各种工具方法 ========== | 241 | // ========== 各种工具方法 ========== |
| 226 | 242 | ||
| 227 | private PayException buildPayException(Throwable ex) { | 243 | private PayException buildPayException(Throwable ex) { |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
| @@ -23,14 +23,8 @@ import com.alipay.api.AlipayResponse; | @@ -23,14 +23,8 @@ import com.alipay.api.AlipayResponse; | ||
| 23 | import com.alipay.api.DefaultAlipayClient; | 23 | import com.alipay.api.DefaultAlipayClient; |
| 24 | import com.alipay.api.domain.*; | 24 | import com.alipay.api.domain.*; |
| 25 | import com.alipay.api.internal.util.AlipaySignature; | 25 | import com.alipay.api.internal.util.AlipaySignature; |
| 26 | -import com.alipay.api.request.AlipayFundTransUniTransferRequest; | ||
| 27 | -import com.alipay.api.request.AlipayTradeFastpayRefundQueryRequest; | ||
| 28 | -import com.alipay.api.request.AlipayTradeQueryRequest; | ||
| 29 | -import com.alipay.api.request.AlipayTradeRefundRequest; | ||
| 30 | -import com.alipay.api.response.AlipayFundTransUniTransferResponse; | ||
| 31 | -import com.alipay.api.response.AlipayTradeFastpayRefundQueryResponse; | ||
| 32 | -import com.alipay.api.response.AlipayTradeQueryResponse; | ||
| 33 | -import com.alipay.api.response.AlipayTradeRefundResponse; | 26 | +import com.alipay.api.request.*; |
| 27 | +import com.alipay.api.response.*; | ||
| 34 | import lombok.Getter; | 28 | import lombok.Getter; |
| 35 | import lombok.SneakyThrows; | 29 | import lombok.SneakyThrows; |
| 36 | import lombok.extern.slf4j.Slf4j; | 30 | import lombok.extern.slf4j.Slf4j; |
| @@ -126,7 +120,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa | @@ -126,7 +120,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa | ||
| 126 | } | 120 | } |
| 127 | // 2.2 解析订单的状态 | 121 | // 2.2 解析订单的状态 |
| 128 | Integer status = parseStatus(response.getTradeStatus()); | 122 | Integer status = parseStatus(response.getTradeStatus()); |
| 129 | - Assert.notNull(status, () -> { | 123 | + Assert.notNull(status, () -> { |
| 130 | throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", response.getBody())); | 124 | throw new IllegalArgumentException(StrUtil.format("body({}) 的 trade_status 不正确", response.getBody())); |
| 131 | }); | 125 | }); |
| 132 | return PayOrderRespDTO.of(status, response.getTradeNo(), response.getBuyerUserId(), LocalDateTimeUtil.of(response.getSendPayDate()), | 126 | return PayOrderRespDTO.of(status, response.getTradeNo(), response.getBuyerUserId(), LocalDateTimeUtil.of(response.getSendPayDate()), |
| @@ -228,7 +222,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa | @@ -228,7 +222,7 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa | ||
| 228 | protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws AlipayApiException { | 222 | protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) throws AlipayApiException { |
| 229 | // 1.1 校验公钥类型 必须使用公钥证书模式 | 223 | // 1.1 校验公钥类型 必须使用公钥证书模式 |
| 230 | if (!Objects.equals(config.getMode(), MODE_CERTIFICATE)) { | 224 | if (!Objects.equals(config.getMode(), MODE_CERTIFICATE)) { |
| 231 | - throw exception0(ERROR_CONFIGURATION.getCode(),"支付宝单笔转账必须使用公钥证书模式"); | 225 | + throw exception0(ERROR_CONFIGURATION.getCode(), "支付宝单笔转账必须使用公钥证书模式"); |
| 232 | } | 226 | } |
| 233 | // 1.2 构建 AlipayFundTransUniTransferModel | 227 | // 1.2 构建 AlipayFundTransUniTransferModel |
| 234 | AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel(); | 228 | AlipayFundTransUniTransferModel model = new AlipayFundTransUniTransferModel(); |
| @@ -238,44 +232,96 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa | @@ -238,44 +232,96 @@ public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPa | ||
| 238 | model.setOutBizNo(reqDTO.getOutTransferNo()); | 232 | model.setOutBizNo(reqDTO.getOutTransferNo()); |
| 239 | model.setProductCode("TRANS_ACCOUNT_NO_PWD"); // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD | 233 | model.setProductCode("TRANS_ACCOUNT_NO_PWD"); // 销售产品码。单笔无密转账固定为 TRANS_ACCOUNT_NO_PWD |
| 240 | model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER | 234 | model.setBizScene("DIRECT_TRANSFER"); // 业务场景 单笔无密转账固定为 DIRECT_TRANSFER |
| 241 | - model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras())); | 235 | + if (reqDTO.getChannelExtras() != null) { |
| 236 | + model.setBusinessParams(JsonUtils.toJsonString(reqDTO.getChannelExtras())); | ||
| 237 | + } | ||
| 238 | + // ② 个性化的参数 | ||
| 239 | + Participant payeeInfo = new Participant(); | ||
| 242 | PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(reqDTO.getType()); | 240 | PayTransferTypeEnum transferType = PayTransferTypeEnum.typeOf(reqDTO.getType()); |
| 243 | switch (transferType) { | 241 | switch (transferType) { |
| 244 | // TODO @jason:是不是不用传递 transferType 参数哈?因为应该已经明确是支付宝啦? | 242 | // TODO @jason:是不是不用传递 transferType 参数哈?因为应该已经明确是支付宝啦? |
| 245 | // @芋艿。 是不是还要考虑转账到银行卡。所以传 transferType 但是转账到银行卡不知道要如何测试?? | 243 | // @芋艿。 是不是还要考虑转账到银行卡。所以传 transferType 但是转账到银行卡不知道要如何测试?? |
| 246 | case ALIPAY_BALANCE: { | 244 | case ALIPAY_BALANCE: { |
| 247 | - // ② 个性化的参数 | ||
| 248 | - Participant payeeInfo = new Participant(); | ||
| 249 | payeeInfo.setIdentityType("ALIPAY_LOGON_ID"); | 245 | payeeInfo.setIdentityType("ALIPAY_LOGON_ID"); |
| 250 | payeeInfo.setIdentity(reqDTO.getAlipayLogonId()); // 支付宝登录号 | 246 | payeeInfo.setIdentity(reqDTO.getAlipayLogonId()); // 支付宝登录号 |
| 251 | payeeInfo.setName(reqDTO.getUserName()); // 支付宝账号姓名 | 247 | payeeInfo.setName(reqDTO.getUserName()); // 支付宝账号姓名 |
| 252 | model.setPayeeInfo(payeeInfo); | 248 | model.setPayeeInfo(payeeInfo); |
| 253 | - // 1.3 构建 AlipayFundTransUniTransferRequest | ||
| 254 | - AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest(); | ||
| 255 | - request.setBizModel(model); | ||
| 256 | - // 执行请求 | ||
| 257 | - AlipayFundTransUniTransferResponse response = client.certificateExecute(request); | ||
| 258 | - // 处理结果 | ||
| 259 | - if (!response.isSuccess()) { | ||
| 260 | - // 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询 | ||
| 261 | - if (ObjectUtils.equalsAny(response.getSubCode(), "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) { | ||
| 262 | - return PayTransferRespDTO.waitingOf(null, reqDTO.getOutTransferNo(), response); | ||
| 263 | - } | ||
| 264 | - return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(), | ||
| 265 | - reqDTO.getOutTransferNo(), response); | ||
| 266 | - } | ||
| 267 | - return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()), | ||
| 268 | - response.getOutBizNo(), response); | 249 | + break; |
| 269 | } | 250 | } |
| 270 | case BANK_CARD: { | 251 | case BANK_CARD: { |
| 271 | - Participant payeeInfo = new Participant(); | ||
| 272 | payeeInfo.setIdentityType("BANKCARD_ACCOUNT"); | 252 | payeeInfo.setIdentityType("BANKCARD_ACCOUNT"); |
| 273 | // TODO 待实现 | 253 | // TODO 待实现 |
| 274 | throw exception(NOT_IMPLEMENTED); | 254 | throw exception(NOT_IMPLEMENTED); |
| 275 | } | 255 | } |
| 276 | default: { | 256 | default: { |
| 277 | - throw exception0(BAD_REQUEST.getCode(),"不正确的转账类型: {}",transferType); | 257 | + throw exception0(BAD_REQUEST.getCode(), "不正确的转账类型: {}", transferType); |
| 258 | + } | ||
| 259 | + } | ||
| 260 | + // 1.3 构建 AlipayFundTransUniTransferRequest | ||
| 261 | + AlipayFundTransUniTransferRequest request = new AlipayFundTransUniTransferRequest(); | ||
| 262 | + request.setBizModel(model); | ||
| 263 | + // 执行请求 | ||
| 264 | + AlipayFundTransUniTransferResponse response = client.certificateExecute(request); | ||
| 265 | + // 处理结果 | ||
| 266 | + if (!response.isSuccess()) { | ||
| 267 | + // 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询,或相同 outBizNo 重新发起转账 | ||
| 268 | + // 发现 outBizNo 相同 两次请求参数相同. 会返回 "PAYMENT_INFO_INCONSISTENCY", 不知道哪里的问题. 暂时返回 WAIT. 后续job 会轮询 | ||
| 269 | + if (ObjectUtils.equalsAny(response.getSubCode(),"PAYMENT_INFO_INCONSISTENCY", "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) { | ||
| 270 | + return PayTransferRespDTO.waitingOf(null, reqDTO.getOutTransferNo(), response); | ||
| 271 | + } | ||
| 272 | + return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(), | ||
| 273 | + reqDTO.getOutTransferNo(), response); | ||
| 274 | + } else { | ||
| 275 | + if (ObjectUtils.equalsAny(response.getStatus(), "REFUND", "FAIL")) { // 转账到银行卡会出现 "REFUND" "FAIL" | ||
| 276 | + return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(), | ||
| 277 | + reqDTO.getOutTransferNo(), response); | ||
| 278 | + } | ||
| 279 | + if (Objects.equals(response.getStatus(), "DEALING")) { // 转账到银行卡会出现 "DEALING" 处理中 | ||
| 280 | + return PayTransferRespDTO.dealingOf(response.getOrderId(), reqDTO.getOutTransferNo(), response); | ||
| 281 | + } | ||
| 282 | + return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getTransDate()), | ||
| 283 | + response.getOutBizNo(), response); | ||
| 284 | + } | ||
| 285 | + | ||
| 286 | + } | ||
| 287 | + | ||
| 288 | + @Override | ||
| 289 | + protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) throws Throwable { | ||
| 290 | + // 1.1 构建 AlipayFundTransCommonQueryModel | ||
| 291 | + AlipayFundTransCommonQueryModel model = new AlipayFundTransCommonQueryModel(); | ||
| 292 | + model.setProductCode(type == PayTransferTypeEnum.BANK_CARD ? "TRANS_BANKCARD_NO_PWD" : "TRANS_ACCOUNT_NO_PWD"); | ||
| 293 | + model.setBizScene("DIRECT_TRANSFER"); //业务场景 | ||
| 294 | + model.setOutBizNo(outTradeNo); | ||
| 295 | + // 1.2 构建 AlipayFundTransCommonQueryRequest | ||
| 296 | + AlipayFundTransCommonQueryRequest request = new AlipayFundTransCommonQueryRequest(); | ||
| 297 | + request.setBizModel(model); | ||
| 298 | + | ||
| 299 | + // 2.1 执行请求 | ||
| 300 | + AlipayFundTransCommonQueryResponse response; | ||
| 301 | + if (Objects.equals(config.getMode(), MODE_CERTIFICATE)) { // 证书模式 | ||
| 302 | + response = client.certificateExecute(request); | ||
| 303 | + } else { | ||
| 304 | + response = client.execute(request); | ||
| 305 | + } | ||
| 306 | + // 2.2 处理返回结果 | ||
| 307 | + if (response.isSuccess()) { | ||
| 308 | + if (ObjectUtils.equalsAny(response.getStatus(), "REFUND", "FAIL")) { // 转账到银行卡会出现 "REFUND" "FAIL" | ||
| 309 | + return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(), | ||
| 310 | + outTradeNo, response); | ||
| 311 | + } | ||
| 312 | + if (Objects.equals(response.getStatus(), "DEALING")) { // 转账到银行卡会出现 "DEALING" 处理中 | ||
| 313 | + return PayTransferRespDTO.dealingOf(response.getOrderId(), outTradeNo, response); | ||
| 278 | } | 314 | } |
| 315 | + return PayTransferRespDTO.successOf(response.getOrderId(), parseTime(response.getPayDate()), | ||
| 316 | + response.getOutBizNo(), response); | ||
| 317 | + } else { | ||
| 318 | + // 当出现 SYSTEM_ERROR, 转账可能成功也可能失败。 返回 WAIT 状态. 后续 job 会轮询, 或相同 outBizNo 重新发起转账 | ||
| 319 | + // 当出现 ORDER_NOT_EXIST 可能是转账还在处理中,也可能是转账处理失败. 返回 WAIT 状态. 后续 job 会轮询, 或相同 outBizNo 重新发起转账 | ||
| 320 | + if (ObjectUtils.equalsAny(response.getSubCode(), "ORDER_NOT_EXIST", "SYSTEM_ERROR", "ACQ.SYSTEM_ERROR")) { | ||
| 321 | + return PayTransferRespDTO.waitingOf(null, outTradeNo, response); | ||
| 322 | + } | ||
| 323 | + return PayTransferRespDTO.closedOf(response.getSubCode(), response.getSubMsg(), | ||
| 324 | + outTradeNo, response); | ||
| 279 | } | 325 | } |
| 280 | } | 326 | } |
| 281 | 327 |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/mock/MockPayClient.java
| @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifie | @@ -9,6 +9,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifie | ||
| 9 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; | 9 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; |
| 10 | import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig; | 10 | import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig; |
| 11 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; | 11 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; |
| 12 | +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; | ||
| 12 | 13 | ||
| 13 | import java.time.LocalDateTime; | 14 | import java.time.LocalDateTime; |
| 14 | import java.util.Map; | 15 | import java.util.Map; |
| @@ -71,4 +72,9 @@ public class MockPayClient extends AbstractPayClient<NonePayClientConfig> { | @@ -71,4 +72,9 @@ public class MockPayClient extends AbstractPayClient<NonePayClientConfig> { | ||
| 71 | throw new UnsupportedOperationException("待实现"); | 72 | throw new UnsupportedOperationException("待实现"); |
| 72 | } | 73 | } |
| 73 | 74 | ||
| 75 | + @Override | ||
| 76 | + protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) { | ||
| 77 | + throw new UnsupportedOperationException("待实现"); | ||
| 78 | + } | ||
| 79 | + | ||
| 74 | } | 80 | } |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java
| @@ -16,6 +16,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDT | @@ -16,6 +16,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDT | ||
| 16 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; | 16 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; |
| 17 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; | 17 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; |
| 18 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; | 18 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; |
| 19 | +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; | ||
| 19 | import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; | 20 | import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; |
| 20 | import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; | 21 | import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; |
| 21 | import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; | 22 | import com.github.binarywang.wxpay.bean.notify.WxPayRefundNotifyResult; |
| @@ -431,6 +432,12 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC | @@ -431,6 +432,12 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC | ||
| 431 | protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { | 432 | protected PayTransferRespDTO doUnifiedTransfer(PayTransferUnifiedReqDTO reqDTO) { |
| 432 | throw new UnsupportedOperationException("待实现"); | 433 | throw new UnsupportedOperationException("待实现"); |
| 433 | } | 434 | } |
| 435 | + | ||
| 436 | + @Override | ||
| 437 | + protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) { | ||
| 438 | + throw new UnsupportedOperationException("待实现"); | ||
| 439 | + } | ||
| 440 | + | ||
| 434 | // ========== 各种工具方法 ========== | 441 | // ========== 各种工具方法 ========== |
| 435 | 442 | ||
| 436 | static String formatDateV2(LocalDateTime time) { | 443 | static String formatDateV2(LocalDateTime time) { |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/transfer/PayTransferStatusRespEnum.java
| @@ -38,4 +38,8 @@ public enum PayTransferStatusRespEnum { | @@ -38,4 +38,8 @@ public enum PayTransferStatusRespEnum { | ||
| 38 | public static boolean isClosed(Integer status) { | 38 | public static boolean isClosed(Integer status) { |
| 39 | return Objects.equals(status, CLOSED.getStatus()); | 39 | return Objects.equals(status, CLOSED.getStatus()); |
| 40 | } | 40 | } |
| 41 | + | ||
| 42 | + public static boolean isInProgress(Integer status) { | ||
| 43 | + return Objects.equals(status, IN_PROGRESS.getStatus()); | ||
| 44 | + } | ||
| 41 | } | 45 | } |
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
| @@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage; | @@ -12,6 +12,7 @@ import com.baomidou.mybatisplus.core.metadata.IPage; | ||
| 12 | import com.baomidou.mybatisplus.core.toolkit.support.SFunction; | 12 | import com.baomidou.mybatisplus.core.toolkit.support.SFunction; |
| 13 | import com.baomidou.mybatisplus.extension.toolkit.Db; | 13 | import com.baomidou.mybatisplus.extension.toolkit.Db; |
| 14 | import com.github.yulichang.base.MPJBaseMapper; | 14 | import com.github.yulichang.base.MPJBaseMapper; |
| 15 | +import com.github.yulichang.interfaces.MPJBaseJoin; | ||
| 15 | import org.apache.ibatis.annotations.Param; | 16 | import org.apache.ibatis.annotations.Param; |
| 16 | 17 | ||
| 17 | import java.util.Collection; | 18 | import java.util.Collection; |
| @@ -39,6 +40,13 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> { | @@ -39,6 +40,13 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> { | ||
| 39 | return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); | 40 | return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); |
| 40 | } | 41 | } |
| 41 | 42 | ||
| 43 | + default <DTO> PageResult<DTO> selectJoinPage(PageParam pageParam, Class<DTO> resultTypeClass, MPJBaseJoin<T> joinQueryWrapper) { | ||
| 44 | + IPage<DTO> mpPage = MyBatisUtils.buildPage(pageParam); | ||
| 45 | + selectJoinPage(mpPage, resultTypeClass, joinQueryWrapper); | ||
| 46 | + // 转换返回 | ||
| 47 | + return new PageResult<>(mpPage.getRecords(), mpPage.getTotal()); | ||
| 48 | + } | ||
| 49 | + | ||
| 42 | default T selectOne(String field, Object value) { | 50 | default T selectOne(String field, Object value) { |
| 43 | return selectOne(new QueryWrapper<T>().eq(field, value)); | 51 | return selectOne(new QueryWrapper<T>().eq(field, value)); |
| 44 | } | 52 | } |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/favorite/ProductFavoriteController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.product.controller.admin.favorite; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.collection.CollUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 5 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | +import cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoritePageReqVO; | ||
| 7 | +import cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoriteRespVO; | ||
| 8 | +import cn.iocoder.yudao.module.product.convert.favorite.ProductFavoriteConvert; | ||
| 9 | +import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO; | ||
| 10 | +import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; | ||
| 11 | +import cn.iocoder.yudao.module.product.service.favorite.ProductFavoriteService; | ||
| 12 | +import cn.iocoder.yudao.module.product.service.spu.ProductSpuService; | ||
| 13 | +import io.swagger.v3.oas.annotations.Operation; | ||
| 14 | +import io.swagger.v3.oas.annotations.tags.Tag; | ||
| 15 | +import org.springframework.security.access.prepost.PreAuthorize; | ||
| 16 | +import org.springframework.validation.annotation.Validated; | ||
| 17 | +import org.springframework.web.bind.annotation.GetMapping; | ||
| 18 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
| 19 | +import org.springframework.web.bind.annotation.RestController; | ||
| 20 | + | ||
| 21 | +import javax.annotation.Resource; | ||
| 22 | +import javax.validation.Valid; | ||
| 23 | +import java.util.List; | ||
| 24 | + | ||
| 25 | +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | ||
| 26 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||
| 27 | + | ||
| 28 | +@Tag(name = "管理后台 - 商品收藏") | ||
| 29 | +@RestController | ||
| 30 | +@RequestMapping("/product/favorite") | ||
| 31 | +@Validated | ||
| 32 | +public class ProductFavoriteController { | ||
| 33 | + | ||
| 34 | + @Resource | ||
| 35 | + private ProductFavoriteService productFavoriteService; | ||
| 36 | + | ||
| 37 | + @Resource | ||
| 38 | + private ProductSpuService productSpuService; | ||
| 39 | + | ||
| 40 | + @GetMapping("/page") | ||
| 41 | + @Operation(summary = "获得商品收藏分页") | ||
| 42 | + @PreAuthorize("@ss.hasPermission('product:favorite:query')") | ||
| 43 | + public CommonResult<PageResult<ProductFavoriteRespVO>> getFavoritePage(@Valid ProductFavoritePageReqVO pageVO) { | ||
| 44 | + PageResult<ProductFavoriteDO> pageResult = productFavoriteService.getFavoritePage(pageVO); | ||
| 45 | + if (CollUtil.isEmpty(pageResult.getList())) { | ||
| 46 | + return success(PageResult.empty()); | ||
| 47 | + } | ||
| 48 | + // 拼接数据 | ||
| 49 | + List<ProductSpuDO> spuList = productSpuService.getSpuList(convertSet(pageResult.getList(), ProductFavoriteDO::getSpuId)); | ||
| 50 | + return success(ProductFavoriteConvert.INSTANCE.convertPage(pageResult, spuList)); | ||
| 51 | + } | ||
| 52 | + | ||
| 53 | +} |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/favorite/vo/ProductFavoriteBaseVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.product.controller.admin.favorite.vo; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | + | ||
| 6 | +import javax.validation.constraints.NotNull; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 商品收藏 Base VO,提供给添加、修改、详细的子 VO 使用 | ||
| 10 | + * 如果子 VO 存在差异的字段,请不要添加到这里,影响 Swagger 文档生成 | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +public class ProductFavoriteBaseVO { | ||
| 14 | + | ||
| 15 | + @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "5036") | ||
| 16 | + @NotNull(message = "用户编号不能为空") | ||
| 17 | + private Long userId; | ||
| 18 | + | ||
| 19 | +} |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/favorite/vo/ProductFavoritePageReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.product.controller.admin.favorite.vo; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.EqualsAndHashCode; | ||
| 7 | +import lombok.ToString; | ||
| 8 | + | ||
| 9 | +@Schema(description = "管理后台 - 商品收藏分页 Request VO") | ||
| 10 | +@Data | ||
| 11 | +@EqualsAndHashCode(callSuper = true) | ||
| 12 | +@ToString(callSuper = true) | ||
| 13 | +public class ProductFavoritePageReqVO extends PageParam { | ||
| 14 | + | ||
| 15 | + @Schema(description = "用户编号", example = "5036") | ||
| 16 | + private Long userId; | ||
| 17 | + | ||
| 18 | +} |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/favorite/vo/ProductFavoriteReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.product.controller.admin.favorite.vo; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import lombok.Data; | ||
| 5 | +import lombok.ToString; | ||
| 6 | + | ||
| 7 | +import javax.validation.constraints.NotNull; | ||
| 8 | + | ||
| 9 | +@Schema(description = "管理后台 - 商品收藏的单个 Response VO") | ||
| 10 | +@Data | ||
| 11 | +@ToString(callSuper = true) | ||
| 12 | +public class ProductFavoriteReqVO extends ProductFavoriteBaseVO { | ||
| 13 | + | ||
| 14 | + @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "32734") | ||
| 15 | + @NotNull(message = "商品 SPU 编号不能为空") | ||
| 16 | + private Long spuId; | ||
| 17 | +} |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/favorite/vo/ProductFavoriteRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.product.controller.admin.favorite.vo; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.module.product.controller.admin.spu.vo.ProductSpuRespVO; | ||
| 4 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.ToString; | ||
| 7 | + | ||
| 8 | +@Schema(description = "管理后台 - 商品收藏 Response VO") | ||
| 9 | +@Data | ||
| 10 | +@ToString(callSuper = true) | ||
| 11 | +public class ProductFavoriteRespVO extends ProductSpuRespVO { | ||
| 12 | + | ||
| 13 | + @Schema(description = "userId", requiredMode = Schema.RequiredMode.REQUIRED, example = "111") | ||
| 14 | + private Long userId; | ||
| 15 | + | ||
| 16 | + @Schema(description = "spuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "111") | ||
| 17 | + private Long spuId; | ||
| 18 | + | ||
| 19 | +} |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuDetailRespVO.java
| @@ -12,19 +12,7 @@ import java.util.List; | @@ -12,19 +12,7 @@ import java.util.List; | ||
| 12 | @Data | 12 | @Data |
| 13 | @EqualsAndHashCode(callSuper = true) | 13 | @EqualsAndHashCode(callSuper = true) |
| 14 | @ToString(callSuper = true) | 14 | @ToString(callSuper = true) |
| 15 | -public class ProductSpuDetailRespVO extends ProductSpuBaseVO { | ||
| 16 | - | ||
| 17 | - @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1212") | ||
| 18 | - private Long id; | ||
| 19 | - | ||
| 20 | - @Schema(description = "商品销量", requiredMode = Schema.RequiredMode.REQUIRED, example = "10000") | ||
| 21 | - private Integer salesCount; | ||
| 22 | - | ||
| 23 | - @Schema(description = "浏览量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20000") | ||
| 24 | - private Integer browseCount; | ||
| 25 | - | ||
| 26 | - @Schema(description = "商品状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 27 | - private Integer status; | 15 | +public class ProductSpuDetailRespVO extends ProductSpuRespVO { |
| 28 | 16 | ||
| 29 | // ========== SKU 相关字段 ========= | 17 | // ========== SKU 相关字段 ========= |
| 30 | 18 |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/vo/ProductSpuRespVO.java
| @@ -13,7 +13,7 @@ import java.time.LocalDateTime; | @@ -13,7 +13,7 @@ import java.time.LocalDateTime; | ||
| 13 | @ToString(callSuper = true) | 13 | @ToString(callSuper = true) |
| 14 | public class ProductSpuRespVO extends ProductSpuBaseVO { | 14 | public class ProductSpuRespVO extends ProductSpuBaseVO { |
| 15 | 15 | ||
| 16 | - @Schema(description = "spuId", requiredMode = Schema.RequiredMode.REQUIRED, example = "111") | 16 | + @Schema(description = "商品 SPU 编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "111") |
| 17 | private Long id; | 17 | private Long id; |
| 18 | 18 | ||
| 19 | @Schema(description = "商品价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "1999") | 19 | @Schema(description = "商品价格", requiredMode = Schema.RequiredMode.REQUIRED, example = "1999") |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/AppProductSpuController.java
| @@ -30,6 +30,7 @@ import javax.annotation.Resource; | @@ -30,6 +30,7 @@ import javax.annotation.Resource; | ||
| 30 | import javax.validation.Valid; | 30 | import javax.validation.Valid; |
| 31 | import java.util.Collections; | 31 | import java.util.Collections; |
| 32 | import java.util.List; | 32 | import java.util.List; |
| 33 | +import java.util.Set; | ||
| 33 | 34 | ||
| 34 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 35 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 35 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 36 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| @@ -75,6 +76,25 @@ public class AppProductSpuController { | @@ -75,6 +76,25 @@ public class AppProductSpuController { | ||
| 75 | return success(voList); | 76 | return success(voList); |
| 76 | } | 77 | } |
| 77 | 78 | ||
| 79 | + @GetMapping("/list-by-ids") | ||
| 80 | + @Operation(summary = "获得商品 SPU 列表") | ||
| 81 | + @Parameters({ | ||
| 82 | + @Parameter(name = "ids", description = "编号列表", required = true) | ||
| 83 | + }) | ||
| 84 | + public CommonResult<List<AppProductSpuPageRespVO>> getSpuList(@RequestParam("ids") Set<Long> ids) { | ||
| 85 | + List<ProductSpuDO> list = productSpuService.getSpuList(ids); | ||
| 86 | + if (CollUtil.isEmpty(list)) { | ||
| 87 | + return success(Collections.emptyList()); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + // 拼接返回 | ||
| 91 | + List<AppProductSpuPageRespVO> voList = ProductSpuConvert.INSTANCE.convertListForGetSpuList(list); | ||
| 92 | + // 处理 vip 价格 | ||
| 93 | + MemberLevelRespDTO memberLevel = getMemberLevel(); | ||
| 94 | + voList.forEach(vo -> vo.setVipPrice(calculateVipPrice(vo.getPrice(), memberLevel))); | ||
| 95 | + return success(voList); | ||
| 96 | + } | ||
| 97 | + | ||
| 78 | @GetMapping("/page") | 98 | @GetMapping("/page") |
| 79 | @Operation(summary = "获得商品 SPU 分页") | 99 | @Operation(summary = "获得商品 SPU 分页") |
| 80 | public CommonResult<PageResult<AppProductSpuPageRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) { | 100 | public CommonResult<PageResult<AppProductSpuPageRespVO>> getSpuPage(@Valid AppProductSpuPageReqVO pageVO) { |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/controller/app/spu/vo/AppProductSpuPageRespVO.java
| @@ -15,6 +15,9 @@ public class AppProductSpuPageRespVO { | @@ -15,6 +15,9 @@ public class AppProductSpuPageRespVO { | ||
| 15 | @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") | 15 | @Schema(description = "商品名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道") |
| 16 | private String name; | 16 | private String name; |
| 17 | 17 | ||
| 18 | + @Schema(description = "商品简介", requiredMode = Schema.RequiredMode.REQUIRED, example = "清凉小短袖简介") | ||
| 19 | + private String introduction; | ||
| 20 | + | ||
| 18 | @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED) | 21 | @Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED) |
| 19 | private Long categoryId; | 22 | private Long categoryId; |
| 20 | 23 |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/convert/favorite/ProductFavoriteConvert.java
| 1 | package cn.iocoder.yudao.module.product.convert.favorite; | 1 | package cn.iocoder.yudao.module.product.convert.favorite; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 4 | +import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||
| 5 | +import cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoriteRespVO; | ||
| 3 | import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteRespVO; | 6 | import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoriteRespVO; |
| 4 | import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO; | 7 | import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO; |
| 5 | import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; | 8 | import cn.iocoder.yudao.module.product.dal.dataobject.spu.ProductSpuDO; |
| @@ -34,4 +37,18 @@ public interface ProductFavoriteConvert { | @@ -34,4 +37,18 @@ public interface ProductFavoriteConvert { | ||
| 34 | return resultList; | 37 | return resultList; |
| 35 | } | 38 | } |
| 36 | 39 | ||
| 40 | + default PageResult<ProductFavoriteRespVO> convertPage(PageResult<ProductFavoriteDO> pageResult, List<ProductSpuDO> spuList) { | ||
| 41 | + Map<Long, ProductSpuDO> spuMap = convertMap(spuList, ProductSpuDO::getId); | ||
| 42 | + List<ProductFavoriteRespVO> voList = CollectionUtils.convertList(pageResult.getList(), favorite -> { | ||
| 43 | + ProductSpuDO spu = spuMap.get(favorite.getSpuId()); | ||
| 44 | + return convert02(spu, favorite); | ||
| 45 | + }); | ||
| 46 | + return new PageResult<>(voList, pageResult.getTotal()); | ||
| 47 | + } | ||
| 48 | + @Mapping(target = "id", source = "favorite.id") | ||
| 49 | + @Mapping(target = "userId", source = "favorite.userId") | ||
| 50 | + @Mapping(target = "spuId", source = "favorite.spuId") | ||
| 51 | + @Mapping(target = "createTime", source = "favorite.createTime") | ||
| 52 | + ProductFavoriteRespVO convert02(ProductSpuDO spu, ProductFavoriteDO favorite); | ||
| 53 | + | ||
| 37 | } | 54 | } |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/favorite/ProductFavoriteMapper.java
| @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.product.dal.mysql.favorite; | @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.product.dal.mysql.favorite; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 4 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 5 | +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | ||
| 6 | +import cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoritePageReqVO; | ||
| 5 | import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO; | 7 | import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO; |
| 6 | import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO; | 8 | import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO; |
| 7 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | 9 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| @@ -21,6 +23,12 @@ public interface ProductFavoriteMapper extends BaseMapperX<ProductFavoriteDO> { | @@ -21,6 +23,12 @@ public interface ProductFavoriteMapper extends BaseMapperX<ProductFavoriteDO> { | ||
| 21 | .orderByDesc(ProductFavoriteDO::getId)); | 23 | .orderByDesc(ProductFavoriteDO::getId)); |
| 22 | } | 24 | } |
| 23 | 25 | ||
| 26 | + default PageResult<ProductFavoriteDO> selectPageByUserId(ProductFavoritePageReqVO reqVO) { | ||
| 27 | + return selectPage(reqVO, new LambdaQueryWrapperX<ProductFavoriteDO>() | ||
| 28 | + .eqIfPresent(ProductFavoriteDO::getUserId, reqVO.getUserId()) | ||
| 29 | + .orderByDesc(ProductFavoriteDO::getId)); | ||
| 30 | + } | ||
| 31 | + | ||
| 24 | default Long selectCountByUserId(Long userId) { | 32 | default Long selectCountByUserId(Long userId) { |
| 25 | return selectCount(ProductFavoriteDO::getUserId, userId); | 33 | return selectCount(ProductFavoriteDO::getUserId, userId); |
| 26 | } | 34 | } |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteService.java
| 1 | package cn.iocoder.yudao.module.product.service.favorite; | 1 | package cn.iocoder.yudao.module.product.service.favorite; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | +import cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoritePageReqVO; | ||
| 4 | import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO; | 5 | import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO; |
| 5 | import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO; | 6 | import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO; |
| 6 | 7 | ||
| @@ -38,6 +39,13 @@ public interface ProductFavoriteService { | @@ -38,6 +39,13 @@ public interface ProductFavoriteService { | ||
| 38 | PageResult<ProductFavoriteDO> getFavoritePage(Long userId, @Valid AppFavoritePageReqVO reqVO); | 39 | PageResult<ProductFavoriteDO> getFavoritePage(Long userId, @Valid AppFavoritePageReqVO reqVO); |
| 39 | 40 | ||
| 40 | /** | 41 | /** |
| 42 | + * 分页查询用户收藏列表 | ||
| 43 | + * | ||
| 44 | + * @param reqVO 请求 vo | ||
| 45 | + */ | ||
| 46 | + PageResult<ProductFavoriteDO> getFavoritePage(@Valid ProductFavoritePageReqVO reqVO); | ||
| 47 | + | ||
| 48 | + /** | ||
| 41 | * 获取收藏过商品 | 49 | * 获取收藏过商品 |
| 42 | * | 50 | * |
| 43 | * @param userId 用户编号 | 51 | * @param userId 用户编号 |
yudao-module-mall/yudao-module-product-biz/src/main/java/cn/iocoder/yudao/module/product/service/favorite/ProductFavoriteServiceImpl.java
| 1 | package cn.iocoder.yudao.module.product.service.favorite; | 1 | package cn.iocoder.yudao.module.product.service.favorite; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | +import cn.iocoder.yudao.module.product.controller.admin.favorite.vo.ProductFavoritePageReqVO; | ||
| 4 | import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO; | 5 | import cn.iocoder.yudao.module.product.controller.app.favorite.vo.AppFavoritePageReqVO; |
| 5 | import cn.iocoder.yudao.module.product.convert.favorite.ProductFavoriteConvert; | 6 | import cn.iocoder.yudao.module.product.convert.favorite.ProductFavoriteConvert; |
| 6 | import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO; | 7 | import cn.iocoder.yudao.module.product.dal.dataobject.favorite.ProductFavoriteDO; |
| @@ -55,6 +56,11 @@ public class ProductFavoriteServiceImpl implements ProductFavoriteService { | @@ -55,6 +56,11 @@ public class ProductFavoriteServiceImpl implements ProductFavoriteService { | ||
| 55 | } | 56 | } |
| 56 | 57 | ||
| 57 | @Override | 58 | @Override |
| 59 | + public PageResult<ProductFavoriteDO> getFavoritePage(@Valid ProductFavoritePageReqVO reqVO) { | ||
| 60 | + return productFavoriteMapper.selectPageByUserId(reqVO); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + @Override | ||
| 58 | public ProductFavoriteDO getFavorite(Long userId, Long spuId) { | 64 | public ProductFavoriteDO getFavorite(Long userId, Long spuId) { |
| 59 | return productFavoriteMapper.selectByUserIdAndSpuId(userId, spuId); | 65 | return productFavoriteMapper.selectByUserIdAndSpuId(userId, spuId); |
| 60 | } | 66 | } |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/diy/vo/template/DiyTemplateBaseVO.java
| @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template; | @@ -3,7 +3,7 @@ package cn.iocoder.yudao.module.promotion.controller.admin.diy.vo.template; | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | -import javax.validation.constraints.NotNull; | 6 | +import javax.validation.constraints.NotEmpty; |
| 7 | import java.util.List; | 7 | import java.util.List; |
| 8 | 8 | ||
| 9 | /** | 9 | /** |
| @@ -14,7 +14,7 @@ import java.util.List; | @@ -14,7 +14,7 @@ import java.util.List; | ||
| 14 | public class DiyTemplateBaseVO { | 14 | public class DiyTemplateBaseVO { |
| 15 | 15 | ||
| 16 | @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "默认主题") | 16 | @Schema(description = "模板名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "默认主题") |
| 17 | - @NotNull(message = "模板名称不能为空") | 17 | + @NotEmpty(message = "模板名称不能为空") |
| 18 | private String name; | 18 | private String name; |
| 19 | 19 | ||
| 20 | @Schema(description = "备注", example = "默认主题") | 20 | @Schema(description = "备注", example = "默认主题") |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/diy/DiyPageDO.java
| @@ -32,6 +32,8 @@ public class DiyPageDO extends BaseDO { | @@ -32,6 +32,8 @@ public class DiyPageDO extends BaseDO { | ||
| 32 | private Long id; | 32 | private Long id; |
| 33 | /** | 33 | /** |
| 34 | * 装修模板编号 | 34 | * 装修模板编号 |
| 35 | + * | ||
| 36 | + * 关联 {@link DiyTemplateDO#getId()} | ||
| 35 | */ | 37 | */ |
| 36 | private Long templateId; | 38 | private Long templateId; |
| 37 | /** | 39 | /** |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/dal/dataobject/diy/DiyTemplateDO.java
| @@ -14,6 +14,9 @@ import java.util.List; | @@ -14,6 +14,9 @@ import java.util.List; | ||
| 14 | /** | 14 | /** |
| 15 | * 装修模板 DO | 15 | * 装修模板 DO |
| 16 | * | 16 | * |
| 17 | + * 1. 新建一个模版,下面可以包含多个 {@link DiyPageDO} 页面,例如说首页、我的 | ||
| 18 | + * 2. 如果需要使用某个模版,则将 {@link #used} 设置为 true,表示已使用,有且仅有一个 | ||
| 19 | + * | ||
| 17 | * @author owen | 20 | * @author owen |
| 18 | */ | 21 | */ |
| 19 | @TableName(value = "promotion_diy_template", autoResultMap = true) | 22 | @TableName(value = "promotion_diy_template", autoResultMap = true) |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyPageServiceImpl.java
| @@ -42,7 +42,6 @@ public class DiyPageServiceImpl implements DiyPageService { | @@ -42,7 +42,6 @@ public class DiyPageServiceImpl implements DiyPageService { | ||
| 42 | DiyPageDO diyPage = DiyPageConvert.INSTANCE.convert(createReqVO); | 42 | DiyPageDO diyPage = DiyPageConvert.INSTANCE.convert(createReqVO); |
| 43 | diyPage.setProperty("{}"); | 43 | diyPage.setProperty("{}"); |
| 44 | diyPageMapper.insert(diyPage); | 44 | diyPageMapper.insert(diyPage); |
| 45 | - // 返回 | ||
| 46 | return diyPage.getId(); | 45 | return diyPage.getId(); |
| 47 | } | 46 | } |
| 48 | 47 | ||
| @@ -57,6 +56,13 @@ public class DiyPageServiceImpl implements DiyPageService { | @@ -57,6 +56,13 @@ public class DiyPageServiceImpl implements DiyPageService { | ||
| 57 | diyPageMapper.updateById(updateObj); | 56 | diyPageMapper.updateById(updateObj); |
| 58 | } | 57 | } |
| 59 | 58 | ||
| 59 | + /** | ||
| 60 | + * 校验 Page 页面,在一个 template 模版下的名字是唯一的 | ||
| 61 | + * | ||
| 62 | + * @param id Page 编号 | ||
| 63 | + * @param templateId 模版编号 | ||
| 64 | + * @param name Page 名字 | ||
| 65 | + */ | ||
| 60 | void validateNameUnique(Long id, Long templateId, String name) { | 66 | void validateNameUnique(Long id, Long templateId, String name) { |
| 61 | if (templateId != null || StrUtil.isBlank(name)) { | 67 | if (templateId != null || StrUtil.isBlank(name)) { |
| 62 | return; | 68 | return; |
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/diy/DiyTemplateServiceImpl.java
| @@ -32,9 +32,11 @@ public class DiyTemplateServiceImpl implements DiyTemplateService { | @@ -32,9 +32,11 @@ public class DiyTemplateServiceImpl implements DiyTemplateService { | ||
| 32 | 32 | ||
| 33 | @Resource | 33 | @Resource |
| 34 | private DiyTemplateMapper diyTemplateMapper; | 34 | private DiyTemplateMapper diyTemplateMapper; |
| 35 | + | ||
| 35 | @Resource | 36 | @Resource |
| 36 | private DiyPageService diyPageService; | 37 | private DiyPageService diyPageService; |
| 37 | 38 | ||
| 39 | + // TODO @疯狂:事务; | ||
| 38 | @Override | 40 | @Override |
| 39 | public Long createDiyTemplate(DiyTemplateCreateReqVO createReqVO) { | 41 | public Long createDiyTemplate(DiyTemplateCreateReqVO createReqVO) { |
| 40 | // 校验名称唯一 | 42 | // 校验名称唯一 |
| @@ -120,9 +122,11 @@ public class DiyTemplateServiceImpl implements DiyTemplateService { | @@ -120,9 +122,11 @@ public class DiyTemplateServiceImpl implements DiyTemplateService { | ||
| 120 | } | 122 | } |
| 121 | 123 | ||
| 122 | @Override | 124 | @Override |
| 125 | + // TODO @疯狂:事务; | ||
| 123 | public void useDiyTemplate(Long id) { | 126 | public void useDiyTemplate(Long id) { |
| 124 | // 校验存在 | 127 | // 校验存在 |
| 125 | validateDiyTemplateExists(id); | 128 | validateDiyTemplateExists(id); |
| 129 | + // TODO @疯狂:要不已使用的情况,抛个业务异常? | ||
| 126 | // 已使用的更新为未使用 | 130 | // 已使用的更新为未使用 |
| 127 | DiyTemplateDO used = diyTemplateMapper.selectByUsed(true); | 131 | DiyTemplateDO used = diyTemplateMapper.selectByUsed(true); |
| 128 | if (used != null) { | 132 | if (used != null) { |
| @@ -136,8 +140,8 @@ public class DiyTemplateServiceImpl implements DiyTemplateService { | @@ -136,8 +140,8 @@ public class DiyTemplateServiceImpl implements DiyTemplateService { | ||
| 136 | this.updateUsed(id, true, LocalDateTime.now()); | 140 | this.updateUsed(id, true, LocalDateTime.now()); |
| 137 | } | 141 | } |
| 138 | 142 | ||
| 139 | - @Transactional(rollbackFor = Exception.class) | ||
| 140 | @Override | 143 | @Override |
| 144 | + @Transactional(rollbackFor = Exception.class) | ||
| 141 | public void updateDiyTemplateProperty(DiyTemplatePropertyUpdateRequestVO updateReqVO) { | 145 | public void updateDiyTemplateProperty(DiyTemplatePropertyUpdateRequestVO updateReqVO) { |
| 142 | // 校验存在 | 146 | // 校验存在 |
| 143 | validateDiyTemplateExists(updateReqVO.getId()); | 147 | validateDiyTemplateExists(updateReqVO.getId()); |
| @@ -151,6 +155,7 @@ public class DiyTemplateServiceImpl implements DiyTemplateService { | @@ -151,6 +155,7 @@ public class DiyTemplateServiceImpl implements DiyTemplateService { | ||
| 151 | return diyTemplateMapper.selectByUsed(true); | 155 | return diyTemplateMapper.selectByUsed(true); |
| 152 | } | 156 | } |
| 153 | 157 | ||
| 158 | + // TODO @疯狂:挪到 useDiyTemplate 下面,改名 updateTemplateUsed 会不会好点哈; | ||
| 154 | /** | 159 | /** |
| 155 | * 更新模板是否使用 | 160 | * 更新模板是否使用 |
| 156 | * | 161 | * |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/api/notify/dto/PayTransferNotifyReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.api.notify.dto; | ||
| 2 | + | ||
| 3 | +import lombok.Data; | ||
| 4 | + | ||
| 5 | +import javax.validation.constraints.NotEmpty; | ||
| 6 | +import javax.validation.constraints.NotNull; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 转账单的通知 Request DTO | ||
| 10 | + * | ||
| 11 | + * @author jason | ||
| 12 | + */ | ||
| 13 | +@Data | ||
| 14 | +public class PayTransferNotifyReqDTO { | ||
| 15 | + | ||
| 16 | + /** | ||
| 17 | + * 商户转账单号 | ||
| 18 | + */ | ||
| 19 | + @NotEmpty(message = "商户转账单号不能为空") | ||
| 20 | + private String merchantTransferId; | ||
| 21 | + | ||
| 22 | + /** | ||
| 23 | + * 转账订单编号 | ||
| 24 | + */ | ||
| 25 | + @NotNull(message = "转账订单编号不能为空") | ||
| 26 | + private Long payTransferId; | ||
| 27 | +} |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/ErrorCodeConstants.java
| @@ -65,12 +65,13 @@ public interface ErrorCodeConstants { | @@ -65,12 +65,13 @@ public interface ErrorCodeConstants { | ||
| 65 | 65 | ||
| 66 | // ========== 转账模块 1-007-009-000 ========== | 66 | // ========== 转账模块 1-007-009-000 ========== |
| 67 | ErrorCode PAY_TRANSFER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1_007_009_000, "发起转账报错,错误码:{},错误提示:{}"); | 67 | ErrorCode PAY_TRANSFER_SUBMIT_CHANNEL_ERROR = new ErrorCode(1_007_009_000, "发起转账报错,错误码:{},错误提示:{}"); |
| 68 | - ErrorCode PAY_TRANSFER_NOT_FOUND = new ErrorCode(1_007_009_001, "转账交易单不存在"); | ||
| 69 | - ErrorCode PAY_TRANSFER_STATUS_IS_SUCCESS = new ErrorCode(1_007_009_002, "转账单已成功转账"); | ||
| 70 | - ErrorCode PAY_TRANSFER_EXISTS = new ErrorCode(1_007_009_003, "已经存在转账单"); | ||
| 71 | - ErrorCode PAY_MERCHANT_TRANSFER_EXISTS = new ErrorCode(1_007_009_004, "该笔业务的转账已经存在,请查询转账订单相关状态"); | 68 | + ErrorCode PAY_TRANSFER_NOT_FOUND = new ErrorCode(1_007_009_001, "转账单不存在"); |
| 69 | + ErrorCode PAY_SAME_MERCHANT_TRANSFER_TYPE_NOT_MATCH = new ErrorCode(1_007_009_002, "两次相同转账请求的类型不匹配"); | ||
| 70 | + ErrorCode PAY_SAME_MERCHANT_TRANSFER_PRICE_NOT_MATCH = new ErrorCode(1_007_009_003, "两次相同转账请求的金额不匹配"); | ||
| 71 | + ErrorCode PAY_MERCHANT_TRANSFER_EXISTS = new ErrorCode(1_007_009_004, "该笔业务的转账已经发起,请查询转账订单相关状态"); | ||
| 72 | ErrorCode PAY_TRANSFER_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_009_005, "转账单不处于待转账"); | 72 | ErrorCode PAY_TRANSFER_STATUS_IS_NOT_WAITING = new ErrorCode(1_007_009_005, "转账单不处于待转账"); |
| 73 | ErrorCode PAY_TRANSFER_STATUS_IS_NOT_PENDING = new ErrorCode(1_007_009_006, "转账单不处于待转账或转账中"); | 73 | ErrorCode PAY_TRANSFER_STATUS_IS_NOT_PENDING = new ErrorCode(1_007_009_006, "转账单不处于待转账或转账中"); |
| 74 | + | ||
| 74 | // ========== 示例订单 1-007-900-000 ========== | 75 | // ========== 示例订单 1-007-900-000 ========== |
| 75 | ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1_007_900_000, "示例订单不存在"); | 76 | ErrorCode DEMO_ORDER_NOT_FOUND = new ErrorCode(1_007_900_000, "示例订单不存在"); |
| 76 | ErrorCode DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1_007_900_001, "示例订单更新支付状态失败,订单不是【未支付】状态"); | 77 | ErrorCode DEMO_ORDER_UPDATE_PAID_STATUS_NOT_UNPAID = new ErrorCode(1_007_900_001, "示例订单更新支付状态失败,订单不是【未支付】状态"); |
| @@ -84,4 +85,8 @@ public interface ErrorCodeConstants { | @@ -84,4 +85,8 @@ public interface ErrorCodeConstants { | ||
| 84 | ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1_007_900_009, "发起退款失败,退款单编号不匹配"); | 85 | ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_ORDER_ID_ERROR = new ErrorCode(1_007_900_009, "发起退款失败,退款单编号不匹配"); |
| 85 | ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1_007_900_010, "发起退款失败,退款单金额不匹配"); | 86 | ErrorCode DEMO_ORDER_REFUND_FAIL_REFUND_PRICE_NOT_MATCH = new ErrorCode(1_007_900_010, "发起退款失败,退款单金额不匹配"); |
| 86 | 87 | ||
| 88 | + // ========== 示例转账订单 1-007-901-001 ========== | ||
| 89 | + ErrorCode DEMO_TRANSFER_NOT_FOUND = new ErrorCode(1_007_901_001, "示例转账单不存在"); | ||
| 90 | + ErrorCode DEMO_TRANSFER_FAIL_TRANSFER_ID_ERROR = new ErrorCode(1_007_901_002, "转账失败,转账单编号不匹配"); | ||
| 91 | + ErrorCode DEMO_TRANSFER_FAIL_PRICE_NOT_MATCH = new ErrorCode(1_007_901_003, "转账失败,转账单金额不匹配"); | ||
| 87 | } | 92 | } |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/notify/PayNotifyTypeEnum.java
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/transfer/PayTransferStatusEnum.java
| @@ -40,9 +40,13 @@ public enum PayTransferStatusEnum { | @@ -40,9 +40,13 @@ public enum PayTransferStatusEnum { | ||
| 40 | public static boolean isClosed(Integer status) { | 40 | public static boolean isClosed(Integer status) { |
| 41 | return Objects.equals(status, CLOSED.getStatus()); | 41 | return Objects.equals(status, CLOSED.getStatus()); |
| 42 | } | 42 | } |
| 43 | + | ||
| 43 | public static boolean isWaiting(Integer status) { | 44 | public static boolean isWaiting(Integer status) { |
| 44 | return Objects.equals(status, WAITING.getStatus()); | 45 | return Objects.equals(status, WAITING.getStatus()); |
| 45 | } | 46 | } |
| 47 | + public static boolean isInProgress(Integer status) { | ||
| 48 | + return Objects.equals(status, IN_PROGRESS.getStatus()); | ||
| 49 | + } | ||
| 46 | 50 | ||
| 47 | /** | 51 | /** |
| 48 | * 是否处于待转账或者转账中的状态 | 52 | * 是否处于待转账或者转账中的状态 |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoOrderController.java
| @@ -6,8 +6,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | @@ -6,8 +6,8 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 6 | import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | 6 | import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; |
| 7 | import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; | 7 | import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; |
| 8 | import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; | 8 | import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; |
| 9 | -import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.PayDemoOrderCreateReqVO; | ||
| 10 | -import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.PayDemoOrderRespVO; | 9 | +import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order.PayDemoOrderCreateReqVO; |
| 10 | +import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order.PayDemoOrderRespVO; | ||
| 11 | import cn.iocoder.yudao.module.pay.convert.demo.PayDemoOrderConvert; | 11 | import cn.iocoder.yudao.module.pay.convert.demo.PayDemoOrderConvert; |
| 12 | import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoOrderDO; | 12 | import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoOrderDO; |
| 13 | import cn.iocoder.yudao.module.pay.service.demo.PayDemoOrderService; | 13 | import cn.iocoder.yudao.module.pay.service.demo.PayDemoOrderService; |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/PayDemoTransferController.java
| @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.pay.controller.admin.demo; | @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.pay.controller.admin.demo; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | +import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; | ||
| 7 | +import cn.iocoder.yudao.module.pay.api.notify.dto.PayTransferNotifyReqDTO; | ||
| 6 | import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; | 8 | import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; |
| 7 | import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferRespVO; | 9 | import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferRespVO; |
| 8 | import cn.iocoder.yudao.module.pay.convert.demo.PayDemoTransferConvert; | 10 | import cn.iocoder.yudao.module.pay.convert.demo.PayDemoTransferConvert; |
| @@ -14,6 +16,7 @@ import org.springframework.validation.annotation.Validated; | @@ -14,6 +16,7 @@ import org.springframework.validation.annotation.Validated; | ||
| 14 | import org.springframework.web.bind.annotation.*; | 16 | import org.springframework.web.bind.annotation.*; |
| 15 | 17 | ||
| 16 | import javax.annotation.Resource; | 18 | import javax.annotation.Resource; |
| 19 | +import javax.annotation.security.PermitAll; | ||
| 17 | import javax.validation.Valid; | 20 | import javax.validation.Valid; |
| 18 | 21 | ||
| 19 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 22 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| @@ -33,9 +36,19 @@ public class PayDemoTransferController { | @@ -33,9 +36,19 @@ public class PayDemoTransferController { | ||
| 33 | } | 36 | } |
| 34 | 37 | ||
| 35 | @GetMapping("/page") | 38 | @GetMapping("/page") |
| 36 | - @Operation(summary = "获得示例订单分页") | 39 | + @Operation(summary = "获得示例转账订单分页") |
| 37 | public CommonResult<PageResult<PayDemoTransferRespVO>> getDemoTransferPage(@Valid PageParam pageVO) { | 40 | public CommonResult<PageResult<PayDemoTransferRespVO>> getDemoTransferPage(@Valid PageParam pageVO) { |
| 38 | PageResult<PayDemoTransferDO> pageResult = demoTransferService.getDemoTransferPage(pageVO); | 41 | PageResult<PayDemoTransferDO> pageResult = demoTransferService.getDemoTransferPage(pageVO); |
| 39 | return success(PayDemoTransferConvert.INSTANCE.convertPage(pageResult)); | 42 | return success(PayDemoTransferConvert.INSTANCE.convertPage(pageResult)); |
| 40 | } | 43 | } |
| 44 | + | ||
| 45 | + @PostMapping("/update-status") | ||
| 46 | + @Operation(summary = "更新示例转账订单的转账状态") // 由 pay-module 转账服务,进行回调 | ||
| 47 | + @PermitAll // 无需登录,安全由 PayDemoTransferService 内部校验实现 | ||
| 48 | + @OperateLog(enable = false) // 禁用操作日志,因为没有操作人 | ||
| 49 | + public CommonResult<Boolean> updateDemoTransferStatus(@RequestBody PayTransferNotifyReqDTO notifyReqDTO) { | ||
| 50 | + demoTransferService.updateDemoTransferStatus(Long.valueOf(notifyReqDTO.getMerchantTransferId()), | ||
| 51 | + notifyReqDTO.getPayTransferId()); | ||
| 52 | + return success(true); | ||
| 53 | + } | ||
| 41 | } | 54 | } |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/vo/PayDemoOrderCreateReqVO.java renamed to yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/vo/order/PayDemoOrderCreateReqVO.java
| 1 | -package cn.iocoder.yudao.module.pay.controller.admin.demo.vo; | 1 | +package cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order; |
| 2 | 2 | ||
| 3 | -import lombok.*; | ||
| 4 | import io.swagger.v3.oas.annotations.media.Schema; | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | +import lombok.Data; | ||
| 5 | 5 | ||
| 6 | -import javax.validation.constraints.NotEmpty; | ||
| 7 | import javax.validation.constraints.NotNull; | 6 | import javax.validation.constraints.NotNull; |
| 8 | 7 | ||
| 9 | @Schema(description = "管理后台 - 示例订单创建 Request VO") | 8 | @Schema(description = "管理后台 - 示例订单创建 Request VO") |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/vo/PayDemoOrderRespVO.java renamed to yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/demo/vo/order/PayDemoOrderRespVO.java
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/demo/PayDemoOrderConvert.java
| 1 | package cn.iocoder.yudao.module.pay.convert.demo; | 1 | package cn.iocoder.yudao.module.pay.convert.demo; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | -import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.PayDemoOrderCreateReqVO; | ||
| 5 | -import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.PayDemoOrderRespVO; | 4 | +import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order.PayDemoOrderCreateReqVO; |
| 5 | +import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order.PayDemoOrderRespVO; | ||
| 6 | import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoOrderDO; | 6 | import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoOrderDO; |
| 7 | import org.mapstruct.Mapper; | 7 | import org.mapstruct.Mapper; |
| 8 | import org.mapstruct.factory.Mappers; | 8 | import org.mapstruct.factory.Mappers; |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/transfer/PayTransferConvert.java
| @@ -18,7 +18,7 @@ public interface PayTransferConvert { | @@ -18,7 +18,7 @@ public interface PayTransferConvert { | ||
| 18 | 18 | ||
| 19 | PayTransferDO convert(PayTransferCreateReqDTO dto); | 19 | PayTransferDO convert(PayTransferCreateReqDTO dto); |
| 20 | 20 | ||
| 21 | - PayTransferUnifiedReqDTO convert2(PayTransferCreateReqDTO dto); | 21 | + PayTransferUnifiedReqDTO convert2(PayTransferDO dto); |
| 22 | 22 | ||
| 23 | PayTransferCreateReqDTO convert(PayTransferCreateReqVO vo); | 23 | PayTransferCreateReqDTO convert(PayTransferCreateReqVO vo); |
| 24 | 24 |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/app/PayAppDO.java
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/dataobject/notify/PayNotifyTaskDO.java
| @@ -67,6 +67,10 @@ public class PayNotifyTaskDO extends TenantBaseDO { | @@ -67,6 +67,10 @@ public class PayNotifyTaskDO extends TenantBaseDO { | ||
| 67 | */ | 67 | */ |
| 68 | private String merchantOrderId; | 68 | private String merchantOrderId; |
| 69 | /** | 69 | /** |
| 70 | + * 商户转账单编号 | ||
| 71 | + */ | ||
| 72 | + private String merchantTransferId; | ||
| 73 | + /** | ||
| 70 | * 通知状态 | 74 | * 通知状态 |
| 71 | * | 75 | * |
| 72 | * 外键 {@link PayNotifyStatusEnum} | 76 | * 外键 {@link PayNotifyStatusEnum} |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java
| 1 | package cn.iocoder.yudao.module.pay.dal.mysql.transfer; | 1 | package cn.iocoder.yudao.module.pay.dal.mysql.transfer; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 4 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 4 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 5 | import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageReqVO; | 6 | import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageReqVO; |
| 6 | import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; | 7 | import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; |
| 7 | -import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 8 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; | 8 | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| 9 | import org.apache.ibatis.annotations.Mapper; | 9 | import org.apache.ibatis.annotations.Mapper; |
| 10 | 10 | ||
| @@ -40,6 +40,10 @@ public interface PayTransferMapper extends BaseMapperX<PayTransferDO> { | @@ -40,6 +40,10 @@ public interface PayTransferMapper extends BaseMapperX<PayTransferDO> { | ||
| 40 | .betweenIfPresent(PayTransferDO::getCreateTime, reqVO.getCreateTime()) | 40 | .betweenIfPresent(PayTransferDO::getCreateTime, reqVO.getCreateTime()) |
| 41 | .orderByDesc(PayTransferDO::getId)); | 41 | .orderByDesc(PayTransferDO::getId)); |
| 42 | } | 42 | } |
| 43 | + | ||
| 44 | + default List<PayTransferDO> selectListByStatus(Integer status){ | ||
| 45 | + return selectList(PayTransferDO::getStatus, status); | ||
| 46 | + } | ||
| 43 | } | 47 | } |
| 44 | 48 | ||
| 45 | 49 |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/WalletPayClient.java
| @@ -14,6 +14,7 @@ import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; | @@ -14,6 +14,7 @@ import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; | ||
| 14 | import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig; | 14 | import cn.iocoder.yudao.framework.pay.core.client.impl.NonePayClientConfig; |
| 15 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; | 15 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; |
| 16 | import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; | 16 | import cn.iocoder.yudao.framework.pay.core.enums.refund.PayRefundStatusRespEnum; |
| 17 | +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; | ||
| 17 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; | 18 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderExtensionDO; |
| 18 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; | 19 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| 19 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; | 20 | import cn.iocoder.yudao.module.pay.dal.dataobject.wallet.PayWalletTransactionDO; |
| @@ -181,4 +182,9 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> { | @@ -181,4 +182,9 @@ public class WalletPayClient extends AbstractPayClient<NonePayClientConfig> { | ||
| 181 | throw new UnsupportedOperationException("待实现"); | 182 | throw new UnsupportedOperationException("待实现"); |
| 182 | } | 183 | } |
| 183 | 184 | ||
| 185 | + @Override | ||
| 186 | + protected PayTransferRespDTO doGetTransfer(String outTradeNo, PayTransferTypeEnum type) { | ||
| 187 | + throw new UnsupportedOperationException("待实现"); | ||
| 188 | + } | ||
| 189 | + | ||
| 184 | } | 190 | } |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/job/transfer/PayTransferSyncJob.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.pay.job.transfer; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | ||
| 5 | +import cn.iocoder.yudao.framework.tenant.core.job.TenantJob; | ||
| 6 | +import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService; | ||
| 7 | +import org.springframework.stereotype.Component; | ||
| 8 | + | ||
| 9 | +import javax.annotation.Resource; | ||
| 10 | + | ||
| 11 | +/** | ||
| 12 | + * 转账订单的同步 Job | ||
| 13 | + * | ||
| 14 | + * 由于转账订单的转账结果,有些渠道是异步通知进行同步的,考虑到异步通知可能会失败(小概率),所以需要定时进行同步。 | ||
| 15 | + * | ||
| 16 | + * @author jason | ||
| 17 | + */ | ||
| 18 | +@Component | ||
| 19 | +public class PayTransferSyncJob implements JobHandler { | ||
| 20 | + | ||
| 21 | + @Resource | ||
| 22 | + private PayTransferService transferService; | ||
| 23 | + | ||
| 24 | + @Override | ||
| 25 | + @TenantJob | ||
| 26 | + public String execute(String param) { | ||
| 27 | + int count = transferService.syncTransfer(); | ||
| 28 | + return StrUtil.format("同步转账订单 {} 个", count); | ||
| 29 | + } | ||
| 30 | +} |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderService.java
| @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.pay.service.demo; | @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.pay.service.demo; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | -import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.PayDemoOrderCreateReqVO; | 5 | +import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order.PayDemoOrderCreateReqVO; |
| 6 | import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoOrderDO; | 6 | import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoOrderDO; |
| 7 | 7 | ||
| 8 | import javax.validation.Valid; | 8 | import javax.validation.Valid; |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoOrderServiceImpl.java
| @@ -9,7 +9,7 @@ import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; | @@ -9,7 +9,7 @@ import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderRespDTO; | ||
| 9 | import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; | 9 | import cn.iocoder.yudao.module.pay.api.refund.PayRefundApi; |
| 10 | import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; | 10 | import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO; |
| 11 | import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; | 11 | import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundRespDTO; |
| 12 | -import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.PayDemoOrderCreateReqVO; | 12 | +import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.order.PayDemoOrderCreateReqVO; |
| 13 | import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoOrderDO; | 13 | import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoOrderDO; |
| 14 | import cn.iocoder.yudao.module.pay.dal.mysql.demo.PayDemoOrderMapper; | 14 | import cn.iocoder.yudao.module.pay.dal.mysql.demo.PayDemoOrderMapper; |
| 15 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; | 15 | import cn.iocoder.yudao.module.pay.enums.order.PayOrderStatusEnum; |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferService.java
| @@ -28,4 +28,12 @@ public interface PayDemoTransferService { | @@ -28,4 +28,12 @@ public interface PayDemoTransferService { | ||
| 28 | * @param pageVO 分页查询参数 | 28 | * @param pageVO 分页查询参数 |
| 29 | */ | 29 | */ |
| 30 | PageResult<PayDemoTransferDO> getDemoTransferPage(PageParam pageVO); | 30 | PageResult<PayDemoTransferDO> getDemoTransferPage(PageParam pageVO); |
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 更新转账业务示例订单的转账状态 | ||
| 34 | + * | ||
| 35 | + * @param id 编号 | ||
| 36 | + * @param payTransferId 转账单编号 | ||
| 37 | + */ | ||
| 38 | + void updateDemoTransferStatus(Long id, Long payTransferId); | ||
| 31 | } | 39 | } |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/demo/PayDemoTransferServiceImpl.java
| 1 | package cn.iocoder.yudao.module.pay.service.demo; | 1 | package cn.iocoder.yudao.module.pay.service.demo; |
| 2 | 2 | ||
| 3 | +import cn.hutool.core.util.ObjectUtil; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; | 6 | import cn.iocoder.yudao.module.pay.controller.admin.demo.vo.transfer.PayDemoTransferCreateReqVO; |
| 6 | import cn.iocoder.yudao.module.pay.convert.demo.PayDemoTransferConvert; | 7 | import cn.iocoder.yudao.module.pay.convert.demo.PayDemoTransferConvert; |
| 7 | import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoTransferDO; | 8 | import cn.iocoder.yudao.module.pay.dal.dataobject.demo.PayDemoTransferDO; |
| 9 | +import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; | ||
| 8 | import cn.iocoder.yudao.module.pay.dal.mysql.demo.PayDemoTransferMapper; | 10 | import cn.iocoder.yudao.module.pay.dal.mysql.demo.PayDemoTransferMapper; |
| 11 | +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; | ||
| 12 | +import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService; | ||
| 9 | import org.springframework.stereotype.Service; | 13 | import org.springframework.stereotype.Service; |
| 10 | import org.springframework.validation.annotation.Validated; | 14 | import org.springframework.validation.annotation.Validated; |
| 11 | 15 | ||
| 12 | import javax.annotation.Resource; | 16 | import javax.annotation.Resource; |
| 13 | import javax.validation.Valid; | 17 | import javax.validation.Valid; |
| 14 | import javax.validation.Validator; | 18 | import javax.validation.Validator; |
| 19 | +import java.util.Objects; | ||
| 15 | 20 | ||
| 21 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 22 | +import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.*; | ||
| 16 | import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum.WAITING; | 23 | import static cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum.WAITING; |
| 17 | 24 | ||
| 18 | /** | 25 | /** |
| @@ -33,6 +40,8 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService { | @@ -33,6 +40,8 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService { | ||
| 33 | @Resource | 40 | @Resource |
| 34 | private PayDemoTransferMapper demoTransferMapper; | 41 | private PayDemoTransferMapper demoTransferMapper; |
| 35 | @Resource | 42 | @Resource |
| 43 | + private PayTransferService payTransferService; | ||
| 44 | + @Resource | ||
| 36 | private Validator validator; | 45 | private Validator validator; |
| 37 | 46 | ||
| 38 | @Override | 47 | @Override |
| @@ -50,4 +59,41 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService { | @@ -50,4 +59,41 @@ public class PayDemoTransferServiceImpl implements PayDemoTransferService { | ||
| 50 | public PageResult<PayDemoTransferDO> getDemoTransferPage(PageParam pageVO) { | 59 | public PageResult<PayDemoTransferDO> getDemoTransferPage(PageParam pageVO) { |
| 51 | return demoTransferMapper.selectPage(pageVO); | 60 | return demoTransferMapper.selectPage(pageVO); |
| 52 | } | 61 | } |
| 62 | + | ||
| 63 | + @Override | ||
| 64 | + public void updateDemoTransferStatus(Long id, Long payTransferId) { | ||
| 65 | + PayTransferDO payTransfer = validateDemoTransferStatusCanUpdate(id, payTransferId); | ||
| 66 | + // 更新示例订单状态 | ||
| 67 | + if (payTransfer != null) { | ||
| 68 | + demoTransferMapper.updateById(new PayDemoTransferDO().setId(id) | ||
| 69 | + .setPayTransferId(payTransferId) | ||
| 70 | + .setPayChannelCode(payTransfer.getChannelCode()) | ||
| 71 | + .setTransferStatus(payTransfer.getStatus()) | ||
| 72 | + .setTransferTime(payTransfer.getSuccessTime())); | ||
| 73 | + } | ||
| 74 | + } | ||
| 75 | + | ||
| 76 | + private PayTransferDO validateDemoTransferStatusCanUpdate(Long id, Long payTransferId) { | ||
| 77 | + PayDemoTransferDO demoTransfer = demoTransferMapper.selectById(id); | ||
| 78 | + if (demoTransfer == null) { | ||
| 79 | + throw exception(DEMO_TRANSFER_NOT_FOUND); | ||
| 80 | + } | ||
| 81 | + if (PayTransferStatusEnum.isSuccess(demoTransfer.getTransferStatus()) | ||
| 82 | + || PayTransferStatusEnum.isClosed(demoTransfer.getTransferStatus())) { | ||
| 83 | + // 无需更新返回 null | ||
| 84 | + return null; | ||
| 85 | + } | ||
| 86 | + PayTransferDO transfer = payTransferService.getTransfer(payTransferId); | ||
| 87 | + if (transfer == null) { | ||
| 88 | + throw exception(PAY_TRANSFER_NOT_FOUND); | ||
| 89 | + } | ||
| 90 | + if (!Objects.equals(demoTransfer.getPrice(), transfer.getPrice())) { | ||
| 91 | + throw exception(DEMO_TRANSFER_FAIL_PRICE_NOT_MATCH); | ||
| 92 | + } | ||
| 93 | + if (ObjectUtil.notEqual(transfer.getMerchantTransferId(), id.toString())) { | ||
| 94 | + throw exception(DEMO_TRANSFER_FAIL_TRANSFER_ID_ERROR); | ||
| 95 | + } | ||
| 96 | + // TODO 校验账号 | ||
| 97 | + return transfer; | ||
| 98 | + } | ||
| 53 | } | 99 | } |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/notify/PayNotifyServiceImpl.java
| @@ -13,11 +13,13 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | @@ -13,11 +13,13 @@ import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 13 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | 13 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| 14 | import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; | 14 | import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO; |
| 15 | import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; | 15 | import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO; |
| 16 | +import cn.iocoder.yudao.module.pay.api.notify.dto.PayTransferNotifyReqDTO; | ||
| 16 | import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; | 17 | import cn.iocoder.yudao.module.pay.controller.admin.notify.vo.PayNotifyTaskPageReqVO; |
| 17 | import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO; | 18 | import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyLogDO; |
| 18 | import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO; | 19 | import cn.iocoder.yudao.module.pay.dal.dataobject.notify.PayNotifyTaskDO; |
| 19 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; | 20 | import cn.iocoder.yudao.module.pay.dal.dataobject.order.PayOrderDO; |
| 20 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; | 21 | import cn.iocoder.yudao.module.pay.dal.dataobject.refund.PayRefundDO; |
| 22 | +import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; | ||
| 21 | import cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyLogMapper; | 23 | import cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyLogMapper; |
| 22 | import cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyTaskMapper; | 24 | import cn.iocoder.yudao.module.pay.dal.mysql.notify.PayNotifyTaskMapper; |
| 23 | import cn.iocoder.yudao.module.pay.dal.redis.notify.PayNotifyLockRedisDAO; | 25 | import cn.iocoder.yudao.module.pay.dal.redis.notify.PayNotifyLockRedisDAO; |
| @@ -25,6 +27,7 @@ import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; | @@ -25,6 +27,7 @@ import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyStatusEnum; | ||
| 25 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; | 27 | import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; |
| 26 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; | 28 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 27 | import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; | 29 | import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; |
| 30 | +import cn.iocoder.yudao.module.pay.service.transfer.PayTransferService; | ||
| 28 | import com.google.common.annotations.VisibleForTesting; | 31 | import com.google.common.annotations.VisibleForTesting; |
| 29 | import lombok.extern.slf4j.Slf4j; | 32 | import lombok.extern.slf4j.Slf4j; |
| 30 | import org.springframework.context.annotation.Lazy; | 33 | import org.springframework.context.annotation.Lazy; |
| @@ -73,6 +76,9 @@ public class PayNotifyServiceImpl implements PayNotifyService { | @@ -73,6 +76,9 @@ public class PayNotifyServiceImpl implements PayNotifyService { | ||
| 73 | @Resource | 76 | @Resource |
| 74 | @Lazy // 循环依赖,避免报错 | 77 | @Lazy // 循环依赖,避免报错 |
| 75 | private PayRefundService refundService; | 78 | private PayRefundService refundService; |
| 79 | + @Resource | ||
| 80 | + @Lazy // 循环依赖,避免报错 | ||
| 81 | + private PayTransferService transferService; | ||
| 76 | 82 | ||
| 77 | @Resource | 83 | @Resource |
| 78 | private PayNotifyTaskMapper notifyTaskMapper; | 84 | private PayNotifyTaskMapper notifyTaskMapper; |
| @@ -100,6 +106,10 @@ public class PayNotifyServiceImpl implements PayNotifyService { | @@ -100,6 +106,10 @@ public class PayNotifyServiceImpl implements PayNotifyService { | ||
| 100 | PayRefundDO refundDO = refundService.getRefund(task.getDataId()); | 106 | PayRefundDO refundDO = refundService.getRefund(task.getDataId()); |
| 101 | task.setAppId(refundDO.getAppId()) | 107 | task.setAppId(refundDO.getAppId()) |
| 102 | .setMerchantOrderId(refundDO.getMerchantOrderId()).setNotifyUrl(refundDO.getNotifyUrl()); | 108 | .setMerchantOrderId(refundDO.getMerchantOrderId()).setNotifyUrl(refundDO.getNotifyUrl()); |
| 109 | + } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.TRANSFER.getType())) { | ||
| 110 | + PayTransferDO transfer = transferService.getTransfer(task.getDataId()); | ||
| 111 | + task.setAppId(transfer.getAppId()).setMerchantTransferId(transfer.getMerchantTransferId()) | ||
| 112 | + .setNotifyUrl(transfer.getNotifyUrl()); | ||
| 103 | } | 113 | } |
| 104 | 114 | ||
| 105 | // 执行插入 | 115 | // 执行插入 |
| @@ -214,6 +224,9 @@ public class PayNotifyServiceImpl implements PayNotifyService { | @@ -214,6 +224,9 @@ public class PayNotifyServiceImpl implements PayNotifyService { | ||
| 214 | } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) { | 224 | } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) { |
| 215 | request = PayRefundNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId()) | 225 | request = PayRefundNotifyReqDTO.builder().merchantOrderId(task.getMerchantOrderId()) |
| 216 | .payRefundId(task.getDataId()).build(); | 226 | .payRefundId(task.getDataId()).build(); |
| 227 | + } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.TRANSFER.getType())) { | ||
| 228 | + request = new PayTransferNotifyReqDTO().setMerchantTransferId(task.getMerchantTransferId()) | ||
| 229 | + .setPayTransferId(task.getDataId()); | ||
| 217 | } else { | 230 | } else { |
| 218 | throw new RuntimeException("未知的通知任务类型:" + JsonUtils.toJsonString(task)); | 231 | throw new RuntimeException("未知的通知任务类型:" + JsonUtils.toJsonString(task)); |
| 219 | } | 232 | } |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferService.java
| @@ -48,4 +48,10 @@ public interface PayTransferService { | @@ -48,4 +48,10 @@ public interface PayTransferService { | ||
| 48 | */ | 48 | */ |
| 49 | PageResult<PayTransferDO> getTransferPage(PayTransferPageReqVO pageReqVO); | 49 | PageResult<PayTransferDO> getTransferPage(PayTransferPageReqVO pageReqVO); |
| 50 | 50 | ||
| 51 | + /** | ||
| 52 | + * 同步渠道转账单状态 | ||
| 53 | + * | ||
| 54 | + * @return 同步到状态的转账数量,包括转账成功、转账失败、转账中的 | ||
| 55 | + */ | ||
| 56 | + int syncTransfer(); | ||
| 51 | } | 57 | } |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java
| 1 | package cn.iocoder.yudao.module.pay.service.transfer; | 1 | package cn.iocoder.yudao.module.pay.service.transfer; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.util.ObjectUtil; | ||
| 4 | import cn.hutool.extra.spring.SpringUtil; | 5 | import cn.hutool.extra.spring.SpringUtil; |
| 5 | -import cn.iocoder.yudao.framework.common.exception.ServiceException; | ||
| 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 7 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 8 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; | 8 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; |
| 9 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; | 9 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferRespDTO; |
| 10 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; | 10 | import cn.iocoder.yudao.framework.pay.core.client.dto.transfer.PayTransferUnifiedReqDTO; |
| 11 | import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum; | 11 | import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferStatusRespEnum; |
| 12 | +import cn.iocoder.yudao.framework.pay.core.enums.transfer.PayTransferTypeEnum; | ||
| 13 | +import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | ||
| 12 | import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; | 14 | import cn.iocoder.yudao.module.pay.api.transfer.dto.PayTransferCreateReqDTO; |
| 13 | import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; | 15 | import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferCreateReqVO; |
| 14 | import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageReqVO; | 16 | import cn.iocoder.yudao.module.pay.controller.admin.transfer.vo.PayTransferPageReqVO; |
| 17 | +import cn.iocoder.yudao.module.pay.dal.dataobject.app.PayAppDO; | ||
| 15 | import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; | 18 | import cn.iocoder.yudao.module.pay.dal.dataobject.channel.PayChannelDO; |
| 16 | import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; | 19 | import cn.iocoder.yudao.module.pay.dal.dataobject.transfer.PayTransferDO; |
| 17 | import cn.iocoder.yudao.module.pay.dal.mysql.transfer.PayTransferMapper; | 20 | import cn.iocoder.yudao.module.pay.dal.mysql.transfer.PayTransferMapper; |
| 18 | import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO; | 21 | import cn.iocoder.yudao.module.pay.dal.redis.no.PayNoRedisDAO; |
| 22 | +import cn.iocoder.yudao.module.pay.enums.notify.PayNotifyTypeEnum; | ||
| 23 | +import cn.iocoder.yudao.module.pay.enums.transfer.PayTransferStatusEnum; | ||
| 19 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; | 24 | import cn.iocoder.yudao.module.pay.service.app.PayAppService; |
| 20 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; | 25 | import cn.iocoder.yudao.module.pay.service.channel.PayChannelService; |
| 26 | +import cn.iocoder.yudao.module.pay.service.notify.PayNotifyService; | ||
| 21 | import lombok.extern.slf4j.Slf4j; | 27 | import lombok.extern.slf4j.Slf4j; |
| 22 | import org.springframework.stereotype.Service; | 28 | import org.springframework.stereotype.Service; |
| 23 | import org.springframework.transaction.annotation.Transactional; | 29 | import org.springframework.transaction.annotation.Transactional; |
| 24 | 30 | ||
| 25 | import javax.annotation.Resource; | 31 | import javax.annotation.Resource; |
| 26 | import javax.validation.Validator; | 32 | import javax.validation.Validator; |
| 27 | -import java.util.Objects; | 33 | +import java.util.List; |
| 28 | 34 | ||
| 29 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 35 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 30 | import static cn.iocoder.yudao.module.pay.convert.transfer.PayTransferConvert.INSTANCE; | 36 | import static cn.iocoder.yudao.module.pay.convert.transfer.PayTransferConvert.INSTANCE; |
| @@ -51,6 +57,8 @@ public class PayTransferServiceImpl implements PayTransferService { | @@ -51,6 +57,8 @@ public class PayTransferServiceImpl implements PayTransferService { | ||
| 51 | @Resource | 57 | @Resource |
| 52 | private PayChannelService channelService; | 58 | private PayChannelService channelService; |
| 53 | @Resource | 59 | @Resource |
| 60 | + private PayNotifyService notifyService; | ||
| 61 | + @Resource | ||
| 54 | private PayNoRedisDAO noRedisDAO; | 62 | private PayNoRedisDAO noRedisDAO; |
| 55 | @Resource | 63 | @Resource |
| 56 | private Validator validator; | 64 | private Validator validator; |
| @@ -70,56 +78,60 @@ public class PayTransferServiceImpl implements PayTransferService { | @@ -70,56 +78,60 @@ public class PayTransferServiceImpl implements PayTransferService { | ||
| 70 | 78 | ||
| 71 | @Override | 79 | @Override |
| 72 | public Long createTransfer(PayTransferCreateReqDTO reqDTO) { | 80 | public Long createTransfer(PayTransferCreateReqDTO reqDTO) { |
| 73 | - // 1.1 校验转账单是否可以提交 | ||
| 74 | - validateTransferCanCreate(reqDTO.getAppId(), reqDTO.getMerchantTransferId()); | ||
| 75 | - // 1.2 校验 App | ||
| 76 | - appService.validPayApp(reqDTO.getAppId()); | ||
| 77 | - // 1.3 校验支付渠道是否有效 | 81 | + // 1.1 校验 App |
| 82 | + PayAppDO payApp = appService.validPayApp(reqDTO.getAppId()); | ||
| 83 | + // 1.2 校验支付渠道是否有效 | ||
| 78 | PayChannelDO channel = channelService.validPayChannel(reqDTO.getAppId(), reqDTO.getChannelCode()); | 84 | PayChannelDO channel = channelService.validPayChannel(reqDTO.getAppId(), reqDTO.getChannelCode()); |
| 79 | PayClient client = channelService.getPayClient(channel.getId()); | 85 | PayClient client = channelService.getPayClient(channel.getId()); |
| 80 | if (client == null) { | 86 | if (client == null) { |
| 81 | log.error("[createTransfer][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); | 87 | log.error("[createTransfer][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); |
| 82 | throw exception(CHANNEL_NOT_FOUND); | 88 | throw exception(CHANNEL_NOT_FOUND); |
| 83 | } | 89 | } |
| 84 | - // 2.创建转账单 | ||
| 85 | - String no = noRedisDAO.generate(TRANSFER_NO_PREFIX); | ||
| 86 | - PayTransferDO transfer = INSTANCE.convert(reqDTO) | ||
| 87 | - .setChannelId(channel.getId()) | ||
| 88 | - .setNo(no).setStatus(WAITING.getStatus()) | ||
| 89 | - .setNotifyUrl("http://127.0.0.1:48080/admin-api/pay/todo"); // TODO 需要加个transfer Notify url | ||
| 90 | - transferMapper.insert(transfer); | ||
| 91 | - PayTransferRespDTO unifiedTransferResp = null; | 90 | + // 1.3 校验转账单已经发起过转账。 |
| 91 | + PayTransferDO transfer = validateTransferCanCreate(reqDTO); | ||
| 92 | + | ||
| 93 | + if (transfer == null) { | ||
| 94 | + // 2.不存在创建转账单. 否则允许使用相同的 no 再次发起转账 | ||
| 95 | + String no = noRedisDAO.generate(TRANSFER_NO_PREFIX); | ||
| 96 | + transfer = INSTANCE.convert(reqDTO) | ||
| 97 | + .setChannelId(channel.getId()) | ||
| 98 | + .setNo(no).setStatus(WAITING.getStatus()) | ||
| 99 | + .setNotifyUrl(payApp.getTransferNotifyUrl()); | ||
| 100 | + transferMapper.insert(transfer); | ||
| 101 | + } | ||
| 92 | try { | 102 | try { |
| 93 | // 3. 调用三方渠道发起转账 | 103 | // 3. 调用三方渠道发起转账 |
| 94 | - PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(reqDTO) | ||
| 95 | - .setOutTransferNo(no); | ||
| 96 | - unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq); | ||
| 97 | - } catch (ServiceException ex) { | ||
| 98 | - // 业务异常.直接返回转账失败的结果 | ||
| 99 | - log.error("[createTransfer][转账 id({}) requestDTO({}) 发生业务异常]", transfer.getId(), reqDTO, ex); | ||
| 100 | - unifiedTransferResp = PayTransferRespDTO.closedOf("", "", no, ex); | 104 | + PayTransferUnifiedReqDTO transferUnifiedReq = INSTANCE.convert2(transfer) |
| 105 | + .setOutTransferNo(transfer.getNo()); | ||
| 106 | + PayTransferRespDTO unifiedTransferResp = client.unifiedTransfer(transferUnifiedReq); | ||
| 107 | + // 4. 通知转账结果 | ||
| 108 | + getSelf().notifyTransfer(channel, unifiedTransferResp); | ||
| 101 | } catch (Throwable e) { | 109 | } catch (Throwable e) { |
| 102 | // 注意这里仅打印异常,不进行抛出。 | 110 | // 注意这里仅打印异常,不进行抛出。 |
| 103 | - // 原因是:虽然调用支付渠道进行转账发生异常(网络请求超时),实际转账成功。这个结果,后续通过转账回调、或者转账轮询可以拿到。 | ||
| 104 | - // TODO 需要加转账回调业务接口 和 转账轮询未实现 | ||
| 105 | - // 最终,在异常的情况下,支付中心会异步回调业务的转账回调接口,提供转账结果 | 111 | + // 原因是:虽然调用支付渠道进行转账发生异常(网络请求超时),实际转账成功。这个结果,后续转账轮询可以拿到。 |
| 112 | + // 或者使用相同 no 再次发起转账请求 | ||
| 106 | log.error("[createTransfer][转账 id({}) requestDTO({}) 发生异常]", transfer.getId(), reqDTO, e); | 113 | log.error("[createTransfer][转账 id({}) requestDTO({}) 发生异常]", transfer.getId(), reqDTO, e); |
| 107 | } | 114 | } |
| 108 | - if (Objects.nonNull(unifiedTransferResp)) { | ||
| 109 | - // 4. 通知转账结果 | ||
| 110 | - getSelf().notifyTransfer(channel, unifiedTransferResp); | ||
| 111 | - } | ||
| 112 | - return transfer.getId(); | ||
| 113 | - } | ||
| 114 | 115 | ||
| 115 | - @Override | ||
| 116 | - public PayTransferDO getTransfer(Long id) { | ||
| 117 | - return transferMapper.selectById(id); | 116 | + return transfer.getId(); |
| 118 | } | 117 | } |
| 119 | 118 | ||
| 120 | - @Override | ||
| 121 | - public PageResult<PayTransferDO> getTransferPage(PayTransferPageReqVO pageReqVO) { | ||
| 122 | - return transferMapper.selectPage(pageReqVO); | 119 | + private PayTransferDO validateTransferCanCreate(PayTransferCreateReqDTO dto) { |
| 120 | + PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(dto.getAppId(), dto.getMerchantTransferId()); | ||
| 121 | + if (transfer != null) { | ||
| 122 | + // 已经存在,并且状态不为等待状态。说明已经调用渠道转账并返回结果. | ||
| 123 | + if (!PayTransferStatusEnum.isWaiting(transfer.getStatus())) { | ||
| 124 | + throw exception(PAY_MERCHANT_TRANSFER_EXISTS); | ||
| 125 | + } | ||
| 126 | + if (ObjectUtil.notEqual(dto.getPrice(), transfer.getPrice())) { | ||
| 127 | + throw exception(PAY_SAME_MERCHANT_TRANSFER_PRICE_NOT_MATCH); | ||
| 128 | + } | ||
| 129 | + if (ObjectUtil.notEqual(dto.getType(), transfer.getType())) { | ||
| 130 | + throw exception(PAY_SAME_MERCHANT_TRANSFER_TYPE_NOT_MATCH); | ||
| 131 | + } | ||
| 132 | + } | ||
| 133 | + // 如果状态为等待状态。不知道渠道转账是否发起成功。 允许使用相同的 no 再次发起转账,渠道会保证幂等 | ||
| 134 | + return transfer; | ||
| 123 | } | 135 | } |
| 124 | 136 | ||
| 125 | @Transactional(rollbackFor = Exception.class) | 137 | @Transactional(rollbackFor = Exception.class) |
| @@ -133,34 +145,48 @@ public class PayTransferServiceImpl implements PayTransferService { | @@ -133,34 +145,48 @@ public class PayTransferServiceImpl implements PayTransferService { | ||
| 133 | if (PayTransferStatusRespEnum.isClosed(notify.getStatus())) { | 145 | if (PayTransferStatusRespEnum.isClosed(notify.getStatus())) { |
| 134 | notifyTransferClosed(channel, notify); | 146 | notifyTransferClosed(channel, notify); |
| 135 | } | 147 | } |
| 148 | + // 转账处理中的回调 | ||
| 149 | + if (PayTransferStatusRespEnum.isInProgress(notify.getStatus())) { | ||
| 150 | + notifyTransferInProgress(channel, notify); | ||
| 151 | + } | ||
| 136 | // WAITING 状态无需处理 | 152 | // WAITING 状态无需处理 |
| 137 | - // TODO IN_PROGRESS 待处理 | ||
| 138 | } | 153 | } |
| 139 | 154 | ||
| 140 | - private void validateTransferCanCreate(Long appId, String merchantTransferId) { | ||
| 141 | - PayTransferDO transfer = transferMapper.selectByAppIdAndMerchantTransferId(appId, merchantTransferId); | ||
| 142 | - if (transfer != null) { // 是否存在 | ||
| 143 | - throw exception(PAY_MERCHANT_TRANSFER_EXISTS); | 155 | + private void notifyTransferInProgress(PayChannelDO channel, PayTransferRespDTO notify) { |
| 156 | + // 1.校验 | ||
| 157 | + PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); | ||
| 158 | + if (transfer == null) { | ||
| 159 | + throw exception(PAY_TRANSFER_NOT_FOUND); | ||
| 144 | } | 160 | } |
| 145 | - } | ||
| 146 | - | ||
| 147 | - private void notifyTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) { | ||
| 148 | - // 1. 更新 PayTransferDO 转账成功 | ||
| 149 | - Boolean transferred = updateTransferSuccess(channel, notify); | ||
| 150 | - if (transferred) { | 161 | + if (isInProgress(transfer.getStatus())) { // 如果已经是转账中,直接返回,不用重复更新 |
| 151 | return; | 162 | return; |
| 152 | } | 163 | } |
| 153 | - // 2. TODO 插入转账通知记录 | 164 | + if (!isWaiting(transfer.getStatus())) { |
| 165 | + throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING); | ||
| 166 | + } | ||
| 167 | + // 2.更新 | ||
| 168 | + int updateCounts = transferMapper.updateByIdAndStatus(transfer.getId(), | ||
| 169 | + CollUtil.newArrayList(WAITING.getStatus()), | ||
| 170 | + new PayTransferDO().setStatus(IN_PROGRESS.getStatus())); | ||
| 171 | + if (updateCounts == 0) { | ||
| 172 | + throw exception(PAY_TRANSFER_STATUS_IS_NOT_WAITING); | ||
| 173 | + } | ||
| 174 | + log.info("[notifyTransferInProgress][transfer({}) 更新为转账进行中状态]", transfer.getId()); | ||
| 175 | + | ||
| 176 | + // 3. 插入转账通知记录 | ||
| 177 | + notifyService.createPayNotifyTask(PayNotifyTypeEnum.TRANSFER.getType(), | ||
| 178 | + transfer.getId()); | ||
| 154 | } | 179 | } |
| 155 | 180 | ||
| 156 | - private Boolean updateTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) { | 181 | + |
| 182 | + private void notifyTransferSuccess(PayChannelDO channel, PayTransferRespDTO notify) { | ||
| 157 | // 1.校验 | 183 | // 1.校验 |
| 158 | PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); | 184 | PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); |
| 159 | if (transfer == null) { | 185 | if (transfer == null) { |
| 160 | throw exception(PAY_TRANSFER_NOT_FOUND); | 186 | throw exception(PAY_TRANSFER_NOT_FOUND); |
| 161 | } | 187 | } |
| 162 | if (isSuccess(transfer.getStatus())) { // 如果已成功,直接返回,不用重复更新 | 188 | if (isSuccess(transfer.getStatus())) { // 如果已成功,直接返回,不用重复更新 |
| 163 | - return Boolean.TRUE; | 189 | + return; |
| 164 | } | 190 | } |
| 165 | if (!isPendingStatus(transfer.getStatus())) { | 191 | if (!isPendingStatus(transfer.getStatus())) { |
| 166 | throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); | 192 | throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); |
| @@ -176,10 +202,13 @@ public class PayTransferServiceImpl implements PayTransferService { | @@ -176,10 +202,13 @@ public class PayTransferServiceImpl implements PayTransferService { | ||
| 176 | throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); | 202 | throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); |
| 177 | } | 203 | } |
| 178 | log.info("[updateTransferSuccess][transfer({}) 更新为已转账]", transfer.getId()); | 204 | log.info("[updateTransferSuccess][transfer({}) 更新为已转账]", transfer.getId()); |
| 179 | - return Boolean.FALSE; | 205 | + |
| 206 | + // 3. 插入转账通知记录 | ||
| 207 | + notifyService.createPayNotifyTask(PayNotifyTypeEnum.TRANSFER.getType(), | ||
| 208 | + transfer.getId()); | ||
| 180 | } | 209 | } |
| 181 | 210 | ||
| 182 | - private void updateTransferClosed(PayChannelDO channel, PayTransferRespDTO notify) { | 211 | + private void notifyTransferClosed(PayChannelDO channel, PayTransferRespDTO notify) { |
| 183 | // 1.校验 | 212 | // 1.校验 |
| 184 | PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); | 213 | PayTransferDO transfer = transferMapper.selectByNo(notify.getOutTransferNo()); |
| 185 | if (transfer == null) { | 214 | if (transfer == null) { |
| @@ -192,6 +221,7 @@ public class PayTransferServiceImpl implements PayTransferService { | @@ -192,6 +221,7 @@ public class PayTransferServiceImpl implements PayTransferService { | ||
| 192 | if (!isPendingStatus(transfer.getStatus())) { | 221 | if (!isPendingStatus(transfer.getStatus())) { |
| 193 | throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); | 222 | throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); |
| 194 | } | 223 | } |
| 224 | + | ||
| 195 | // 2.更新 | 225 | // 2.更新 |
| 196 | int updateCount = transferMapper.updateByIdAndStatus(transfer.getId(), | 226 | int updateCount = transferMapper.updateByIdAndStatus(transfer.getId(), |
| 197 | CollUtil.newArrayList(WAITING.getStatus(), IN_PROGRESS.getStatus()), | 227 | CollUtil.newArrayList(WAITING.getStatus(), IN_PROGRESS.getStatus()), |
| @@ -203,11 +233,61 @@ public class PayTransferServiceImpl implements PayTransferService { | @@ -203,11 +233,61 @@ public class PayTransferServiceImpl implements PayTransferService { | ||
| 203 | throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); | 233 | throw exception(PAY_TRANSFER_STATUS_IS_NOT_PENDING); |
| 204 | } | 234 | } |
| 205 | log.info("[updateTransferClosed][transfer({}) 更新为关闭状态]", transfer.getId()); | 235 | log.info("[updateTransferClosed][transfer({}) 更新为关闭状态]", transfer.getId()); |
| 236 | + | ||
| 237 | + // 3. 插入转账通知记录 | ||
| 238 | + notifyService.createPayNotifyTask(PayNotifyTypeEnum.TRANSFER.getType(), | ||
| 239 | + transfer.getId()); | ||
| 240 | + | ||
| 206 | } | 241 | } |
| 207 | 242 | ||
| 208 | - private void notifyTransferClosed(PayChannelDO channel, PayTransferRespDTO notify) { | ||
| 209 | - // 更新 PayTransferDO 转账关闭 | ||
| 210 | - updateTransferClosed(channel, notify); | 243 | + @Override |
| 244 | + public PayTransferDO getTransfer(Long id) { | ||
| 245 | + return transferMapper.selectById(id); | ||
| 246 | + } | ||
| 247 | + | ||
| 248 | + @Override | ||
| 249 | + public PageResult<PayTransferDO> getTransferPage(PayTransferPageReqVO pageReqVO) { | ||
| 250 | + return transferMapper.selectPage(pageReqVO); | ||
| 251 | + } | ||
| 252 | + | ||
| 253 | + @Override | ||
| 254 | + public int syncTransfer() { | ||
| 255 | + List<PayTransferDO> list = transferMapper.selectListByStatus(WAITING.getStatus()); | ||
| 256 | + if (CollUtil.isEmpty(list)) { | ||
| 257 | + return 0; | ||
| 258 | + } | ||
| 259 | + int count = 0; | ||
| 260 | + for (PayTransferDO transfer : list) { | ||
| 261 | + count += syncTransfer(transfer) ? 1 : 0; | ||
| 262 | + } | ||
| 263 | + return count; | ||
| 264 | + } | ||
| 265 | + | ||
| 266 | + private boolean syncTransfer(PayTransferDO transfer) { | ||
| 267 | + try { | ||
| 268 | + // 1. 查询转账订单信息 | ||
| 269 | + PayClient payClient = channelService.getPayClient(transfer.getChannelId()); | ||
| 270 | + if (payClient == null) { | ||
| 271 | + log.error("[syncTransfer][渠道编号({}) 找不到对应的支付客户端]", transfer.getChannelId()); | ||
| 272 | + return false; | ||
| 273 | + } | ||
| 274 | + PayTransferRespDTO resp = payClient.getTransfer(transfer.getNo(), | ||
| 275 | + PayTransferTypeEnum.typeOf(transfer.getType())); | ||
| 276 | + | ||
| 277 | + // 2. 回调转账结果 | ||
| 278 | + notifyTransfer(transfer.getChannelId(), resp); | ||
| 279 | + return true; | ||
| 280 | + } catch (Throwable ex) { | ||
| 281 | + log.error("[syncTransfer][transfer({}) 同步转账单状态异常]", transfer.getId(), ex); | ||
| 282 | + return false; | ||
| 283 | + } | ||
| 284 | + } | ||
| 285 | + | ||
| 286 | + private void notifyTransfer(Long channelId, PayTransferRespDTO notify) { | ||
| 287 | + // 校验渠道是否有效 | ||
| 288 | + PayChannelDO channel = channelService.validPayChannel(channelId); | ||
| 289 | + // 通知转账结果给对应的业务 | ||
| 290 | + TenantUtils.execute(channel.getTenantId(), () -> getSelf().notifyTransfer(channel, notify)); | ||
| 211 | } | 291 | } |
| 212 | 292 | ||
| 213 | /** | 293 | /** |
yudao-server/pom.xml
| @@ -37,11 +37,11 @@ | @@ -37,11 +37,11 @@ | ||
| 37 | </dependency> | 37 | </dependency> |
| 38 | 38 | ||
| 39 | <!-- 会员中心。默认注释,保证编译速度 --> | 39 | <!-- 会员中心。默认注释,保证编译速度 --> |
| 40 | -<!-- <dependency>--> | ||
| 41 | -<!-- <groupId>cn.iocoder.boot</groupId>--> | ||
| 42 | -<!-- <artifactId>yudao-module-member-biz</artifactId>--> | ||
| 43 | -<!-- <version>${revision}</version>--> | ||
| 44 | -<!-- </dependency>--> | 40 | + <dependency> |
| 41 | + <groupId>cn.iocoder.boot</groupId> | ||
| 42 | + <artifactId>yudao-module-member-biz</artifactId> | ||
| 43 | + <version>${revision}</version> | ||
| 44 | + </dependency> | ||
| 45 | 45 | ||
| 46 | <!-- 数据报表。默认注释,保证编译速度 --> | 46 | <!-- 数据报表。默认注释,保证编译速度 --> |
| 47 | <!-- <dependency>--> | 47 | <!-- <dependency>--> |
| @@ -56,11 +56,11 @@ | @@ -56,11 +56,11 @@ | ||
| 56 | <!-- <version>${revision}</version>--> | 56 | <!-- <version>${revision}</version>--> |
| 57 | <!-- </dependency>--> | 57 | <!-- </dependency>--> |
| 58 | <!-- 支付服务。默认注释,保证编译速度 --> | 58 | <!-- 支付服务。默认注释,保证编译速度 --> |
| 59 | -<!-- <dependency>--> | ||
| 60 | -<!-- <groupId>cn.iocoder.boot</groupId>--> | ||
| 61 | -<!-- <artifactId>yudao-module-pay-biz</artifactId>--> | ||
| 62 | -<!-- <version>${revision}</version>--> | ||
| 63 | -<!-- </dependency>--> | 59 | + <dependency> |
| 60 | + <groupId>cn.iocoder.boot</groupId> | ||
| 61 | + <artifactId>yudao-module-pay-biz</artifactId> | ||
| 62 | + <version>${revision}</version> | ||
| 63 | + </dependency> | ||
| 64 | 64 | ||
| 65 | <!-- 微信公众号模块。默认注释,保证编译速度 --> | 65 | <!-- 微信公众号模块。默认注释,保证编译速度 --> |
| 66 | <!-- <dependency>--> | 66 | <!-- <dependency>--> |
| @@ -69,27 +69,27 @@ | @@ -69,27 +69,27 @@ | ||
| 69 | <!-- <version>${revision}</version>--> | 69 | <!-- <version>${revision}</version>--> |
| 70 | <!-- </dependency>--> | 70 | <!-- </dependency>--> |
| 71 | 71 | ||
| 72 | -<!-- 商城相关模块。默认注释,保证编译速度--> | ||
| 73 | -<!-- <dependency>--> | ||
| 74 | -<!-- <groupId>cn.iocoder.boot</groupId>--> | ||
| 75 | -<!-- <artifactId>yudao-module-promotion-biz</artifactId>--> | ||
| 76 | -<!-- <version>${revision}</version>--> | ||
| 77 | -<!-- </dependency>--> | ||
| 78 | -<!-- <dependency>--> | ||
| 79 | -<!-- <groupId>cn.iocoder.boot</groupId>--> | ||
| 80 | -<!-- <artifactId>yudao-module-product-biz</artifactId>--> | ||
| 81 | -<!-- <version>${revision}</version>--> | ||
| 82 | -<!-- </dependency>--> | ||
| 83 | -<!-- <dependency>--> | ||
| 84 | -<!-- <groupId>cn.iocoder.boot</groupId>--> | ||
| 85 | -<!-- <artifactId>yudao-module-trade-biz</artifactId>--> | ||
| 86 | -<!-- <version>${revision}</version>--> | ||
| 87 | -<!-- </dependency>--> | ||
| 88 | -<!-- <dependency>--> | ||
| 89 | -<!-- <groupId>cn.iocoder.boot</groupId>--> | ||
| 90 | -<!-- <artifactId>yudao-module-statistics-biz</artifactId>--> | ||
| 91 | -<!-- <version>${revision}</version>--> | ||
| 92 | -<!-- </dependency>--> | 72 | + <!-- 商城相关模块。默认注释,保证编译速度--> |
| 73 | + <dependency> | ||
| 74 | + <groupId>cn.iocoder.boot</groupId> | ||
| 75 | + <artifactId>yudao-module-promotion-biz</artifactId> | ||
| 76 | + <version>${revision}</version> | ||
| 77 | + </dependency> | ||
| 78 | + <dependency> | ||
| 79 | + <groupId>cn.iocoder.boot</groupId> | ||
| 80 | + <artifactId>yudao-module-product-biz</artifactId> | ||
| 81 | + <version>${revision}</version> | ||
| 82 | + </dependency> | ||
| 83 | + <dependency> | ||
| 84 | + <groupId>cn.iocoder.boot</groupId> | ||
| 85 | + <artifactId>yudao-module-trade-biz</artifactId> | ||
| 86 | + <version>${revision}</version> | ||
| 87 | + </dependency> | ||
| 88 | + <dependency> | ||
| 89 | + <groupId>cn.iocoder.boot</groupId> | ||
| 90 | + <artifactId>yudao-module-statistics-biz</artifactId> | ||
| 91 | + <version>${revision}</version> | ||
| 92 | + </dependency> | ||
| 93 | 93 | ||
| 94 | <!-- CRM 相关模块。默认注释,保证编译速度 --> | 94 | <!-- CRM 相关模块。默认注释,保证编译速度 --> |
| 95 | <!-- <dependency>--> | 95 | <!-- <dependency>--> |