8.1 使用设计模式(责任链、策略模式、模板方法)优化退单流程

This commit is contained in:
zhangsan 2025-08-01 21:38:28 +08:00
parent 111f9e4181
commit 322f8d0f1e
15 changed files with 335 additions and 165 deletions

View File

@ -20,9 +20,9 @@
<insert id="insert" parameterType="edu.whut.infrastructure.dao.po.NotifyTask">
insert into notify_task(
activity_id, team_id, notify_type, notify_mq, notify_url, notify_count,
activity_id, team_id, notify_category, notify_type, notify_mq, notify_url, notify_count,
notify_status, parameter_json,uuid, create_time, update_time)
values(#{activityId}, #{teamId}, #{notifyType}, #{notifyMQ}, #{notifyUrl},
values(#{activityId}, #{teamId}, #{notifyCategory}, #{notifyType}, #{notifyMQ}, #{notifyUrl},
#{notifyCount}, #{notifyStatus}, #{parameterJson},#{uuid},now(),now())
</insert>
@ -42,19 +42,19 @@
<update id="updateNotifyTaskStatusSuccess" parameterType="java.lang.String">
update notify_task
set notify_count = notify_count + 1, notify_status = 1, update_time = now()
where team_id = #{teamId}
where team_id = #{teamId} and uuid = #{uuid}
</update>
<update id="updateNotifyTaskStatusError" parameterType="java.lang.String">
update notify_task
set notify_count = notify_count + 1, notify_status = 3, update_time = now()
where team_id = #{teamId}
where team_id = #{teamId} and uuid = #{uuid}
</update>
<update id="updateNotifyTaskStatusRetry" parameterType="java.lang.String">
update notify_task
set notify_count = notify_count + 1, notify_status = 2, update_time = now()
where team_id = #{teamId}
where team_id = #{teamId} and uuid = #{uuid}
</update>
</mapper>

View File

@ -19,6 +19,7 @@ import java.util.concurrent.CountDownLatch;
/**
* 锁单恢复锁单
* 测试退单流程先下3单再退一单再下一单
*/
@Slf4j
@RunWith(SpringRunner.class)
@ -34,8 +35,8 @@ public class ITradeReverseStockServiceTest {
@Test
public void test_refundOrder() throws Exception {
TradeRefundCommandEntity tradeRefundCommandEntity = TradeRefundCommandEntity.builder()
.userId("smile11")
.outTradeNo("690268736199")
.userId("smile21")
.outTradeNo("910878918962")
.source("s01")
.channel("c01")
.build();
@ -54,7 +55,7 @@ public class ITradeReverseStockServiceTest {
String teamId = null;
for (int i = 1; i < 4; i++) {
LockMarketPayOrderRequestDTO lockMarketPayOrderRequestDTO = new LockMarketPayOrderRequestDTO();
lockMarketPayOrderRequestDTO.setUserId("smile1" + i);
lockMarketPayOrderRequestDTO.setUserId("smile2" + i);
lockMarketPayOrderRequestDTO.setTeamId(teamId);
lockMarketPayOrderRequestDTO.setActivityId(100123L);
lockMarketPayOrderRequestDTO.setGoodsId("9890001");
@ -74,8 +75,8 @@ public class ITradeReverseStockServiceTest {
@Test
public void test_lockMarketPayOrder_reverse() throws InterruptedException {
LockMarketPayOrderRequestDTO lockMarketPayOrderRequestDTO = new LockMarketPayOrderRequestDTO();
lockMarketPayOrderRequestDTO.setUserId("smile14");
lockMarketPayOrderRequestDTO.setTeamId("56790750");
lockMarketPayOrderRequestDTO.setUserId("smile24");
lockMarketPayOrderRequestDTO.setTeamId("68803511");
lockMarketPayOrderRequestDTO.setActivityId(100123L);
lockMarketPayOrderRequestDTO.setGoodsId("9890001");
lockMarketPayOrderRequestDTO.setSource("s01");

View File

@ -36,11 +36,11 @@ public interface ITradeRepository {
List<NotifyTaskEntity> queryUnExecutedNotifyTaskList(String teamId);
int updateNotifyTaskStatusSuccess(String teamId);
int updateNotifyTaskStatusSuccess(NotifyTaskEntity notifyTaskEntity);
int updateNotifyTaskStatusError(String teamId);
int updateNotifyTaskStatusError(NotifyTaskEntity notifyTaskEntity);
int updateNotifyTaskStatusRetry(String teamId);
int updateNotifyTaskStatusRetry(NotifyTaskEntity notifyTaskEntity);
boolean occupyTeamStock(String teamStockKey, String recoveryTeamStockKey, Integer target, Integer validTime);

View File

@ -7,11 +7,14 @@ import edu.whut.domain.trade.model.valobj.TeamRefundSuccess;
import edu.whut.domain.trade.model.valobj.TradeOrderStatusEnumVO;
import edu.whut.domain.trade.service.ITradeRefundOrderService;
import edu.whut.domain.trade.service.refund.business.IRefundOrderStrategy;
import edu.whut.domain.trade.service.refund.factory.TradeRefundRuleFilterFactory;
import edu.whut.types.design.framework.link.model2.chain.BusinessLinkedList;
import edu.whut.types.enums.GroupBuyOrderStatusEnumVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;
/**
@ -22,53 +25,24 @@ import java.util.Map;
@RequiredArgsConstructor
public class TradeRefundOrderService implements ITradeRefundOrderService {
private final ITradeRepository repository;
//注入策略Map
private final Map<String, IRefundOrderStrategy> refundOrderStrategyMap;
private final BusinessLinkedList<TradeRefundCommandEntity, TradeRefundRuleFilterFactory.DynamicContext, TradeRefundBehaviorEntity> tradeRefundRuleFilter;
/**
* 用户点击退单主动触发=更新数据库操锁单量完成量退款拼团状态订单状态...
*/
@Override
public TradeRefundBehaviorEntity refundOrder(TradeRefundCommandEntity tradeRefundCommandEntity) throws Exception {
log.info("逆向流程,退单操作 userId:{} outTradeNo:{}", tradeRefundCommandEntity.getUserId(), tradeRefundCommandEntity.getOutTradeNo());
// 1. 查询外部交易单组队idorderId拼团状态
MarketPayOrderEntity marketPayOrderEntity = repository.queryMarketPayOrderEntityByOutTradeNo(tradeRefundCommandEntity.getUserId(), tradeRefundCommandEntity.getOutTradeNo());
TradeOrderStatusEnumVO tradeOrderStatusEnumVO = marketPayOrderEntity.getTradeOrderStatusEnumVO();
String teamId = marketPayOrderEntity.getTeamId();
String orderId = marketPayOrderEntity.getOrderId();
// 返回幂等已完成退单
if (TradeOrderStatusEnumVO.CLOSE.equals(tradeOrderStatusEnumVO)) {
log.info("逆向流程,退单操作(幂等-重复退单) userId:{} outTradeNo:{}", tradeRefundCommandEntity.getUserId(), tradeRefundCommandEntity.getOutTradeNo());
return TradeRefundBehaviorEntity.builder()
.userId(tradeRefundCommandEntity.getUserId())
.orderId(orderId)
.teamId(teamId)
.tradeRefundBehaviorEnum(TradeRefundBehaviorEntity.TradeRefundBehaviorEnum.REPEAT)
.build();
}
// 2. 查询拼团状态
GroupBuyTeamEntity groupBuyTeamEntity = repository.queryGroupBuyTeamByTeamId(teamId);
GroupBuyOrderStatusEnumVO groupBuyOrderStatusEnumVO = groupBuyTeamEntity.getStatus();
// 3. 根据拼团状态和交易状态判断退单类型 -> 判断使用哪种策略模式
RefundTypeEnumVO refundType = RefundTypeEnumVO.getRefundStrategy(groupBuyOrderStatusEnumVO, tradeOrderStatusEnumVO);
IRefundOrderStrategy refundOrderStrategy = refundOrderStrategyMap.get(refundType.getStrategy());
refundOrderStrategy.refundOrder(TradeRefundOrderEntity.builder()
.userId(tradeRefundCommandEntity.getUserId())
.orderId(orderId)
.teamId(teamId)
.activityId(groupBuyTeamEntity.getActivityId())
.build());
return TradeRefundBehaviorEntity.builder()
.userId(tradeRefundCommandEntity.getUserId())
.orderId(orderId)
.teamId(teamId)
.tradeRefundBehaviorEnum(TradeRefundBehaviorEntity.TradeRefundBehaviorEnum.SUCCESS)
.build();
return tradeRefundRuleFilter.apply(tradeRefundCommandEntity, new TradeRefundRuleFilterFactory.DynamicContext());
}
/**
* 退单成功后消息监听触发-恢复锁单量
*/
@Override
public void restoreTeamLockStock(TeamRefundSuccess teamRefundSuccess) throws Exception {
log.info("逆向流程,恢复锁单量 userId:{} activityId:{} teamId:{}", teamRefundSuccess.getUserId(), teamRefundSuccess.getActivityId(), teamRefundSuccess.getTeamId());

View File

@ -0,0 +1,68 @@
package edu.whut.domain.trade.service.refund.business;
import com.alibaba.fastjson.JSON;
import edu.whut.domain.trade.adapter.repository.ITradeRepository;
import edu.whut.domain.trade.model.entity.NotifyTaskEntity;
import edu.whut.domain.trade.model.valobj.TeamRefundSuccess;
import edu.whut.domain.trade.service.ITradeTaskService;
import edu.whut.domain.trade.service.lock.factory.TradeLockRuleFilterFactory;
import edu.whut.types.exception.AppException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 退单策略抽象基类
* 提供共用的依赖注入和MQ消息发送功能
*/
@Slf4j
@RequiredArgsConstructor
public abstract class AbstractRefundOrderStrategy implements IRefundOrderStrategy {
@Resource
protected ITradeRepository repository;
@Resource
protected ITradeTaskService tradeTaskService;
@Resource
protected ThreadPoolExecutor threadPoolExecutor;
/**
* 异步发送MQ消息
* @param notifyTaskEntity 通知任务实体
* @param refundType 退单类型描述
*/
protected void sendRefundNotifyMessage(NotifyTaskEntity notifyTaskEntity, String refundType) {
if (null != notifyTaskEntity) {
threadPoolExecutor.execute(() -> {
Map<String, Integer> notifyResultMap = null;
try {
notifyResultMap = tradeTaskService.execNotifyJob(notifyTaskEntity);
log.info("回调通知交易退单({}) result:{}", refundType, JSON.toJSONString(notifyResultMap));
} catch (Exception e) {
log.error("回调通知交易退单失败({}) result:{}", refundType, JSON.toJSONString(notifyResultMap), e);
throw new AppException(e.getMessage());
}
});
}
}
/**
* 通用库存恢复逻辑
* @param teamRefundSuccess 团队退单成功信息
* @param refundType 退单类型描述
* @throws Exception 异常
*/
protected void doReverseStock(TeamRefundSuccess teamRefundSuccess, String refundType) throws Exception {
log.info("退单;恢复锁单量 - {} {} {} {}", refundType, teamRefundSuccess.getUserId(), teamRefundSuccess.getActivityId(), teamRefundSuccess.getTeamId());
// 1. 恢复库存key
String recoveryTeamStockKey = TradeLockRuleFilterFactory.generateRecoveryTeamStockKey(teamRefundSuccess.getActivityId(), teamRefundSuccess.getTeamId());
// 2. 退单恢复库存
repository.refund2AddRecovery(recoveryTeamStockKey, teamRefundSuccess.getOrderId());
}
}

View File

@ -1,21 +1,12 @@
package edu.whut.domain.trade.service.refund.business.impl;
import com.alibaba.fastjson.JSON;
import edu.whut.domain.trade.adapter.port.ITradePort;
import edu.whut.domain.trade.adapter.repository.ITradeRepository;
import edu.whut.domain.trade.model.aggregate.GroupBuyRefundAggregate;
import edu.whut.domain.trade.model.entity.NotifyTaskEntity;
import edu.whut.domain.trade.model.entity.TradeRefundOrderEntity;
import edu.whut.domain.trade.model.valobj.TeamRefundSuccess;
import edu.whut.domain.trade.service.ITradeTaskService;
import edu.whut.domain.trade.service.lock.factory.TradeLockRuleFilterFactory;
import edu.whut.domain.trade.service.refund.business.IRefundOrderStrategy;
import edu.whut.types.exception.AppException;
import edu.whut.domain.trade.service.refund.business.AbstractRefundOrderStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 发起退单未成团&已支付锁单量-1完成量-1组队订单状态更新发送退单消息MQ
@ -23,13 +14,7 @@ import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
@Service("paid2RefundStrategy")
@RequiredArgsConstructor
public class Paid2RefundStrategy implements IRefundOrderStrategy {
private final ITradeRepository repository;
private final ITradeTaskService tradeTaskService;
private final ThreadPoolExecutor threadPoolExecutor;
public class Paid2RefundStrategy extends AbstractRefundOrderStrategy {
@Override
public void refundOrder(TradeRefundOrderEntity tradeRefundOrderEntity) throws Exception {
@ -38,27 +23,12 @@ public class Paid2RefundStrategy implements IRefundOrderStrategy {
// 1. 退单已支付&未成团
NotifyTaskEntity notifyTaskEntity = repository.paid2Refund(GroupBuyRefundAggregate.buildPaid2RefundAggregate(tradeRefundOrderEntity, -1, -1));
// 2. 发送MQ消息
if (null != notifyTaskEntity) {
threadPoolExecutor.execute(() -> {
Map<String, Integer> notifyResultMap = null;
try {
notifyResultMap = tradeTaskService.execNotifyJob(notifyTaskEntity);
log.info("回调通知交易退单(已支付,未成团) result:{}", JSON.toJSONString(notifyResultMap));
} catch (Exception e) {
log.error("回调通知交易退单失败(已支付,未成团) result:{}", JSON.toJSONString(notifyResultMap), e);
throw new AppException(e.getMessage());
}
});
}
// 2. 发送MQ消息 - 发送MQ恢复锁单库存量使用
sendRefundNotifyMessage(notifyTaskEntity, "已支付,未成团");
}
@Override
public void reverseStock(TeamRefundSuccess teamRefundSuccess) throws Exception {
log.info("退单;恢复锁单量 - 已支付,未成团,但有锁单记录,要恢复锁单库存 {} {} {}", teamRefundSuccess.getUserId(), teamRefundSuccess.getActivityId(), teamRefundSuccess.getTeamId());
// 1. 恢复库存key
String recoveryTeamStockKey = TradeLockRuleFilterFactory.generateRecoveryTeamStockKey(teamRefundSuccess.getActivityId(), teamRefundSuccess.getTeamId());
// 2. 退单恢复已支付未成团有锁单记录要恢复锁单库存
repository.refund2AddRecovery(recoveryTeamStockKey, teamRefundSuccess.getOrderId());
doReverseStock(teamRefundSuccess, "已支付,未成团,但有锁单记录,要恢复锁单库存");
}
}

View File

@ -1,21 +1,15 @@
package edu.whut.domain.trade.service.refund.business.impl;
import com.alibaba.fastjson.JSON;
import edu.whut.domain.trade.adapter.repository.ITradeRepository;
import edu.whut.domain.trade.model.aggregate.GroupBuyRefundAggregate;
import edu.whut.domain.trade.model.entity.GroupBuyTeamEntity;
import edu.whut.domain.trade.model.entity.NotifyTaskEntity;
import edu.whut.domain.trade.model.entity.TradeRefundOrderEntity;
import edu.whut.domain.trade.model.valobj.TeamRefundSuccess;
import edu.whut.domain.trade.service.ITradeTaskService;
import edu.whut.domain.trade.service.refund.business.IRefundOrderStrategy;
import edu.whut.domain.trade.service.refund.business.AbstractRefundOrderStrategy;
import edu.whut.types.enums.GroupBuyOrderStatusEnumVO;
import edu.whut.types.exception.AppException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 发起退单已成团&已支付锁单量-1完成量-1组队订单状态更新发送退单消息MQ
@ -23,41 +17,23 @@ import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
@Service("paidTeam2RefundStrategy")
@RequiredArgsConstructor
public class PaidTeam2RefundStrategy implements IRefundOrderStrategy {
private final ITradeRepository repository;
private final ITradeTaskService tradeTaskService;
private final ThreadPoolExecutor threadPoolExecutor;
public class PaidTeam2RefundStrategy extends AbstractRefundOrderStrategy {
@Override
public void refundOrder(TradeRefundOrderEntity tradeRefundOrderEntity) {
log.info("退单;已支付,已成团 userId:{} teamId:{} orderId:{}", tradeRefundOrderEntity.getUserId(), tradeRefundOrderEntity.getTeamId(), tradeRefundOrderEntity.getOrderId());
GroupBuyTeamEntity groupBuyTeamEntity = repository.queryGroupBuyTeamByTeamId(tradeRefundOrderEntity.getTeamId());
// 当前拼团完成量
Integer completeCount = groupBuyTeamEntity.getCompleteCount();
// 该拼团中最后一个用户也要退单则更新拼团订单为失败
// 最后一笔也退单则更新拼团订单为失败
GroupBuyOrderStatusEnumVO groupBuyOrderEnumVO = 1 == completeCount ? GroupBuyOrderStatusEnumVO.FAIL : GroupBuyOrderStatusEnumVO.COMPLETE_FAIL;
// 1. 退单已支付&已成团
NotifyTaskEntity notifyTaskEntity = repository.paidTeam2Refund(GroupBuyRefundAggregate.buildPaidTeam2RefundAggregate(tradeRefundOrderEntity, -1, -1, groupBuyOrderEnumVO));
// 2. 发送MQ消息
if (null != notifyTaskEntity) {
threadPoolExecutor.execute(() -> {
Map<String, Integer> notifyResultMap = null;
try {
notifyResultMap = tradeTaskService.execNotifyJob(notifyTaskEntity);
log.info("回调通知交易退单(已支付,已成团) result:{}", JSON.toJSONString(notifyResultMap));
} catch (Exception e) {
log.error("回调通知交易退单失败(已支付,已成团) result:{}", JSON.toJSONString(notifyResultMap), e);
throw new AppException(e.getMessage());
}
});
}
// 2. 发送MQ消息 - 发送MQ恢复锁单库存量使用
sendRefundNotifyMessage(notifyTaskEntity, "已支付,已成团");
}
@Override
public void reverseStock(TeamRefundSuccess teamRefundSuccess) throws Exception {

View File

@ -1,22 +1,14 @@
package edu.whut.domain.trade.service.refund.business.impl;
import com.alibaba.fastjson.JSON;
import edu.whut.domain.trade.adapter.repository.ITradeRepository;
import edu.whut.domain.trade.model.aggregate.GroupBuyRefundAggregate;
import edu.whut.domain.trade.model.entity.NotifyTaskEntity;
import edu.whut.domain.trade.model.entity.TradeRefundOrderEntity;
import edu.whut.domain.trade.model.valobj.TeamRefundSuccess;
import edu.whut.domain.trade.service.ITradeTaskService;
import edu.whut.domain.trade.service.lock.factory.TradeLockRuleFilterFactory;
import edu.whut.domain.trade.service.refund.business.IRefundOrderStrategy;
import edu.whut.types.exception.AppException;
import edu.whut.domain.trade.service.refund.business.AbstractRefundOrderStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 未支付未成团发起退单未支付锁单量-1组队订单状态更新
@ -24,13 +16,7 @@ import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
@Service("unpaid2RefundStrategy")
@RequiredArgsConstructor
public class Unpaid2RefundStrategy implements IRefundOrderStrategy {
private final ITradeRepository repository;
private final ITradeTaskService tradeTaskService;
private final ThreadPoolExecutor threadPoolExecutor;
public class Unpaid2RefundStrategy extends AbstractRefundOrderStrategy {
/**
* 用户未支付的退单流程仅需更新订单状态为已退单释放lockcount锁单量
@ -42,27 +28,12 @@ public class Unpaid2RefundStrategy implements IRefundOrderStrategy {
NotifyTaskEntity notifyTaskEntity = repository.unpaid2Refund(GroupBuyRefundAggregate.buildUnpaid2RefundAggregate(tradeRefundOrderEntity, -1));
// 2. 发送MQ消息 - 发送MQ恢复锁单库存量使用
if (null != notifyTaskEntity) {
threadPoolExecutor.execute(() -> {
Map<String, Integer> notifyResultMap = null;
try {
notifyResultMap = tradeTaskService.execNotifyJob(notifyTaskEntity);
log.info("回调通知交易退单(未支付,未成团) result:{}", JSON.toJSONString(notifyResultMap));
} catch (Exception e) {
log.error("回调通知交易退单失败(未支付,未成团) result:{}", JSON.toJSONString(notifyResultMap), e);
throw new AppException(e.getMessage());
}
});
}
sendRefundNotifyMessage(notifyTaskEntity, "未支付,未成团");
}
@Override
public void reverseStock(TeamRefundSuccess teamRefundSuccess) throws Exception {
log.info("退单;恢复锁单量 - 未支付,未成团,但有锁单记录,要恢复锁单库存 {} {} {}", teamRefundSuccess.getUserId(), teamRefundSuccess.getActivityId(), teamRefundSuccess.getTeamId());
// 1. 恢复库存key
String recoveryTeamStockKey = TradeLockRuleFilterFactory.generateRecoveryTeamStockKey(teamRefundSuccess.getActivityId(), teamRefundSuccess.getTeamId());
// 2. 退单恢复未支付未成团但有锁单记录要恢复锁单库存
repository.refund2AddRecovery(recoveryTeamStockKey, teamRefundSuccess.getOrderId());
doReverseStock(teamRefundSuccess, "未支付,未成团,但有锁单记录,要恢复锁单库存");
}
}

View File

@ -0,0 +1,55 @@
package edu.whut.domain.trade.service.refund.factory;
import edu.whut.domain.trade.model.entity.GroupBuyTeamEntity;
import edu.whut.domain.trade.model.entity.MarketPayOrderEntity;
import edu.whut.domain.trade.model.entity.TradeRefundBehaviorEntity;
import edu.whut.domain.trade.model.entity.TradeRefundCommandEntity;
import edu.whut.domain.trade.service.refund.filter.DataNodeFilter;
import edu.whut.domain.trade.service.refund.filter.RefundOrderNodeFilter;
import edu.whut.domain.trade.service.refund.filter.UniqueRefundNodeFilter;
import edu.whut.types.design.framework.link.model2.LinkArmory;
import edu.whut.types.design.framework.link.model2.chain.BusinessLinkedList;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
/**
* 交易退单工程
*/
@Slf4j
@Service
public class TradeRefundRuleFilterFactory {
@Bean("tradeRefundRuleFilter")
public BusinessLinkedList<TradeRefundCommandEntity, DynamicContext, TradeRefundBehaviorEntity> tradeRefundRuleFilter(
DataNodeFilter dataNodeFilter,
UniqueRefundNodeFilter uniqueRefundNodeFilter,
RefundOrderNodeFilter refundOrderNodeFilter) {
// 组装链
LinkArmory<TradeRefundCommandEntity, DynamicContext, TradeRefundBehaviorEntity> linkArmory =
new LinkArmory<>("退单规则过滤链",
dataNodeFilter,
uniqueRefundNodeFilter,
refundOrderNodeFilter);
// 链对象
return linkArmory.getLogicLink();
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public static class DynamicContext {
private MarketPayOrderEntity marketPayOrderEntity;
private GroupBuyTeamEntity groupBuyTeamEntity;
}
}

View File

@ -0,0 +1,45 @@
package edu.whut.domain.trade.service.refund.filter;
import edu.whut.domain.trade.adapter.repository.ITradeRepository;
import edu.whut.domain.trade.model.entity.GroupBuyTeamEntity;
import edu.whut.domain.trade.model.entity.MarketPayOrderEntity;
import edu.whut.domain.trade.model.entity.TradeRefundBehaviorEntity;
import edu.whut.domain.trade.model.entity.TradeRefundCommandEntity;
import edu.whut.domain.trade.service.refund.factory.TradeRefundRuleFilterFactory;
import edu.whut.types.design.framework.link.model2.handler.ILogicHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* 数据节点
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class DataNodeFilter implements ILogicHandler<TradeRefundCommandEntity, TradeRefundRuleFilterFactory.DynamicContext, TradeRefundBehaviorEntity> {
private final ITradeRepository repository;
@Override
public TradeRefundBehaviorEntity apply(TradeRefundCommandEntity tradeRefundCommandEntity, TradeRefundRuleFilterFactory.DynamicContext dynamicContext) throws Exception {
log.info("逆向流程-退单操作,数据加载节点 userId:{} outTradeNo:{}", tradeRefundCommandEntity.getUserId(), tradeRefundCommandEntity.getOutTradeNo());
// 1. 查询外部交易单组队idorderId拼团状态
MarketPayOrderEntity marketPayOrderEntity = repository.queryMarketPayOrderEntityByOutTradeNo(tradeRefundCommandEntity.getUserId(), tradeRefundCommandEntity.getOutTradeNo());
String teamId = marketPayOrderEntity.getTeamId();
// 2. 查询拼团状态
GroupBuyTeamEntity groupBuyTeamEntity = repository.queryGroupBuyTeamByTeamId(teamId);
// 3. 写入上下文如果查询数据是比较多的可以参考 MarketNode2CompletableFuture 通过多线程进行加载
dynamicContext.setMarketPayOrderEntity(marketPayOrderEntity);
dynamicContext.setGroupBuyTeamEntity(groupBuyTeamEntity);
return next(tradeRefundCommandEntity, dynamicContext);
}
}

View File

@ -0,0 +1,58 @@
package edu.whut.domain.trade.service.refund.filter;
import edu.whut.domain.trade.model.entity.*;
import edu.whut.domain.trade.model.valobj.RefundTypeEnumVO;
import edu.whut.domain.trade.model.valobj.TradeOrderStatusEnumVO;
import edu.whut.domain.trade.service.refund.business.IRefundOrderStrategy;
import edu.whut.domain.trade.service.refund.factory.TradeRefundRuleFilterFactory;
import edu.whut.types.design.framework.link.model2.handler.ILogicHandler;
import edu.whut.types.enums.GroupBuyOrderStatusEnumVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Map;
/**
* 退单节点
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class RefundOrderNodeFilter implements ILogicHandler<TradeRefundCommandEntity, TradeRefundRuleFilterFactory.DynamicContext, TradeRefundBehaviorEntity> {
//依赖注入策略选择map
private final Map<String, IRefundOrderStrategy> refundOrderStrategyMap;
@Override
public TradeRefundBehaviorEntity apply(TradeRefundCommandEntity tradeRefundCommandEntity, TradeRefundRuleFilterFactory.DynamicContext dynamicContext) throws Exception {
log.info("逆向流程-退单操作,退单策略处理 userId:{} outTradeNo:{}", tradeRefundCommandEntity.getUserId(), tradeRefundCommandEntity.getOutTradeNo());
// 上下文数据
MarketPayOrderEntity marketPayOrderEntity = dynamicContext.getMarketPayOrderEntity();
TradeOrderStatusEnumVO tradeOrderStatusEnumVO = marketPayOrderEntity.getTradeOrderStatusEnumVO();
GroupBuyTeamEntity groupBuyTeamEntity = dynamicContext.getGroupBuyTeamEntity();
GroupBuyOrderStatusEnumVO groupBuyOrderEnumVO = groupBuyTeamEntity.getStatus();
// 获取执行策略
RefundTypeEnumVO refundType = RefundTypeEnumVO.getRefundStrategy(groupBuyOrderEnumVO, tradeOrderStatusEnumVO);
IRefundOrderStrategy refundOrderStrategy = refundOrderStrategyMap.get(refundType.getStrategy());
// 执行退单操作
refundOrderStrategy.refundOrder(TradeRefundOrderEntity.builder()
.userId(tradeRefundCommandEntity.getUserId())
.orderId(marketPayOrderEntity.getOrderId())
.teamId(marketPayOrderEntity.getTeamId())
.activityId(groupBuyTeamEntity.getActivityId())
.build());
return TradeRefundBehaviorEntity.builder()
.userId(tradeRefundCommandEntity.getUserId())
.orderId(marketPayOrderEntity.getOrderId())
.teamId(marketPayOrderEntity.getTeamId())
.tradeRefundBehaviorEnum(TradeRefundBehaviorEntity.TradeRefundBehaviorEnum.SUCCESS)
.build();
}
}

View File

@ -0,0 +1,40 @@
package edu.whut.domain.trade.service.refund.filter;
import edu.whut.domain.trade.model.entity.MarketPayOrderEntity;
import edu.whut.domain.trade.model.entity.TradeRefundBehaviorEntity;
import edu.whut.domain.trade.model.entity.TradeRefundCommandEntity;
import edu.whut.domain.trade.model.valobj.TradeOrderStatusEnumVO;
import edu.whut.domain.trade.service.refund.factory.TradeRefundRuleFilterFactory;
import edu.whut.types.design.framework.link.model2.handler.ILogicHandler;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* 重复退单检查
*/
@Slf4j
@Service
public class UniqueRefundNodeFilter implements ILogicHandler<TradeRefundCommandEntity, TradeRefundRuleFilterFactory.DynamicContext, TradeRefundBehaviorEntity> {
@Override
public TradeRefundBehaviorEntity apply(TradeRefundCommandEntity tradeRefundCommandEntity, TradeRefundRuleFilterFactory.DynamicContext dynamicContext) throws Exception {
log.info("逆向流程-退单操作,重复退单检查 userId:{} outTradeNo:{}", tradeRefundCommandEntity.getUserId(), tradeRefundCommandEntity.getOutTradeNo());
MarketPayOrderEntity marketPayOrderEntity = dynamicContext.getMarketPayOrderEntity();
TradeOrderStatusEnumVO tradeOrderStatusEnumVO = marketPayOrderEntity.getTradeOrderStatusEnumVO();
// 返回幂等已完成退单
if (TradeOrderStatusEnumVO.CLOSE.equals(tradeOrderStatusEnumVO)) {
log.info("逆向流程,退单操作(幂等-重复退单) userId:{} outTradeNo:{}", tradeRefundCommandEntity.getUserId(), tradeRefundCommandEntity.getOutTradeNo());
return TradeRefundBehaviorEntity.builder()
.userId(tradeRefundCommandEntity.getUserId())
.orderId(marketPayOrderEntity.getOrderId())
.teamId(marketPayOrderEntity.getTeamId())
.tradeRefundBehaviorEnum(TradeRefundBehaviorEntity.TradeRefundBehaviorEnum.REPEAT)
.build();
}
return next(tradeRefundCommandEntity, dynamicContext);
}
}

View File

@ -64,8 +64,7 @@ public class TradeTaskService implements ITradeTaskService {
/**
* 公共逻辑抽取遍历任务列表调用外部服务更新数据库并计数
* @param notifyTaskEntityList 待处理的通知任务列表
* @return key任务总量successCounterrorCountretryCount
* 拼团成功消息 or 退单消息
*/
private Map<String, Integer> execNotifyJob(List<NotifyTaskEntity> notifyTaskEntityList) throws Exception {
//successCount:成功回调的任务数量
@ -76,19 +75,19 @@ public class TradeTaskService implements ITradeTaskService {
// 更新状态判断&变更数据库表回调任务状态
if (NotifyTaskHTTPEnumVO.SUCCESS.getCode().equals(response)) {
int updateCount = repository.updateNotifyTaskStatusSuccess(notifyTask.getTeamId());
int updateCount = repository.updateNotifyTaskStatusSuccess(notifyTask);
if (1 == updateCount) {
successCount += 1;
}
} else if (NotifyTaskHTTPEnumVO.ERROR.getCode().equals(response)) {
if (notifyTask.getNotifyCount() < 5) {
// 失败但可以重试 标记为 RETRY等待下一次收集 待处理的通知任务列表
if (repository.updateNotifyTaskStatusRetry(notifyTask.getTeamId()) == 1) {
if (repository.updateNotifyTaskStatusRetry(notifyTask) == 1) {
retryCount++;
}
} else {
// 已达最大重试次数 标记为 ERROR不再重试
if (repository.updateNotifyTaskStatusError(notifyTask.getTeamId()) == 1) {
if (repository.updateNotifyTaskStatusError(notifyTask) == 1) {
errorCount++;
}
}

View File

@ -309,8 +309,8 @@ public class TradeRepository implements ITradeRepository {
NotifyTask task = new NotifyTask();
task.setActivityId(team.getActivityId());
task.setTeamId(team.getTeamId());
task.setNotifyCategory(TaskNotifyCategoryEnumVO.TRADE_SETTLEMENT.getCode());
task.setNotifyType(notifyConfigVO.getNotifyType().getCode());
task.setNotifyCategory(TaskNotifyCategoryEnumVO.TRADE_SETTLEMENT.getCode()); //拼团成团消息
task.setNotifyType(notifyConfigVO.getNotifyType().getCode()); //HTTP or MQ
task.setNotifyMQ(NotifyTypeEnumVO.MQ.equals(notifyConfigVO.getNotifyType()) ? notifyConfigVO.getNotifyMQ() : null);
task.setNotifyUrl(NotifyTypeEnumVO.HTTP.equals(notifyConfigVO.getNotifyType()) ? notifyConfigVO.getNotifyUrl() : null);
task.setNotifyCount(0);
@ -386,6 +386,7 @@ public class TradeRepository implements ITradeRepository {
.notifyUrl(notifyTask.getNotifyUrl())
.notifyCount(notifyTask.getNotifyCount())
.parameterJson(notifyTask.getParameterJson())
.uuid(notifyTask.getUuid())
.build());
}
@ -393,24 +394,36 @@ public class TradeRepository implements ITradeRepository {
* 更新指定 teamId 的通知任务状态为成功
*/
@Override
public int updateNotifyTaskStatusSuccess(String teamId) {
return notifyTaskDao.updateNotifyTaskStatusSuccess(teamId);
public int updateNotifyTaskStatusSuccess(NotifyTaskEntity notifyTaskEntity) {
NotifyTask notifyTask = NotifyTask.builder()
.teamId(notifyTaskEntity.getTeamId())
.uuid(notifyTaskEntity.getUuid())
.build();
return notifyTaskDao.updateNotifyTaskStatusSuccess(notifyTask);
}
/**
* 更新指定 teamId 的通知任务状态为失败不可重试
*/
@Override
public int updateNotifyTaskStatusError(String teamId) {
return notifyTaskDao.updateNotifyTaskStatusError(teamId);
public int updateNotifyTaskStatusError(NotifyTaskEntity notifyTaskEntity) {
NotifyTask notifyTask = NotifyTask.builder()
.teamId(notifyTaskEntity.getTeamId())
.uuid(notifyTaskEntity.getUuid())
.build();
return notifyTaskDao.updateNotifyTaskStatusError(notifyTask);
}
/**
* 更新指定 teamId 的通知任务状态为重试
*/
@Override
public int updateNotifyTaskStatusRetry(String teamId) {
return notifyTaskDao.updateNotifyTaskStatusRetry(teamId);
public int updateNotifyTaskStatusRetry(NotifyTaskEntity notifyTaskEntity) {
NotifyTask notifyTask = NotifyTask.builder()
.teamId(notifyTaskEntity.getTeamId())
.uuid(notifyTaskEntity.getUuid())
.build();
return notifyTaskDao.updateNotifyTaskStatusRetry(notifyTask);
}
/**

View File

@ -16,10 +16,10 @@ public interface INotifyTaskDao {
NotifyTask queryUnExecutedNotifyTaskByTeamId(String teamId);
int updateNotifyTaskStatusSuccess(String teamId);
int updateNotifyTaskStatusSuccess(NotifyTask notifyTask);
int updateNotifyTaskStatusError(String teamId);
int updateNotifyTaskStatusError(NotifyTask notifyTask);
int updateNotifyTaskStatusRetry(String teamId);
int updateNotifyTaskStatusRetry(NotifyTask notifyTask);
}