feat: 支付补偿定时任务
parent
9be241314b
commit
e08eb3f988
|
|
@ -1,6 +1,7 @@
|
|||
package com.ruoyi.web.controller.xkt;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.github.pagehelper.Page;
|
||||
import com.ruoyi.common.core.controller.XktBaseController;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
|
|
@ -85,7 +86,6 @@ public class AssetController extends XktBaseController {
|
|||
//创建付款单
|
||||
WithdrawPrepareResult prepareResult = assetService.prepareWithdraw(SecurityUtils.getStoreId(), vo.getAmount(),
|
||||
vo.getTransactionPassword(), EPayChannel.ALI_PAY);
|
||||
//TODO 失败补偿
|
||||
//支付宝转账
|
||||
boolean success = aliPaymentManger.transfer(prepareResult.getBillNo(), prepareResult.getAccountOwnerNumber(),
|
||||
prepareResult.getAccountOwnerName(), prepareResult.getAmount());
|
||||
|
|
@ -93,7 +93,7 @@ public class AssetController extends XktBaseController {
|
|||
if (success) {
|
||||
assetService.withdrawSuccess(prepareResult.getFinanceBillId());
|
||||
} else {
|
||||
fsNotice.sendMsg2DefaultChat("档口提现异常", "付款单: " + prepareResult.getFinanceBillId());
|
||||
fsNotice.sendMsg2DefaultChat("档口提现失败", "参数: " + JSONUtil.toJsonStr(prepareResult));
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,19 @@
|
|||
package com.ruoyi.web.controller.xkt;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.github.pagehelper.Page;
|
||||
import com.ruoyi.common.annotation.Log;
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.core.controller.XktBaseController;
|
||||
import com.ruoyi.common.core.domain.R;
|
||||
import com.ruoyi.common.core.domain.SimpleEntity;
|
||||
import com.ruoyi.common.core.page.PageVO;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.enums.BusinessType;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.framework.notice.fs.FsNotice;
|
||||
import com.ruoyi.web.controller.xkt.vo.order.*;
|
||||
import com.ruoyi.xkt.domain.StoreOrderDetail;
|
||||
import com.ruoyi.xkt.dto.express.ExpressCancelReqDTO;
|
||||
|
|
@ -35,6 +39,7 @@ import javax.validation.Valid;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
|
|
@ -52,6 +57,10 @@ public class StoreOrderController extends XktBaseController {
|
|||
private IExpressService expressService;
|
||||
@Autowired
|
||||
private List<PaymentManager> paymentManagers;
|
||||
@Autowired
|
||||
private FsNotice fsNotice;
|
||||
@Autowired
|
||||
private RedisCache redisCache;
|
||||
|
||||
@Log(title = "订单", businessType = BusinessType.INSERT)
|
||||
@ApiOperation("创建订单")
|
||||
|
|
@ -227,16 +236,27 @@ public class StoreOrderController extends XktBaseController {
|
|||
//TODO 权限
|
||||
StoreOrderRefundConfirmDTO dto = BeanUtil.toBean(vo, StoreOrderRefundConfirmDTO.class);
|
||||
dto.setOperatorId(SecurityUtils.getUserId());
|
||||
//支付宝接口要求:同一笔交易的退款至少间隔3s后发起
|
||||
String markKey = CacheConstants.STORE_ORDER_REFUND_PROCESSING_MARK + vo.getStoreOrderId();
|
||||
boolean less3s = redisCache.hasKey(markKey);
|
||||
if (less3s) {
|
||||
throw new ServiceException("同一订单确认退款操作至少间隔3s后发起");
|
||||
}
|
||||
//售后状态->售后完成,支付状态->支付中,创建收款单
|
||||
StoreOrderRefund storeOrderRefund = storeOrderService.prepareRefundOrder(dto);
|
||||
//TODO 失败补偿
|
||||
//三方退款
|
||||
PaymentManager paymentManager = getPaymentManager(EPayChannel.of(storeOrderRefund.getRefundOrder().getPayChannel()));
|
||||
paymentManager.refundStoreOrder(storeOrderRefund);
|
||||
//支付状态->已支付,收款单到账
|
||||
storeOrderService.refundSuccess(storeOrderRefund.getRefundOrder().getId(),
|
||||
storeOrderRefund.getRefundOrderDetails().stream().map(SimpleEntity::getId).collect(Collectors.toList()),
|
||||
SecurityUtils.getUserId());
|
||||
boolean success = paymentManager.refundStoreOrder(storeOrderRefund);
|
||||
//标记
|
||||
redisCache.setCacheObject(markKey, 1, 3, TimeUnit.SECONDS);
|
||||
if (success) {
|
||||
//支付状态->已支付,收款单到账
|
||||
storeOrderService.refundSuccess(storeOrderRefund.getRefundOrder().getId(),
|
||||
storeOrderRefund.getRefundOrderDetails().stream().map(SimpleEntity::getId).collect(Collectors.toList()),
|
||||
SecurityUtils.getUserId());
|
||||
} else {
|
||||
fsNotice.sendMsg2DefaultChat("确认退款失败", "参数: " + JSON.toJSONString(storeOrderRefund));
|
||||
}
|
||||
return success();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,11 @@ public class CacheConstants
|
|||
*/
|
||||
public static final String USER_STS_KEY = "user_sts:";
|
||||
|
||||
/**
|
||||
* 退款处理中标识
|
||||
*/
|
||||
public static final String STORE_ORDER_REFUND_PROCESSING_MARK = "store_order_refund_processing_mark_";
|
||||
|
||||
/**
|
||||
* 档口
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
package com.ruoyi.common.core.redis;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.BoundSetOperations;
|
||||
import org.springframework.data.redis.core.HashOperations;
|
||||
|
|
@ -13,6 +7,9 @@ import org.springframework.data.redis.core.RedisTemplate;
|
|||
import org.springframework.data.redis.core.ValueOperations;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* spring redis 工具类
|
||||
*
|
||||
|
|
@ -270,4 +267,8 @@ public class RedisCache
|
|||
{
|
||||
return redisTemplate.keys(pattern);
|
||||
}
|
||||
|
||||
public boolean exists(final String key) {
|
||||
return redisTemplate.hasKey(key);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,12 +2,16 @@ package com.ruoyi.framework.notice.fs.entity;
|
|||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* @author liangyuqi
|
||||
* @date 2021/7/14 15:20
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class FeiShuAtField extends FeiShuMsg.BaseField {
|
||||
private String tag = "at";
|
||||
@JSONField(name = "user_id")
|
||||
|
|
|
|||
|
|
@ -2,12 +2,16 @@ package com.ruoyi.framework.notice.fs.entity;
|
|||
|
||||
import com.alibaba.fastjson2.annotation.JSONField;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* @author liangyuqi
|
||||
* @date 2021/7/14 15:20
|
||||
*/
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
@ToString(callSuper = true)
|
||||
public class FeiShuTextField extends FeiShuMsg.BaseField {
|
||||
@JSONField(name = "un_escape")
|
||||
private boolean unEscape = true;
|
||||
|
|
|
|||
|
|
@ -1,27 +1,42 @@
|
|||
package com.ruoyi.quartz.task;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.date.DateField;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import co.elastic.clients.elasticsearch.core.BulkResponse;
|
||||
import co.elastic.clients.elasticsearch.core.bulk.BulkOperation;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.ruoyi.common.constant.CacheConstants;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.constant.HttpStatus;
|
||||
import com.ruoyi.common.core.domain.SimpleEntity;
|
||||
import com.ruoyi.common.core.domain.entity.SysProductCategory;
|
||||
import com.ruoyi.common.core.redis.RedisCache;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
import com.ruoyi.common.utils.SecurityUtils;
|
||||
import com.ruoyi.framework.es.EsClientWrapper;
|
||||
import com.ruoyi.framework.notice.fs.FsNotice;
|
||||
import com.ruoyi.system.mapper.SysProductCategoryMapper;
|
||||
import com.ruoyi.xkt.domain.*;
|
||||
import com.ruoyi.xkt.dto.account.WithdrawPrepareResult;
|
||||
import com.ruoyi.xkt.dto.dailySale.DailySaleCusDTO;
|
||||
import com.ruoyi.xkt.dto.dailySale.DailySaleDTO;
|
||||
import com.ruoyi.xkt.dto.dailySale.DailySaleProdDTO;
|
||||
import com.ruoyi.xkt.dto.dailySale.WeekCateSaleDTO;
|
||||
import com.ruoyi.xkt.dto.dailyStoreTag.DailyStoreTagDTO;
|
||||
import com.ruoyi.xkt.dto.order.StoreOrderCancelDTO;
|
||||
import com.ruoyi.xkt.dto.order.StoreOrderRefund;
|
||||
import com.ruoyi.xkt.enums.*;
|
||||
import com.ruoyi.xkt.manager.PaymentManager;
|
||||
import com.ruoyi.xkt.mapper.*;
|
||||
import com.ruoyi.xkt.service.IAdvertRoundService;
|
||||
import com.ruoyi.xkt.service.IAlipayCallbackService;
|
||||
import com.ruoyi.xkt.service.IAssetService;
|
||||
import com.ruoyi.xkt.service.IStoreOrderService;
|
||||
import io.jsonwebtoken.lang.Assert;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.collections4.CollectionUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
|
@ -42,6 +57,7 @@ import java.util.stream.Collectors;
|
|||
*
|
||||
* @author ruoyi
|
||||
*/
|
||||
@Slf4j
|
||||
@Component("xktTask")
|
||||
@RequiredArgsConstructor
|
||||
public class XktTask {
|
||||
|
|
@ -68,6 +84,11 @@ public class XktTask {
|
|||
final AdvertRoundMapper advertRoundMapper;
|
||||
final RedisCache redisCache;
|
||||
final IAdvertRoundService advertRoundService;
|
||||
final IStoreOrderService storeOrderService;
|
||||
final IAssetService assetService;
|
||||
final IAlipayCallbackService alipayCallbackService;
|
||||
final FsNotice fsNotice;
|
||||
final List<PaymentManager> paymentManagers;
|
||||
|
||||
/**
|
||||
* 每晚1点同步档口销售数据
|
||||
|
|
@ -461,6 +482,140 @@ public class XktTask {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 自动关闭超时订单
|
||||
*/
|
||||
public void autoCloseTimeoutStoreOrder() {
|
||||
log.info("-------------自动关闭超时订单开始-------------");
|
||||
Integer batchCount = 20;
|
||||
Date beforeDate = DateUtil.offset(new Date(), DateField.MINUTE, 60);
|
||||
List<Long> storeOrderIds = storeOrderService.listNeedAutoCloseOrder(beforeDate, batchCount);
|
||||
for (Long storeOrderId : storeOrderIds) {
|
||||
log.info("开始处理: {}", storeOrderId);
|
||||
try {
|
||||
StoreOrderCancelDTO cancelDTO = new StoreOrderCancelDTO();
|
||||
cancelDTO.setStoreOrderId(storeOrderId);
|
||||
cancelDTO.setRemark("超时订单自动关闭");
|
||||
storeOrderService.cancelOrder(cancelDTO);
|
||||
} catch (Exception e) {
|
||||
log.error("自动关闭超时订单异常", e);
|
||||
fsNotice.sendMsg2DefaultChat("自动关闭超时订单异常", "订单ID:" + storeOrderId);
|
||||
}
|
||||
}
|
||||
log.info("-------------自动关闭超时订单结束-------------");
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续处理退款(异常中断补偿,非正常流程)
|
||||
*/
|
||||
public void continueProcessRefund() {
|
||||
log.info("-------------继续处理退款开始-------------");
|
||||
Integer batchCount = 20;
|
||||
List<StoreOrderRefund> storeOrderRefunds = storeOrderService.listNeedContinueRefundOrder(batchCount);
|
||||
for (StoreOrderRefund storeOrderRefund : storeOrderRefunds) {
|
||||
log.info("开始处理: {}", storeOrderRefund);
|
||||
try {
|
||||
//支付宝接口要求:同一笔交易的退款至少间隔3s后发起
|
||||
String markKey = CacheConstants.STORE_ORDER_REFUND_PROCESSING_MARK +
|
||||
storeOrderRefund.getRefundOrder().getId();
|
||||
boolean less3s = redisCache.hasKey(markKey);
|
||||
if (less3s) {
|
||||
log.warn("订单[{}]退款间隔小于3s跳过执行", storeOrderRefund.getRefundOrder().getId());
|
||||
continue;
|
||||
}
|
||||
PaymentManager paymentManager = getPaymentManager(EPayChannel.of(storeOrderRefund.getRefundOrder().getPayChannel()));
|
||||
//查询退款结果
|
||||
ENetResult queryResult = paymentManager.queryStoreOrderRefundResult(
|
||||
storeOrderRefund.getRefundOrder().getOrderNo(),
|
||||
storeOrderRefund.getOriginOrder().getOrderNo());
|
||||
if (ENetResult.SUCCESS == queryResult) {
|
||||
//退款成功
|
||||
//支付状态->已支付,收款单到账
|
||||
storeOrderService.refundSuccess(storeOrderRefund.getRefundOrder().getId(),
|
||||
storeOrderRefund.getRefundOrderDetails().stream().map(SimpleEntity::getId).collect(Collectors.toList()),
|
||||
SecurityUtils.getUserId());
|
||||
} else {
|
||||
//可能是退款失败,也可能是退款处理中,重复调用支付宝接口时只要参数正确也不会重复退款
|
||||
boolean success = paymentManager.refundStoreOrder(storeOrderRefund);
|
||||
//标记
|
||||
redisCache.setCacheObject(markKey, 1, 3, TimeUnit.SECONDS);
|
||||
if (success) {
|
||||
//支付状态->已支付,收款单到账
|
||||
storeOrderService.refundSuccess(storeOrderRefund.getRefundOrder().getId(),
|
||||
storeOrderRefund.getRefundOrderDetails().stream().map(SimpleEntity::getId).collect(Collectors.toList()),
|
||||
SecurityUtils.getUserId());
|
||||
} else {
|
||||
fsNotice.sendMsg2DefaultChat("退款失败", "参数: " + JSON.toJSONString(storeOrderRefund));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("继续处理退款异常", e);
|
||||
fsNotice.sendMsg2DefaultChat("退款异常", "参数: " + JSON.toJSONString(storeOrderRefund));
|
||||
}
|
||||
}
|
||||
log.info("-------------继续处理退款结束-------------");
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续处理档口提现(异常中断补偿,非正常流程)
|
||||
*/
|
||||
public void continueProcessWithdraw() {
|
||||
log.info("-------------继续处理档口提现开始-------------");
|
||||
Integer batchCount = 20;
|
||||
Map<EPayChannel, List<WithdrawPrepareResult>> map = assetService.getNeedContinueWithdrawGroupMap(batchCount);
|
||||
for (Map.Entry<EPayChannel, List<WithdrawPrepareResult>> entry : map.entrySet()) {
|
||||
PaymentManager paymentManager = getPaymentManager(entry.getKey());
|
||||
for (WithdrawPrepareResult prepareResult : entry.getValue()) {
|
||||
log.info("开始处理: {}", prepareResult);
|
||||
try {
|
||||
//查询转账结果
|
||||
ENetResult queryResult = paymentManager.queryTransferResult(prepareResult.getBillNo());
|
||||
if (ENetResult.SUCCESS == queryResult) {
|
||||
//转账成功,直接到账
|
||||
assetService.withdrawSuccess(prepareResult.getFinanceBillId());
|
||||
} else if (ENetResult.FAILURE == queryResult) {
|
||||
//转账失败,尝试重新支付
|
||||
boolean success = paymentManager.transfer(prepareResult.getBillNo(),
|
||||
prepareResult.getAccountOwnerNumber(),
|
||||
prepareResult.getAccountOwnerName(),
|
||||
prepareResult.getAmount());
|
||||
//付款单到账
|
||||
if (success) {
|
||||
assetService.withdrawSuccess(prepareResult.getFinanceBillId());
|
||||
} else {
|
||||
fsNotice.sendMsg2DefaultChat("档口提现失败", "参数: " + JSON.toJSONString(prepareResult));
|
||||
}
|
||||
} else {
|
||||
log.warn("档口提现支付宝处理中: {}", prepareResult);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("继续继续处理档口提现异常", e);
|
||||
fsNotice.sendMsg2DefaultChat("档口提现异常", "参数: " + JSON.toJSONString(prepareResult));
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("-------------继续处理档口提现结束-------------");
|
||||
}
|
||||
|
||||
/**
|
||||
* 继续处理支付宝支付回调信息(异常中断补偿,非正常流程)
|
||||
*/
|
||||
public void continueProcessAliCallback() {
|
||||
log.info("-------------继续处理支付宝支付回调信息开始-------------");
|
||||
Integer batchCount = 20;
|
||||
List<AlipayCallback> callbacks = alipayCallbackService.listNeedContinueProcessCallback(batchCount);
|
||||
for (AlipayCallback callback : callbacks) {
|
||||
log.info("开始处理: {}", callback);
|
||||
try {
|
||||
alipayCallbackService.continueProcess(Collections.singletonList(callback));
|
||||
} catch (Exception e) {
|
||||
log.error("继续处理支付宝支付回调异常", e);
|
||||
fsNotice.sendMsg2DefaultChat("支付回调处理异常", "回调信息: " + JSON.toJSONString(callback));
|
||||
}
|
||||
}
|
||||
log.info("-------------继续处理支付宝支付回调信息结束-------------");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -775,6 +930,22 @@ public class XktTask {
|
|||
System.out.println("bulkResponse.items() = " + bulkResponse.items());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据支付渠道匹配支付类
|
||||
*
|
||||
* @param payChannel
|
||||
* @return
|
||||
*/
|
||||
private PaymentManager getPaymentManager(EPayChannel payChannel) {
|
||||
Assert.notNull(payChannel);
|
||||
for (PaymentManager paymentManager : paymentManagers) {
|
||||
if (paymentManager.channel() == payChannel) {
|
||||
return paymentManager;
|
||||
}
|
||||
}
|
||||
throw new ServiceException("未知支付渠道");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -47,7 +47,16 @@ public interface PaymentManager {
|
|||
*
|
||||
* @param orderRefund
|
||||
*/
|
||||
void refundStoreOrder(StoreOrderRefund orderRefund);
|
||||
boolean refundStoreOrder(StoreOrderRefund orderRefund);
|
||||
|
||||
/**
|
||||
* 退款结果
|
||||
*
|
||||
* @param refundOrderNo
|
||||
* @param originOrderNo
|
||||
* @return
|
||||
*/
|
||||
ENetResult queryStoreOrderRefundResult(String refundOrderNo, String originOrderNo);
|
||||
|
||||
/**
|
||||
* 是否已支付
|
||||
|
|
|
|||
|
|
@ -181,11 +181,11 @@ public class AliPaymentMangerImpl implements PaymentManager {
|
|||
throw new ServiceException("订单[" + orderExt.getOrder().getOrderNo() + "]支付状态异常");
|
||||
}
|
||||
return pay(orderExt.getOrder().getOrderNo(), orderExt.getOrder().getTotalAmount(),
|
||||
"代发订单" + orderExt.getOrder().getOrderNo(), payPage,null);
|
||||
"代发订单" + orderExt.getOrder().getOrderNo(), payPage, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refundStoreOrder(StoreOrderRefund orderRefund) {
|
||||
public boolean refundStoreOrder(StoreOrderRefund orderRefund) {
|
||||
Assert.notNull(orderRefund);
|
||||
Assert.notNull(orderRefund.getOriginOrder());
|
||||
Assert.notNull(orderRefund.getRefundOrder());
|
||||
|
|
@ -199,7 +199,7 @@ public class AliPaymentMangerImpl implements PaymentManager {
|
|||
// 设置商户订单号
|
||||
model.setOutTradeNo(orderRefund.getOriginOrder().getOrderNo());
|
||||
// 设置支付宝交易号
|
||||
model.setTradeNo(orderRefund.getOriginOrder().getPayTradeNo());
|
||||
// model.setTradeNo(orderRefund.getOriginOrder().getPayTradeNo());
|
||||
// 设置退款金额
|
||||
BigDecimal amount = BigDecimal.ZERO;
|
||||
for (StoreOrderDetail orderDetail : orderRefund.getRefundOrderDetails()) {
|
||||
|
|
@ -212,11 +212,22 @@ public class AliPaymentMangerImpl implements PaymentManager {
|
|||
// 设置退款请求号
|
||||
model.setOutRequestNo(orderRefund.getRefundOrder().getOrderNo());
|
||||
try {
|
||||
//TODO 沙箱环境接口无法完全退款?
|
||||
// AlipayTradeRefundResponse response = alipayClient.certificateExecute(request);
|
||||
AlipayTradeRefundResponse response = alipayClient.execute(request);
|
||||
log.info("支付宝退款:{}", response.getBody());
|
||||
String fundChange = JSON.parseObject(response.getBody())
|
||||
.getJSONObject("alipay_trade_refund_response")
|
||||
.getString("fund_change");
|
||||
if (response.isSuccess()) {
|
||||
//TODO 沙箱环境接口不通
|
||||
return;
|
||||
//退款成功判断说明:接口返回fund_change=Y为退款成功,
|
||||
// fund_change=N或无此字段值返回时需通过退款查询接口进一步确认退款状态。
|
||||
return "Y".equals(fundChange);
|
||||
} else {
|
||||
//获取诊断链接
|
||||
String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
|
||||
log.warn("支付宝退款异常: {}", diagnosisUrl);
|
||||
return false;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("退款异常", e);
|
||||
|
|
@ -226,6 +237,43 @@ public class AliPaymentMangerImpl implements PaymentManager {
|
|||
throw new ServiceException("退款失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ENetResult queryStoreOrderRefundResult(String refundOrderNo, String originOrderNo) {
|
||||
Assert.notEmpty(refundOrderNo);
|
||||
Assert.notEmpty(originOrderNo);
|
||||
AlipayClient alipayClient = new DefaultAlipayClient(gatewayUrl, appId, privateKey, DEFAULT_FORMAT, charset,
|
||||
alipayPublicKey, signType);
|
||||
// 构造请求参数以调用接口
|
||||
AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
|
||||
AlipayTradeFastpayRefundQueryModel model = new AlipayTradeFastpayRefundQueryModel();
|
||||
model.setOutRequestNo(refundOrderNo);
|
||||
model.setOutTradeNo(originOrderNo);
|
||||
request.setBizModel(model);
|
||||
try {
|
||||
AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request);
|
||||
log.warn("查询支付宝订单退款结果: {}", response.getBody());
|
||||
if (response.isSuccess()) {
|
||||
String refundStatus = JSON.parseObject(response.getBody())
|
||||
.getJSONObject("alipay_trade_fastpay_refund_query_response")
|
||||
.getString("refund_status");
|
||||
if ("REFUND_SUCCESS".equals(refundStatus)) {
|
||||
return ENetResult.SUCCESS;
|
||||
}
|
||||
return ENetResult.FAILURE;
|
||||
} else {
|
||||
//获取诊断链接
|
||||
String diagnosisUrl = DiagnosisUtils.getDiagnosisUrl(response);
|
||||
log.error("查询支付宝订单退款果异常: {}", diagnosisUrl);
|
||||
return ENetResult.FAILURE;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("查询支付宝订单退款结果异常", e);
|
||||
}
|
||||
//告警
|
||||
fsNotice.sendMsg2DefaultChat("查询支付宝订单退款结果异常", JSON.toJSONString(model));
|
||||
throw new ServiceException("查询支付宝订单退款结果失败");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ENetResult queryStoreOrderPayResult(String orderNo) {
|
||||
Assert.notEmpty(orderNo);
|
||||
|
|
|
|||
|
|
@ -16,4 +16,6 @@ import java.util.List;
|
|||
public interface StoreOrderMapper extends BaseMapper<StoreOrder> {
|
||||
|
||||
List<StoreOrderPageItemDTO> listStoreOrderPageItem(StoreOrderQueryDTO queryDTO);
|
||||
|
||||
List<StoreOrder> listNeedContinueRefundOrder();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ package com.ruoyi.xkt.service;
|
|||
|
||||
import com.ruoyi.xkt.domain.AlipayCallback;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
* @date 2025-04-08 17:39
|
||||
|
|
@ -46,9 +48,17 @@ public interface IAlipayCallbackService {
|
|||
void noNeedProcess(AlipayCallback info);
|
||||
|
||||
/**
|
||||
* 继续处理回调
|
||||
* 获取需要继续处理的回调
|
||||
*
|
||||
* @param count
|
||||
* @return
|
||||
*/
|
||||
void continueProcess(int count);
|
||||
List<AlipayCallback> listNeedContinueProcessCallback(Integer count);
|
||||
|
||||
/**
|
||||
* 继续处理回调
|
||||
*
|
||||
* @param infoList
|
||||
*/
|
||||
void continueProcess(List<AlipayCallback> infoList);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@ import com.ruoyi.xkt.dto.finance.RechargeAddResult;
|
|||
import com.ruoyi.xkt.enums.EPayChannel;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
|
|
@ -120,4 +122,12 @@ public interface IAssetService {
|
|||
* @param amount
|
||||
*/
|
||||
void refundAdvertFee(Long storeId, BigDecimal amount);
|
||||
|
||||
/**
|
||||
* 获取需要继续处理的提现信息
|
||||
*
|
||||
* @param count
|
||||
* @return
|
||||
*/
|
||||
Map<EPayChannel, List<WithdrawPrepareResult>> getNeedContinueWithdrawGroupMap(Integer count);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
package com.ruoyi.xkt.service;
|
||||
|
||||
import com.ruoyi.xkt.domain.FinanceBill;
|
||||
import com.ruoyi.xkt.dto.finance.FinanceBillDTO;
|
||||
import com.ruoyi.xkt.dto.finance.FinanceBillExt;
|
||||
import com.ruoyi.xkt.dto.order.StoreOrderExt;
|
||||
import com.ruoyi.xkt.enums.EFinBillSrcType;
|
||||
import com.ruoyi.xkt.enums.EFinBillStatus;
|
||||
import com.ruoyi.xkt.enums.EFinBillType;
|
||||
import com.ruoyi.xkt.enums.EPayChannel;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
|
@ -113,5 +117,17 @@ public interface IFinanceBillService {
|
|||
FinanceBillExt createInternalTransferBill(Long inputAccountId, Long outputAccountId, BigDecimal amount,
|
||||
Integer srcType, Long srcId, Integer relType, Long relId, String remark);
|
||||
|
||||
/**
|
||||
* 单据列表
|
||||
*
|
||||
* @param billStatus
|
||||
* @param billType
|
||||
* @param billSrcType
|
||||
* @param count
|
||||
* @return
|
||||
*/
|
||||
List<FinanceBillDTO> listByStatus(EFinBillStatus billStatus, EFinBillType billType, EFinBillSrcType billSrcType,
|
||||
Integer count);
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -178,10 +178,19 @@ public interface IStoreOrderService {
|
|||
void addTrack(StoreOrderExpressTrackAddDTO trackAddDTO);
|
||||
|
||||
/**
|
||||
* 自动取消
|
||||
* 获取需要自动关闭的订单
|
||||
*
|
||||
* @param beforeDate
|
||||
* @param count
|
||||
* @return
|
||||
*/
|
||||
void autoCancel(Date beforeDate, int count);
|
||||
List<Long> listNeedAutoCloseOrder(Date beforeDate, Integer count);
|
||||
|
||||
/**
|
||||
* 获取需要继续退款的订单信息
|
||||
*
|
||||
* @param count
|
||||
* @return
|
||||
*/
|
||||
List<StoreOrderRefund> listNeedContinueRefundOrder(Integer count);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,13 +84,20 @@ public class AlipayCallbackServiceImpl implements IAlipayCallbackService {
|
|||
alipayCallbackMapper.updateById(info);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void continueProcess(int count) {
|
||||
PageHelper.startPage(1, count, false);
|
||||
public List<AlipayCallback> listNeedContinueProcessCallback(Integer count) {
|
||||
if (count != null) {
|
||||
PageHelper.startPage(1, count, false);
|
||||
}
|
||||
List<AlipayCallback> infoList = alipayCallbackMapper.selectList(Wrappers.lambdaQuery(AlipayCallback.class)
|
||||
.eq(AlipayCallback::getProcessStatus, EProcessStatus.INIT.getValue())
|
||||
.eq(SimpleEntity::getDelFlag, Constants.UNDELETED));
|
||||
return infoList;
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void continueProcess(List<AlipayCallback> infoList) {
|
||||
for (AlipayCallback info : infoList) {
|
||||
if (!"TRADE_SUCCESS".equals(info.getTradeStatus())) {
|
||||
//非交易支付成功的回调不处理
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.ruoyi.xkt.domain.ExternalAccount;
|
|||
import com.ruoyi.xkt.domain.FinanceBill;
|
||||
import com.ruoyi.xkt.domain.InternalAccount;
|
||||
import com.ruoyi.xkt.dto.account.*;
|
||||
import com.ruoyi.xkt.dto.finance.FinanceBillDTO;
|
||||
import com.ruoyi.xkt.dto.finance.FinanceBillExt;
|
||||
import com.ruoyi.xkt.dto.finance.RechargeAddDTO;
|
||||
import com.ruoyi.xkt.dto.finance.RechargeAddResult;
|
||||
|
|
@ -29,7 +30,10 @@ import org.springframework.stereotype.Service;
|
|||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author liangyq
|
||||
|
|
@ -242,6 +246,28 @@ public class AssetServiceImpl implements IAssetService {
|
|||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<EPayChannel, List<WithdrawPrepareResult>> getNeedContinueWithdrawGroupMap(Integer count) {
|
||||
//TODO 付款单据没有支付渠道和账户快照
|
||||
List<FinanceBillDTO> bills = financeBillService.listByStatus(EFinBillStatus.PROCESSING,
|
||||
EFinBillType.PAYMENT, EFinBillSrcType.WITHDRAW, count);
|
||||
List<WithdrawPrepareResult> results = new ArrayList<>(bills.size());
|
||||
for (FinanceBillDTO bill : bills) {
|
||||
Long storeId = bill.getSrcId();
|
||||
ExternalAccount externalAccount = externalAccountService.getAccountAndCheck(storeId,
|
||||
EAccountOwnerType.STORE, EAccountType.ALI_PAY);
|
||||
results.add(new WithdrawPrepareResult(bill.getId(),
|
||||
bill.getBillNo(),
|
||||
bill.getTransAmount(),
|
||||
externalAccount.getAccountOwnerNumber(),
|
||||
externalAccount.getAccountOwnerName(),
|
||||
externalAccount.getAccountOwnerPhoneNumber()));
|
||||
}
|
||||
Map<EPayChannel, List<WithdrawPrepareResult>> rtn = new HashMap<>(1);
|
||||
rtn.put(EPayChannel.ALI_PAY, results);
|
||||
return rtn;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据支付渠道匹配支付类
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.ruoyi.xkt.service.impl;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
|
|
@ -8,6 +9,7 @@ import cn.hutool.core.text.CharSequenceUtil;
|
|||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.github.pagehelper.PageHelper;
|
||||
import com.ruoyi.common.constant.Constants;
|
||||
import com.ruoyi.common.core.domain.SimpleEntity;
|
||||
import com.ruoyi.common.exception.ServiceException;
|
||||
|
|
@ -16,6 +18,7 @@ import com.ruoyi.xkt.domain.FinanceBill;
|
|||
import com.ruoyi.xkt.domain.FinanceBillDetail;
|
||||
import com.ruoyi.xkt.domain.InternalAccount;
|
||||
import com.ruoyi.xkt.domain.StoreOrderDetail;
|
||||
import com.ruoyi.xkt.dto.finance.FinanceBillDTO;
|
||||
import com.ruoyi.xkt.dto.finance.FinanceBillExt;
|
||||
import com.ruoyi.xkt.dto.finance.TransInfo;
|
||||
import com.ruoyi.xkt.dto.order.StoreOrderExt;
|
||||
|
|
@ -562,6 +565,20 @@ public class FinanceBillServiceImpl implements IFinanceBillService {
|
|||
return new FinanceBillExt(bill, ListUtil.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FinanceBillDTO> listByStatus(EFinBillStatus billStatus, EFinBillType billType,
|
||||
EFinBillSrcType billSrcType, Integer count) {
|
||||
if (count != null) {
|
||||
PageHelper.startPage(1, count, false);
|
||||
}
|
||||
List<FinanceBill> doList = financeBillMapper.selectList(Wrappers.lambdaQuery(FinanceBill.class)
|
||||
.eq(FinanceBill::getBillStatus, Optional.ofNullable(billStatus).map(EFinBillStatus::getValue).orElse(null))
|
||||
.eq(FinanceBill::getBillType, Optional.ofNullable(billType).map(EFinBillType::getValue).orElse(null))
|
||||
.eq(FinanceBill::getSrcType, Optional.ofNullable(billSrcType).map(EFinBillSrcType::getValue).orElse(null))
|
||||
.eq(FinanceBill::getDelFlag, Constants.UNDELETED));
|
||||
return BeanUtil.copyToList(doList, FinanceBillDTO.class);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成单号
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.ruoyi.xkt.service.impl;
|
|||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.text.CharSequenceUtil;
|
||||
|
|
@ -1259,46 +1260,45 @@ public class StoreOrderServiceImpl implements IStoreOrderService {
|
|||
expressService.addTrackRecord(expressTrackRecordAddDTO);
|
||||
}
|
||||
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
@Override
|
||||
public void autoCancel(Date beforeDate, int count) {
|
||||
PageHelper.startPage(1, count, false);
|
||||
public List<Long> listNeedAutoCloseOrder(Date beforeDate, Integer count) {
|
||||
if (count != null) {
|
||||
PageHelper.startPage(1, count, false);
|
||||
}
|
||||
List<StoreOrder> orders = storeOrderMapper.selectList(Wrappers.lambdaQuery(StoreOrder.class)
|
||||
.eq(StoreOrder::getOrderStatus, EOrderStatus.PENDING_PAYMENT.getValue())
|
||||
.ne(StoreOrder::getPayStatus, EPayStatus.PAID.getValue())
|
||||
.eq(SimpleEntity::getDelFlag, Constants.UNDELETED)
|
||||
.le(SimpleEntity::getCreateTime, beforeDate));
|
||||
for (StoreOrder order : orders) {
|
||||
PaymentManager paymentManager = getPaymentManager(EPayChannel.of(order.getPayChannel()));
|
||||
ENetResult result = paymentManager.queryStoreOrderPayResult(order.getOrderNo());
|
||||
if (ENetResult.SUCCESS == result) {
|
||||
log.warn("订单[{}]已支付,无法取消", order.getId());
|
||||
continue;
|
||||
}
|
||||
//订单已取消
|
||||
order.setOrderStatus(EOrderStatus.CANCELLED.getValue());
|
||||
int orderSuccess = storeOrderMapper.updateById(order);
|
||||
if (orderSuccess == 0) {
|
||||
throw new ServiceException(Constants.VERSION_LOCK_ERROR_COMMON_MSG);
|
||||
}
|
||||
List<StoreOrderDetail> orderDetails = storeOrderDetailMapper.selectList(
|
||||
Wrappers.lambdaQuery(StoreOrderDetail.class)
|
||||
.eq(StoreOrderDetail::getStoreOrderId, order.getId())
|
||||
.eq(SimpleEntity::getDelFlag, Constants.UNDELETED));
|
||||
List<Long> orderDetailIdList = new ArrayList<>(orderDetails.size());
|
||||
for (StoreOrderDetail orderDetail : orderDetails) {
|
||||
//明细已取消
|
||||
orderDetail.setDetailStatus(EOrderStatus.CANCELLED.getValue());
|
||||
int orderDetailSuccess = storeOrderDetailMapper.updateById(orderDetail);
|
||||
if (orderDetailSuccess == 0) {
|
||||
throw new ServiceException(Constants.VERSION_LOCK_ERROR_COMMON_MSG);
|
||||
}
|
||||
orderDetailIdList.add(orderDetail.getId());
|
||||
}
|
||||
//操作记录
|
||||
addOperationRecords(order.getId(), EOrderAction.CANCEL, orderDetailIdList, EOrderAction.CANCEL,
|
||||
"超时取消", null, new Date());
|
||||
return orders.stream().map(SimpleEntity::getId).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<StoreOrderRefund> listNeedContinueRefundOrder(Integer count) {
|
||||
if (count != null) {
|
||||
PageHelper.startPage(1, count, false);
|
||||
}
|
||||
List<StoreOrder> orders = storeOrderMapper.listNeedContinueRefundOrder();
|
||||
List<StoreOrderRefund> storeOrderRefunds = new ArrayList<>(orders.size());
|
||||
if (CollUtil.isEmpty(orders)) {
|
||||
return ListUtil.empty();
|
||||
}
|
||||
List<StoreOrderDetail> orderDetails = storeOrderDetailMapper.selectList(Wrappers
|
||||
.lambdaQuery(StoreOrderDetail.class)
|
||||
.in(StoreOrderDetail::getStoreOrderId, orders.stream().map(SimpleEntity::getId)
|
||||
.collect(Collectors.toSet()))
|
||||
.eq(SimpleEntity::getDelFlag, Constants.UNDELETED));
|
||||
Map<Long, List<StoreOrderDetail>> orderDetailMap = orderDetails.stream()
|
||||
.filter(o -> EOrderStatus.AFTER_SALE_COMPLETED.getValue().equals(o.getDetailStatus())
|
||||
&& EPayStatus.PAYING.getValue().equals(o.getPayStatus()))
|
||||
.collect(Collectors.groupingBy(StoreOrderDetail::getStoreOrderId));
|
||||
for (StoreOrder order : orders) {
|
||||
List<StoreOrderDetail> orderDetailList = orderDetailMap.get(order.getId());
|
||||
Assert.notEmpty(orderDetailList);
|
||||
storeOrderRefunds.add(new StoreOrderRefund(order, orderDetailList,
|
||||
getAndBaseCheck(order.getOriginOrderId())));
|
||||
}
|
||||
return storeOrderRefunds;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -50,4 +50,21 @@
|
|||
</if>
|
||||
</where>
|
||||
</select>
|
||||
<select id="listNeedContinueRefundOrder" resultType="com.ruoyi.xkt.domain.StoreOrder">
|
||||
SELECT
|
||||
so.*
|
||||
FROM
|
||||
store_order so
|
||||
WHERE
|
||||
so.del_flag = '0'
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM
|
||||
store_order_detail sod
|
||||
WHERE
|
||||
sod.store_order_id = so.id
|
||||
AND sod.del_flag = '0'
|
||||
AND sod.detail_status = 24
|
||||
AND sod.pay_status = 2
|
||||
)
|
||||
</select>
|
||||
</mapper>
|
||||
Loading…
Reference in New Issue